xref: /illumos-gate/usr/src/cmd/hotplugd/hotplugd.c (revision 26947304)
1*26947304SEvan Yan /*
2*26947304SEvan Yan  * CDDL HEADER START
3*26947304SEvan Yan  *
4*26947304SEvan Yan  * The contents of this file are subject to the terms of the
5*26947304SEvan Yan  * Common Development and Distribution License (the "License").
6*26947304SEvan Yan  * You may not use this file except in compliance with the License.
7*26947304SEvan Yan  *
8*26947304SEvan Yan  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*26947304SEvan Yan  * or http://www.opensolaris.org/os/licensing.
10*26947304SEvan Yan  * See the License for the specific language governing permissions
11*26947304SEvan Yan  * and limitations under the License.
12*26947304SEvan Yan  *
13*26947304SEvan Yan  * When distributing Covered Code, include this CDDL HEADER in each
14*26947304SEvan Yan  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*26947304SEvan Yan  * If applicable, add the following below this CDDL HEADER, with the
16*26947304SEvan Yan  * fields enclosed by brackets "[]" replaced with your own identifying
17*26947304SEvan Yan  * information: Portions Copyright [yyyy] [name of copyright owner]
18*26947304SEvan Yan  *
19*26947304SEvan Yan  * CDDL HEADER END
20*26947304SEvan Yan  */
21*26947304SEvan Yan 
22*26947304SEvan Yan /*
23*26947304SEvan Yan  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24*26947304SEvan Yan  * Use is subject to license terms.
25*26947304SEvan Yan  */
26*26947304SEvan Yan 
27*26947304SEvan Yan #include <stdio.h>
28*26947304SEvan Yan #include <stdlib.h>
29*26947304SEvan Yan #include <unistd.h>
30*26947304SEvan Yan #include <errno.h>
31*26947304SEvan Yan #include <fcntl.h>
32*26947304SEvan Yan #include <signal.h>
33*26947304SEvan Yan #include <stdarg.h>
34*26947304SEvan Yan #include <strings.h>
35*26947304SEvan Yan #include <syslog.h>
36*26947304SEvan Yan #include <priv.h>
37*26947304SEvan Yan #include <wait.h>
38*26947304SEvan Yan #include <getopt.h>
39*26947304SEvan Yan #include <synch.h>
40*26947304SEvan Yan #include <sys/param.h>
41*26947304SEvan Yan #include <sys/stat.h>
42*26947304SEvan Yan #include <sys/types.h>
43*26947304SEvan Yan #include <libhotplug.h>
44*26947304SEvan Yan #include <libhotplug_impl.h>
45*26947304SEvan Yan #include "hotplugd_impl.h"
46*26947304SEvan Yan 
47*26947304SEvan Yan /*
48*26947304SEvan Yan  * Define long options for command line.
49*26947304SEvan Yan  */
50*26947304SEvan Yan static const struct option lopts[] = {
51*26947304SEvan Yan 	{ "help",	no_argument,	0, '?' },
52*26947304SEvan Yan 	{ "version",	no_argument,	0, 'V' },
53*26947304SEvan Yan 	{ "debug",	no_argument,	0, 'd' },
54*26947304SEvan Yan 	{ 0, 0, 0, 0 }
55*26947304SEvan Yan };
56*26947304SEvan Yan 
57*26947304SEvan Yan /*
58*26947304SEvan Yan  * Local functions.
59*26947304SEvan Yan  */
60*26947304SEvan Yan static void		usage(void);
61*26947304SEvan Yan static boolean_t	check_privileges(void);
62*26947304SEvan Yan static int		daemonize(void);
63*26947304SEvan Yan static void		init_signals(void);
64*26947304SEvan Yan static void		signal_handler(int signum);
65*26947304SEvan Yan static void		shutdown_daemon(void);
66*26947304SEvan Yan 
67*26947304SEvan Yan /*
68*26947304SEvan Yan  * Global variables.
69*26947304SEvan Yan  */
70*26947304SEvan Yan static char		*prog;
71*26947304SEvan Yan static char		version[] = "1.0";
72*26947304SEvan Yan static boolean_t	log_flag = B_FALSE;
73*26947304SEvan Yan static boolean_t	debug_flag = B_FALSE;
74*26947304SEvan Yan static boolean_t	exit_flag = B_FALSE;
75*26947304SEvan Yan static sema_t		signal_sem;
76*26947304SEvan Yan 
77*26947304SEvan Yan /*
78*26947304SEvan Yan  * main()
79*26947304SEvan Yan  *
80*26947304SEvan Yan  *	The hotplug daemon is designed to be a background daemon
81*26947304SEvan Yan  *	controlled by SMF.  So by default it will daemonize and
82*26947304SEvan Yan  *	do some coordination with its parent process in order to
83*26947304SEvan Yan  *	indicate proper success or failure back to SMF.  And all
84*26947304SEvan Yan  *	output will be sent to syslog.
85*26947304SEvan Yan  *
86*26947304SEvan Yan  *	But if given the '-d' command line option, it will instead
87*26947304SEvan Yan  *	run in the foreground in a standalone, debug mode.  Errors
88*26947304SEvan Yan  *	and additional debug messages will be printed to the controlling
89*26947304SEvan Yan  *	terminal instead of to syslog.
90*26947304SEvan Yan  */
91*26947304SEvan Yan int
main(int argc,char * argv[])92*26947304SEvan Yan main(int argc, char *argv[])
93*26947304SEvan Yan {
94*26947304SEvan Yan 	int	opt;
95*26947304SEvan Yan 	int	pfd;
96*26947304SEvan Yan 	int	status;
97*26947304SEvan Yan 
98*26947304SEvan Yan 	if ((prog = strrchr(argv[0], '/')) == NULL)
99*26947304SEvan Yan 		prog = argv[0];
100*26947304SEvan Yan 	else
101*26947304SEvan Yan 		prog++;
102*26947304SEvan Yan 
103*26947304SEvan Yan 	/* Check privileges */
104*26947304SEvan Yan 	if (!check_privileges()) {
105*26947304SEvan Yan 		(void) fprintf(stderr, "Insufficient privileges.  "
106*26947304SEvan Yan 		    "(All privileges are required.)\n");
107*26947304SEvan Yan 		return (-1);
108*26947304SEvan Yan 	}
109*26947304SEvan Yan 
110*26947304SEvan Yan 	/* Process options  */
111*26947304SEvan Yan 	while ((opt = getopt_clip(argc, argv, "dV?", lopts, NULL)) != -1) {
112*26947304SEvan Yan 		switch (opt) {
113*26947304SEvan Yan 		case 'd':
114*26947304SEvan Yan 			debug_flag = B_TRUE;
115*26947304SEvan Yan 			break;
116*26947304SEvan Yan 		case 'V':
117*26947304SEvan Yan 			(void) printf("%s: Version %s\n", prog, version);
118*26947304SEvan Yan 			return (0);
119*26947304SEvan Yan 		default:
120*26947304SEvan Yan 			if (optopt == '?') {
121*26947304SEvan Yan 				usage();
122*26947304SEvan Yan 				return (0);
123*26947304SEvan Yan 			}
124*26947304SEvan Yan 			(void) fprintf(stderr, "Unrecognized option '%c'.\n",
125*26947304SEvan Yan 			    optopt);
126*26947304SEvan Yan 			usage();
127*26947304SEvan Yan 			return (-1);
128*26947304SEvan Yan 		}
129*26947304SEvan Yan 	}
130*26947304SEvan Yan 
131*26947304SEvan Yan 	/* Initialize semaphore for daemon shutdown */
132*26947304SEvan Yan 	if (sema_init(&signal_sem, 1, USYNC_THREAD, NULL) != 0)
133*26947304SEvan Yan 		exit(EXIT_FAILURE);
134*26947304SEvan Yan 
135*26947304SEvan Yan 	/* Initialize signal handling */
136*26947304SEvan Yan 	init_signals();
137*26947304SEvan Yan 
138*26947304SEvan Yan 	/* Daemonize, if not in DEBUG mode */
139*26947304SEvan Yan 	if (!debug_flag)
140*26947304SEvan Yan 		pfd = daemonize();
141*26947304SEvan Yan 
142*26947304SEvan Yan 	/* Initialize door service */
143*26947304SEvan Yan 	if (!door_server_init()) {
144*26947304SEvan Yan 		if (!debug_flag) {
145*26947304SEvan Yan 			status = EXIT_FAILURE;
146*26947304SEvan Yan 			(void) write(pfd, &status, sizeof (status));
147*26947304SEvan Yan 			(void) close(pfd);
148*26947304SEvan Yan 		}
149*26947304SEvan Yan 		exit(EXIT_FAILURE);
150*26947304SEvan Yan 	}
151*26947304SEvan Yan 
152*26947304SEvan Yan 	/* Daemon initialized */
153*26947304SEvan Yan 	if (!debug_flag) {
154*26947304SEvan Yan 		status = 0;
155*26947304SEvan Yan 		(void) write(pfd, &status, sizeof (status));
156*26947304SEvan Yan 		(void) close(pfd);
157*26947304SEvan Yan 	}
158*26947304SEvan Yan 
159*26947304SEvan Yan 	/* Note that daemon is running */
160*26947304SEvan Yan 	log_info("hotplug daemon started.\n");
161*26947304SEvan Yan 
162*26947304SEvan Yan 	/* Wait for shutdown signal */
163*26947304SEvan Yan 	while (!exit_flag)
164*26947304SEvan Yan 		(void) sema_wait(&signal_sem);
165*26947304SEvan Yan 
166*26947304SEvan Yan 	shutdown_daemon();
167*26947304SEvan Yan 	return (0);
168*26947304SEvan Yan }
169*26947304SEvan Yan 
170*26947304SEvan Yan /*
171*26947304SEvan Yan  * usage()
172*26947304SEvan Yan  *
173*26947304SEvan Yan  *	Print a brief usage synopsis for the command line options.
174*26947304SEvan Yan  */
175*26947304SEvan Yan static void
usage(void)176*26947304SEvan Yan usage(void)
177*26947304SEvan Yan {
178*26947304SEvan Yan 	(void) printf("Usage: %s [-d]\n", prog);
179*26947304SEvan Yan }
180*26947304SEvan Yan 
181*26947304SEvan Yan /*
182*26947304SEvan Yan  * check_privileges()
183*26947304SEvan Yan  *
184*26947304SEvan Yan  *	Check if the current process has enough privileges
185*26947304SEvan Yan  *	to run the daemon.  Note that all privileges are
186*26947304SEvan Yan  *	required in order for RCM interactions to work.
187*26947304SEvan Yan  */
188*26947304SEvan Yan static boolean_t
check_privileges(void)189*26947304SEvan Yan check_privileges(void)
190*26947304SEvan Yan {
191*26947304SEvan Yan 	priv_set_t	*privset;
192*26947304SEvan Yan 	boolean_t	rv = B_FALSE;
193*26947304SEvan Yan 
194*26947304SEvan Yan 	if ((privset = priv_allocset()) != NULL) {
195*26947304SEvan Yan 		if (getppriv(PRIV_EFFECTIVE, privset) == 0) {
196*26947304SEvan Yan 			rv = priv_isfullset(privset);
197*26947304SEvan Yan 		}
198*26947304SEvan Yan 		priv_freeset(privset);
199*26947304SEvan Yan 	}
200*26947304SEvan Yan 
201*26947304SEvan Yan 	return (rv);
202*26947304SEvan Yan }
203*26947304SEvan Yan 
204*26947304SEvan Yan /*
205*26947304SEvan Yan  * daemonize()
206*26947304SEvan Yan  *
207*26947304SEvan Yan  *	Fork the daemon process into the background, and detach from
208*26947304SEvan Yan  *	the controlling terminal.  Setup a shared pipe that will later
209*26947304SEvan Yan  *	be used to report startup status to the parent process.
210*26947304SEvan Yan  */
211*26947304SEvan Yan static int
daemonize(void)212*26947304SEvan Yan daemonize(void)
213*26947304SEvan Yan {
214*26947304SEvan Yan 	int		status;
215*26947304SEvan Yan 	int		pfds[2];
216*26947304SEvan Yan 	pid_t		pid;
217*26947304SEvan Yan 	sigset_t	set;
218*26947304SEvan Yan 	sigset_t	oset;
219*26947304SEvan Yan 
220*26947304SEvan Yan 	/*
221*26947304SEvan Yan 	 * Temporarily block all signals.  They will remain blocked in
222*26947304SEvan Yan 	 * the parent, but will be unblocked in the child once it has
223*26947304SEvan Yan 	 * notified the parent of its startup status.
224*26947304SEvan Yan 	 */
225*26947304SEvan Yan 	(void) sigfillset(&set);
226*26947304SEvan Yan 	(void) sigdelset(&set, SIGABRT);
227*26947304SEvan Yan 	(void) sigprocmask(SIG_BLOCK, &set, &oset);
228*26947304SEvan Yan 
229*26947304SEvan Yan 	/* Create the shared pipe */
230*26947304SEvan Yan 	if (pipe(pfds) == -1) {
231*26947304SEvan Yan 		log_err("Cannot create pipe (%s)\n", strerror(errno));
232*26947304SEvan Yan 		exit(EXIT_FAILURE);
233*26947304SEvan Yan 	}
234*26947304SEvan Yan 
235*26947304SEvan Yan 	/* Fork the daemon process */
236*26947304SEvan Yan 	if ((pid = fork()) == -1) {
237*26947304SEvan Yan 		log_err("Cannot fork daemon process (%s)\n", strerror(errno));
238*26947304SEvan Yan 		exit(EXIT_FAILURE);
239*26947304SEvan Yan 	}
240*26947304SEvan Yan 
241*26947304SEvan Yan 	/* Parent:  waits for exit status from child. */
242*26947304SEvan Yan 	if (pid > 0) {
243*26947304SEvan Yan 		(void) close(pfds[1]);
244*26947304SEvan Yan 		if (read(pfds[0], &status, sizeof (status)) == sizeof (status))
245*26947304SEvan Yan 			_exit(status);
246*26947304SEvan Yan 		if ((waitpid(pid, &status, 0) == pid) && WIFEXITED(status))
247*26947304SEvan Yan 			_exit(WEXITSTATUS(status));
248*26947304SEvan Yan 		log_err("Failed to spawn daemon process.\n");
249*26947304SEvan Yan 		_exit(EXIT_FAILURE);
250*26947304SEvan Yan 	}
251*26947304SEvan Yan 
252*26947304SEvan Yan 	/* Child continues... */
253*26947304SEvan Yan 
254*26947304SEvan Yan 	(void) setsid();
255*26947304SEvan Yan 	(void) chdir("/");
256*26947304SEvan Yan 	(void) umask(CMASK);
257*26947304SEvan Yan 	(void) sigprocmask(SIG_SETMASK, &oset, NULL);
258*26947304SEvan Yan 	(void) close(pfds[0]);
259*26947304SEvan Yan 
260*26947304SEvan Yan 	/* Detach from controlling terminal */
261*26947304SEvan Yan 	(void) close(0);
262*26947304SEvan Yan 	(void) close(1);
263*26947304SEvan Yan 	(void) close(2);
264*26947304SEvan Yan 	(void) open("/dev/null", O_RDONLY);
265*26947304SEvan Yan 	(void) open("/dev/null", O_WRONLY);
266*26947304SEvan Yan 	(void) open("/dev/null", O_WRONLY);
267*26947304SEvan Yan 
268*26947304SEvan Yan 	/* Use syslog for future messages */
269*26947304SEvan Yan 	log_flag = B_TRUE;
270*26947304SEvan Yan 	openlog(prog, LOG_PID, LOG_DAEMON);
271*26947304SEvan Yan 
272*26947304SEvan Yan 	return (pfds[1]);
273*26947304SEvan Yan }
274*26947304SEvan Yan 
275*26947304SEvan Yan /*
276*26947304SEvan Yan  * init_signals()
277*26947304SEvan Yan  *
278*26947304SEvan Yan  *	Initialize signal handling.
279*26947304SEvan Yan  */
280*26947304SEvan Yan static void
init_signals(void)281*26947304SEvan Yan init_signals(void)
282*26947304SEvan Yan {
283*26947304SEvan Yan 	struct sigaction	act;
284*26947304SEvan Yan 	sigset_t		set;
285*26947304SEvan Yan 
286*26947304SEvan Yan 	(void) sigfillset(&set);
287*26947304SEvan Yan 	(void) sigdelset(&set, SIGABRT);
288*26947304SEvan Yan 
289*26947304SEvan Yan 	(void) sigfillset(&act.sa_mask);
290*26947304SEvan Yan 	act.sa_handler = signal_handler;
291*26947304SEvan Yan 	act.sa_flags = 0;
292*26947304SEvan Yan 
293*26947304SEvan Yan 	(void) sigaction(SIGTERM, &act, NULL);
294*26947304SEvan Yan 	(void) sigaction(SIGHUP, &act, NULL);
295*26947304SEvan Yan 	(void) sigaction(SIGINT, &act, NULL);
296*26947304SEvan Yan 	(void) sigaction(SIGPIPE, &act, NULL);
297*26947304SEvan Yan 
298*26947304SEvan Yan 	(void) sigdelset(&set, SIGTERM);
299*26947304SEvan Yan 	(void) sigdelset(&set, SIGHUP);
300*26947304SEvan Yan 	(void) sigdelset(&set, SIGINT);
301*26947304SEvan Yan 	(void) sigdelset(&set, SIGPIPE);
302*26947304SEvan Yan }
303*26947304SEvan Yan 
304*26947304SEvan Yan /*
305*26947304SEvan Yan  * signal_handler()
306*26947304SEvan Yan  *
307*26947304SEvan Yan  *	Most signals cause the hotplug daemon to shut down.
308*26947304SEvan Yan  *	Shutdown is triggered using a semaphore to wake up
309*26947304SEvan Yan  *	the main thread for a clean exit.
310*26947304SEvan Yan  *
311*26947304SEvan Yan  *	Except SIGPIPE is used to coordinate between the parent
312*26947304SEvan Yan  *	and child processes when the daemon first starts.
313*26947304SEvan Yan  */
314*26947304SEvan Yan static void
signal_handler(int signum)315*26947304SEvan Yan signal_handler(int signum)
316*26947304SEvan Yan {
317*26947304SEvan Yan 	log_info("Received signal %d.\n", signum);
318*26947304SEvan Yan 
319*26947304SEvan Yan 	switch (signum) {
320*26947304SEvan Yan 	case 0:
321*26947304SEvan Yan 	case SIGPIPE:
322*26947304SEvan Yan 		break;
323*26947304SEvan Yan 	default:
324*26947304SEvan Yan 		exit_flag = B_TRUE;
325*26947304SEvan Yan 		(void) sema_post(&signal_sem);
326*26947304SEvan Yan 		break;
327*26947304SEvan Yan 	}
328*26947304SEvan Yan }
329*26947304SEvan Yan 
330*26947304SEvan Yan /*
331*26947304SEvan Yan  * shutdown_daemon()
332*26947304SEvan Yan  *
333*26947304SEvan Yan  *	Perform a clean shutdown of the daemon.
334*26947304SEvan Yan  */
335*26947304SEvan Yan static void
shutdown_daemon(void)336*26947304SEvan Yan shutdown_daemon(void)
337*26947304SEvan Yan {
338*26947304SEvan Yan 	log_info("Hotplug daemon shutting down.\n");
339*26947304SEvan Yan 
340*26947304SEvan Yan 	door_server_fini();
341*26947304SEvan Yan 
342*26947304SEvan Yan 	if (log_flag)
343*26947304SEvan Yan 		closelog();
344*26947304SEvan Yan 
345*26947304SEvan Yan 	(void) sema_destroy(&signal_sem);
346*26947304SEvan Yan }
347*26947304SEvan Yan 
348*26947304SEvan Yan /*
349*26947304SEvan Yan  * log_err()
350*26947304SEvan Yan  *
351*26947304SEvan Yan  *	Display an error message.  Use syslog if in daemon
352*26947304SEvan Yan  *	mode, otherwise print to stderr when in debug mode.
353*26947304SEvan Yan  */
354*26947304SEvan Yan /*PRINTFLIKE1*/
355*26947304SEvan Yan void
log_err(char * fmt,...)356*26947304SEvan Yan log_err(char *fmt, ...)
357*26947304SEvan Yan {
358*26947304SEvan Yan 	va_list	ap;
359*26947304SEvan Yan 
360*26947304SEvan Yan 	va_start(ap, fmt);
361*26947304SEvan Yan 	if (debug_flag || !log_flag)
362*26947304SEvan Yan 		(void) vfprintf(stderr, fmt, ap);
363*26947304SEvan Yan 	else
364*26947304SEvan Yan 		vsyslog(LOG_ERR, fmt, ap);
365*26947304SEvan Yan 	va_end(ap);
366*26947304SEvan Yan }
367*26947304SEvan Yan 
368*26947304SEvan Yan /*
369*26947304SEvan Yan  * log_info()
370*26947304SEvan Yan  *
371*26947304SEvan Yan  *	Display an information message.  Use syslog if in daemon
372*26947304SEvan Yan  *	mode, otherwise print to stdout when in debug mode.
373*26947304SEvan Yan  */
374*26947304SEvan Yan /*PRINTFLIKE1*/
375*26947304SEvan Yan void
log_info(char * fmt,...)376*26947304SEvan Yan log_info(char *fmt, ...)
377*26947304SEvan Yan {
378*26947304SEvan Yan 	va_list ap;
379*26947304SEvan Yan 
380*26947304SEvan Yan 	va_start(ap, fmt);
381*26947304SEvan Yan 	if (debug_flag || !log_flag)
382*26947304SEvan Yan 		(void) vfprintf(stdout, fmt, ap);
383*26947304SEvan Yan 	else
384*26947304SEvan Yan 		vsyslog(LOG_INFO, fmt, ap);
385*26947304SEvan Yan 	va_end(ap);
386*26947304SEvan Yan }
387*26947304SEvan Yan 
388*26947304SEvan Yan /*
389*26947304SEvan Yan  * dprintf()
390*26947304SEvan Yan  *
391*26947304SEvan Yan  *	Print a debug tracing statement.  Only works in debug
392*26947304SEvan Yan  *	mode, and always prints to stdout.
393*26947304SEvan Yan  */
394*26947304SEvan Yan /*PRINTFLIKE1*/
395*26947304SEvan Yan void
dprintf(char * fmt,...)396*26947304SEvan Yan dprintf(char *fmt, ...)
397*26947304SEvan Yan {
398*26947304SEvan Yan 	va_list	ap;
399*26947304SEvan Yan 
400*26947304SEvan Yan 	if (debug_flag) {
401*26947304SEvan Yan 		va_start(ap, fmt);
402*26947304SEvan Yan 		(void) vprintf(fmt, ap);
403*26947304SEvan Yan 		va_end(ap);
404*26947304SEvan Yan 	}
405*26947304SEvan Yan }
406