Back to index

lightdm  1.3.2
process.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2010-2011 Robert Ancell.
00003  * Author: Robert Ancell <robert.ancell@canonical.com>
00004  * 
00005  * This program is free software: you can redistribute it and/or modify it under
00006  * the terms of the GNU General Public License as published by the Free Software
00007  * Foundation, either version 3 of the License, or (at your option) any later
00008  * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
00009  * license.
00010  */
00011 
00012 #include <stdlib.h>
00013 #include <string.h>
00014 #include <unistd.h>
00015 #include <errno.h>
00016 #include <sys/wait.h>
00017 #include <fcntl.h>
00018 #include <signal.h>
00019 #include <grp.h>
00020 #include <glib/gstdio.h>
00021 #include <config.h>
00022 
00023 #include "process.h"
00024 
00025 enum {
00026     RUN,
00027     GOT_DATA,
00028     GOT_SIGNAL,  
00029     STOPPED,
00030     LAST_SIGNAL
00031 };
00032 static guint signals[LAST_SIGNAL] = { 0 };
00033 
00034 struct ProcessPrivate
00035 {  
00036     /* Command to run */
00037     gchar *command;
00038   
00039     /* TRUE to clear the environment in this process */
00040     gboolean clear_environment;
00041 
00042     /* Environment variables to set */
00043     GHashTable *env;
00044 
00045     /* Process ID */
00046     GPid pid;
00047   
00048     /* Exit status of process */
00049     int exit_status;
00050 
00051     /* Timeout waiting for process to quit */
00052     guint quit_timeout;
00053 };
00054 
00055 G_DEFINE_TYPE (Process, process, G_TYPE_OBJECT);
00056 
00057 static Process *current_process = NULL;
00058 static GHashTable *processes = NULL;
00059 static int signal_pipe[2];
00060 
00061 Process *
00062 process_get_current (void)
00063 {
00064     if (current_process)
00065         return current_process;
00066 
00067     current_process = process_new ();
00068     current_process->priv->pid = getpid ();
00069 
00070     return current_process;
00071 }
00072 
00073 Process *
00074 process_new (void)
00075 {
00076     return g_object_new (PROCESS_TYPE, NULL);
00077 }
00078 
00079 void
00080 process_set_clear_environment (Process *process, gboolean clear_environment)
00081 {
00082     g_return_if_fail (process != NULL);
00083     process->priv->clear_environment = clear_environment;
00084 }
00085 
00086 gboolean
00087 process_get_clear_environment (Process *process)
00088 {
00089     g_return_val_if_fail (process != NULL, FALSE);
00090     return process->priv->clear_environment;
00091 }
00092 
00093 void
00094 process_set_env (Process *process, const gchar *name, const gchar *value)
00095 {
00096     g_return_if_fail (process != NULL);
00097     g_return_if_fail (name != NULL);
00098     g_hash_table_insert (process->priv->env, g_strdup (name), g_strdup (value));  
00099 }
00100 
00101 const gchar *
00102 process_get_env (Process *process, const gchar *name)
00103 {
00104     g_return_val_if_fail (process != NULL, NULL);
00105     g_return_val_if_fail (name != NULL, NULL);
00106     return g_hash_table_lookup (process->priv->env, name);
00107 }
00108 
00109 void
00110 process_set_command (Process *process, const gchar *command)
00111 {
00112     g_return_if_fail (process != NULL);
00113 
00114     g_free (process->priv->command);
00115     process->priv->command = g_strdup (command);
00116 }
00117 
00118 const gchar *
00119 process_get_command (Process *process)
00120 {
00121     g_return_val_if_fail (process != NULL, NULL);
00122     return process->priv->command;
00123 }
00124 
00125 static void
00126 process_watch_cb (GPid pid, gint status, gpointer data)
00127 {
00128     Process *process = data;
00129 
00130     process->priv->exit_status = status;
00131 
00132     if (WIFEXITED (status))
00133         g_debug ("Process %d exited with return value %d", pid, WEXITSTATUS (status));
00134     else if (WIFSIGNALED (status))
00135         g_debug ("Process %d terminated with signal %d", pid, WTERMSIG (status));
00136 
00137     if (process->priv->quit_timeout)
00138         g_source_remove (process->priv->quit_timeout);
00139     process->priv->quit_timeout = 0;  
00140     process->priv->pid = 0;
00141     g_hash_table_remove (processes, GINT_TO_POINTER (pid));
00142 
00143     g_signal_emit (process, signals[STOPPED], 0);
00144 }
00145 
00146 static void
00147 process_run (Process *process)
00148 {
00149     gint argc;
00150     gchar **argv;
00151     GHashTableIter iter;
00152     gpointer key, value;
00153     GError *error = NULL;
00154 
00155     if (!g_shell_parse_argv (process->priv->command, &argc, &argv, &error))
00156     {
00157         g_warning ("Error parsing command %s: %s", process->priv->command, error->message);
00158         _exit (EXIT_FAILURE);
00159     }
00160 
00161     if (process->priv->clear_environment)
00162 #ifdef HAVE_CLEARENV
00163         clearenv ();
00164 #else
00165         environ = NULL;
00166 #endif
00167 
00168     g_hash_table_iter_init (&iter, process->priv->env);
00169     while (g_hash_table_iter_next (&iter, &key, &value))
00170         g_setenv ((gchar *)key, (gchar *)value, TRUE);
00171   
00172     execvp (argv[0], argv);
00173 
00174     g_warning ("Error executing child process %s: %s", argv[0], g_strerror (errno));
00175     _exit (EXIT_FAILURE);
00176 }
00177 
00178 gboolean
00179 process_start (Process *process)
00180 {
00181     pid_t pid;
00182 
00183     g_return_val_if_fail (process != NULL, FALSE);
00184     g_return_val_if_fail (process->priv->command != NULL, FALSE);  
00185     g_return_val_if_fail (process->priv->pid == 0, FALSE);
00186 
00187     pid = fork ();
00188     if (pid < 0)
00189     {
00190         g_warning ("Failed to fork: %s", strerror (errno));
00191         return FALSE;
00192     }
00193 
00194     if (pid == 0)
00195         g_signal_emit (process, signals[RUN], 0);
00196 
00197     g_debug ("Launching process %d: %s", pid, process->priv->command);
00198 
00199     process->priv->pid = pid;
00200 
00201     g_hash_table_insert (processes, GINT_TO_POINTER (process->priv->pid), g_object_ref (process));
00202     g_child_watch_add (process->priv->pid, process_watch_cb, process);
00203 
00204     return TRUE;
00205 }
00206 
00207 gboolean
00208 process_get_is_running (Process *process)
00209 {
00210     g_return_val_if_fail (process != NULL, FALSE);
00211     return process->priv->pid != 0;
00212 }
00213 
00214 GPid
00215 process_get_pid (Process *process)
00216 {
00217     g_return_val_if_fail (process != NULL, 0);
00218     return process->priv->pid;
00219 }
00220 
00221 void
00222 process_signal (Process *process, int signum)
00223 {
00224     g_return_if_fail (process != NULL);
00225 
00226     if (process->priv->pid == 0)
00227         return;
00228 
00229     g_debug ("Sending signal %d to process %d", signum, process->priv->pid);
00230 
00231     if (kill (process->priv->pid, signum) < 0)
00232         g_warning ("Error sending signal %d to process %d: %s", signum, process->priv->pid, strerror (errno));
00233 }
00234 
00235 static gboolean
00236 quit_timeout_cb (Process *process)
00237 {
00238     process->priv->quit_timeout = 0;
00239     process_signal (process, SIGKILL);
00240     return FALSE;
00241 }
00242 
00243 void
00244 process_stop (Process *process)
00245 {
00246     g_return_if_fail (process != NULL);
00247 
00248     /* Send SIGTERM, and then SIGKILL if no response */
00249     process->priv->quit_timeout = g_timeout_add (5000, (GSourceFunc) quit_timeout_cb, process);
00250     process_signal (process, SIGTERM);
00251 }
00252 
00253 void
00254 process_wait (Process *process)
00255 {
00256     int exit_status;
00257 
00258     g_return_if_fail (process != NULL);
00259 
00260     waitpid (process->priv->pid, &exit_status, 0);
00261     process_watch_cb (process->priv->pid, exit_status, process);
00262 }
00263 
00264 int
00265 process_get_exit_status (Process *process)
00266 {
00267     g_return_val_if_fail (process != NULL, -1);
00268     return process->priv->exit_status;
00269 }
00270 
00271 static void
00272 process_init (Process *process)
00273 {
00274     process->priv = G_TYPE_INSTANCE_GET_PRIVATE (process, PROCESS_TYPE, ProcessPrivate);
00275     process->priv->env = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
00276 }
00277 
00278 static void
00279 process_stopped (Process *process)
00280 {
00281 }
00282 
00283 static void
00284 process_finalize (GObject *object)
00285 {
00286     Process *self;
00287 
00288     self = PROCESS (object);
00289 
00290     if (self->priv->pid > 0)
00291         g_hash_table_remove (processes, GINT_TO_POINTER (self->priv->pid));
00292 
00293     g_free (self->priv->command);
00294     g_hash_table_unref (self->priv->env);
00295 
00296     if (self->priv->pid)
00297         kill (self->priv->pid, SIGTERM);
00298 
00299     G_OBJECT_CLASS (process_parent_class)->finalize (object);
00300 }
00301 
00302 static void
00303 signal_cb (int signum, siginfo_t *info, void *data)
00304 {
00305     /* NOTE: Using g_printerr as can't call g_warning from a signal callback */
00306     if (write (signal_pipe[1], &info->si_signo, sizeof (int)) < 0 ||
00307         write (signal_pipe[1], &info->si_pid, sizeof (pid_t)) < 0)
00308         g_printerr ("Failed to write to signal pipe: %s", strerror (errno));
00309 }
00310 
00311 static gboolean
00312 handle_signal (GIOChannel *source, GIOCondition condition, gpointer data)
00313 {
00314     int signo;
00315     pid_t pid;
00316     Process *process;
00317 
00318     if (read (signal_pipe[0], &signo, sizeof (int)) < 0 || 
00319         read (signal_pipe[0], &pid, sizeof (pid_t)) < 0)
00320     {
00321         g_warning ("Error reading from signal pipe: %s", strerror (errno));
00322         return TRUE;
00323     }
00324 
00325     g_debug ("Got signal %d from process %d", signo, pid);
00326 
00327     process = g_hash_table_lookup (processes, GINT_TO_POINTER (pid));
00328     if (process == NULL)
00329         process = process_get_current ();
00330     if (process)
00331         g_signal_emit (process, signals[GOT_SIGNAL], 0, signo);
00332 
00333     return TRUE;
00334 }
00335 
00336 static void
00337 process_class_init (ProcessClass *klass)
00338 {
00339     GObjectClass *object_class = G_OBJECT_CLASS (klass);
00340     struct sigaction action;
00341 
00342     klass->run = process_run;
00343     klass->stopped = process_stopped;
00344     object_class->finalize = process_finalize;  
00345 
00346     g_type_class_add_private (klass, sizeof (ProcessPrivate));
00347 
00348     signals[RUN] =
00349         g_signal_new ("run",
00350                       G_TYPE_FROM_CLASS (klass),
00351                       G_SIGNAL_RUN_LAST,
00352                       G_STRUCT_OFFSET (ProcessClass, run),
00353                       NULL, NULL,
00354                       g_cclosure_marshal_VOID__VOID,
00355                       G_TYPE_NONE, 0); 
00356     signals[GOT_DATA] =
00357         g_signal_new ("got-data",
00358                       G_TYPE_FROM_CLASS (klass),
00359                       G_SIGNAL_RUN_LAST,
00360                       G_STRUCT_OFFSET (ProcessClass, got_data),
00361                       NULL, NULL,
00362                       g_cclosure_marshal_VOID__VOID,
00363                       G_TYPE_NONE, 0); 
00364     signals[GOT_SIGNAL] =
00365         g_signal_new ("got-signal",
00366                       G_TYPE_FROM_CLASS (klass),
00367                       G_SIGNAL_RUN_LAST,
00368                       G_STRUCT_OFFSET (ProcessClass, got_signal),
00369                       NULL, NULL,
00370                       g_cclosure_marshal_VOID__INT,
00371                       G_TYPE_NONE, 1, G_TYPE_INT);
00372     signals[STOPPED] =
00373         g_signal_new ("stopped",
00374                       G_TYPE_FROM_CLASS (klass),
00375                       G_SIGNAL_RUN_LAST,
00376                       G_STRUCT_OFFSET (ProcessClass, stopped),
00377                       NULL, NULL,
00378                       g_cclosure_marshal_VOID__VOID,
00379                       G_TYPE_NONE, 0);
00380 
00381     /* Catch signals and feed them to the main loop via a pipe */
00382     processes = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref);
00383     if (pipe (signal_pipe) != 0)
00384         g_critical ("Failed to create signal pipe");
00385     fcntl (signal_pipe[0], F_SETFD, FD_CLOEXEC);
00386     fcntl (signal_pipe[1], F_SETFD, FD_CLOEXEC);
00387     g_io_add_watch (g_io_channel_unix_new (signal_pipe[0]), G_IO_IN, handle_signal, NULL);
00388     action.sa_sigaction = signal_cb;
00389     sigemptyset (&action.sa_mask);
00390     action.sa_flags = SA_SIGINFO;
00391     sigaction (SIGTERM, &action, NULL);
00392     sigaction (SIGINT, &action, NULL);
00393     sigaction (SIGHUP, &action, NULL);
00394     sigaction (SIGUSR1, &action, NULL);
00395     sigaction (SIGUSR2, &action, NULL);
00396 }