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