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