1 /*************************************************************************** 2 * CVSID: $Id$ 3 * 4 * hald.c : main startup for HAL daemon 5 * 6 * Copyright (C) 2003 David Zeuthen, <david@fubar.dk> 7 * Copyright (C) 2005 Danny Kukawka, <danny.kukawka@web.de> 8 * 9 * Licensed under the Academic Free License version 2.1 10 * 11 * This program is free software; you can redistribute it and/or modify 12 * it under the terms of the GNU General Public License as published by 13 * the Free Software Foundation; either version 2 of the License, or 14 * (at your option) any later version. 15 * 16 * This program is distributed in the hope that it will be useful, 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 * GNU General Public License for more details. 20 * 21 * You should have received a copy of the GNU General Public License 22 * along with this program; if not, write to the Free Software 23 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 24 * 25 **************************************************************************/ 26 27 #ifdef HAVE_CONFIG_H 28 # include <config.h> 29 #endif 30 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <string.h> 34 #include <unistd.h> 35 #include <getopt.h> 36 #include <pwd.h> 37 #include <stdint.h> 38 #include <sys/stat.h> 39 #include <fcntl.h> 40 #include <errno.h> 41 #include <signal.h> 42 #include <grp.h> 43 #include <syslog.h> 44 45 #include <dbus/dbus.h> 46 #include <dbus/dbus-glib.h> 47 #include <dbus/dbus-glib-lowlevel.h> 48 49 /*#include "master_slave.h"*/ 50 51 #include "logger.h" 52 #include "hald.h" 53 #include "device_store.h" 54 #include "device_info.h" 55 #include "osspec.h" 56 #include "hald_dbus.h" 57 #include "util.h" 58 #include "hald_runner.h" 59 #include "util_helper.h" 60 61 static void delete_pid(void) 62 { 63 unlink(HALD_PID_FILE); 64 } 65 66 /** 67 * @defgroup HalDaemon HAL daemon 68 * @brief The HAL daemon manages persistent device objects available through 69 * a D-BUS network API 70 */ 71 72 static HalDeviceStore *global_device_list = NULL; 73 74 static HalDeviceStore *temporary_device_list = NULL; 75 76 77 static void 78 addon_terminated (HalDevice *device, guint32 exit_type, 79 gint return_code, gchar **error, 80 gpointer data1, gpointer data2) 81 { 82 HAL_INFO (("in addon_terminated for udi=%s", device->udi)); 83 84 /* TODO: log to syslog - addons shouldn't just terminate, this is a bug with the addon */ 85 86 /* however, the world can stop, mark this addon as ready 87 * (TODO: potential bug if the addon crashed after calling libhal_device_addon_is_ready()) 88 */ 89 if (hal_device_inc_num_ready_addons (device)) { 90 if (hal_device_are_all_addons_ready (device)) { 91 manager_send_signal_device_added (device); 92 } 93 } 94 } 95 96 97 98 99 static void 100 gdl_store_changed (HalDeviceStore *store, HalDevice *device, 101 gboolean is_added, gpointer user_data) 102 { 103 if (is_added) { 104 GSList *addons; 105 106 HAL_INFO (("Added device to GDL; udi=%s", hal_device_get_udi(device))); 107 108 if ((addons = hal_device_property_get_strlist (device, "info.addons")) != NULL) { 109 GSList *i; 110 111 for (i = addons; i != NULL; i = g_slist_next (i)) { 112 const gchar *command_line; 113 gchar *extra_env[2] = {"HALD_ACTION=addon", NULL}; 114 115 command_line = (const gchar *) i->data; 116 if (hald_runner_start(device, command_line, extra_env, addon_terminated, NULL, NULL)) { 117 HAL_INFO (("Started addon %s for udi %s", 118 command_line, hal_device_get_udi(device))); 119 hal_device_inc_num_addons (device); 120 } else { 121 HAL_ERROR (("Cannot start addon %s for udi %s", 122 command_line, hal_device_get_udi(device))); 123 } 124 } 125 } 126 } else { 127 HAL_INFO (("Removed device from GDL; udi=%s", hal_device_get_udi(device))); 128 hald_runner_kill_device(device); 129 } 130 131 /*hal_device_print (device);*/ 132 133 if (is_added) { 134 if (hal_device_are_all_addons_ready (device)) { 135 manager_send_signal_device_added (device); 136 } 137 } else { 138 if (hal_device_are_all_addons_ready (device)) { 139 manager_send_signal_device_removed (device); 140 } 141 } 142 } 143 144 static void 145 gdl_property_changed (HalDeviceStore *store, HalDevice *device, 146 const char *key, gboolean added, gboolean removed, 147 gpointer user_data) 148 { 149 if (hal_device_are_all_addons_ready (device)) { 150 device_send_signal_property_modified (device, key, removed, added); 151 } 152 153 /* only execute the callouts if the property _changed_ */ 154 if (added == FALSE && removed == FALSE) 155 /*hal_callout_property (device, key)*/; 156 } 157 158 static void 159 gdl_capability_added (HalDeviceStore *store, HalDevice *device, 160 const char *capability, gpointer user_data) 161 { 162 if (hal_device_are_all_addons_ready (device)) { 163 manager_send_signal_new_capability (device, capability); 164 } 165 /*hal_callout_capability (device, capability, TRUE)*/; 166 } 167 168 HalDeviceStore * 169 hald_get_gdl (void) 170 { 171 if (global_device_list == NULL) { 172 global_device_list = hal_device_store_new (); 173 174 g_signal_connect (global_device_list, 175 "store_changed", 176 G_CALLBACK (gdl_store_changed), NULL); 177 g_signal_connect (global_device_list, 178 "device_property_changed", 179 G_CALLBACK (gdl_property_changed), NULL); 180 g_signal_connect (global_device_list, 181 "device_capability_added", 182 G_CALLBACK (gdl_capability_added), NULL); 183 } 184 185 return global_device_list; 186 } 187 188 HalDeviceStore * 189 hald_get_tdl (void) 190 { 191 if (temporary_device_list == NULL) { 192 temporary_device_list = hal_device_store_new (); 193 194 } 195 196 return temporary_device_list; 197 } 198 199 /** 200 * @defgroup MainDaemon Basic functions 201 * @ingroup HalDaemon 202 * @brief Basic functions in the HAL daemon 203 * @{ 204 */ 205 206 /** Print out program usage. 207 * 208 */ 209 static void 210 usage () 211 { 212 fprintf (stderr, "\n" "usage : hald [--daemon=yes|no] [--verbose=yes|no] [--help]\n"); 213 fprintf (stderr, 214 "\n" 215 " --daemon=yes|no Become a daemon\n" 216 " --verbose=yes|no Print out debug (overrides HALD_VERBOSE)\n" 217 " --use-syslog Print out debug messages to syslog instead of stderr.\n" 218 " Use this option to get debug messages if HAL runs as\n" 219 " daemon.\n" 220 " --help Show this information and exit\n" 221 " --version Output version information and exit" 222 "\n" 223 "The HAL daemon detects devices present in the system and provides the\n" 224 "org.freedesktop.Hal service through the system-wide message bus provided\n" 225 "by D-BUS.\n" 226 "\n" 227 "For more information visit http://freedesktop.org/Software/hal\n" 228 "\n"); 229 } 230 231 /** If #TRUE, we will daemonize */ 232 static dbus_bool_t opt_become_daemon = TRUE; 233 234 /** If #TRUE, we will spew out debug */ 235 dbus_bool_t hald_is_verbose = FALSE; 236 dbus_bool_t hald_use_syslog = FALSE; 237 238 static int sigterm_unix_signal_pipe_fds[2]; 239 static GIOChannel *sigterm_iochn; 240 241 static void 242 handle_sigterm (int value) 243 { 244 ssize_t written; 245 static char marker[1] = {'S'}; 246 247 /* write a 'S' character to the other end to tell about 248 * the signal. Note that 'the other end' is a GIOChannel thingy 249 * that is only called from the mainloop - thus this is how we 250 * defer this since UNIX signal handlers are evil 251 * 252 * Oh, and write(2) is indeed reentrant */ 253 written = write (sigterm_unix_signal_pipe_fds[1], marker, 1); 254 } 255 256 static gboolean 257 sigterm_iochn_data (GIOChannel *source, 258 GIOCondition condition, 259 gpointer user_data) 260 { 261 GError *err = NULL; 262 gchar data[1]; 263 gsize bytes_read; 264 265 /* Empty the pipe */ 266 if (G_IO_STATUS_NORMAL != 267 g_io_channel_read_chars (source, data, 1, &bytes_read, &err)) { 268 HAL_ERROR (("Error emptying sigterm pipe: %s", 269 err->message)); 270 g_error_free (err); 271 goto out; 272 } 273 274 HAL_INFO (("Caught SIGTERM, initiating shutdown")); 275 hald_runner_kill_all(); 276 exit (0); 277 278 out: 279 return TRUE; 280 } 281 282 283 /** This is set to #TRUE if we are probing and #FALSE otherwise */ 284 dbus_bool_t hald_is_initialising; 285 286 static int startup_daemonize_pipe[2]; 287 288 289 /*--------------------------------------------------------------------------------------------------*/ 290 291 static gboolean child_died = FALSE; 292 293 static void 294 handle_sigchld (int value) 295 { 296 child_died = TRUE; 297 } 298 299 static int 300 parent_wait_for_child (int child_fd, pid_t child_pid) 301 { 302 fd_set rfds; 303 fd_set efds; 304 struct timeval tv; 305 int retval; 306 int ret; 307 308 signal(SIGCHLD, handle_sigchld); 309 310 /* wait for either 311 * 312 * o Child writes something to the child_fd; means that device 313 * probing is completed and the parent should exit with success 314 * 315 * o Child is killed (segfault etc.); means that parent should exit 316 * with failure 317 * 318 * o Timeout; means that we should kill the child and exit with 319 * failure 320 * 321 */ 322 323 FD_ZERO(&rfds); 324 FD_SET(child_fd, &rfds); 325 FD_ZERO(&efds); 326 FD_SET(child_fd, &efds); 327 /* Wait up to 250 seconds for device probing */ 328 tv.tv_sec = 250; 329 tv.tv_usec = 0; 330 331 retval = select (child_fd + 1, &rfds, NULL, &efds, &tv); 332 333 if (child_died) { 334 /* written from handle_sigchld */ 335 ret = 1; 336 goto out; 337 } 338 339 if (retval > 0) { 340 /* means child wrote to socket or closed it; all good */ 341 ret = 0; 342 goto out; 343 } 344 345 /* assume timeout; kill child */ 346 kill (child_pid, SIGTERM); 347 ret = 2; 348 349 out: 350 return ret; 351 } 352 353 /*--------------------------------------------------------------------------------------------------*/ 354 355 /** Entry point for HAL daemon 356 * 357 * @param argc Number of arguments 358 * @param argv Array of arguments 359 * @return Exit code 360 */ 361 int 362 main (int argc, char *argv[]) 363 { 364 GMainLoop *loop; 365 guint sigterm_iochn_listener_source_id; 366 char *path; 367 char newpath[512]; 368 369 openlog ("hald", LOG_PID, LOG_DAEMON); 370 #if !defined(GLIB_VERSION_2_36) 371 g_type_init (); 372 #endif 373 if (getenv ("HALD_VERBOSE")) 374 hald_is_verbose = TRUE; 375 else 376 hald_is_verbose = FALSE; 377 378 /* our helpers are installed into libexec, so adjust out $PATH 379 * to include this at the end (since we want to overide in 380 * run-hald.sh and friends) 381 */ 382 path = getenv ("PATH"); 383 if (path != NULL) { 384 g_strlcpy (newpath, path, sizeof (newpath)); 385 g_strlcat (newpath, ":", sizeof (newpath)); 386 } else { 387 /* No PATH was set */ 388 newpath[0] = '\0'; 389 } 390 391 g_strlcat (newpath, PACKAGE_LIBEXEC_DIR, sizeof (newpath)); 392 g_strlcat (newpath, ":", sizeof (newpath)); 393 g_strlcat (newpath, PACKAGE_SCRIPT_DIR, sizeof (newpath)); 394 395 setenv ("PATH", newpath, TRUE); 396 397 while (1) { 398 int c; 399 int option_index = 0; 400 const char *opt; 401 static struct option long_options[] = { 402 {"daemon", 1, NULL, 0}, 403 {"verbose", 1, NULL, 0}, 404 {"use-syslog", 0, NULL, 0}, 405 {"help", 0, NULL, 0}, 406 {"version", 0, NULL, 0}, 407 {NULL, 0, NULL, 0} 408 }; 409 410 c = getopt_long (argc, argv, "", 411 long_options, &option_index); 412 if (c == -1) 413 break; 414 415 switch (c) { 416 case 0: 417 opt = long_options[option_index].name; 418 419 if (strcmp (opt, "help") == 0) { 420 usage (); 421 return 0; 422 } else if (strcmp (opt, "version") == 0) { 423 fprintf (stderr, "HAL package version: " PACKAGE_VERSION "\n"); 424 return 0; 425 } else if (strcmp (opt, "daemon") == 0) { 426 if (strcmp ("yes", optarg) == 0) { 427 opt_become_daemon = TRUE; 428 } else if (strcmp ("no", optarg) == 0) { 429 opt_become_daemon = FALSE; 430 } else { 431 usage (); 432 return 1; 433 } 434 } else if (strcmp (opt, "verbose") == 0) { 435 if (strcmp ("yes", optarg) == 0) { 436 hald_is_verbose = TRUE; 437 } else if (strcmp ("no", optarg) == 0) { 438 hald_is_verbose = FALSE; 439 } else { 440 usage (); 441 return 1; 442 } 443 } else if (strcmp (opt, "use-syslog") == 0) { 444 hald_use_syslog = TRUE; 445 } 446 447 break; 448 449 default: 450 usage (); 451 return 1; 452 break; 453 } 454 } 455 456 if (hald_is_verbose) 457 logger_enable (); 458 else 459 logger_disable (); 460 461 if (hald_use_syslog) 462 logger_enable_syslog (); 463 else 464 logger_disable_syslog (); 465 466 /* will fork into two; only the child will return here if we are successful */ 467 /*master_slave_setup (); 468 sleep (100000000);*/ 469 470 loop = g_main_loop_new (NULL, FALSE); 471 472 HAL_INFO ((PACKAGE_STRING)); 473 474 if (opt_become_daemon) { 475 int child_pid; 476 int dev_null_fd; 477 int pf; 478 ssize_t written; 479 char pid[9]; 480 481 HAL_INFO (("Will daemonize")); 482 HAL_INFO (("Becoming a daemon")); 483 484 if (pipe (startup_daemonize_pipe) != 0) { 485 fprintf (stderr, "Could not setup pipe: %s\n", strerror(errno)); 486 exit (1); 487 } 488 489 490 if (chdir ("/") < 0) { 491 fprintf (stderr, "Could not chdir to /: %s\n", strerror(errno)); 492 exit (1); 493 } 494 495 child_pid = fork (); 496 switch (child_pid) { 497 case -1: 498 fprintf (stderr, "Cannot fork(): %s\n", strerror(errno)); 499 break; 500 501 case 0: 502 /* child */ 503 504 dev_null_fd = open ("/dev/null", O_RDWR); 505 /* ignore if we can't open /dev/null */ 506 if (dev_null_fd >= 0) { 507 /* attach /dev/null to stdout, stdin, stderr */ 508 dup2 (dev_null_fd, 0); 509 dup2 (dev_null_fd, 1); 510 dup2 (dev_null_fd, 2); 511 close (dev_null_fd); 512 } 513 514 umask (022); 515 break; 516 517 default: 518 /* parent, block until child writes */ 519 exit (parent_wait_for_child (startup_daemonize_pipe[0], child_pid)); 520 break; 521 } 522 523 /* Create session */ 524 setsid (); 525 526 /* remove old pid file */ 527 unlink (HALD_PID_FILE); 528 529 /* Make a new one */ 530 if ((pf= open (HALD_PID_FILE, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644)) > 0) { 531 snprintf (pid, sizeof(pid), "%lu\n", (long unsigned) getpid ()); 532 written = write (pf, pid, strlen(pid)); 533 close (pf); 534 atexit (delete_pid); 535 } 536 } else { 537 HAL_INFO (("Will not daemonize")); 538 } 539 540 541 /* we need to do stuff when we are expected to terminate, thus 542 * this involves looking for SIGTERM; UNIX signal handlers are 543 * evil though, so set up a pipe to transmit the signal. 544 */ 545 546 /* create pipe */ 547 if (pipe (sigterm_unix_signal_pipe_fds) != 0) { 548 DIE (("Could not setup pipe, errno=%d", errno)); 549 } 550 551 /* setup glib handler - 0 is for reading, 1 is for writing */ 552 sigterm_iochn = g_io_channel_unix_new (sigterm_unix_signal_pipe_fds[0]); 553 if (sigterm_iochn == NULL) 554 DIE (("Could not create GIOChannel")); 555 556 /* get callback when there is data to read */ 557 sigterm_iochn_listener_source_id = g_io_add_watch ( 558 sigterm_iochn, G_IO_IN, sigterm_iochn_data, NULL); 559 560 /* Finally, setup unix signal handler for TERM */ 561 signal (SIGTERM, handle_sigterm); 562 563 /* set up the local dbus server */ 564 if (!hald_dbus_local_server_init ()) 565 return 1; 566 /* Start the runner helper daemon */ 567 if (!hald_runner_start_runner ()) { 568 return 1; 569 } 570 571 drop_privileges(0); 572 573 /* initialize operating system specific parts */ 574 osspec_init (); 575 576 hald_is_initialising = TRUE; 577 578 /* detect devices */ 579 osspec_probe (); 580 581 /* run the main loop and serve clients */ 582 g_main_loop_run (loop); 583 584 return 0; 585 } 586 587 #ifdef HALD_MEMLEAK_DBG 588 extern int dbg_hal_device_object_delta; 589 590 /* useful for valgrinding; see below */ 591 static gboolean 592 my_shutdown (gpointer data) 593 { 594 HalDeviceStore *gdl; 595 596 printf ("Num devices in TDL: %d\n", g_slist_length ((hald_get_tdl ())->devices)); 597 printf ("Num devices in GDL: %d\n", g_slist_length ((hald_get_gdl ())->devices)); 598 599 gdl = hald_get_gdl (); 600 next: 601 if (g_slist_length (gdl->devices) > 0) { 602 HalDevice *d = HAL_DEVICE(gdl->devices->data); 603 hal_device_store_remove (gdl, d); 604 g_object_unref (d); 605 goto next; 606 } 607 608 printf ("hal_device_object_delta = %d (should be zero)\n", dbg_hal_device_object_delta); 609 exit (1); 610 } 611 #endif 612 613 void 614 osspec_probe_done (void) 615 { 616 ssize_t written; 617 char buf[1] = {0}; 618 619 HAL_INFO (("Device probing completed")); 620 621 if (!hald_dbus_init ()) { 622 hald_runner_kill_all(); 623 exit (1); 624 } 625 626 /* tell parent to exit */ 627 written = write (startup_daemonize_pipe[1], buf, sizeof (buf)); 628 close (startup_daemonize_pipe[0]); 629 close (startup_daemonize_pipe[1]); 630 631 hald_is_initialising = FALSE; 632 633 #ifdef HALD_MEMLEAK_DBG 634 g_timeout_add ((HALD_MEMLEAK_DBG) * 1000, 635 my_shutdown, 636 NULL); 637 #endif 638 } 639 640 641 /** @} */