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 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * Reconfiguration Coordination Daemon 28 * 29 * Accept RCM messages in the form of RCM events and process them 30 * - to build and update the system resource map 31 * - to allow clients to register/unregister for resource 32 * - to allow dr initiators to offline a resource before removal 33 * - to call into clients to perform suspend/offline actions 34 * 35 * The goal is to enable fully automated Dynamic Reconfiguration and better 36 * DR information tracking. 37 */ 38 39 #include <librcm_event.h> 40 41 #include "rcm_impl.h" 42 43 /* will run in daemon mode if debug level < DEBUG_LEVEL_FORK */ 44 #define DEBUG_LEVEL_FORK RCM_DEBUG 45 46 #define DAEMON_LOCK_FILE "/var/run/rcm_daemon_lock" 47 48 static int hold_daemon_lock; 49 static int daemon_lock_fd; 50 static const char *daemon_lock_file = DAEMON_LOCK_FILE; 51 52 int debug_level = 0; 53 static int idle_timeout; 54 static int logflag = 0; 55 static char *prog; 56 57 static void usage(void); 58 static void catch_sighup(int); 59 static void catch_sigusr1(int); 60 static pid_t enter_daemon_lock(void); 61 static void exit_daemon_lock(void); 62 63 extern void init_poll_thread(); 64 extern void cleanup_poll_thread(); 65 66 /* 67 * Print command line syntax for starting rcm_daemon 68 */ 69 static void 70 usage() 71 { 72 (void) fprintf(stderr, 73 gettext("usage: %s [-d debug_level] [-t idle_timeout]\n"), prog); 74 rcmd_exit(EINVAL); 75 } 76 77 /* 78 * common cleanup/exit functions to ensure releasing locks 79 */ 80 static void 81 rcmd_cleanup(int status) 82 { 83 if (status == 0) { 84 rcm_log_message(RCM_INFO, 85 gettext("rcm_daemon normal exit\n")); 86 } else { 87 rcm_log_message(RCM_ERROR, 88 gettext("rcm_daemon exit: errno = %d\n"), status); 89 } 90 91 if (hold_daemon_lock) { 92 exit_daemon_lock(); 93 } 94 } 95 96 void 97 rcmd_exit(int status) 98 { 99 rcmd_cleanup(status); 100 exit(status); 101 } 102 103 /* 104 * When SIGHUP is received, reload modules at the next safe moment (when 105 * there is no DR activity. 106 */ 107 void 108 catch_sighup(int signal __unused) 109 { 110 rcm_log_message(RCM_INFO, 111 gettext("SIGHUP received, will exit when daemon is idle\n")); 112 rcmd_thr_signal(); 113 } 114 115 /* 116 * When SIGUSR1 is received, exit the thread 117 */ 118 void 119 catch_sigusr1(int signal __unused) 120 { 121 rcm_log_message(RCM_DEBUG, "SIGUSR1 received in thread %d\n", 122 thr_self()); 123 cleanup_poll_thread(); 124 thr_exit(NULL); 125 } 126 127 /* 128 * Use an advisory lock to ensure that only one daemon process is 129 * active at any point in time. 130 */ 131 static pid_t 132 enter_daemon_lock(void) 133 { 134 struct flock lock; 135 136 rcm_log_message(RCM_TRACE1, 137 "enter_daemon_lock: lock file = %s\n", daemon_lock_file); 138 139 daemon_lock_fd = open(daemon_lock_file, O_CREAT|O_RDWR, 0644); 140 if (daemon_lock_fd < 0) { 141 rcm_log_message(RCM_ERROR, gettext("open(%s) - %s\n"), 142 daemon_lock_file, strerror(errno)); 143 rcmd_exit(errno); 144 } 145 146 lock.l_type = F_WRLCK; 147 lock.l_whence = SEEK_SET; 148 lock.l_start = 0; 149 lock.l_len = 0; 150 151 if (fcntl(daemon_lock_fd, F_SETLK, &lock) == 0) { 152 hold_daemon_lock = 1; 153 return (getpid()); 154 } 155 156 /* failed to get lock, attempt to find lock owner */ 157 if ((errno == EAGAIN || errno == EDEADLK) && 158 (fcntl(daemon_lock_fd, F_GETLK, &lock) == 0)) { 159 return (lock.l_pid); 160 } 161 162 /* die a horrible death */ 163 rcm_log_message(RCM_ERROR, gettext("lock(%s) - %s"), daemon_lock_file, 164 strerror(errno)); 165 exit(errno); 166 /*NOTREACHED*/ 167 } 168 169 /* 170 * Drop the advisory daemon lock, close lock file 171 */ 172 static void 173 exit_daemon_lock(void) 174 { 175 struct flock lock; 176 177 lock.l_type = F_UNLCK; 178 lock.l_whence = SEEK_SET; 179 lock.l_start = 0; 180 lock.l_len = 0; 181 182 if (fcntl(daemon_lock_fd, F_SETLK, &lock) == -1) { 183 rcm_log_message(RCM_ERROR, gettext("unlock(%s) - %s"), 184 daemon_lock_file, strerror(errno)); 185 } 186 187 (void) close(daemon_lock_fd); 188 } 189 190 /*PRINTFLIKE2*/ 191 static void 192 rcm_log_msg_impl(int level, char *message, va_list ap) 193 { 194 int log_level; 195 196 if (!logflag) { 197 /* 198 * RCM_ERROR goes to stderr, others go to stdout 199 */ 200 FILE *out = (level <= RCM_ERROR) ? stderr : stdout; 201 (void) vfprintf(out, message, ap); 202 return; 203 } 204 205 /* 206 * translate RCM_* to LOG_* 207 */ 208 switch (level) { 209 case RCM_ERROR: 210 log_level = LOG_ERR; 211 break; 212 213 case RCM_WARNING: 214 log_level = LOG_WARNING; 215 break; 216 217 case RCM_NOTICE: 218 log_level = LOG_NOTICE; 219 break; 220 221 case RCM_INFO: 222 log_level = LOG_INFO; 223 break; 224 225 case RCM_DEBUG: 226 log_level = LOG_DEBUG; 227 break; 228 229 default: 230 /* 231 * Don't log RCM_TRACEn messages 232 */ 233 return; 234 } 235 236 (void) vsyslog(log_level, message, ap); 237 } 238 239 /* 240 * print error messages to the terminal or to syslog 241 */ 242 void 243 rcm_log_message(int level, char *message, ...) 244 { 245 va_list ap; 246 247 if (level > debug_level) { 248 return; 249 } 250 251 va_start(ap, message); 252 rcm_log_msg_impl(level, message, ap); 253 va_end(ap); 254 } 255 256 /* 257 * Print error messages to the terminal or to syslog. 258 * Same as rcm_log_message except that it does not check for 259 * level > debug_level 260 * allowing callers to override the global debug_level. 261 */ 262 void 263 rcm_log_msg(int level, char *message, ...) 264 { 265 va_list ap; 266 267 va_start(ap, message); 268 rcm_log_msg_impl(level, message, ap); 269 va_end(ap); 270 } 271 272 /* 273 * grab daemon_lock and direct messages to syslog 274 */ 275 static void 276 detachfromtty() 277 { 278 (void) chdir("/"); 279 (void) setsid(); 280 (void) close(0); 281 (void) close(1); 282 (void) close(2); 283 (void) open("/dev/null", O_RDWR, 0); 284 (void) dup2(0, 1); 285 (void) dup2(0, 2); 286 openlog(prog, LOG_PID, LOG_DAEMON); 287 logflag = 1; 288 } 289 290 int 291 main(int argc, char **argv) 292 { 293 int c; 294 pid_t pid; 295 extern char *optarg; 296 sigset_t mask; 297 struct sigaction act; 298 299 (void) setlocale(LC_ALL, ""); 300 #ifndef TEXT_DOMAIN 301 #define TEXT_DOMAIN "SYS_TEST" 302 #endif 303 (void) textdomain(TEXT_DOMAIN); 304 305 if ((prog = strrchr(argv[0], '/')) == NULL) { 306 prog = argv[0]; 307 } else { 308 prog++; 309 } 310 311 (void) enable_extended_FILE_stdio(-1, -1); 312 313 /* 314 * process arguments 315 */ 316 if (argc > 3) { 317 usage(); 318 } 319 while ((c = getopt(argc, argv, "d:t:")) != EOF) { 320 switch (c) { 321 case 'd': 322 debug_level = atoi(optarg); 323 break; 324 case 't': 325 idle_timeout = atoi(optarg); 326 break; 327 case '?': 328 default: 329 usage(); 330 /*NOTREACHED*/ 331 } 332 } 333 334 /* 335 * Check permission 336 */ 337 if (getuid() != 0) { 338 (void) fprintf(stderr, gettext("Must be root to run %s\n"), 339 prog); 340 exit(EPERM); 341 } 342 343 /* 344 * When rcm_daemon is started by a call to librcm, it inherits file 345 * descriptors from the DR initiator making a call. The file 346 * descriptors may correspond to devices that can be removed by DR. 347 * Since keeping them remain opened is problematic, close everything 348 * but stdin/stdout/stderr. 349 */ 350 closefrom(3); 351 352 /* 353 * When rcm_daemon is started by the caller, it will inherit the 354 * signal block mask. We unblock all signals to make sure the 355 * signal handling will work normally. 356 */ 357 (void) sigfillset(&mask); 358 (void) thr_sigsetmask(SIG_UNBLOCK, &mask, NULL); 359 360 /* 361 * block SIGUSR1, use it for killing specific threads 362 */ 363 (void) sigemptyset(&mask); 364 (void) sigaddset(&mask, SIGUSR1); 365 (void) thr_sigsetmask(SIG_BLOCK, &mask, NULL); 366 367 /* 368 * Setup signal handlers for SIGHUP and SIGUSR1 369 * SIGHUP - causes a "delayed" daemon exit, effectively the same 370 * as a daemon restart. 371 * SIGUSR1 - causes a thr_exit(). Unblocked in selected threads. 372 */ 373 act.sa_flags = 0; 374 act.sa_handler = catch_sighup; 375 (void) sigaction(SIGHUP, &act, NULL); 376 act.sa_handler = catch_sigusr1; 377 (void) sigaction(SIGUSR1, &act, NULL); 378 379 /* 380 * ignore SIGPIPE so that the rcm daemon does not exit when it 381 * attempts to read or write from a pipe whose corresponding 382 * rcm script process exited. 383 */ 384 act.sa_handler = SIG_IGN; 385 (void) sigaction(SIGPIPE, &act, NULL); 386 387 /* 388 * run in daemon mode 389 */ 390 if (debug_level < DEBUG_LEVEL_FORK) { 391 if (fork()) { 392 exit(0); 393 } 394 detachfromtty(); 395 } 396 397 /* only one daemon can run at a time */ 398 if ((pid = enter_daemon_lock()) != getpid()) { 399 rcm_log_message(RCM_DEBUG, "%s pid %d already running\n", 400 prog, pid); 401 exit(EDEADLK); 402 } 403 404 rcm_log_message(RCM_TRACE1, "%s started, debug level = %d\n", 405 prog, debug_level); 406 407 /* 408 * Set daemon state to block RCM requests before rcm_daemon is 409 * fully initialized. See rcmd_thr_incr(). 410 */ 411 rcmd_set_state(RCMD_INIT); 412 413 /* 414 * create rcm_daemon door and set permission to 0400 415 */ 416 if (create_event_service(RCM_SERVICE_DOOR, event_service) == -1) { 417 rcm_log_message(RCM_ERROR, 418 gettext("cannot create door service: %s\n"), 419 strerror(errno)); 420 rcmd_exit(errno); 421 } 422 (void) chmod(RCM_SERVICE_DOOR, S_IRUSR); 423 424 init_poll_thread(); /* initialize poll thread related data */ 425 426 /* 427 * Initialize database by asking modules to register. 428 */ 429 rcmd_db_init(); 430 431 /* 432 * Initialize locking, including lock recovery in the event of 433 * unexpected daemon failure. 434 */ 435 rcmd_lock_init(); 436 437 /* 438 * Start accepting normal requests 439 */ 440 rcmd_set_state(RCMD_NORMAL); 441 442 /* 443 * Start cleanup thread 444 */ 445 rcmd_db_clean(); 446 447 /* 448 * Loop within daemon and return after a period of inactivity. 449 */ 450 rcmd_start_timer(idle_timeout); 451 452 rcmd_cleanup(0); 453 return (0); 454 } 455