2 * Copyright (C) 2010-2011 Robert Ancell.
3 * Author: Robert Ancell <robert.ancell@canonical.com>
5 * This program is free software: you can redistribute it and/or modify it under
6 * the terms of the GNU General Public License as published by the Free Software
7 * Foundation, either version 3 of the License, or (at your option) any later
8 * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
20 #include <glib/gstdio.h>
32 static guint signals[LAST_SIGNAL] = { 0 };
39 /* TRUE to clear the environment in this process */
40 gboolean clear_environment;
42 /* Environment variables to set */
48 /* Exit status of process */
51 /* Timeout waiting for process to quit */
54 /* Watch on process */
58 G_DEFINE_TYPE (Process, process, G_TYPE_OBJECT);
60 static Process *current_process = NULL;
61 static GHashTable *processes = NULL;
62 static int signal_pipe[2];
65 process_get_current (void)
68 return current_process;
70 current_process = process_new ();
71 current_process->priv->pid = getpid ();
73 return current_process;
79 return g_object_new (PROCESS_TYPE, NULL);
83 process_set_clear_environment (Process *process, gboolean clear_environment)
85 g_return_if_fail (process != NULL);
86 process->priv->clear_environment = clear_environment;
90 process_get_clear_environment (Process *process)
92 g_return_val_if_fail (process != NULL, FALSE);
93 return process->priv->clear_environment;
97 process_set_env (Process *process, const gchar *name, const gchar *value)
99 g_return_if_fail (process != NULL);
100 g_return_if_fail (name != NULL);
101 g_hash_table_insert (process->priv->env, g_strdup (name), g_strdup (value));
105 process_get_env (Process *process, const gchar *name)
107 g_return_val_if_fail (process != NULL, NULL);
108 g_return_val_if_fail (name != NULL, NULL);
109 return g_hash_table_lookup (process->priv->env, name);
113 process_set_command (Process *process, const gchar *command)
115 g_return_if_fail (process != NULL);
117 g_free (process->priv->command);
118 process->priv->command = g_strdup (command);
122 process_get_command (Process *process)
124 g_return_val_if_fail (process != NULL, NULL);
125 return process->priv->command;
129 process_watch_cb (GPid pid, gint status, gpointer data)
131 Process *process = data;
133 process->priv->exit_status = status;
135 if (WIFEXITED (status))
136 g_debug ("Process %d exited with return value %d", pid, WEXITSTATUS (status));
137 else if (WIFSIGNALED (status))
138 g_debug ("Process %d terminated with signal %d", pid, WTERMSIG (status));
140 if (process->priv->watch)
141 g_source_remove (process->priv->watch);
142 process->priv->watch = 0;
144 if (process->priv->quit_timeout)
145 g_source_remove (process->priv->quit_timeout);
146 process->priv->quit_timeout = 0;
147 process->priv->pid = 0;
148 g_hash_table_remove (processes, GINT_TO_POINTER (pid));
150 g_signal_emit (process, signals[STOPPED], 0);
154 process_run (Process *process)
160 GError *error = NULL;
162 if (!g_shell_parse_argv (process->priv->command, &argc, &argv, &error))
164 g_warning ("Error parsing command %s: %s", process->priv->command, error->message);
165 _exit (EXIT_FAILURE);
168 if (process->priv->clear_environment)
175 g_hash_table_iter_init (&iter, process->priv->env);
176 while (g_hash_table_iter_next (&iter, &key, &value))
177 g_setenv ((gchar *)key, (gchar *)value, TRUE);
179 execvp (argv[0], argv);
181 g_warning ("Error executing child process %s: %s", argv[0], g_strerror (errno));
182 _exit (EXIT_FAILURE);
186 process_start (Process *process, gboolean block)
190 g_return_val_if_fail (process != NULL, FALSE);
191 g_return_val_if_fail (process->priv->command != NULL, FALSE);
192 g_return_val_if_fail (process->priv->pid == 0, FALSE);
197 g_warning ("Failed to fork: %s", strerror (errno));
202 g_signal_emit (process, signals[RUN], 0);
204 g_debug ("Launching process %d: %s", pid, process->priv->command);
206 process->priv->pid = pid;
211 waitpid (process->priv->pid, &exit_status, 0);
212 process_watch_cb (process->priv->pid, exit_status, process);
216 g_hash_table_insert (processes, GINT_TO_POINTER (process->priv->pid), g_object_ref (process));
217 process->priv->watch = g_child_watch_add (process->priv->pid, process_watch_cb, process);
224 process_get_is_running (Process *process)
226 g_return_val_if_fail (process != NULL, FALSE);
227 return process->priv->pid != 0;
231 process_get_pid (Process *process)
233 g_return_val_if_fail (process != NULL, 0);
234 return process->priv->pid;
238 process_signal (Process *process, int signum)
240 g_return_if_fail (process != NULL);
242 if (process->priv->pid == 0)
245 g_debug ("Sending signal %d to process %d", signum, process->priv->pid);
247 if (kill (process->priv->pid, signum) < 0)
248 g_warning ("Error sending signal %d to process %d: %s", signum, process->priv->pid, strerror (errno));
252 quit_timeout_cb (Process *process)
254 process->priv->quit_timeout = 0;
255 process_signal (process, SIGKILL);
260 process_stop (Process *process)
262 g_return_if_fail (process != NULL);
264 /* Send SIGTERM, and then SIGKILL if no response */
265 process->priv->quit_timeout = g_timeout_add (5000, (GSourceFunc) quit_timeout_cb, process);
266 process_signal (process, SIGTERM);
270 process_get_exit_status (Process *process)
272 g_return_val_if_fail (process != NULL, -1);
273 return process->priv->exit_status;
277 process_init (Process *process)
279 process->priv = G_TYPE_INSTANCE_GET_PRIVATE (process, PROCESS_TYPE, ProcessPrivate);
280 process->priv->env = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
284 process_stopped (Process *process)
289 process_finalize (GObject *object)
293 self = PROCESS (object);
295 if (self->priv->pid > 0)
296 g_hash_table_remove (processes, GINT_TO_POINTER (self->priv->pid));
298 g_free (self->priv->command);
299 g_hash_table_unref (self->priv->env);
300 if (self->priv->quit_timeout)
301 g_source_remove (self->priv->quit_timeout);
302 if (self->priv->watch)
303 g_source_remove (self->priv->watch);
306 kill (self->priv->pid, SIGTERM);
308 G_OBJECT_CLASS (process_parent_class)->finalize (object);
312 signal_cb (int signum, siginfo_t *info, void *data)
314 /* NOTE: Using g_printerr as can't call g_warning from a signal callback */
315 if (write (signal_pipe[1], &info->si_signo, sizeof (int)) < 0 ||
316 write (signal_pipe[1], &info->si_pid, sizeof (pid_t)) < 0)
317 g_printerr ("Failed to write to signal pipe: %s", strerror (errno));
321 handle_signal (GIOChannel *source, GIOCondition condition, gpointer data)
328 if (read (signal_pipe[0], &signo, sizeof (int)) != sizeof (int) ||
329 read (signal_pipe[0], &pid, sizeof (pid_t)) != sizeof (pid_t))
331 g_warning ("Error reading from signal pipe: %s", strerror (errno));
335 g_debug ("Got signal %d from process %d", signo, pid);
337 process = g_hash_table_lookup (processes, GINT_TO_POINTER (pid));
339 process = process_get_current ();
341 g_signal_emit (process, signals[GOT_SIGNAL], 0, signo);
347 process_class_init (ProcessClass *klass)
349 GObjectClass *object_class = G_OBJECT_CLASS (klass);
350 struct sigaction action;
352 klass->run = process_run;
353 klass->stopped = process_stopped;
354 object_class->finalize = process_finalize;
356 g_type_class_add_private (klass, sizeof (ProcessPrivate));
360 G_TYPE_FROM_CLASS (klass),
362 G_STRUCT_OFFSET (ProcessClass, run),
367 g_signal_new ("got-data",
368 G_TYPE_FROM_CLASS (klass),
370 G_STRUCT_OFFSET (ProcessClass, got_data),
374 signals[GOT_SIGNAL] =
375 g_signal_new ("got-signal",
376 G_TYPE_FROM_CLASS (klass),
378 G_STRUCT_OFFSET (ProcessClass, got_signal),
381 G_TYPE_NONE, 1, G_TYPE_INT);
383 g_signal_new ("stopped",
384 G_TYPE_FROM_CLASS (klass),
386 G_STRUCT_OFFSET (ProcessClass, stopped),
391 /* Catch signals and feed them to the main loop via a pipe */
392 processes = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref);
393 if (pipe (signal_pipe) != 0)
394 g_critical ("Failed to create signal pipe");
395 fcntl (signal_pipe[0], F_SETFD, FD_CLOEXEC);
396 fcntl (signal_pipe[1], F_SETFD, FD_CLOEXEC);
397 g_io_add_watch (g_io_channel_unix_new (signal_pipe[0]), G_IO_IN, handle_signal, NULL);
398 action.sa_sigaction = signal_cb;
399 sigemptyset (&action.sa_mask);
400 action.sa_flags = SA_SIGINFO;
401 sigaction (SIGTERM, &action, NULL);
402 sigaction (SIGINT, &action, NULL);
403 sigaction (SIGHUP, &action, NULL);
404 sigaction (SIGUSR1, &action, NULL);
405 sigaction (SIGUSR2, &action, NULL);