diff options
author | Sébastien Helleu <flashcode@flashtux.org> | 2018-08-14 07:15:39 +0200 |
---|---|---|
committer | Sébastien Helleu <flashcode@flashtux.org> | 2018-08-14 07:15:39 +0200 |
commit | 49c3e6210666f271d1c87115c091b54e4f35ae53 (patch) | |
tree | b8adea435b71bdcc1d2d46aec967590c5cf419c8 /src/core/hook/wee-hook-process.c | |
parent | 42be1a74a036bb0318201a40d91eadc7d9d6454f (diff) | |
download | weechat-49c3e6210666f271d1c87115c091b54e4f35ae53.zip |
core: split wee-hook.c into multiple sources
Diffstat (limited to 'src/core/hook/wee-hook-process.c')
-rw-r--r-- | src/core/hook/wee-hook-process.c | 974 |
1 files changed, 974 insertions, 0 deletions
diff --git a/src/core/hook/wee-hook-process.c b/src/core/hook/wee-hook-process.c new file mode 100644 index 000000000..c3ac87ce4 --- /dev/null +++ b/src/core/hook/wee-hook-process.c @@ -0,0 +1,974 @@ +/* + * 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 <http://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)); +} |