1 /*************************************************************************** 2 * CVSID: $Id$ 3 * 4 * hald_runner.c - Interface to the hal runner helper daemon 5 * 6 * Copyright (C) 2006 Sjoerd Simons, <sjoerd@luon.net> 7 * 8 * Licensed under the Academic Free License version 2.1 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License as published by 12 * the Free Software Foundation; either version 2 of the License, or 13 * (at your option) any later version. 14 * 15 * This program is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU General Public License for more details. 19 * 20 * You should have received a copy of the GNU General Public License 21 * along with this program; if not, write to the Free Software 22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 23 * 24 **************************************************************************/ 25 26 #ifdef HAVE_CONFIG_H 27 # include <config.h> 28 #endif 29 30 #include <sys/utsname.h> 31 #include <stdio.h> 32 33 #include <glib.h> 34 #include <dbus/dbus.h> 35 #include <dbus/dbus-glib-lowlevel.h> 36 37 #include "hald.h" 38 #include "util.h" 39 #include "logger.h" 40 #include "hald_dbus.h" 41 #include "hald_runner.h" 42 43 typedef struct { 44 HalDevice *d; 45 HalRunTerminatedCB cb; 46 gpointer data1; 47 gpointer data2; 48 } HelperData; 49 50 #define DBUS_SERVER_ADDRESS "unix:tmpdir=" HALD_SOCKET_DIR 51 52 static DBusConnection *runner_connection = NULL; 53 54 typedef struct 55 { 56 GPid pid; 57 HalDevice *device; 58 HalRunTerminatedCB cb; 59 gpointer data1; 60 gpointer data2; 61 } RunningProcess; 62 63 /* mapping from PID to RunningProcess */ 64 static GHashTable *running_processes; 65 66 static gboolean 67 rprd_foreach (gpointer key, 68 gpointer value, 69 gpointer user_data) 70 { 71 gboolean remove = FALSE; 72 RunningProcess *rp = value; 73 HalDevice *device = user_data; 74 75 if (rp->device == device) { 76 remove = TRUE; 77 g_free (rp); 78 } 79 80 return remove; 81 } 82 83 static void 84 running_processes_remove_device (HalDevice *device) 85 { 86 g_hash_table_foreach_remove (running_processes, rprd_foreach, device); 87 } 88 89 void 90 runner_device_finalized (HalDevice *device) 91 { 92 running_processes_remove_device (device); 93 } 94 95 96 static DBusHandlerResult 97 runner_server_message_handler (DBusConnection *connection, 98 DBusMessage *message, 99 void *user_data) 100 { 101 102 /*HAL_INFO (("runner_server_message_handler: destination=%s obj_path=%s interface=%s method=%s", 103 dbus_message_get_destination (message), 104 dbus_message_get_path (message), 105 dbus_message_get_interface (message), 106 dbus_message_get_member (message)));*/ 107 if (dbus_message_is_signal (message, 108 "org.freedesktop.HalRunner", 109 "StartedProcessExited")) { 110 dbus_uint64_t dpid; 111 DBusError error; 112 dbus_error_init (&error); 113 if (dbus_message_get_args (message, &error, 114 DBUS_TYPE_INT64, &dpid, 115 DBUS_TYPE_INVALID)) { 116 RunningProcess *rp; 117 GPid pid; 118 119 pid = (GPid) dpid; 120 121 /*HAL_INFO (("Previously started process with pid %d exited", pid));*/ 122 rp = g_hash_table_lookup (running_processes, (gpointer) pid); 123 if (rp != NULL) { 124 rp->cb (rp->device, 0, 0, NULL, rp->data1, rp->data2); 125 g_hash_table_remove (running_processes, (gpointer) pid); 126 g_free (rp); 127 } 128 } 129 } 130 return DBUS_HANDLER_RESULT_HANDLED; 131 } 132 133 static void 134 runner_server_unregister_handler (DBusConnection *connection, void *user_data) 135 { 136 HAL_INFO (("unregistered")); 137 } 138 139 140 static void 141 handle_connection(DBusServer *server, 142 DBusConnection *new_connection, 143 void *data) 144 { 145 146 if (runner_connection == NULL) { 147 DBusObjectPathVTable vtable = { &runner_server_unregister_handler, 148 &runner_server_message_handler, 149 NULL, NULL, NULL, NULL}; 150 151 runner_connection = new_connection; 152 dbus_connection_ref (new_connection); 153 dbus_connection_setup_with_g_main (new_connection, NULL); 154 155 dbus_connection_register_fallback (new_connection, 156 "/org/freedesktop", 157 &vtable, 158 NULL); 159 160 /* dbus_server_unref(server); */ 161 162 } 163 } 164 165 static void 166 runner_died(GPid pid, gint status, gpointer data) { 167 g_spawn_close_pid (pid); 168 DIE (("Runner died")); 169 } 170 171 gboolean 172 hald_runner_start_runner(void) 173 { 174 DBusServer *server = NULL; 175 DBusError err; 176 GError *error = NULL; 177 GPid pid; 178 char *argv[] = { NULL, NULL}; 179 char *env[] = { NULL, NULL, NULL, NULL}; 180 const char *hald_runner_path; 181 char *server_addr; 182 183 running_processes = g_hash_table_new (g_direct_hash, g_direct_equal); 184 185 dbus_error_init(&err); 186 server = dbus_server_listen(DBUS_SERVER_ADDRESS, &err); 187 if (server == NULL) { 188 HAL_ERROR (("Cannot create D-BUS server for the runner")); 189 goto error; 190 } 191 192 dbus_server_setup_with_g_main(server, NULL); 193 dbus_server_set_new_connection_function(server, handle_connection, 194 NULL, NULL); 195 196 197 argv[0] = "hald-runner"; 198 server_addr = dbus_server_get_address (server); 199 env[0] = g_strdup_printf("HALD_RUNNER_DBUS_ADDRESS=%s", server_addr); 200 dbus_free (server_addr); 201 hald_runner_path = g_getenv("HALD_RUNNER_PATH"); 202 if (hald_runner_path != NULL) { 203 env[1] = g_strdup_printf ("PATH=%s:" PACKAGE_LIBEXEC_DIR ":" PACKAGE_SCRIPT_DIR ":" PACKAGE_BIN_DIR, hald_runner_path); 204 } else { 205 env[1] = g_strdup_printf ("PATH=" PACKAGE_LIBEXEC_DIR ":" PACKAGE_SCRIPT_DIR ":" PACKAGE_BIN_DIR); 206 } 207 208 /*env[2] = "DBUS_VERBOSE=1";*/ 209 210 211 if (!g_spawn_async(NULL, argv, env, G_SPAWN_DO_NOT_REAP_CHILD|G_SPAWN_SEARCH_PATH, 212 NULL, NULL, &pid, &error)) { 213 HAL_ERROR (("Could not spawn runner : '%s'", error->message)); 214 g_error_free (error); 215 goto error; 216 } 217 g_free(env[0]); 218 g_free(env[1]); 219 220 HAL_INFO (("Runner has pid %d", pid)); 221 222 g_child_watch_add(pid, runner_died, NULL); 223 while (runner_connection == NULL) { 224 /* Wait for the runner */ 225 g_main_context_iteration(NULL, TRUE); 226 } 227 return TRUE; 228 229 error: 230 if (server != NULL) 231 dbus_server_unref(server); 232 return FALSE; 233 } 234 235 static gboolean 236 add_property_to_msg (HalDevice *device, HalProperty *property, 237 gpointer user_data) 238 { 239 char *prop_upper, *value; 240 char *c; 241 gchar *env; 242 DBusMessageIter *iter = (DBusMessageIter *)user_data; 243 244 prop_upper = g_ascii_strup (hal_property_get_key (property), -1); 245 246 /* periods aren't valid in the environment, so replace them with 247 * underscores. */ 248 for (c = prop_upper; *c; c++) { 249 if (*c == '.') 250 *c = '_'; 251 } 252 253 value = hal_property_to_string (property); 254 env = g_strdup_printf ("HAL_PROP_%s=%s", prop_upper, value); 255 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &env); 256 257 g_free (env); 258 g_free (value); 259 g_free (prop_upper); 260 261 return TRUE; 262 } 263 264 static void 265 add_env(DBusMessageIter *iter, const gchar *key, const gchar *value) { 266 gchar *env; 267 env = g_strdup_printf ("%s=%s", key, value); 268 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &env); 269 g_free(env); 270 } 271 272 static void 273 add_basic_env(DBusMessageIter *iter, const gchar *udi) { 274 struct utsname un; 275 char *server_addr; 276 277 if (hald_is_verbose) { 278 add_env(iter, "HALD_VERBOSE", "1"); 279 } 280 if (hald_is_initialising) { 281 add_env(iter, "HALD_STARTUP", "1"); 282 } 283 if (hald_use_syslog) { 284 add_env(iter, "HALD_USE_SYSLOG", "1"); 285 } 286 add_env(iter, "UDI", udi); 287 server_addr = hald_dbus_local_server_addr(); 288 add_env(iter, "HALD_DIRECT_ADDR", server_addr); 289 dbus_free (server_addr); 290 #ifdef HAVE_POLKIT 291 add_env(iter, "HAVE_POLKIT", "1"); 292 #endif 293 294 if (uname(&un) >= 0) { 295 char *sysname; 296 297 sysname = g_ascii_strdown(un.sysname, -1); 298 add_env(iter, "HALD_UNAME_S", sysname); 299 g_free(sysname); 300 } 301 } 302 303 static void 304 add_extra_env(DBusMessageIter *iter, gchar **env) { 305 int i; 306 if (env != NULL) for (i = 0; env[i] != NULL; i++) { 307 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &env[i]); 308 } 309 } 310 311 static gboolean 312 add_command(DBusMessageIter *iter, const gchar *command_line) { 313 gint argc; 314 gint x; 315 char **argv; 316 GError *err = NULL; 317 DBusMessageIter array_iter; 318 319 if (!g_shell_parse_argv(command_line, &argc, &argv, &err)) { 320 HAL_ERROR (("Error parsing commandline '%s': %s", 321 command_line, err->message)); 322 g_error_free (err); 323 return FALSE; 324 } 325 if (!dbus_message_iter_open_container(iter, 326 DBUS_TYPE_ARRAY, 327 DBUS_TYPE_STRING_AS_STRING, 328 &array_iter)) 329 DIE (("No memory")); 330 for (x = 0 ; argv[x] != NULL; x++) { 331 dbus_message_iter_append_basic(&array_iter, DBUS_TYPE_STRING, &argv[x]); 332 } 333 dbus_message_iter_close_container(iter, &array_iter); 334 335 g_strfreev(argv); 336 return TRUE; 337 } 338 339 static gboolean 340 add_first_part(DBusMessageIter *iter, HalDevice *device, 341 const gchar *command_line, char **extra_env) { 342 DBusMessageIter array_iter; 343 const char *udi; 344 345 udi = hal_device_get_udi(device); 346 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &udi); 347 348 dbus_message_iter_open_container(iter, 349 DBUS_TYPE_ARRAY, 350 DBUS_TYPE_STRING_AS_STRING, 351 &array_iter); 352 hal_device_property_foreach (device, add_property_to_msg, &array_iter); 353 add_basic_env(&array_iter, udi); 354 add_extra_env(&array_iter, extra_env); 355 dbus_message_iter_close_container(iter, &array_iter); 356 357 if (!add_command(iter, command_line)) { 358 return FALSE; 359 } 360 return TRUE; 361 } 362 363 /* Start a helper, returns true on a successfull start */ 364 gboolean 365 hald_runner_start (HalDevice *device, const gchar *command_line, char **extra_env, 366 HalRunTerminatedCB cb, gpointer data1, gpointer data2) 367 { 368 DBusMessage *msg, *reply; 369 DBusError err; 370 DBusMessageIter iter; 371 372 dbus_error_init(&err); 373 msg = dbus_message_new_method_call("org.freedesktop.HalRunner", 374 "/org/freedesktop/HalRunner", 375 "org.freedesktop.HalRunner", 376 "Start"); 377 if (msg == NULL) 378 DIE(("No memory")); 379 dbus_message_iter_init_append(msg, &iter); 380 381 if (!add_first_part(&iter, device, command_line, extra_env)) 382 goto error; 383 384 /* Wait for the reply, should be almost instantanious */ 385 reply = 386 dbus_connection_send_with_reply_and_block(runner_connection, 387 msg, -1, &err); 388 if (reply) { 389 gboolean ret = 390 (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_METHOD_RETURN); 391 392 if (ret) { 393 dbus_int64_t pid_from_runner; 394 if (dbus_message_get_args (reply, &err, 395 DBUS_TYPE_INT64, &pid_from_runner, 396 DBUS_TYPE_INVALID)) { 397 if (cb != NULL) { 398 RunningProcess *rp; 399 rp = g_new0 (RunningProcess, 1); 400 rp->pid = (GPid) pid_from_runner; 401 rp->cb = cb; 402 rp->device = device; 403 rp->data1 = data1; 404 rp->data2 = data2; 405 406 g_hash_table_insert (running_processes, (gpointer) rp->pid, rp); 407 } 408 } else { 409 HAL_ERROR (("Error extracting out_pid from runner's Start()")); 410 } 411 } 412 413 dbus_message_unref(reply); 414 dbus_message_unref(msg); 415 return ret; 416 } 417 418 error: 419 dbus_message_unref(msg); 420 return FALSE; 421 } 422 423 static void 424 call_notify(DBusPendingCall *pending, void *user_data) 425 { 426 HelperData *hb = (HelperData *)user_data; 427 dbus_uint32_t exitt = HALD_RUN_SUCCESS; 428 dbus_int32_t return_code = 0; 429 DBusMessage *m; 430 GArray *error = NULL; 431 DBusMessageIter iter; 432 433 error = g_array_new(TRUE, FALSE, sizeof(char *)); 434 435 m = dbus_pending_call_steal_reply(pending); 436 if (dbus_message_get_type(m) != DBUS_MESSAGE_TYPE_METHOD_RETURN) 437 goto malformed; 438 439 if (!dbus_message_iter_init(m, &iter) || 440 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32) 441 goto malformed; 442 dbus_message_iter_get_basic(&iter, &exitt); 443 444 if (!dbus_message_iter_next(&iter) || 445 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INT32) 446 goto malformed; 447 dbus_message_iter_get_basic(&iter, &return_code); 448 449 while (dbus_message_iter_next(&iter) && 450 dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING) { 451 const char *value; 452 dbus_message_iter_get_basic(&iter, &value); 453 g_array_append_vals(error, &value, 1); 454 } 455 456 hb->cb(hb->d, exitt, return_code, 457 (gchar **)error->data, hb->data1, hb->data2); 458 459 g_object_unref (hb->d); 460 461 dbus_message_unref(m); 462 dbus_pending_call_unref (pending); 463 g_array_free(error, TRUE); 464 465 return; 466 malformed: 467 /* Send a Fail callback on malformed messages */ 468 HAL_ERROR (("Malformed or unexpected reply message")); 469 hb->cb(hb->d, HALD_RUN_FAILED, return_code, NULL, hb->data1, hb->data2); 470 471 g_object_unref (hb->d); 472 473 dbus_message_unref(m); 474 dbus_pending_call_unref (pending); 475 g_array_free(error, TRUE); 476 } 477 478 /* Run a helper program using the commandline, with input as infomation on 479 * stdin */ 480 void 481 hald_runner_run_method(HalDevice *device, 482 const gchar *command_line, char **extra_env, 483 gchar *input, gboolean error_on_stderr, 484 guint32 timeout, 485 HalRunTerminatedCB cb, 486 gpointer data1, gpointer data2) { 487 DBusMessage *msg; 488 DBusMessageIter iter; 489 DBusPendingCall *call; 490 HelperData *hd = NULL; 491 msg = dbus_message_new_method_call("org.freedesktop.HalRunner", 492 "/org/freedesktop/HalRunner", 493 "org.freedesktop.HalRunner", 494 "Run"); 495 if (msg == NULL) 496 DIE(("No memory")); 497 dbus_message_iter_init_append(msg, &iter); 498 499 if (!add_first_part(&iter, device, command_line, extra_env)) 500 goto error; 501 502 dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &input); 503 dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &error_on_stderr); 504 dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT32, &timeout); 505 506 if (!dbus_connection_send_with_reply(runner_connection, 507 msg, &call, INT_MAX)) 508 DIE (("No memory")); 509 510 /* the connection was disconnected */ 511 if (call == NULL) 512 goto error; 513 514 hd = malloc(sizeof(HelperData)); 515 hd->d = device; 516 hd->cb = cb; 517 hd->data1 = data1; 518 hd->data2 = data2; 519 520 g_object_ref (device); 521 522 dbus_pending_call_set_notify(call, call_notify, hd, free); 523 dbus_message_unref(msg); 524 return; 525 error: 526 dbus_message_unref(msg); 527 free(hd); 528 cb(device, HALD_RUN_FAILED, 0, NULL, data1, data2); 529 } 530 531 void 532 hald_runner_run(HalDevice *device, 533 const gchar *command_line, char **extra_env, 534 guint timeout, 535 HalRunTerminatedCB cb, 536 gpointer data1, gpointer data2) { 537 hald_runner_run_method(device, command_line, extra_env, 538 "", FALSE, timeout, cb, data1, data2); 539 } 540 541 542 543 void 544 hald_runner_kill_device(HalDevice *device) { 545 DBusMessage *msg, *reply; 546 DBusError err; 547 DBusMessageIter iter; 548 const char *udi; 549 550 running_processes_remove_device (device); 551 552 msg = dbus_message_new_method_call("org.freedesktop.HalRunner", 553 "/org/freedesktop/HalRunner", 554 "org.freedesktop.HalRunner", 555 "Kill"); 556 if (msg == NULL) 557 DIE(("No memory")); 558 dbus_message_iter_init_append(msg, &iter); 559 udi = hal_device_get_udi(device); 560 dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &udi); 561 562 /* Wait for the reply, should be almost instantanious */ 563 dbus_error_init(&err); 564 reply = 565 dbus_connection_send_with_reply_and_block(runner_connection, msg, -1, &err); 566 if (reply) { 567 dbus_message_unref(reply); 568 } 569 570 dbus_message_unref(msg); 571 } 572 573 void 574 hald_runner_kill_all(HalDevice *device) { 575 DBusMessage *msg, *reply; 576 DBusError err; 577 578 /* hald_runner has not yet started, just return */ 579 if (runner_connection == NULL) { 580 return; 581 } 582 583 running_processes_remove_device (device); 584 585 msg = dbus_message_new_method_call("org.freedesktop.HalRunner", 586 "/org/freedesktop/HalRunner", 587 "org.freedesktop.HalRunner", 588 "KillAll"); 589 if (msg == NULL) 590 DIE(("No memory")); 591 592 /* Wait for the reply, should be almost instantanious */ 593 dbus_error_init(&err); 594 reply = 595 dbus_connection_send_with_reply_and_block(runner_connection, 596 msg, -1, &err); 597 if (reply) { 598 dbus_message_unref(reply); 599 } 600 601 dbus_message_unref(msg); 602 } 603