summaryrefslogtreecommitdiff
path: root/src/core/hook/wee-hook-process.c
diff options
context:
space:
mode:
authorSébastien Helleu <flashcode@flashtux.org>2018-08-14 07:15:39 +0200
committerSébastien Helleu <flashcode@flashtux.org>2018-08-14 07:15:39 +0200
commit49c3e6210666f271d1c87115c091b54e4f35ae53 (patch)
treeb8adea435b71bdcc1d2d46aec967590c5cf419c8 /src/core/hook/wee-hook-process.c
parent42be1a74a036bb0318201a40d91eadc7d9d6454f (diff)
downloadweechat-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.c974
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));
+}