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