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

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