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 static char marker[1] = {'S'}; 245 246 /* write a 'S' character to the other end to tell about 247 * the signal. Note that 'the other end' is a GIOChannel thingy 248 * that is only called from the mainloop - thus this is how we 249 * defer this since UNIX signal handlers are evil 250 * 251 * Oh, and write(2) is indeed reentrant */ 252 (void) write (sigterm_unix_signal_pipe_fds[1], marker, 1); 253 } 254 255 static gboolean 256 sigterm_iochn_data (GIOChannel *source, 257 GIOCondition condition, 258 gpointer user_data) 259 { 260 GError *err = NULL; 261 gchar data[1]; 262 gsize bytes_read; 263 264 /* Empty the pipe */ 265 if (G_IO_STATUS_NORMAL != 266 g_io_channel_read_chars (source, data, 1, &bytes_read, &err)) { 267 HAL_ERROR (("Error emptying sigterm pipe: %s", 268 err->message)); 269 g_error_free (err); 270 goto out; 271 } 272 273 HAL_INFO (("Caught SIGTERM, initiating shutdown")); 274 hald_runner_kill_all(); 275 exit (0); 276 277 out: 278 return TRUE; 279 } 280 281 282 /** This is set to #TRUE if we are probing and #FALSE otherwise */ 283 dbus_bool_t hald_is_initialising; 284 285 static int startup_daemonize_pipe[2]; 286 287 288 /*--------------------------------------------------------------------------------------------------*/ 289 290 static gboolean child_died = FALSE; 291 292 static void 293 handle_sigchld (int value) 294 { 295 child_died = TRUE; 296 } 297 298 static int 299 parent_wait_for_child (int child_fd, pid_t child_pid) 300 { 301 fd_set rfds; 302 fd_set efds; 303 struct timeval tv; 304 int retval; 305 int ret; 306 307 signal(SIGCHLD, handle_sigchld); 308 309 /* wait for either 310 * 311 * o Child writes something to the child_fd; means that device 312 * probing is completed and the parent should exit with success 313 * 314 * o Child is killed (segfault etc.); means that parent should exit 315 * with failure 316 * 317 * o Timeout; means that we should kill the child and exit with 318 * failure 319 * 320 */ 321 322 FD_ZERO(&rfds); 323 FD_SET(child_fd, &rfds); 324 FD_ZERO(&efds); 325 FD_SET(child_fd, &efds); 326 /* Wait up to 250 seconds for device probing */ 327 tv.tv_sec = 250; 328 tv.tv_usec = 0; 329 330 retval = select (child_fd + 1, &rfds, NULL, &efds, &tv); 331 332 if (child_died) { 333 /* written from handle_sigchld */ 334 ret = 1; 335 goto out; 336 } 337 338 if (retval > 0) { 339 /* means child wrote to socket or closed it; all good */ 340 ret = 0; 341 goto out; 342 } 343 344 /* assume timeout; kill child */ 345 kill (child_pid, SIGTERM); 346 ret = 2; 347 348 out: 349 return ret; 350 } 351 352 /*--------------------------------------------------------------------------------------------------*/ 353 354 /** Entry point for HAL daemon 355 * 356 * @param argc Number of arguments 357 * @param argv Array of arguments 358 * @return Exit code 359 */ 360 int 361 main (int argc, char *argv[]) 362 { 363 GMainLoop *loop; 364 char *path; 365 char newpath[512]; 366 367 openlog ("hald", LOG_PID, LOG_DAEMON); 368 #if !GLIB_CHECK_VERSION(2,35,0) 369 g_type_init (); 370 #endif 371 if (getenv ("HALD_VERBOSE")) 372 hald_is_verbose = TRUE; 373 else 374 hald_is_verbose = FALSE; 375 376 /* our helpers are installed into libexec, so adjust out $PATH 377 * to include this at the end (since we want to overide in 378 * run-hald.sh and friends) 379 */ 380 path = getenv ("PATH"); 381 if (path != NULL) { 382 g_strlcpy (newpath, path, sizeof (newpath)); 383 g_strlcat (newpath, ":", sizeof (newpath)); 384 } else { 385 /* No PATH was set */ 386 newpath[0] = '\0'; 387 } 388 389 g_strlcat (newpath, PACKAGE_LIBEXEC_DIR, sizeof (newpath)); 390 g_strlcat (newpath, ":", sizeof (newpath)); 391 g_strlcat (newpath, PACKAGE_SCRIPT_DIR, sizeof (newpath)); 392 393 setenv ("PATH", newpath, TRUE); 394 395 while (1) { 396 int c; 397 int option_index = 0; 398 const char *opt; 399 static struct option long_options[] = { 400 {"daemon", 1, NULL, 0}, 401 {"verbose", 1, NULL, 0}, 402 {"use-syslog", 0, NULL, 0}, 403 {"help", 0, NULL, 0}, 404 {"version", 0, NULL, 0}, 405 {NULL, 0, NULL, 0} 406 }; 407 408 c = getopt_long (argc, argv, "", 409 long_options, &option_index); 410 if (c == -1) 411 break; 412 413 switch (c) { 414 case 0: 415 opt = long_options[option_index].name; 416 417 if (strcmp (opt, "help") == 0) { 418 usage (); 419 return 0; 420 } else if (strcmp (opt, "version") == 0) { 421 fprintf (stderr, "HAL package version: " PACKAGE_VERSION "\n"); 422 return 0; 423 } else if (strcmp (opt, "daemon") == 0) { 424 if (strcmp ("yes", optarg) == 0) { 425 opt_become_daemon = TRUE; 426 } else if (strcmp ("no", optarg) == 0) { 427 opt_become_daemon = FALSE; 428 } else { 429 usage (); 430 return 1; 431 } 432 } else if (strcmp (opt, "verbose") == 0) { 433 if (strcmp ("yes", optarg) == 0) { 434 hald_is_verbose = TRUE; 435 } else if (strcmp ("no", optarg) == 0) { 436 hald_is_verbose = FALSE; 437 } else { 438 usage (); 439 return 1; 440 } 441 } else if (strcmp (opt, "use-syslog") == 0) { 442 hald_use_syslog = TRUE; 443 } 444 445 break; 446 447 default: 448 usage (); 449 return 1; 450 break; 451 } 452 } 453 454 if (hald_is_verbose) 455 logger_enable (); 456 else 457 logger_disable (); 458 459 if (hald_use_syslog) 460 logger_enable_syslog (); 461 else 462 logger_disable_syslog (); 463 464 /* will fork into two; only the child will return here if we are successful */ 465 /*master_slave_setup (); 466 sleep (100000000);*/ 467 468 loop = g_main_loop_new (NULL, FALSE); 469 470 HAL_INFO ((PACKAGE_STRING)); 471 472 if (opt_become_daemon) { 473 int child_pid; 474 int dev_null_fd; 475 int pf; 476 char pid[9]; 477 478 HAL_INFO (("Will daemonize")); 479 HAL_INFO (("Becoming a daemon")); 480 481 if (pipe (startup_daemonize_pipe) != 0) { 482 fprintf (stderr, "Could not setup pipe: %s\n", strerror(errno)); 483 exit (1); 484 } 485 486 487 if (chdir ("/") < 0) { 488 fprintf (stderr, "Could not chdir to /: %s\n", strerror(errno)); 489 exit (1); 490 } 491 492 child_pid = fork (); 493 switch (child_pid) { 494 case -1: 495 fprintf (stderr, "Cannot fork(): %s\n", strerror(errno)); 496 break; 497 498 case 0: 499 /* child */ 500 501 dev_null_fd = open ("/dev/null", O_RDWR); 502 /* ignore if we can't open /dev/null */ 503 if (dev_null_fd >= 0) { 504 /* attach /dev/null to stdout, stdin, stderr */ 505 dup2 (dev_null_fd, 0); 506 dup2 (dev_null_fd, 1); 507 dup2 (dev_null_fd, 2); 508 close (dev_null_fd); 509 } 510 511 umask (022); 512 break; 513 514 default: 515 /* parent, block until child writes */ 516 exit (parent_wait_for_child (startup_daemonize_pipe[0], child_pid)); 517 break; 518 } 519 520 /* Create session */ 521 setsid (); 522 523 /* remove old pid file */ 524 unlink (HALD_PID_FILE); 525 526 /* Make a new one */ 527 if ((pf= open (HALD_PID_FILE, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644)) > 0) { 528 snprintf (pid, sizeof(pid), "%lu\n", (long unsigned) getpid ()); 529 (void) write (pf, pid, strlen(pid)); 530 close (pf); 531 atexit (delete_pid); 532 } 533 } else { 534 HAL_INFO (("Will not daemonize")); 535 } 536 537 538 /* we need to do stuff when we are expected to terminate, thus 539 * this involves looking for SIGTERM; UNIX signal handlers are 540 * evil though, so set up a pipe to transmit the signal. 541 */ 542 543 /* create pipe */ 544 if (pipe (sigterm_unix_signal_pipe_fds) != 0) { 545 DIE (("Could not setup pipe, errno=%d", errno)); 546 } 547 548 /* setup glib handler - 0 is for reading, 1 is for writing */ 549 sigterm_iochn = g_io_channel_unix_new (sigterm_unix_signal_pipe_fds[0]); 550 if (sigterm_iochn == NULL) 551 DIE (("Could not create GIOChannel")); 552 553 /* get callback when there is data to read */ 554 (void) g_io_add_watch ( 555 sigterm_iochn, G_IO_IN, sigterm_iochn_data, NULL); 556 557 /* Finally, setup unix signal handler for TERM */ 558 signal (SIGTERM, handle_sigterm); 559 560 /* set up the local dbus server */ 561 if (!hald_dbus_local_server_init ()) 562 return 1; 563 /* Start the runner helper daemon */ 564 if (!hald_runner_start_runner ()) { 565 return 1; 566 } 567 568 drop_privileges(0); 569 570 /* initialize operating system specific parts */ 571 osspec_init (); 572 573 hald_is_initialising = TRUE; 574 575 /* detect devices */ 576 osspec_probe (); 577 578 /* run the main loop and serve clients */ 579 g_main_loop_run (loop); 580 581 return 0; 582 } 583 584 #ifdef HALD_MEMLEAK_DBG 585 extern int dbg_hal_device_object_delta; 586 587 /* useful for valgrinding; see below */ 588 static gboolean 589 my_shutdown (gpointer data) 590 { 591 HalDeviceStore *gdl; 592 593 printf ("Num devices in TDL: %d\n", g_slist_length ((hald_get_tdl ())->devices)); 594 printf ("Num devices in GDL: %d\n", g_slist_length ((hald_get_gdl ())->devices)); 595 596 gdl = hald_get_gdl (); 597 next: 598 if (g_slist_length (gdl->devices) > 0) { 599 HalDevice *d = HAL_DEVICE(gdl->devices->data); 600 hal_device_store_remove (gdl, d); 601 g_object_unref (d); 602 goto next; 603 } 604 605 printf ("hal_device_object_delta = %d (should be zero)\n", dbg_hal_device_object_delta); 606 exit (1); 607 } 608 #endif 609 610 void 611 osspec_probe_done (void) 612 { 613 char buf[1] = {0}; 614 615 HAL_INFO (("Device probing completed")); 616 617 if (!hald_dbus_init ()) { 618 hald_runner_kill_all(); 619 exit (1); 620 } 621 622 /* tell parent to exit */ 623 (void) write (startup_daemonize_pipe[1], buf, sizeof (buf)); 624 close (startup_daemonize_pipe[0]); 625 close (startup_daemonize_pipe[1]); 626 627 hald_is_initialising = FALSE; 628 629 #ifdef HALD_MEMLEAK_DBG 630 g_timeout_add ((HALD_MEMLEAK_DBG) * 1000, 631 my_shutdown, 632 NULL); 633 #endif 634 } 635 636 637 /** @} */ 638