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
usage()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
rcmd_cleanup(int status)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
rcmd_exit(int status)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
catch_sighup(int signal __unused)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
catch_sigusr1(int signal __unused)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
enter_daemon_lock(void)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
exit_daemon_lock(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
rcm_log_msg_impl(int level,char * message,va_list ap)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
rcm_log_message(int level,char * message,...)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
rcm_log_msg(int level,char * message,...)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
detachfromtty()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
main(int argc,char ** argv)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