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