1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 /* gnome-vfs-process.c - Unified method for executing external processes.
4 Copyright (C) 1999 Free Software Foundation
6 The Gnome Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public License as
8 published by the Free Software Foundation; either version 2 of the
9 License, or (at your option) any later version.
11 The Gnome Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Library General Public License for more details.
16 You should have received a copy of the GNU Library General Public
17 License along with the Gnome Library; see the file COPYING.LIB. If not,
18 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA.
21 Author: Ettore Perazzoli <ettore@gnu.org>
24 /* WARNING: This is *NOT* MT-safe at all. It is designed to call all processes
25 from the main thread exclusively. But for now this is fine, because we are
26 only using this module internally. */
29 #include "gnome-vfs-process.h"
31 #include "gnome-vfs-private-utils.h"
33 #include <glib/gconvert.h> /* workaround for giochannel.h bug */
34 #include <glib/ghash.h>
35 #include <glib/giochannel.h>
36 #include <glib/gstrfuncs.h>
37 #include <glib/gmessages.h>
38 #include <glib/gmem.h>
41 #include <sys/types.h>
45 /* A launched process. */
46 struct _GnomeVFSProcess {
48 GnomeVFSProcessCallback callback;
49 gpointer callback_data;
52 /* Have we been initialized yet? */
53 static gboolean initialized = FALSE;
55 /* Table to get a pointer to a GnomeVFSProcess struct from a PID value. */
56 static GHashTable *pid_to_process = NULL;
58 /* Input channel for waking up the main loop whenever a SIGCHLD is received,
59 and call our SIGCHLD handling callback. */
60 static GIOChannel *wake_up_channel_in = NULL;
62 /* The output side of the previous channel. We use low-level I/O in the signal
63 handler instead of `g_io_*' stuff. */
64 static volatile gint wake_up_channel_out_fd = -1;
66 /* The sigaction we had before installing the SIGCHLD handler. */
67 static struct sigaction old_sigchld_action;
70 foreach_pid_func (gpointer key,
74 GnomeVFSProcess *process;
79 pid = GPOINTER_TO_INT (key);
80 process = (GnomeVFSProcess *) value;
81 found = (gboolean *) data;
83 if (waitpid (pid, &status, WNOHANG) == pid) {
84 write (wake_up_channel_out_fd, &process, sizeof (process));
85 write (wake_up_channel_out_fd, &status, sizeof (status));
91 sigchld_handler (int signum)
93 gboolean found = FALSE;
96 g_hash_table_foreach (pid_to_process, foreach_pid_func, &found);
98 if (! found && old_sigchld_action.sa_handler != NULL)
99 (* old_sigchld_action.sa_handler) (signum);
103 wake_up (GIOChannel *source,
104 GIOCondition condition,
107 GnomeVFSProcess *process;
113 result = g_io_channel_read_chars (source, (gchar *) &process,
114 sizeof (process), &bytes_read, NULL);
115 } while (result == G_IO_ERROR_AGAIN);
116 if (result != G_IO_ERROR_NONE) {
117 g_warning (__FILE__ ": Cannot read from the notification channel (error %d)",
123 result = g_io_channel_read_chars (source, (gchar *) &status,
124 sizeof (status), &bytes_read, NULL);
125 } while (result == G_IO_ERROR_AGAIN);
126 if (result != G_IO_ERROR_NONE) {
127 g_warning (__FILE__ ": Cannot read from the notification channel (error %d)",
132 if (process->callback != NULL)
133 (* process->callback) (process, status,
134 process->callback_data);
136 if (WIFSIGNALED (status)) {
137 g_hash_table_remove (pid_to_process,
138 GINT_TO_POINTER (process->pid));
139 _gnome_vfs_process_free (process);
146 _gnome_vfs_process_init (void)
149 struct sigaction sigchld_action;
150 sigset_t sigchld_mask;
155 if (pipe (pipe_fd) == -1) {
156 g_warning ("Cannot create pipe for GnomeVFSProcess initialization: %s",
161 pid_to_process = g_hash_table_new (NULL, NULL);
163 sigchld_action.sa_handler = sigchld_handler;
164 sigemptyset (&sigchld_action.sa_mask);
165 sigchld_action.sa_flags = 0;
167 sigaction (SIGCHLD, &sigchld_action, &old_sigchld_action);
169 wake_up_channel_in = g_io_channel_unix_new (pipe_fd[0]);
170 wake_up_channel_out_fd = pipe_fd[1];
172 g_io_add_watch (wake_up_channel_in, G_IO_IN, wake_up, NULL);
174 sigemptyset (&sigchld_mask);
175 sigaddset (&sigchld_mask, SIGCHLD);
176 sigprocmask (SIG_UNBLOCK, &sigchld_mask, NULL);
182 * _gnome_vfs_process_new:
183 * @file_name: Name of the executable.
184 * @argv: NULL-terminated parameter list.
185 * @use_search_path: If TRUE, use the `PATH' environment variable to locate
187 * @close_file_descriptors: If TRUE, close all the open file descriptors.
188 * except stdio, stdin and stderr before launching the process.
189 * @init_func: Function to be called before launching the process.
190 * @init_data: Value to pass to @init_func.
191 * @callback: Function to invoke when the process die.
192 * @callback_data: Data to pass to @callback when the process dies.
194 * Launch a new process. @init_func is called immediately after calling
195 * fork(), and before closing the file descriptors and executing the program in
198 * Return value: An opaque structure describing the launched process.
201 _gnome_vfs_process_new (const gchar *file_name,
202 const gchar * const argv[],
203 GnomeVFSProcessOptions options,
204 GnomeVFSProcessInitFunc init_func,
206 GnomeVFSProcessCallback callback,
207 gpointer callback_data)
209 GnomeVFSProcess *new;
210 sigset_t sigchld_mask, old_mask;
213 /* Make sure no SIGCHLD happens while we set things up. */
215 sigemptyset (&sigchld_mask);
216 sigaddset (&sigchld_mask, SIGCHLD);
217 sigprocmask (SIG_BLOCK, &sigchld_mask, &old_mask);
219 child_pid = gnome_vfs_forkexec (file_name, argv, options,
220 init_func, init_data);
225 new = g_new (GnomeVFSProcess, 1);
226 new->pid = child_pid;
227 new->callback = callback;
228 new->callback_data = callback_data;
230 g_hash_table_insert (pid_to_process, GINT_TO_POINTER (child_pid), new);
232 sigprocmask (SIG_SETMASK, &old_mask, NULL);
238 * _gnome_vfs_process_free:
239 * @process: An existing process.
241 * Free @process. This will not kill the process, but will prevent the
242 * associated callbacks to be called.
245 _gnome_vfs_process_free (GnomeVFSProcess *process)
247 g_hash_table_remove (pid_to_process, GINT_TO_POINTER (process->pid));
252 * _gnome_vfs_process_signal:
253 * @process: A launched process
254 * @signal_number: A signal number
256 * Send signal @signal_number to the specified @process.
258 * Return value: A numeric value reporting the result of the operation.
260 GnomeVFSProcessRunResult
261 _gnome_vfs_process_signal (GnomeVFSProcess *process,
266 kill_result = kill (process->pid, signal_number);
268 switch (kill_result) {
270 return GNOME_VFS_PROCESS_OK;
272 return GNOME_VFS_PROCESS_ERROR_INVALIDSIGNAL;
274 return GNOME_VFS_PROCESS_ERROR_NOPERM;
276 return GNOME_VFS_PROCESS_ERROR_NOPROCESS;
278 return GNOME_VFS_PROCESS_ERROR_UNKNOWN;