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 
 371         g_type_init ();
 372 
 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 /** @} */