weechat/src/core/hook/wee-hook-process.c

975 lines
31 KiB
C

/*
* wee-hook-process.c - WeeChat process hook
*
* Copyright (C) 2003-2018 Sébastien Helleu <flashcode@flashtux.org>
*
* This file is part of WeeChat, the extensible chat client.
*
* WeeChat is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* WeeChat is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with WeeChat. If not, see <https://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include <poll.h>
#include <fcntl.h>
#include <errno.h>
#include "../weechat.h"
#include "../wee-hashtable.h"
#include "../wee-hook.h"
#include "../wee-infolist.h"
#include "../wee-log.h"
#include "../wee-string.h"
#include "../wee-url.h"
#include "../../gui/gui-chat.h"
#include "../../plugins/plugin.h"
int hook_process_pending = 0; /* 1 if there are some process to */
/* run (via fork) */
void hook_process_run (struct t_hook *hook_process);
/*
* Hooks a process (using fork) with options in hashtable.
*
* Returns pointer to new hook, NULL if error.
*/
struct t_hook *
hook_process_hashtable (struct t_weechat_plugin *plugin,
const char *command,
struct t_hashtable *options,
int timeout,
t_hook_callback_process *callback,
const void *callback_pointer,
void *callback_data)
{
struct t_hook *new_hook;
struct t_hook_process *new_hook_process;
char *stdout_buffer, *stderr_buffer, *error;
const char *ptr_value;
long number;
stdout_buffer = NULL;
stderr_buffer = NULL;
new_hook = NULL;
new_hook_process = NULL;
if (!command || !command[0] || !callback)
goto error;
stdout_buffer = malloc (HOOK_PROCESS_BUFFER_SIZE + 1);
if (!stdout_buffer)
goto error;
stderr_buffer = malloc (HOOK_PROCESS_BUFFER_SIZE + 1);
if (!stderr_buffer)
goto error;
new_hook = malloc (sizeof (*new_hook));
if (!new_hook)
goto error;
new_hook_process = malloc (sizeof (*new_hook_process));
if (!new_hook_process)
goto error;
hook_init_data (new_hook, plugin, HOOK_TYPE_PROCESS, HOOK_PRIORITY_DEFAULT,
callback_pointer, callback_data);
new_hook->hook_data = new_hook_process;
new_hook_process->callback = callback;
new_hook_process->command = strdup (command);
new_hook_process->options = (options) ? hashtable_dup (options) : NULL;
new_hook_process->detached = (options && hashtable_has_key (options,
"detached"));
new_hook_process->timeout = timeout;
new_hook_process->child_read[HOOK_PROCESS_STDIN] = -1;
new_hook_process->child_read[HOOK_PROCESS_STDOUT] = -1;
new_hook_process->child_read[HOOK_PROCESS_STDERR] = -1;
new_hook_process->child_write[HOOK_PROCESS_STDIN] = -1;
new_hook_process->child_write[HOOK_PROCESS_STDOUT] = -1;
new_hook_process->child_write[HOOK_PROCESS_STDERR] = -1;
new_hook_process->child_pid = 0;
new_hook_process->hook_fd[HOOK_PROCESS_STDIN] = NULL;
new_hook_process->hook_fd[HOOK_PROCESS_STDOUT] = NULL;
new_hook_process->hook_fd[HOOK_PROCESS_STDERR] = NULL;
new_hook_process->hook_timer = NULL;
new_hook_process->buffer[HOOK_PROCESS_STDIN] = NULL;
new_hook_process->buffer[HOOK_PROCESS_STDOUT] = stdout_buffer;
new_hook_process->buffer[HOOK_PROCESS_STDERR] = stderr_buffer;
new_hook_process->buffer_size[HOOK_PROCESS_STDIN] = 0;
new_hook_process->buffer_size[HOOK_PROCESS_STDOUT] = 0;
new_hook_process->buffer_size[HOOK_PROCESS_STDERR] = 0;
new_hook_process->buffer_flush = HOOK_PROCESS_BUFFER_SIZE;
if (options)
{
ptr_value = hashtable_get (options, "buffer_flush");
if (ptr_value && ptr_value[0])
{
number = strtol (ptr_value, &error, 10);
if (error && !error[0]
&& (number >= 1) && (number <= HOOK_PROCESS_BUFFER_SIZE))
{
new_hook_process->buffer_flush = (int)number;
}
}
}
hook_add_to_list (new_hook);
if (weechat_debug_core >= 1)
{
gui_chat_printf (NULL,
"debug: hook_process: command=\"%s\", "
"options=\"%s\", timeout=%d",
new_hook_process->command,
hashtable_get_string (new_hook_process->options,
"keys_values"),
new_hook_process->timeout);
}
if (strncmp (new_hook_process->command, "func:", 5) == 0)
hook_process_pending = 1;
else
hook_process_run (new_hook);
return new_hook;
error:
if (stdout_buffer)
free (stdout_buffer);
if (stderr_buffer)
free (stderr_buffer);
if (new_hook)
free (new_hook);
if (new_hook_process)
free (new_hook_process);
return NULL;
}
/*
* Hooks a process (using fork).
*
* Returns pointer to new hook, NULL if error.
*/
struct t_hook *
hook_process (struct t_weechat_plugin *plugin,
const char *command,
int timeout,
t_hook_callback_process *callback,
const void *callback_pointer,
void *callback_data)
{
return hook_process_hashtable (plugin, command, NULL, timeout,
callback, callback_pointer, callback_data);
}
/*
* Child process for hook process: executes command and returns string result
* into pipe for WeeChat process.
*/
void
hook_process_child (struct t_hook *hook_process)
{
char **exec_args, *arg0, str_arg[64];
const char *ptr_url, *ptr_arg;
int rc, i, num_args;
FILE *f;
/* read stdin from parent, if a pipe was defined */
if (HOOK_PROCESS(hook_process, child_read[HOOK_PROCESS_STDIN]) >= 0)
{
if (dup2 (HOOK_PROCESS(hook_process, child_read[HOOK_PROCESS_STDIN]),
STDIN_FILENO) < 0)
{
_exit (EXIT_FAILURE);
}
}
else
{
/* no stdin pipe from parent, use "/dev/null" for stdin stream */
f = freopen ("/dev/null", "r", stdin);
(void) f;
}
if (HOOK_PROCESS(hook_process, child_write[HOOK_PROCESS_STDIN]) >= 0)
close (HOOK_PROCESS(hook_process, child_write[HOOK_PROCESS_STDIN]));
/* redirect stdout/stderr to pipe (so that parent process can read them) */
if (HOOK_PROCESS(hook_process, child_read[HOOK_PROCESS_STDOUT]) >= 0)
{
close (HOOK_PROCESS(hook_process, child_read[HOOK_PROCESS_STDOUT]));
if (dup2 (HOOK_PROCESS(hook_process, child_write[HOOK_PROCESS_STDOUT]),
STDOUT_FILENO) < 0)
{
_exit (EXIT_FAILURE);
}
}
else
{
/* detached mode: write stdout in /dev/null */
f = freopen ("/dev/null", "w", stdout);
(void) f;
}
if (HOOK_PROCESS(hook_process, child_read[HOOK_PROCESS_STDERR]) >= 0)
{
close (HOOK_PROCESS(hook_process, child_read[HOOK_PROCESS_STDERR]));
if (dup2 (HOOK_PROCESS(hook_process, child_write[HOOK_PROCESS_STDERR]),
STDERR_FILENO) < 0)
{
_exit (EXIT_FAILURE);
}
}
else
{
/* detached mode: write stderr in /dev/null */
f = freopen ("/dev/null", "w", stderr);
(void) f;
}
rc = EXIT_FAILURE;
if (strncmp (HOOK_PROCESS(hook_process, command), "url:", 4) == 0)
{
/* get URL output (on stdout or file, depending on options) */
ptr_url = HOOK_PROCESS(hook_process, command) + 4;
while (ptr_url[0] == ' ')
{
ptr_url++;
}
rc = weeurl_download (ptr_url, HOOK_PROCESS(hook_process, options));
}
else if (strncmp (HOOK_PROCESS(hook_process, command), "func:", 5) == 0)
{
/* run a function (via the hook callback) */
rc = (int) (HOOK_PROCESS(hook_process, callback))
(hook_process->callback_pointer,
hook_process->callback_data,
HOOK_PROCESS(hook_process, command),
WEECHAT_HOOK_PROCESS_CHILD,
NULL, NULL);
}
else
{
/* launch command */
num_args = 0;
if (HOOK_PROCESS(hook_process, options))
{
/*
* count number of arguments given in the hashtable options,
* keys are: "arg1", "arg2", ...
*/
while (1)
{
snprintf (str_arg, sizeof (str_arg), "arg%d", num_args + 1);
ptr_arg = hashtable_get (HOOK_PROCESS(hook_process, options),
str_arg);
if (!ptr_arg)
break;
num_args++;
}
}
if (num_args > 0)
{
/*
* if at least one argument was found in hashtable option, the
* "command" contains only path to binary (without arguments), and
* the arguments are in hashtable
*/
exec_args = malloc ((num_args + 2) * sizeof (exec_args[0]));
if (exec_args)
{
exec_args[0] = strdup (HOOK_PROCESS(hook_process, command));
for (i = 1; i <= num_args; i++)
{
snprintf (str_arg, sizeof (str_arg), "arg%d", i);
ptr_arg = hashtable_get (HOOK_PROCESS(hook_process, options),
str_arg);
exec_args[i] = (ptr_arg) ? strdup (ptr_arg) : NULL;
}
exec_args[num_args + 1] = NULL;
}
}
else
{
/*
* if no arguments were found in hashtable, make an automatic split
* of command, like the shell does
*/
exec_args = string_split_shell (HOOK_PROCESS(hook_process, command),
NULL);
}
if (exec_args)
{
arg0 = string_expand_home (exec_args[0]);
if (arg0)
{
free (exec_args[0]);
exec_args[0] = arg0;
}
if (weechat_debug_core >= 1)
{
log_printf ("hook_process, command='%s'",
HOOK_PROCESS(hook_process, command));
for (i = 0; exec_args[i]; i++)
{
log_printf (" args[%02d] == '%s'", i, exec_args[i]);
}
}
execvp (exec_args[0], exec_args);
}
/* should not be executed if execvp was OK */
if (exec_args)
string_free_split (exec_args);
fprintf (stderr, "Error with command '%s'\n",
HOOK_PROCESS(hook_process, command));
}
fflush (stdout);
fflush (stderr);
_exit (rc);
}
/*
* Sends buffers (stdout/stderr) to callback.
*/
void
hook_process_send_buffers (struct t_hook *hook_process, int callback_rc)
{
int size;
/* add '\0' at end of stdout and stderr */
size = HOOK_PROCESS(hook_process, buffer_size[HOOK_PROCESS_STDOUT]);
if (size > 0)
HOOK_PROCESS(hook_process, buffer[HOOK_PROCESS_STDOUT])[size] = '\0';
size = HOOK_PROCESS(hook_process, buffer_size[HOOK_PROCESS_STDERR]);
if (size > 0)
HOOK_PROCESS(hook_process, buffer[HOOK_PROCESS_STDERR])[size] = '\0';
/* send buffers to callback */
(void) (HOOK_PROCESS(hook_process, callback))
(hook_process->callback_pointer,
hook_process->callback_data,
HOOK_PROCESS(hook_process, command),
callback_rc,
(HOOK_PROCESS(hook_process, buffer_size[HOOK_PROCESS_STDOUT]) > 0) ?
HOOK_PROCESS(hook_process, buffer[HOOK_PROCESS_STDOUT]) : NULL,
(HOOK_PROCESS(hook_process, buffer_size[HOOK_PROCESS_STDERR]) > 0) ?
HOOK_PROCESS(hook_process, buffer[HOOK_PROCESS_STDERR]) : NULL);
/* reset size for stdout and stderr */
HOOK_PROCESS(hook_process, buffer_size[HOOK_PROCESS_STDOUT]) = 0;
HOOK_PROCESS(hook_process, buffer_size[HOOK_PROCESS_STDERR]) = 0;
}
/*
* Adds some data to buffer (stdout or stderr).
*/
void
hook_process_add_to_buffer (struct t_hook *hook_process, int index_buffer,
const char *buffer, int size)
{
if (HOOK_PROCESS(hook_process, buffer_size[index_buffer]) + size > HOOK_PROCESS_BUFFER_SIZE)
hook_process_send_buffers (hook_process, WEECHAT_HOOK_PROCESS_RUNNING);
memcpy (HOOK_PROCESS(hook_process, buffer[index_buffer]) +
HOOK_PROCESS(hook_process, buffer_size[index_buffer]),
buffer, size);
HOOK_PROCESS(hook_process, buffer_size[index_buffer]) += size;
}
/*
* Reads process output (stdout or stderr) from child process.
*/
void
hook_process_child_read (struct t_hook *hook_process, int fd,
int index_buffer, struct t_hook **hook_fd)
{
char buffer[HOOK_PROCESS_BUFFER_SIZE / 8];
int num_read;
if (hook_process->deleted)
return;
num_read = read (fd, buffer, sizeof (buffer) - 1);
if (num_read > 0)
{
hook_process_add_to_buffer (hook_process, index_buffer,
buffer, num_read);
if (HOOK_PROCESS(hook_process, buffer_size[index_buffer]) >=
HOOK_PROCESS(hook_process, buffer_flush))
{
hook_process_send_buffers (hook_process,
WEECHAT_HOOK_PROCESS_RUNNING);
}
}
else if (num_read == 0)
{
unhook (*hook_fd);
*hook_fd = NULL;
}
}
/*
* Reads process output (stdout) from child process.
*/
int
hook_process_child_read_stdout_cb (const void *pointer, void *data, int fd)
{
struct t_hook *hook_process;
/* make C compiler happy */
(void) data;
hook_process = (struct t_hook *)pointer;
hook_process_child_read (hook_process, fd, HOOK_PROCESS_STDOUT,
&(HOOK_PROCESS(hook_process, hook_fd[HOOK_PROCESS_STDOUT])));
return WEECHAT_RC_OK;
}
/*
* Reads process output (stderr) from child process.
*/
int
hook_process_child_read_stderr_cb (const void *pointer, void *data, int fd)
{
struct t_hook *hook_process;
/* make C compiler happy */
(void) data;
hook_process = (struct t_hook *)pointer;
hook_process_child_read (hook_process, fd, HOOK_PROCESS_STDERR,
&(HOOK_PROCESS(hook_process, hook_fd[HOOK_PROCESS_STDERR])));
return WEECHAT_RC_OK;
}
/*
* Reads process output from child process until EOF
* (called when the child process has ended).
*/
void
hook_process_child_read_until_eof (struct t_hook *hook_process)
{
struct pollfd poll_fd[2];
int count, fd_stdout, fd_stderr, num_fd, ready, i;
fd_stdout = HOOK_PROCESS(hook_process, child_read[HOOK_PROCESS_STDOUT]);
fd_stderr = HOOK_PROCESS(hook_process, child_read[HOOK_PROCESS_STDERR]);
/*
* use a counter to prevent any infinite loop
* (if child's output is very very long...)
*/
count = 0;
while (count < 1024)
{
num_fd = 0;
if (HOOK_PROCESS(hook_process, hook_fd[HOOK_PROCESS_STDOUT])
&& ((fcntl (fd_stdout, F_GETFD) != -1) || (errno != EBADF)))
{
poll_fd[num_fd].fd = fd_stdout;
poll_fd[num_fd].events = POLLIN;
poll_fd[num_fd].revents = 0;
num_fd++;
}
if (HOOK_PROCESS(hook_process, hook_fd[HOOK_PROCESS_STDERR])
&& ((fcntl (fd_stderr, F_GETFD) != -1) || (errno != EBADF)))
{
poll_fd[num_fd].fd = fd_stderr;
poll_fd[num_fd].events = POLLIN;
poll_fd[num_fd].revents = 0;
num_fd++;
}
if (num_fd == 0)
break;
ready = poll (poll_fd, num_fd, 0);
if (ready <= 0)
break;
for (i = 0; i < num_fd; i++)
{
if (poll_fd[i].revents & POLLIN)
{
if (poll_fd[i].fd == fd_stdout)
{
(void) hook_process_child_read_stdout_cb (
hook_process,
NULL,
HOOK_PROCESS(hook_process,
child_read[HOOK_PROCESS_STDOUT]));
}
else
{
(void) hook_process_child_read_stderr_cb (
hook_process,
NULL,
HOOK_PROCESS(hook_process,
child_read[HOOK_PROCESS_STDERR]));
}
}
}
count++;
}
}
/*
* Checks if child process is still alive.
*/
int
hook_process_timer_cb (const void *pointer, void *data, int remaining_calls)
{
struct t_hook *hook_process;
int status, rc;
/* make C compiler happy */
(void) data;
(void) remaining_calls;
hook_process = (struct t_hook *)pointer;
if (hook_process->deleted)
return WEECHAT_RC_OK;
if (remaining_calls == 0)
{
hook_process_send_buffers (hook_process, WEECHAT_HOOK_PROCESS_ERROR);
if (weechat_debug_core >= 1)
{
gui_chat_printf (NULL,
_("End of command '%s', timeout reached (%.1fs)"),
HOOK_PROCESS(hook_process, command),
((float)HOOK_PROCESS(hook_process, timeout)) / 1000);
}
kill (HOOK_PROCESS(hook_process, child_pid), SIGKILL);
usleep (1000);
unhook (hook_process);
}
else
{
if (waitpid (HOOK_PROCESS(hook_process, child_pid),
&status, WNOHANG) > 0)
{
if (WIFEXITED(status))
{
/* child terminated normally */
rc = WEXITSTATUS(status);
hook_process_child_read_until_eof (hook_process);
hook_process_send_buffers (hook_process, rc);
unhook (hook_process);
}
else if (WIFSIGNALED(status))
{
/* child terminated by a signal */
hook_process_child_read_until_eof (hook_process);
hook_process_send_buffers (hook_process,
WEECHAT_HOOK_PROCESS_ERROR);
unhook (hook_process);
}
}
}
return WEECHAT_RC_OK;
}
/*
* Executes process command in child, and read data in current process,
* with fd hook.
*/
void
hook_process_run (struct t_hook *hook_process)
{
int pipes[3][2], timeout, max_calls, rc, i;
char str_error[1024];
long interval;
pid_t pid;
for (i = 0; i < 3; i++)
{
pipes[i][0] = -1;
pipes[i][1] = -1;
}
/* create pipe for stdin (only if stdin was given in options) */
if (HOOK_PROCESS(hook_process, options)
&& hashtable_has_key (HOOK_PROCESS(hook_process, options), "stdin"))
{
if (pipe (pipes[HOOK_PROCESS_STDIN]) < 0)
goto error;
}
/* create pipes for stdout/err (if not running in detached mode) */
if (!HOOK_PROCESS(hook_process, detached))
{
if (pipe (pipes[HOOK_PROCESS_STDOUT]) < 0)
goto error;
if (pipe (pipes[HOOK_PROCESS_STDERR]) < 0)
goto error;
}
/* assign pipes to variables in hook */
for (i = 0; i < 3; i++)
{
HOOK_PROCESS(hook_process, child_read[i]) = pipes[i][0];
HOOK_PROCESS(hook_process, child_write[i]) = pipes[i][1];
}
/* fork */
switch (pid = fork ())
{
/* fork failed */
case -1:
snprintf (str_error, sizeof (str_error),
"fork error: %s",
strerror (errno));
(void) (HOOK_PROCESS(hook_process, callback))
(hook_process->callback_pointer,
hook_process->callback_data,
HOOK_PROCESS(hook_process, command),
WEECHAT_HOOK_PROCESS_ERROR,
NULL, str_error);
unhook (hook_process);
return;
/* child process */
case 0:
rc = setuid (getuid ());
(void) rc;
hook_process_child (hook_process);
/* never executed */
_exit (EXIT_SUCCESS);
break;
}
/* parent process */
HOOK_PROCESS(hook_process, child_pid) = pid;
if (HOOK_PROCESS(hook_process, child_read[HOOK_PROCESS_STDIN]) >= 0)
{
close (HOOK_PROCESS(hook_process, child_read[HOOK_PROCESS_STDIN]));
HOOK_PROCESS(hook_process, child_read[HOOK_PROCESS_STDIN]) = -1;
}
if (HOOK_PROCESS(hook_process, child_read[HOOK_PROCESS_STDOUT]) >= 0)
{
close (HOOK_PROCESS(hook_process, child_write[HOOK_PROCESS_STDOUT]));
HOOK_PROCESS(hook_process, child_write[HOOK_PROCESS_STDOUT]) = -1;
}
if (HOOK_PROCESS(hook_process, child_read[HOOK_PROCESS_STDERR]) >= 0)
{
close (HOOK_PROCESS(hook_process, child_write[HOOK_PROCESS_STDERR]));
HOOK_PROCESS(hook_process, child_write[HOOK_PROCESS_STDERR]) = -1;
}
if (HOOK_PROCESS(hook_process, child_read[HOOK_PROCESS_STDOUT]) >= 0)
{
HOOK_PROCESS(hook_process, hook_fd[HOOK_PROCESS_STDOUT]) =
hook_fd (hook_process->plugin,
HOOK_PROCESS(hook_process, child_read[HOOK_PROCESS_STDOUT]),
1, 0, 0,
&hook_process_child_read_stdout_cb,
hook_process, NULL);
}
if (HOOK_PROCESS(hook_process, child_read[HOOK_PROCESS_STDERR]) >= 0)
{
HOOK_PROCESS(hook_process, hook_fd[HOOK_PROCESS_STDERR]) =
hook_fd (hook_process->plugin,
HOOK_PROCESS(hook_process, child_read[HOOK_PROCESS_STDERR]),
1, 0, 0,
&hook_process_child_read_stderr_cb,
hook_process, NULL);
}
timeout = HOOK_PROCESS(hook_process, timeout);
interval = 100;
max_calls = 0;
if (timeout > 0)
{
if (timeout <= 100)
{
interval = timeout;
max_calls = 1;
}
else
{
interval = 100;
max_calls = timeout / 100;
if (timeout % 100 == 0)
max_calls++;
}
}
HOOK_PROCESS(hook_process, hook_timer) = hook_timer (hook_process->plugin,
interval, 0, max_calls,
&hook_process_timer_cb,
hook_process,
NULL);
return;
error:
for (i = 0; i < 3; i++)
{
if (pipes[i][0] >= 0)
close (pipes[i][0]);
if (pipes[i][1] >= 0)
close (pipes[i][1]);
}
(void) (HOOK_PROCESS(hook_process, callback))
(hook_process->callback_pointer,
hook_process->callback_data,
HOOK_PROCESS(hook_process, command),
WEECHAT_HOOK_PROCESS_ERROR,
NULL, NULL);
unhook (hook_process);
}
/*
* Executes all process commands pending.
*/
void
hook_process_exec ()
{
struct t_hook *ptr_hook, *next_hook;
hook_exec_start ();
ptr_hook = weechat_hooks[HOOK_TYPE_PROCESS];
while (ptr_hook)
{
next_hook = ptr_hook->next_hook;
if (!ptr_hook->deleted
&& !ptr_hook->running
&& (HOOK_PROCESS(ptr_hook, child_pid) == 0))
{
ptr_hook->running = 1;
hook_process_run (ptr_hook);
ptr_hook->running = 0;
}
ptr_hook = next_hook;
}
hook_exec_end ();
hook_process_pending = 0;
}
/*
* Frees data in a process hook.
*/
void
hook_process_free_data (struct t_hook *hook)
{
if (!hook || !hook->hook_data)
return;
if (HOOK_PROCESS(hook, command))
{
free (HOOK_PROCESS(hook, command));
HOOK_PROCESS(hook, command) = NULL;
}
if (HOOK_PROCESS(hook, options))
{
hashtable_free (HOOK_PROCESS(hook, options));
HOOK_PROCESS(hook, options) = NULL;
}
if (HOOK_PROCESS(hook, hook_fd[HOOK_PROCESS_STDIN]))
{
unhook (HOOK_PROCESS(hook, hook_fd[HOOK_PROCESS_STDIN]));
HOOK_PROCESS(hook, hook_fd[HOOK_PROCESS_STDIN]) = NULL;
}
if (HOOK_PROCESS(hook, hook_fd[HOOK_PROCESS_STDOUT]))
{
unhook (HOOK_PROCESS(hook, hook_fd[HOOK_PROCESS_STDOUT]));
HOOK_PROCESS(hook, hook_fd[HOOK_PROCESS_STDOUT]) = NULL;
}
if (HOOK_PROCESS(hook, hook_fd[HOOK_PROCESS_STDERR]))
{
unhook (HOOK_PROCESS(hook, hook_fd[HOOK_PROCESS_STDERR]));
HOOK_PROCESS(hook, hook_fd[HOOK_PROCESS_STDERR]) = NULL;
}
if (HOOK_PROCESS(hook, hook_timer))
{
unhook (HOOK_PROCESS(hook, hook_timer));
HOOK_PROCESS(hook, hook_timer) = NULL;
}
if (HOOK_PROCESS(hook, child_pid) > 0)
{
kill (HOOK_PROCESS(hook, child_pid), SIGKILL);
waitpid (HOOK_PROCESS(hook, child_pid), NULL, 0);
HOOK_PROCESS(hook, child_pid) = 0;
}
if (HOOK_PROCESS(hook, child_read[HOOK_PROCESS_STDIN]) != -1)
{
close (HOOK_PROCESS(hook, child_read[HOOK_PROCESS_STDIN]));
HOOK_PROCESS(hook, child_read[HOOK_PROCESS_STDIN]) = -1;
}
if (HOOK_PROCESS(hook, child_write[HOOK_PROCESS_STDIN]) != -1)
{
close (HOOK_PROCESS(hook, child_write[HOOK_PROCESS_STDIN]));
HOOK_PROCESS(hook, child_write[HOOK_PROCESS_STDIN]) = -1;
}
if (HOOK_PROCESS(hook, child_read[HOOK_PROCESS_STDOUT]) != -1)
{
close (HOOK_PROCESS(hook, child_read[HOOK_PROCESS_STDOUT]));
HOOK_PROCESS(hook, child_read[HOOK_PROCESS_STDOUT]) = -1;
}
if (HOOK_PROCESS(hook, child_write[HOOK_PROCESS_STDOUT]) != -1)
{
close (HOOK_PROCESS(hook, child_write[HOOK_PROCESS_STDOUT]));
HOOK_PROCESS(hook, child_write[HOOK_PROCESS_STDOUT]) = -1;
}
if (HOOK_PROCESS(hook, child_read[HOOK_PROCESS_STDERR]) != -1)
{
close (HOOK_PROCESS(hook, child_read[HOOK_PROCESS_STDERR]));
HOOK_PROCESS(hook, child_read[HOOK_PROCESS_STDERR]) = -1;
}
if (HOOK_PROCESS(hook, child_write[HOOK_PROCESS_STDERR]) != -1)
{
close (HOOK_PROCESS(hook, child_write[HOOK_PROCESS_STDERR]));
HOOK_PROCESS(hook, child_write[HOOK_PROCESS_STDERR]) = -1;
}
if (HOOK_PROCESS(hook, buffer[HOOK_PROCESS_STDIN]))
{
free (HOOK_PROCESS(hook, buffer[HOOK_PROCESS_STDIN]));
HOOK_PROCESS(hook, buffer[HOOK_PROCESS_STDIN]) = NULL;
}
if (HOOK_PROCESS(hook, buffer[HOOK_PROCESS_STDOUT]))
{
free (HOOK_PROCESS(hook, buffer[HOOK_PROCESS_STDOUT]));
HOOK_PROCESS(hook, buffer[HOOK_PROCESS_STDOUT]) = NULL;
}
if (HOOK_PROCESS(hook, buffer[HOOK_PROCESS_STDERR]))
{
free (HOOK_PROCESS(hook, buffer[HOOK_PROCESS_STDERR]));
HOOK_PROCESS(hook, buffer[HOOK_PROCESS_STDERR]) = NULL;
}
free (hook->hook_data);
hook->hook_data = NULL;
}
/*
* Adds process hook data in the infolist item.
*
* Returns:
* 1: OK
* 0: error
*/
int
hook_process_add_to_infolist (struct t_infolist_item *item,
struct t_hook *hook)
{
if (!item || !hook || !hook->hook_data)
return 0;
if (!infolist_new_var_pointer (item, "callback", HOOK_PROCESS(hook, callback)))
return 0;
if (!infolist_new_var_string (item, "command", HOOK_PROCESS(hook, command)))
return 0;
if (!infolist_new_var_string (item, "options", hashtable_get_string (HOOK_PROCESS(hook, options), "keys_values")))
return 0;
if (!infolist_new_var_integer (item, "detached", HOOK_PROCESS(hook, detached)))
return 0;
if (!infolist_new_var_integer (item, "timeout", (int)(HOOK_PROCESS(hook, timeout))))
return 0;
if (!infolist_new_var_integer (item, "child_read_stdin", HOOK_PROCESS(hook, child_read[HOOK_PROCESS_STDIN])))
return 0;
if (!infolist_new_var_integer (item, "child_write_stdin", HOOK_PROCESS(hook, child_write[HOOK_PROCESS_STDIN])))
return 0;
if (!infolist_new_var_integer (item, "child_read_stdout", HOOK_PROCESS(hook, child_read[HOOK_PROCESS_STDOUT])))
return 0;
if (!infolist_new_var_integer (item, "child_write_stdout", HOOK_PROCESS(hook, child_write[HOOK_PROCESS_STDOUT])))
return 0;
if (!infolist_new_var_integer (item, "child_read_stderr", HOOK_PROCESS(hook, child_read[HOOK_PROCESS_STDERR])))
return 0;
if (!infolist_new_var_integer (item, "child_write_stderr", HOOK_PROCESS(hook, child_write[HOOK_PROCESS_STDERR])))
return 0;
if (!infolist_new_var_integer (item, "child_pid", HOOK_PROCESS(hook, child_pid)))
return 0;
if (!infolist_new_var_pointer (item, "hook_fd_stdin", HOOK_PROCESS(hook, hook_fd[HOOK_PROCESS_STDIN])))
return 0;
if (!infolist_new_var_pointer (item, "hook_fd_stdout", HOOK_PROCESS(hook, hook_fd[HOOK_PROCESS_STDOUT])))
return 0;
if (!infolist_new_var_pointer (item, "hook_fd_stderr", HOOK_PROCESS(hook, hook_fd[HOOK_PROCESS_STDERR])))
return 0;
if (!infolist_new_var_pointer (item, "hook_timer", HOOK_PROCESS(hook, hook_timer)))
return 0;
return 1;
}
/*
* Prints process hook data in WeeChat log file (usually for crash dump).
*/
void
hook_process_print_log (struct t_hook *hook)
{
if (!hook || !hook->hook_data)
return;
log_printf (" process data:");
log_printf (" callback. . . . . . . : 0x%lx", HOOK_PROCESS(hook, callback));
log_printf (" command . . . . . . . : '%s'", HOOK_PROCESS(hook, command));
log_printf (" options . . . . . . . : 0x%lx (hashtable: '%s')",
HOOK_PROCESS(hook, options),
hashtable_get_string (HOOK_PROCESS(hook, options),
"keys_values"));
log_printf (" detached. . . . . . . : %d", HOOK_PROCESS(hook, detached));
log_printf (" timeout . . . . . . . : %ld", HOOK_PROCESS(hook, timeout));
log_printf (" child_read[stdin] . . : %d", HOOK_PROCESS(hook, child_read[HOOK_PROCESS_STDIN]));
log_printf (" child_write[stdin]. . : %d", HOOK_PROCESS(hook, child_write[HOOK_PROCESS_STDIN]));
log_printf (" child_read[stdout]. . : %d", HOOK_PROCESS(hook, child_read[HOOK_PROCESS_STDOUT]));
log_printf (" child_write[stdout] . : %d", HOOK_PROCESS(hook, child_write[HOOK_PROCESS_STDOUT]));
log_printf (" child_read[stderr]. . : %d", HOOK_PROCESS(hook, child_read[HOOK_PROCESS_STDERR]));
log_printf (" child_write[stderr] . : %d", HOOK_PROCESS(hook, child_write[HOOK_PROCESS_STDERR]));
log_printf (" child_pid . . . . . . : %d", HOOK_PROCESS(hook, child_pid));
log_printf (" hook_fd[stdin]. . . . : 0x%lx", HOOK_PROCESS(hook, hook_fd[HOOK_PROCESS_STDIN]));
log_printf (" hook_fd[stdout] . . . : 0x%lx", HOOK_PROCESS(hook, hook_fd[HOOK_PROCESS_STDOUT]));
log_printf (" hook_fd[stderr] . . . : 0x%lx", HOOK_PROCESS(hook, hook_fd[HOOK_PROCESS_STDERR]));
log_printf (" hook_timer. . . . . . : 0x%lx", HOOK_PROCESS(hook, hook_timer));
}