1*2912Sartem /*************************************************************************** 2*2912Sartem * CVSID: $Id$ 3*2912Sartem * 4*2912Sartem * runner.c - Process running code 5*2912Sartem * 6*2912Sartem * Copyright (C) 2006 Sjoerd Simons, <sjoerd@luon.net> 7*2912Sartem * 8*2912Sartem * Licensed under the Academic Free License version 2.1 9*2912Sartem * 10*2912Sartem * This program is free software; you can redistribute it and/or modify 11*2912Sartem * it under the terms of the GNU General Public License as published by 12*2912Sartem * the Free Software Foundation; either version 2 of the License, or 13*2912Sartem * (at your option) any later version. 14*2912Sartem * 15*2912Sartem * This program is distributed in the hope that it will be useful, 16*2912Sartem * but WITHOUT ANY WARRANTY; without even the implied warranty of 17*2912Sartem * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18*2912Sartem * GNU General Public License for more details. 19*2912Sartem * 20*2912Sartem * You should have received a copy of the GNU General Public License 21*2912Sartem * along with this program; if not, write to the Free Software 22*2912Sartem * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 23*2912Sartem * 24*2912Sartem **************************************************************************/ 25*2912Sartem #include <stdio.h> 26*2912Sartem #include <unistd.h> 27*2912Sartem #include <stdlib.h> 28*2912Sartem #include <sys/types.h> 29*2912Sartem #include <sys/stat.h> 30*2912Sartem #include <sys/wait.h> 31*2912Sartem #include <signal.h> 32*2912Sartem #include <string.h> 33*2912Sartem 34*2912Sartem #define DBUS_API_SUBJECT_TO_CHANGE 35*2912Sartem #include <dbus/dbus-glib-lowlevel.h> 36*2912Sartem 37*2912Sartem #include <glib.h> 38*2912Sartem #include "utils.h" 39*2912Sartem #include "runner.h" 40*2912Sartem 41*2912Sartem /* Successful run of the program */ 42*2912Sartem #define HALD_RUN_SUCCESS 0x0 43*2912Sartem /* Process was killed because of running too long */ 44*2912Sartem #define HALD_RUN_TIMEOUT 0x1 45*2912Sartem /* Failed to start for some reason */ 46*2912Sartem #define HALD_RUN_FAILED 0x2 47*2912Sartem /* Killed on purpose, e.g. hal_util_kill_device_helpers */ 48*2912Sartem #define HALD_RUN_KILLED 0x4 49*2912Sartem 50*2912Sartem GHashTable *udi_hash = NULL; 51*2912Sartem 52*2912Sartem typedef struct { 53*2912Sartem run_request *r; 54*2912Sartem DBusMessage *msg; 55*2912Sartem DBusConnection *con; 56*2912Sartem GPid pid; 57*2912Sartem gint stderr_v; 58*2912Sartem guint watch; 59*2912Sartem guint timeout; 60*2912Sartem gboolean sent_kill; 61*2912Sartem gboolean emit_pid_exited; 62*2912Sartem } run_data; 63*2912Sartem 64*2912Sartem static void 65*2912Sartem del_run_data(run_data *rd) 66*2912Sartem { 67*2912Sartem if (rd == NULL) 68*2912Sartem return; 69*2912Sartem 70*2912Sartem del_run_request(rd->r); 71*2912Sartem if (rd->msg) 72*2912Sartem dbus_message_unref(rd->msg); 73*2912Sartem 74*2912Sartem g_spawn_close_pid(rd->pid); 75*2912Sartem 76*2912Sartem if (rd->stderr_v >= 0) 77*2912Sartem close(rd->stderr_v); 78*2912Sartem 79*2912Sartem if (rd->timeout != 0) 80*2912Sartem g_source_remove(rd->timeout); 81*2912Sartem 82*2912Sartem g_free(rd); 83*2912Sartem } 84*2912Sartem 85*2912Sartem run_request * 86*2912Sartem new_run_request(void) 87*2912Sartem { 88*2912Sartem run_request *result; 89*2912Sartem result = g_new0(run_request, 1); 90*2912Sartem g_assert(result != NULL); 91*2912Sartem return result; 92*2912Sartem } 93*2912Sartem 94*2912Sartem void 95*2912Sartem del_run_request(run_request *r) 96*2912Sartem { 97*2912Sartem if (r == NULL) 98*2912Sartem return; 99*2912Sartem g_free(r->udi); 100*2912Sartem free_string_array(r->environment); 101*2912Sartem free_string_array(r->argv); 102*2912Sartem g_free(r->input); 103*2912Sartem g_free(r); 104*2912Sartem } 105*2912Sartem 106*2912Sartem static void 107*2912Sartem send_reply(DBusConnection *con, DBusMessage *msg, guint32 exit_type, gint32 return_code, gchar **error) 108*2912Sartem { 109*2912Sartem DBusMessage *reply; 110*2912Sartem DBusMessageIter iter; 111*2912Sartem int i; 112*2912Sartem 113*2912Sartem if (con == NULL || msg == NULL) 114*2912Sartem return; 115*2912Sartem 116*2912Sartem reply = dbus_message_new_method_return(msg); 117*2912Sartem g_assert(reply != NULL); 118*2912Sartem 119*2912Sartem dbus_message_iter_init_append(reply, &iter); 120*2912Sartem dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT32, &exit_type); 121*2912Sartem dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &return_code); 122*2912Sartem if (error != NULL) for (i = 0; error[i] != NULL; i++) { 123*2912Sartem dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &error[i]); 124*2912Sartem } 125*2912Sartem 126*2912Sartem dbus_connection_send(con, reply, NULL); 127*2912Sartem dbus_message_unref(reply); 128*2912Sartem } 129*2912Sartem 130*2912Sartem static void 131*2912Sartem remove_from_hash_table(run_data *rd) 132*2912Sartem { 133*2912Sartem GList *list; 134*2912Sartem 135*2912Sartem /* Remove to the hashtable */ 136*2912Sartem list = (GList *)g_hash_table_lookup(udi_hash, rd->r->udi); 137*2912Sartem list = g_list_remove(list, rd); 138*2912Sartem /* The hash table will take care to not leak the dupped string */ 139*2912Sartem g_hash_table_insert(udi_hash, g_strdup(rd->r->udi), list); 140*2912Sartem } 141*2912Sartem 142*2912Sartem static void 143*2912Sartem run_exited(GPid pid, gint status, gpointer data) 144*2912Sartem { 145*2912Sartem run_data *rd = (run_data *)data; 146*2912Sartem char **error = NULL; 147*2912Sartem 148*2912Sartem printf("%s exited\n", rd->r->argv[0]); 149*2912Sartem rd->watch = 0; 150*2912Sartem if (rd->sent_kill == TRUE) { 151*2912Sartem /* We send it a kill, so ignore */ 152*2912Sartem del_run_data(rd); 153*2912Sartem return; 154*2912Sartem } 155*2912Sartem /* Check if it was a normal exit */ 156*2912Sartem if (!WIFEXITED(status)) { 157*2912Sartem /* No not normal termination ? crash ? */ 158*2912Sartem send_reply(rd->con, rd->msg, HALD_RUN_FAILED, 0, NULL); 159*2912Sartem goto out; 160*2912Sartem } 161*2912Sartem /* normal exit */ 162*2912Sartem if (rd->stderr_v >= 0) { 163*2912Sartem /* Need to read stderr */ 164*2912Sartem error = get_string_array_from_fd(rd->stderr_v); 165*2912Sartem close(rd->stderr_v); 166*2912Sartem rd->stderr_v = -1; 167*2912Sartem } 168*2912Sartem if (rd->msg != NULL) 169*2912Sartem send_reply(rd->con, rd->msg, HALD_RUN_SUCCESS, WEXITSTATUS(status), error); 170*2912Sartem free_string_array(error); 171*2912Sartem 172*2912Sartem out: 173*2912Sartem remove_from_hash_table(rd); 174*2912Sartem 175*2912Sartem /* emit a signal that this PID exited */ 176*2912Sartem if(rd->con != NULL && rd->emit_pid_exited) { 177*2912Sartem DBusMessage *signal; 178*2912Sartem signal = dbus_message_new_signal ("/org/freedesktop/HalRunner", 179*2912Sartem "org.freedesktop.HalRunner", 180*2912Sartem "StartedProcessExited"); 181*2912Sartem dbus_message_append_args (signal, 182*2912Sartem DBUS_TYPE_INT64, &(rd->pid), 183*2912Sartem DBUS_TYPE_INVALID); 184*2912Sartem dbus_connection_send(rd->con, signal, NULL); 185*2912Sartem } 186*2912Sartem 187*2912Sartem del_run_data(rd); 188*2912Sartem } 189*2912Sartem 190*2912Sartem static gboolean 191*2912Sartem run_timedout(gpointer data) { 192*2912Sartem run_data *rd = (run_data *)data; 193*2912Sartem /* Time is up, kill the process, send reply that it was killed! 194*2912Sartem * Don't wait for exit, because it could hang in state D 195*2912Sartem */ 196*2912Sartem kill(rd->pid, SIGTERM); 197*2912Sartem /* Ensure the timeout is not removed in the delete */ 198*2912Sartem rd->timeout = 0; 199*2912Sartem /* So the exit watch will know it's killed in case it runs*/ 200*2912Sartem rd->sent_kill = TRUE; 201*2912Sartem 202*2912Sartem send_reply(rd->con, rd->msg, HALD_RUN_TIMEOUT, 0, NULL); 203*2912Sartem remove_from_hash_table(rd); 204*2912Sartem return FALSE; 205*2912Sartem } 206*2912Sartem 207*2912Sartem static gboolean 208*2912Sartem find_program(char **argv) 209*2912Sartem { 210*2912Sartem /* Search for the program in the dirs where it's allowed to be */ 211*2912Sartem char *program; 212*2912Sartem char *path = NULL; 213*2912Sartem 214*2912Sartem if (argv[0] == NULL) 215*2912Sartem return FALSE; 216*2912Sartem 217*2912Sartem program = g_path_get_basename(argv[0]); 218*2912Sartem 219*2912Sartem /* first search $PATH to make e.g. run-hald.sh work */ 220*2912Sartem path = g_find_program_in_path (program); 221*2912Sartem g_free(program); 222*2912Sartem if (path == NULL) 223*2912Sartem return FALSE; 224*2912Sartem else { 225*2912Sartem /* Replace program in argv[0] with the full path */ 226*2912Sartem g_free(argv[0]); 227*2912Sartem argv[0] = path; 228*2912Sartem } 229*2912Sartem return TRUE; 230*2912Sartem } 231*2912Sartem 232*2912Sartem /* Run the given request and reply it's result on msg */ 233*2912Sartem gboolean 234*2912Sartem run_request_run (run_request *r, DBusConnection *con, DBusMessage *msg, GPid *out_pid) 235*2912Sartem { 236*2912Sartem GPid pid; 237*2912Sartem GError *error = NULL; 238*2912Sartem gint *stdin_p = NULL; 239*2912Sartem gint *stderr_p = NULL; 240*2912Sartem gint stdin_v; 241*2912Sartem gint stderr_v = -1; 242*2912Sartem run_data *rd = NULL; 243*2912Sartem gboolean program_exists = FALSE; 244*2912Sartem char *program_dir = NULL; 245*2912Sartem GList *list; 246*2912Sartem 247*2912Sartem printf("Run started %s (%d) (%d) \n!", r->argv[0], r->timeout, 248*2912Sartem r->error_on_stderr); 249*2912Sartem if (r->input != NULL) { 250*2912Sartem stdin_p = &stdin_v; 251*2912Sartem } 252*2912Sartem if (r->error_on_stderr) { 253*2912Sartem stderr_p = &stderr_v; 254*2912Sartem } 255*2912Sartem 256*2912Sartem program_exists = find_program(r->argv); 257*2912Sartem 258*2912Sartem if (program_exists) 259*2912Sartem program_dir = g_path_get_dirname (r->argv[0]); 260*2912Sartem 261*2912Sartem printf(" full path is '%s', program_dir is '%s'\n", r->argv[0], program_dir); 262*2912Sartem 263*2912Sartem if (!program_exists || 264*2912Sartem !g_spawn_async_with_pipes(program_dir, r->argv, r->environment, 265*2912Sartem G_SPAWN_DO_NOT_REAP_CHILD, 266*2912Sartem NULL, NULL, &pid, 267*2912Sartem stdin_p, NULL, stderr_p, &error)) { 268*2912Sartem g_free (program_dir); 269*2912Sartem del_run_request(r); 270*2912Sartem if (con && msg) 271*2912Sartem send_reply(con, msg, HALD_RUN_FAILED, 0, NULL); 272*2912Sartem return FALSE; 273*2912Sartem } 274*2912Sartem g_free (program_dir); 275*2912Sartem 276*2912Sartem if (r->input) { 277*2912Sartem if (write(stdin_v, r->input, strlen(r->input)) != (ssize_t) strlen(r->input)); 278*2912Sartem printf("Warning: Error while wite r->input (%s) to stdin_v.\n", r->input); 279*2912Sartem close(stdin_v); 280*2912Sartem } 281*2912Sartem 282*2912Sartem rd = g_new0(run_data,1); 283*2912Sartem g_assert(rd != NULL); 284*2912Sartem rd->r = r; 285*2912Sartem rd->msg = msg; 286*2912Sartem if (msg != NULL) 287*2912Sartem dbus_message_ref(msg); 288*2912Sartem 289*2912Sartem rd->con = con; 290*2912Sartem rd->pid = pid; 291*2912Sartem rd->stderr_v = stderr_v; 292*2912Sartem rd->sent_kill = FALSE; 293*2912Sartem 294*2912Sartem /* Add watch for exit of the program */ 295*2912Sartem rd->watch = g_child_watch_add(pid, run_exited, rd); 296*2912Sartem 297*2912Sartem /* Add timeout if needed */ 298*2912Sartem if (r->timeout > 0) 299*2912Sartem rd->timeout = g_timeout_add(r->timeout, run_timedout, rd); 300*2912Sartem else 301*2912Sartem rd->timeout = 0; 302*2912Sartem 303*2912Sartem /* Add to the hashtable */ 304*2912Sartem list = (GList *)g_hash_table_lookup(udi_hash, r->udi); 305*2912Sartem list = g_list_prepend(list, rd); 306*2912Sartem 307*2912Sartem /* The hash table will take care to not leak the dupped string */ 308*2912Sartem g_hash_table_insert(udi_hash, g_strdup(r->udi), list); 309*2912Sartem 310*2912Sartem /* send back PID if requested.. and only emit StartedProcessExited in this case */ 311*2912Sartem if (out_pid != NULL) { 312*2912Sartem *out_pid = pid; 313*2912Sartem rd->emit_pid_exited = TRUE; 314*2912Sartem } 315*2912Sartem return TRUE; 316*2912Sartem } 317*2912Sartem 318*2912Sartem static void 319*2912Sartem kill_rd(gpointer data, gpointer user_data) 320*2912Sartem { 321*2912Sartem run_data *rd = (run_data *)data; 322*2912Sartem 323*2912Sartem kill(rd->pid, SIGTERM); 324*2912Sartem printf("Sent kill to %d\n", rd->pid); 325*2912Sartem if (rd->timeout != 0) { 326*2912Sartem /* Remove the timeout watch */ 327*2912Sartem g_source_remove(rd->timeout); 328*2912Sartem rd->timeout = 0; 329*2912Sartem } 330*2912Sartem 331*2912Sartem /* So the exit watch will know it's killed in case it runs */ 332*2912Sartem rd->sent_kill = TRUE; 333*2912Sartem 334*2912Sartem if (rd->msg != NULL) 335*2912Sartem send_reply(rd->con, rd->msg, HALD_RUN_KILLED, 0, NULL); 336*2912Sartem } 337*2912Sartem 338*2912Sartem static void 339*2912Sartem do_kill_udi(gchar *udi) 340*2912Sartem { 341*2912Sartem GList *list; 342*2912Sartem list = (GList *)g_hash_table_lookup(udi_hash, udi); 343*2912Sartem g_list_foreach(list, kill_rd, NULL); 344*2912Sartem g_list_free(list); 345*2912Sartem } 346*2912Sartem 347*2912Sartem /* Kill all running request for a udi */ 348*2912Sartem void 349*2912Sartem run_kill_udi(gchar *udi) 350*2912Sartem { 351*2912Sartem do_kill_udi(udi); 352*2912Sartem g_hash_table_remove(udi_hash, udi); 353*2912Sartem } 354*2912Sartem 355*2912Sartem static gboolean 356*2912Sartem hash_kill_udi(gpointer key, gpointer value, gpointer user_data) { 357*2912Sartem do_kill_udi(key); 358*2912Sartem return TRUE; 359*2912Sartem } 360*2912Sartem 361*2912Sartem /* Kill all running request*/ 362*2912Sartem void 363*2912Sartem run_kill_all() 364*2912Sartem { 365*2912Sartem g_hash_table_foreach_remove(udi_hash, hash_kill_udi, NULL); 366*2912Sartem } 367*2912Sartem 368*2912Sartem void 369*2912Sartem run_init() 370*2912Sartem { 371*2912Sartem udi_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); 372*2912Sartem } 373