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