source: trunk/third/gnome-vfs/libgnomevfs/gnome-vfs-process.c @ 15497

Revision 15497, 7.5 KB checked in by ghudson, 24 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r15496, which included commits to RCS files with non-trunk default branches.
Line 
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.
3
4   Copyright (C) 1999 Free Software Foundation
5
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.
10
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.
15
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.
20
21   Author: Ettore Perazzoli <ettore@gnu.org>
22*/
23
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.  */
27
28#ifdef HAVE_CONFIG_H
29#include <config.h>
30#endif
31
32#include <errno.h>
33#include <glib.h>
34#include <signal.h>
35#include <string.h>
36#include <sys/types.h>
37#include <sys/wait.h>
38#include <unistd.h>
39
40#include "gnome-vfs.h"
41#include "gnome-vfs-private.h"
42
43
44/* A launched process.  */
45struct _GnomeVFSProcess {
46        pid_t pid;
47        GnomeVFSProcessCallback callback;
48        gpointer callback_data;
49};
50
51
52/* Have we been initialized yet?  */
53static gboolean initialized = FALSE;
54
55/* Table to get a pointer to a GnomeVFSProcess struct from a PID value.  */
56static GHashTable *pid_to_process = NULL;
57
58/* Input channel for waking up the main loop whenever a SIGCHLD is received,
59   and call our SIGCHLD handling callback.  */
60static GIOChannel *wake_up_channel_in = NULL;
61
62/* The output side of the previous channel.  We use low-level I/O in the signal
63   handler instead of `g_io_*' stuff.  */
64static volatile gint wake_up_channel_out_fd = -1;
65
66/* The sigaction we had before installing the SIGCHLD handler.  */
67static struct sigaction old_sigchld_action;
68
69
70static void
71foreach_pid_func (gpointer key,
72                  gpointer value,
73                  gpointer data)
74{
75        GnomeVFSProcess *process;
76        pid_t pid;
77        gint status;
78        gboolean *found;
79
80        pid = GPOINTER_TO_INT (key);
81        process = (GnomeVFSProcess *) value;
82        found = (gboolean *) data;
83
84        if (waitpid (pid, &status, WNOHANG) == pid) {
85                write (wake_up_channel_out_fd, &process, sizeof (process));
86                write (wake_up_channel_out_fd, &status, sizeof (status));
87                *found = 1;
88        }
89}
90
91static void
92sigchld_handler (int signum)
93{
94        gboolean found;
95
96        g_hash_table_foreach (pid_to_process, foreach_pid_func, &found);
97
98        if (! found && old_sigchld_action.sa_handler != NULL)
99                (* old_sigchld_action.sa_handler) (signum);
100}
101
102static gboolean
103wake_up (GIOChannel *source,
104         GIOCondition condition,
105         gpointer data)
106{
107        GnomeVFSProcess *process;
108        GIOError result;
109        guint bytes_read;
110        gint status;
111
112        do {
113                result = g_io_channel_read (source, (gchar *) &process,
114                                            sizeof (process), &bytes_read);
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)",
118                           result);
119                return TRUE;
120        }
121
122        do {
123                result = g_io_channel_read (source, (gchar *) &status,
124                                            sizeof (status), &bytes_read);
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)",
128                           result);
129                return TRUE;
130        }
131
132        if (process->callback != NULL)
133                (* process->callback) (process, status,
134                                       process->callback_data);
135
136        if (WIFSIGNALED (status)) {
137                g_hash_table_remove (pid_to_process,
138                                     GINT_TO_POINTER (process->pid));
139                gnome_vfs_process_free (process);
140        }
141
142        return TRUE;
143}
144
145
146gboolean
147gnome_vfs_process_init (void)
148{
149        gint pipe_fd[2];
150        struct sigaction sigchld_action;
151        sigset_t sigchld_mask;
152
153        if (initialized)
154                return TRUE;
155
156        if (pipe (pipe_fd) == -1) {
157                g_warning ("Cannot create pipe for GnomeVFSProcess initialization: %s",
158                           g_strerror (errno));
159                return FALSE;
160        }
161
162        sigchld_action.sa_handler =  sigchld_handler;
163        sigemptyset (&sigchld_action.sa_mask);
164        sigchld_action.sa_flags = 0;
165
166        sigaction (SIGCHLD, &sigchld_action, &old_sigchld_action);
167
168        pid_to_process = g_hash_table_new (g_direct_hash, g_direct_equal);
169
170        wake_up_channel_in = g_io_channel_unix_new (pipe_fd[0]);
171        wake_up_channel_out_fd = pipe_fd[1];
172
173        g_io_add_watch (wake_up_channel_in, G_IO_IN, wake_up, NULL);
174
175        sigemptyset (&sigchld_mask);
176        sigaddset (&sigchld_mask, SIGCHLD);
177        sigprocmask (SIG_UNBLOCK, &sigchld_mask, NULL);
178
179        return TRUE;
180}
181
182
183/**
184 * gnome_vfs_process_new:
185 * @file_name: Name of the executable.
186 * @argv: NULL-terminated parameter list.
187 * @use_search_path: If TRUE, use the `PATH' environment variable to locate
188 * the executable.
189 * @close_file_descriptors: If TRUE, close all the open file descriptors.
190 * except stdio, stdin and stderr before launching the process.
191 * @init_func: Function to be called before launching the process.
192 * @init_data: Value to pass to @init_func.
193 * @callback: Function to invoke when the process die.
194 * @callback_data: Data to pass to @callback when the process dies.
195 *
196 * Launch a new process.  @init_func is called immediately after calling
197 * fork(), and before closing the file descriptors and executing the program in
198 * the new process.
199 *
200 * Return value: An opaque structure describing the launched process.
201 **/
202GnomeVFSProcess *
203gnome_vfs_process_new (const gchar *file_name,
204                       const gchar * const argv[],
205                       GnomeVFSProcessOptions options,
206                       GnomeVFSProcessInitFunc init_func,
207                       gpointer init_data,
208                       GnomeVFSProcessCallback callback,
209                       gpointer callback_data)
210{
211        GnomeVFSProcess *new;
212        sigset_t sigchld_mask, old_mask;
213        pid_t child_pid;
214
215        /* Make sure no SIGCHLD happens while we set things up.  */
216
217        sigemptyset (&sigchld_mask);
218        sigaddset (&sigchld_mask, SIGCHLD);
219        sigprocmask (SIG_BLOCK, &sigchld_mask, &old_mask);
220
221        child_pid = gnome_vfs_forkexec (file_name, argv, options,
222                                        init_func, init_data);
223
224        if (child_pid == -1)
225                return NULL;
226
227        new = g_new (GnomeVFSProcess, 1);
228        new->pid = child_pid;
229        new->callback = callback;
230        new->callback_data = callback_data;
231
232        g_hash_table_insert (pid_to_process, GINT_TO_POINTER (child_pid), new);
233
234        sigprocmask (SIG_SETMASK, &old_mask, NULL);
235
236        return new;
237}
238
239/**
240 * gnome_vfs_process_free:
241 * @process: An existing process.
242 *
243 * Free @process.  This will not kill the process, but will prevent the
244 * associated callbacks to be called.
245 **/
246void
247gnome_vfs_process_free (GnomeVFSProcess *process)
248{
249        g_hash_table_remove (pid_to_process, GINT_TO_POINTER (process->pid));
250        g_free (process);
251}
252
253/**
254 * gnome_vfs_process_signal:
255 * @process: A launched process
256 * @signal_number: A signal number
257 *
258 * Send signal @signal_number to the specified @process.
259 *
260 * Return value: A numeric value reporting the result of the operation.
261 **/
262GnomeVFSProcessRunResult
263gnome_vfs_process_signal (GnomeVFSProcess *process,
264                          guint signal_number)
265{
266        gint kill_result;
267
268        kill_result = kill (process->pid, signal_number);
269
270        switch (kill_result) {
271        case 0:
272                return GNOME_VFS_PROCESS_OK;
273        case EINVAL:
274                return GNOME_VFS_PROCESS_ERROR_INVALIDSIGNAL;
275        case EPERM:
276                return GNOME_VFS_PROCESS_ERROR_NOPERM;
277        case ESRCH:
278                return GNOME_VFS_PROCESS_ERROR_NOPROCESS;
279        default:
280                return GNOME_VFS_PROCESS_ERROR_UNKNOWN;
281        }
282}
283
Note: See TracBrowser for help on using the repository browser.