xref: /illumos-gate/usr/src/cmd/hal/hald/hald.c (revision 069e6b7e31ba5dcbc5441b98af272714d9a5455c)
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