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 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 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 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 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 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 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 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 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 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 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