1 /*
2 * CDDL HEADER START
3 *
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.
7 *
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.
12 *
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]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
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"
46
47 /*
48 * Define long options for command line.
49 */
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 }
55 };
56
57 /*
58 * Local functions.
59 */
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);
66
67 /*
68 * Global variables.
69 */
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;
76
77 /*
78 * main()
79 *
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.
85 *
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.
90 */
91 int
main(int argc,char * argv[])92 main(int argc, char *argv[])
93 {
94 int opt;
95 int pfd;
96 int status;
97
98 if ((prog = strrchr(argv[0], '/')) == NULL)
99 prog = argv[0];
100 else
101 prog++;
102
103 /* Check privileges */
104 if (!check_privileges()) {
105 (void) fprintf(stderr, "Insufficient privileges. "
106 "(All privileges are required.)\n");
107 return (-1);
108 }
109
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);
123 }
124 (void) fprintf(stderr, "Unrecognized option '%c'.\n",
125 optopt);
126 usage();
127 return (-1);
128 }
129 }
130
131 /* Initialize semaphore for daemon shutdown */
132 if (sema_init(&signal_sem, 1, USYNC_THREAD, NULL) != 0)
133 exit(EXIT_FAILURE);
134
135 /* Initialize signal handling */
136 init_signals();
137
138 /* Daemonize, if not in DEBUG mode */
139 if (!debug_flag)
140 pfd = daemonize();
141
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);
148 }
149 exit(EXIT_FAILURE);
150 }
151
152 /* Daemon initialized */
153 if (!debug_flag) {
154 status = 0;
155 (void) write(pfd, &status, sizeof (status));
156 (void) close(pfd);
157 }
158
159 /* Note that daemon is running */
160 log_info("hotplug daemon started.\n");
161
162 /* Wait for shutdown signal */
163 while (!exit_flag)
164 (void) sema_wait(&signal_sem);
165
166 shutdown_daemon();
167 return (0);
168 }
169
170 /*
171 * usage()
172 *
173 * Print a brief usage synopsis for the command line options.
174 */
175 static void
usage(void)176 usage(void)
177 {
178 (void) printf("Usage: %s [-d]\n", prog);
179 }
180
181 /*
182 * check_privileges()
183 *
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.
187 */
188 static boolean_t
check_privileges(void)189 check_privileges(void)
190 {
191 priv_set_t *privset;
192 boolean_t rv = B_FALSE;
193
194 if ((privset = priv_allocset()) != NULL) {
195 if (getppriv(PRIV_EFFECTIVE, privset) == 0) {
196 rv = priv_isfullset(privset);
197 }
198 priv_freeset(privset);
199 }
200
201 return (rv);
202 }
203
204 /*
205 * daemonize()
206 *
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.
210 */
211 static int
daemonize(void)212 daemonize(void)
213 {
214 int status;
215 int pfds[2];
216 pid_t pid;
217 sigset_t set;
218 sigset_t oset;
219
220 /*
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.
224 */
225 (void) sigfillset(&set);
226 (void) sigdelset(&set, SIGABRT);
227 (void) sigprocmask(SIG_BLOCK, &set, &oset);
228
229 /* Create the shared pipe */
230 if (pipe(pfds) == -1) {
231 log_err("Cannot create pipe (%s)\n", strerror(errno));
232 exit(EXIT_FAILURE);
233 }
234
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);
239 }
240
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);
250 }
251
252 /* Child continues... */
253
254 (void) setsid();
255 (void) chdir("/");
256 (void) umask(CMASK);
257 (void) sigprocmask(SIG_SETMASK, &oset, NULL);
258 (void) close(pfds[0]);
259
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);
267
268 /* Use syslog for future messages */
269 log_flag = B_TRUE;
270 openlog(prog, LOG_PID, LOG_DAEMON);
271
272 return (pfds[1]);
273 }
274
275 /*
276 * init_signals()
277 *
278 * Initialize signal handling.
279 */
280 static void
init_signals(void)281 init_signals(void)
282 {
283 struct sigaction act;
284 sigset_t set;
285
286 (void) sigfillset(&set);
287 (void) sigdelset(&set, SIGABRT);
288
289 (void) sigfillset(&act.sa_mask);
290 act.sa_handler = signal_handler;
291 act.sa_flags = 0;
292
293 (void) sigaction(SIGTERM, &act, NULL);
294 (void) sigaction(SIGHUP, &act, NULL);
295 (void) sigaction(SIGINT, &act, NULL);
296 (void) sigaction(SIGPIPE, &act, NULL);
297
298 (void) sigdelset(&set, SIGTERM);
299 (void) sigdelset(&set, SIGHUP);
300 (void) sigdelset(&set, SIGINT);
301 (void) sigdelset(&set, SIGPIPE);
302 }
303
304 /*
305 * signal_handler()
306 *
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.
310 *
311 * Except SIGPIPE is used to coordinate between the parent
312 * and child processes when the daemon first starts.
313 */
314 static void
signal_handler(int signum)315 signal_handler(int signum)
316 {
317 log_info("Received signal %d.\n", signum);
318
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;
327 }
328 }
329
330 /*
331 * shutdown_daemon()
332 *
333 * Perform a clean shutdown of the daemon.
334 */
335 static void
shutdown_daemon(void)336 shutdown_daemon(void)
337 {
338 log_info("Hotplug daemon shutting down.\n");
339
340 door_server_fini();
341
342 if (log_flag)
343 closelog();
344
345 (void) sema_destroy(&signal_sem);
346 }
347
348 /*
349 * log_err()
350 *
351 * Display an error message. Use syslog if in daemon
352 * mode, otherwise print to stderr when in debug mode.
353 */
354 /*PRINTFLIKE1*/
355 void
log_err(char * fmt,...)356 log_err(char *fmt, ...)
357 {
358 va_list ap;
359
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);
366 }
367
368 /*
369 * log_info()
370 *
371 * Display an information message. Use syslog if in daemon
372 * mode, otherwise print to stdout when in debug mode.
373 */
374 /*PRINTFLIKE1*/
375 void
log_info(char * fmt,...)376 log_info(char *fmt, ...)
377 {
378 va_list ap;
379
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);
386 }
387
388 /*
389 * dprintf()
390 *
391 * Print a debug tracing statement. Only works in debug
392 * mode, and always prints to stdout.
393 */
394 /*PRINTFLIKE1*/
395 void
dprintf(char * fmt,...)396 dprintf(char *fmt, ...)
397 {
398 va_list ap;
399
400 if (debug_flag) {
401 va_start(ap, fmt);
402 (void) vprintf(fmt, ap);
403 va_end(ap);
404 }
405 }
406