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