1 /*
2  * Copyright (C) 2010-2011 Robert Ancell.
3  * Author: Robert Ancell <robert.ancell@canonical.com>
4  *
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
9  * license.
10  */
11 
12 #include <stdlib.h>
13 #include <string.h>
14 #include <unistd.h>
15 #include <errno.h>
16 #include <sys/wait.h>
17 #include <fcntl.h>
18 #include <signal.h>
19 #include <grp.h>
20 #include <config.h>
21 
22 #include "log-file.h"
23 #include "process.h"
24 
25 enum {
26     GOT_DATA,
27     GOT_SIGNAL,
28     STOPPED,
29     LAST_SIGNAL
30 };
31 static guint signals[LAST_SIGNAL] = { 0 };
32 
33 typedef struct
34 {
35     /* Function to run inside subprocess before exec */
36     ProcessRunFunc run_func;
37     gpointer run_func_data;
38 
39     /* File to log to */
40     gchar *log_file;
41     gboolean log_stdout;
42     LogMode log_mode;
43 
44     /* Command to run */
45     gchar *command;
46 
47     /* TRUE to clear the environment in this process */
48     gboolean clear_environment;
49 
50     /* Environment variables to set */
51     GHashTable *env;
52 
53     /* Process ID */
54     GPid pid;
55 
56     /* Exit status of process */
57     int exit_status;
58 
59     /* TRUE if stopping this process (waiting for child process to stop) */
60     gboolean stopping;
61 
62     /* Timeout waiting for process to quit */
63     guint quit_timeout;
64 
65     /* Watch on process */
66     guint watch;
67 } ProcessPrivate;
68 
69 G_DEFINE_TYPE_WITH_PRIVATE (Process, process, G_TYPE_OBJECT)
70 
71 static Process *current_process = NULL;
72 static GHashTable *processes = NULL;
73 static pid_t signal_pid;
74 static int signal_pipe[2];
75 
76 extern char **environ;
77 
78 Process *
process_get_current(void)79 process_get_current (void)
80 {
81     if (current_process)
82         return current_process;
83 
84     current_process = process_new (NULL, NULL);
85     ProcessPrivate *priv = process_get_instance_private (current_process);
86     priv->pid = getpid ();
87 
88     return current_process;
89 }
90 
91 Process *
process_new(ProcessRunFunc run_func,gpointer run_func_data)92 process_new (ProcessRunFunc run_func, gpointer run_func_data)
93 {
94     Process *process = g_object_new (PROCESS_TYPE, NULL);
95     ProcessPrivate *priv = process_get_instance_private (process);
96 
97     priv->run_func = run_func;
98     priv->run_func_data = run_func_data;
99     priv->log_mode = LOG_MODE_INVALID;
100     return process;
101 }
102 
103 void
process_set_log_file(Process * process,const gchar * path,gboolean log_stdout,LogMode log_mode)104 process_set_log_file (Process *process, const gchar *path, gboolean log_stdout, LogMode log_mode)
105 {
106     ProcessPrivate *priv = process_get_instance_private (process);
107 
108     g_return_if_fail (process != NULL);
109 
110     g_free (priv->log_file);
111     priv->log_file = g_strdup (path);
112     priv->log_stdout = log_stdout;
113     priv->log_mode = log_mode;
114 }
115 
116 void
process_set_clear_environment(Process * process,gboolean clear_environment)117 process_set_clear_environment (Process *process, gboolean clear_environment)
118 {
119     ProcessPrivate *priv = process_get_instance_private (process);
120     g_return_if_fail (process != NULL);
121     priv->clear_environment = clear_environment;
122 }
123 
124 gboolean
process_get_clear_environment(Process * process)125 process_get_clear_environment (Process *process)
126 {
127     ProcessPrivate *priv = process_get_instance_private (process);
128     g_return_val_if_fail (process != NULL, FALSE);
129     return priv->clear_environment;
130 }
131 
132 void
process_set_env(Process * process,const gchar * name,const gchar * value)133 process_set_env (Process *process, const gchar *name, const gchar *value)
134 {
135     ProcessPrivate *priv = process_get_instance_private (process);
136     g_return_if_fail (process != NULL);
137     g_return_if_fail (name != NULL);
138     g_hash_table_insert (priv->env, g_strdup (name), g_strdup (value));
139 }
140 
141 const gchar *
process_get_env(Process * process,const gchar * name)142 process_get_env (Process *process, const gchar *name)
143 {
144     ProcessPrivate *priv = process_get_instance_private (process);
145     g_return_val_if_fail (process != NULL, NULL);
146     g_return_val_if_fail (name != NULL, NULL);
147     return g_hash_table_lookup (priv->env, name);
148 }
149 
150 void
process_set_command(Process * process,const gchar * command)151 process_set_command (Process *process, const gchar *command)
152 {
153     ProcessPrivate *priv = process_get_instance_private (process);
154     g_return_if_fail (process != NULL);
155     g_free (priv->command);
156     priv->command = g_strdup (command);
157 }
158 
159 const gchar *
process_get_command(Process * process)160 process_get_command (Process *process)
161 {
162     ProcessPrivate *priv = process_get_instance_private (process);
163     g_return_val_if_fail (process != NULL, NULL);
164     return priv->command;
165 }
166 
167 static void
process_watch_cb(GPid pid,gint status,gpointer data)168 process_watch_cb (GPid pid, gint status, gpointer data)
169 {
170     Process *process = data;
171     ProcessPrivate *priv = process_get_instance_private (process);
172 
173     priv->watch = 0;
174     priv->exit_status = status;
175 
176     if (WIFEXITED (status))
177         g_debug ("Process %d exited with return value %d", pid, WEXITSTATUS (status));
178     else if (WIFSIGNALED (status))
179         g_debug ("Process %d terminated with signal %d", pid, WTERMSIG (status));
180 
181     if (priv->quit_timeout)
182         g_source_remove (priv->quit_timeout);
183     priv->quit_timeout = 0;
184     priv->pid = 0;
185     g_hash_table_remove (processes, GINT_TO_POINTER (pid));
186 
187     g_signal_emit (process, signals[STOPPED], 0);
188 }
189 
190 gboolean
process_start(Process * process,gboolean block)191 process_start (Process *process, gboolean block)
192 {
193     ProcessPrivate *priv = process_get_instance_private (process);
194 
195     g_return_val_if_fail (process != NULL, FALSE);
196     g_return_val_if_fail (priv->command != NULL, FALSE);
197     g_return_val_if_fail (priv->pid == 0, FALSE);
198 
199     gint argc;
200     g_auto(GStrv) argv = NULL;
201     g_autoptr(GError) error = NULL;
202     if (!g_shell_parse_argv (priv->command, &argc, &argv, &error))
203     {
204         g_warning ("Error parsing command %s: %s", priv->command, error->message);
205         return FALSE;
206     }
207 
208     int log_fd = -1;
209     if (priv->log_file)
210         log_fd = log_file_open (priv->log_file, priv->log_mode);
211 
212     /* Work out variables to set */
213     guint env_length = g_hash_table_size (priv->env);
214     g_autofree gchar **env_keys = g_malloc (sizeof (gchar *) * env_length);
215     g_autofree gchar **env_values = g_malloc (sizeof (gchar *) * env_length);
216     GList *keys = g_hash_table_get_keys (priv->env);
217     guint i = 0;
218     for (GList *link = keys; i < env_length; i++, link = link->next)
219     {
220         env_keys[i] = link->data;
221         env_values[i] = g_hash_table_lookup (priv->env, env_keys[i]);
222     }
223     g_list_free (keys);
224 
225     pid_t pid = fork ();
226     if (pid == 0)
227     {
228         /* Do custom setup */
229         if (priv->run_func)
230             priv->run_func (process, priv->run_func_data);
231 
232         /* Redirect output to logfile */
233         if (log_fd >= 0)
234         {
235              if (priv->log_stdout)
236                  dup2 (log_fd, STDOUT_FILENO);
237              dup2 (log_fd, STDERR_FILENO);
238              close (log_fd);
239         }
240 
241         /* Set environment */
242         if (priv->clear_environment)
243 #ifdef HAVE_CLEARENV
244             clearenv ();
245 #else
246             environ = NULL;
247 #endif
248         for (guint i = 0; i < env_length; i++)
249             setenv (env_keys[i], env_values[i], TRUE);
250 
251         /* Reset SIGPIPE handler so the child has default behaviour (we disabled it at LightDM start) */
252         signal (SIGPIPE, SIG_DFL);
253 
254         execvp (argv[0], argv);
255         _exit (EXIT_FAILURE);
256     }
257 
258     close (log_fd);
259 
260     if (pid < 0)
261     {
262         g_warning ("Failed to fork: %s", strerror (errno));
263         return FALSE;
264     }
265 
266     g_debug ("Launching process %d: %s", pid, priv->command);
267 
268     priv->pid = pid;
269 
270     if (block)
271     {
272         int exit_status;
273         waitpid (priv->pid, &exit_status, 0);
274         process_watch_cb (priv->pid, exit_status, process);
275     }
276     else
277     {
278         g_hash_table_insert (processes, GINT_TO_POINTER (priv->pid), g_object_ref (process));
279         priv->watch = g_child_watch_add (priv->pid, process_watch_cb, process);
280     }
281 
282     return TRUE;
283 }
284 
285 gboolean
process_get_is_running(Process * process)286 process_get_is_running (Process *process)
287 {
288     ProcessPrivate *priv = process_get_instance_private (process);
289     g_return_val_if_fail (process != NULL, FALSE);
290     return priv->pid != 0;
291 }
292 
293 GPid
process_get_pid(Process * process)294 process_get_pid (Process *process)
295 {
296     ProcessPrivate *priv = process_get_instance_private (process);
297     g_return_val_if_fail (process != NULL, 0);
298     return priv->pid;
299 }
300 
301 void
process_signal(Process * process,int signum)302 process_signal (Process *process, int signum)
303 {
304     ProcessPrivate *priv = process_get_instance_private (process);
305 
306     g_return_if_fail (process != NULL);
307 
308     if (priv->pid == 0)
309         return;
310 
311     g_debug ("Sending signal %d to process %d", signum, priv->pid);
312 
313     if (kill (priv->pid, signum) < 0)
314     {
315         /* Ignore ESRCH, we will pick that up in our wait */
316         if (errno != ESRCH)
317             g_warning ("Error sending signal %d to process %d: %s", signum, priv->pid, strerror (errno));
318     }
319 }
320 
321 static gboolean
quit_timeout_cb(Process * process)322 quit_timeout_cb (Process *process)
323 {
324     ProcessPrivate *priv = process_get_instance_private (process);
325 
326     priv->quit_timeout = 0;
327     process_signal (process, SIGKILL);
328 
329     return FALSE;
330 }
331 
332 void
process_stop(Process * process)333 process_stop (Process *process)
334 {
335     ProcessPrivate *priv = process_get_instance_private (process);
336 
337     g_return_if_fail (process != NULL);
338 
339     if (priv->stopping)
340         return;
341     priv->stopping = TRUE;
342 
343     /* If already stopped then we're done! */
344     if (priv->pid == 0)
345         return;
346 
347     /* Send SIGTERM, and then SIGKILL if no response */
348     priv->quit_timeout = g_timeout_add (5000, (GSourceFunc) quit_timeout_cb, process);
349     process_signal (process, SIGTERM);
350 }
351 
352 int
process_get_exit_status(Process * process)353 process_get_exit_status (Process *process)
354 {
355     ProcessPrivate *priv = process_get_instance_private (process);
356     g_return_val_if_fail (process != NULL, -1);
357     return priv->exit_status;
358 }
359 
360 static void
process_init(Process * process)361 process_init (Process *process)
362 {
363     ProcessPrivate *priv = process_get_instance_private (process);
364     priv->env = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
365 }
366 
367 static void
process_stopped(Process * process)368 process_stopped (Process *process)
369 {
370 }
371 
372 static void
process_finalize(GObject * object)373 process_finalize (GObject *object)
374 {
375     Process *self = PROCESS (object);
376     ProcessPrivate *priv = process_get_instance_private (self);
377 
378     if (priv->pid > 0)
379         g_hash_table_remove (processes, GINT_TO_POINTER (priv->pid));
380 
381     g_clear_pointer (&priv->log_file, g_free);
382     g_clear_pointer (&priv->command, g_free);
383     g_hash_table_unref (priv->env);
384     if (priv->quit_timeout)
385         g_source_remove (priv->quit_timeout);
386     if (priv->watch)
387         g_source_remove (priv->watch);
388 
389     if (priv->pid)
390         kill (priv->pid, SIGTERM);
391 
392     G_OBJECT_CLASS (process_parent_class)->finalize (object);
393 }
394 
395 static void
signal_cb(int signum,siginfo_t * info,void * data)396 signal_cb (int signum, siginfo_t *info, void *data)
397 {
398     /* Check if we are from a forked process that hasn't updated the signal handlers or execed.
399        If so, then we should just quit */
400     if (getpid () != signal_pid)
401         _exit (EXIT_SUCCESS);
402 
403     /* Write signal to main thread, if something goes wrong just close the pipe so it is detected on the other end */
404     if (write (signal_pipe[1], &info->si_signo, sizeof (int)) < 0 ||
405         write (signal_pipe[1], &info->si_pid, sizeof (pid_t)) < 0)
406         close (signal_pipe[1]);
407 }
408 
409 static gboolean
handle_signal(GIOChannel * source,GIOCondition condition,gpointer data)410 handle_signal (GIOChannel *source, GIOCondition condition, gpointer data)
411 {
412     errno = 0;
413     int signo;
414     pid_t pid;
415     if (read (signal_pipe[0], &signo, sizeof (int)) != sizeof (int) ||
416         read (signal_pipe[0], &pid, sizeof (pid_t)) != sizeof (pid_t))
417     {
418         g_warning ("Error reading from signal pipe: %s", strerror (errno));
419         return FALSE;
420     }
421 
422     g_debug ("Got signal %d from process %d", signo, pid);
423 
424     Process *process = g_hash_table_lookup (processes, GINT_TO_POINTER (pid));
425     if (process == NULL)
426         process = process_get_current ();
427     if (process)
428         g_signal_emit (process, signals[GOT_SIGNAL], 0, signo);
429 
430     return TRUE;
431 }
432 
433 static void
process_class_init(ProcessClass * klass)434 process_class_init (ProcessClass *klass)
435 {
436     GObjectClass *object_class = G_OBJECT_CLASS (klass);
437     struct sigaction action;
438 
439     klass->stopped = process_stopped;
440     object_class->finalize = process_finalize;
441 
442     signals[GOT_DATA] =
443         g_signal_new (PROCESS_SIGNAL_GOT_DATA,
444                       G_TYPE_FROM_CLASS (klass),
445                       G_SIGNAL_RUN_LAST,
446                       G_STRUCT_OFFSET (ProcessClass, got_data),
447                       NULL, NULL,
448                       NULL,
449                       G_TYPE_NONE, 0);
450     signals[GOT_SIGNAL] =
451         g_signal_new (PROCESS_SIGNAL_GOT_SIGNAL,
452                       G_TYPE_FROM_CLASS (klass),
453                       G_SIGNAL_RUN_LAST,
454                       G_STRUCT_OFFSET (ProcessClass, got_signal),
455                       NULL, NULL,
456                       NULL,
457                       G_TYPE_NONE, 1, G_TYPE_INT);
458     signals[STOPPED] =
459         g_signal_new (PROCESS_SIGNAL_STOPPED,
460                       G_TYPE_FROM_CLASS (klass),
461                       G_SIGNAL_RUN_LAST,
462                       G_STRUCT_OFFSET (ProcessClass, stopped),
463                       NULL, NULL,
464                       NULL,
465                       G_TYPE_NONE, 0);
466 
467     /* Catch signals and feed them to the main loop via a pipe */
468     processes = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref);
469     signal_pid = getpid ();
470     if (pipe (signal_pipe) != 0)
471         g_critical ("Failed to create signal pipe");
472     fcntl (signal_pipe[0], F_SETFD, FD_CLOEXEC);
473     fcntl (signal_pipe[1], F_SETFD, FD_CLOEXEC);
474     g_io_add_watch (g_io_channel_unix_new (signal_pipe[0]), G_IO_IN, handle_signal, NULL);
475     action.sa_sigaction = signal_cb;
476     sigemptyset (&action.sa_mask);
477     action.sa_flags = SA_SIGINFO | SA_RESTART;
478     sigaction (SIGTERM, &action, NULL);
479     sigaction (SIGINT, &action, NULL);
480     sigaction (SIGUSR1, &action, NULL);
481     sigaction (SIGUSR2, &action, NULL);
482 }
483