8354 sync regcomp(3C) with upstream (fix make catalog)
[unleashed/tickless.git] / usr / src / cmd / syseventd / daemons / syseventconfd / syseventconfd.c
blob8812c6d8ab6d8189fb57fc6243b3d85431c18175
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
23 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
30 * syseventconfd - The sysevent conf daemon
32 * This daemon is a companion to the sysevent_conf_mod module.
34 * The sysevent_conf_mod module receives events from syseventd,
35 * and compares those events against event specs in the
36 * sysevent.conf files. For each matching event spec, the
37 * specified command is invoked.
39 * This daemon manages the fork/exec's on behalf of sysevent_conf_mod.
40 * The events and associated nvlist are delivered via a door upcall
41 * from sysevent_conf_mod. Arriving events are queued, and the
42 * main thread of this daemon dequeues events one by one, and
43 * builds the necessary arguments to fork/exec the command.
45 * Since sysevent_conf_mod is running in the context of syseventd,
46 * invoking the fork/exec from that module blocks the door upcalls
47 * from the kernel delivering events to syseventd. We avoid a
48 * major performance bottleneck in this fashion.
51 #include <stdio.h>
52 #include <stdarg.h>
53 #include <stddef.h>
54 #include <stdlib.h>
55 #include <errno.h>
56 #include <fcntl.h>
57 #include <signal.h>
58 #include <strings.h>
59 #include <unistd.h>
60 #include <synch.h>
61 #include <syslog.h>
62 #include <pthread.h>
63 #include <door.h>
64 #include <libsysevent.h>
65 #include <limits.h>
66 #include <locale.h>
67 #include <sys/modctl.h>
68 #include <sys/stat.h>
69 #include <sys/systeminfo.h>
70 #include <sys/wait.h>
72 #include "syseventconfd.h"
73 #include "syseventconfd_door.h"
74 #include "message_confd.h"
78 static int debug_level = 0;
79 static char *root_dir = ""; /* Relative root for lock and door */
80 static char *prog;
82 static struct cmd *cmd_list;
83 static struct cmd *cmd_tail;
85 static mutex_t cmd_list_lock;
86 static cond_t cmd_list_cv;
88 extern char *optarg;
91 * Support for door server thread handling
93 #define MAX_SERVER_THREADS 1
95 static mutex_t create_cnt_lock;
96 static int cnt_servers = 0;
99 static void
100 usage() {
101 (void) fprintf(stderr, "usage: syseventconfd [-d <debug_level>]\n");
102 exit(2);
106 static void
107 set_root_dir(char *dir)
109 root_dir = malloc(strlen(dir) + 1);
110 if (root_dir == NULL) {
111 syserrmsg(INIT_ROOT_DIR_ERR, strerror(errno));
112 exit(2);
114 (void) strcpy(root_dir, dir);
119 main(int argc, char **argv)
121 int c;
122 int fd;
123 sigset_t set;
124 struct cmd *cmd;
126 (void) setlocale(LC_ALL, "");
127 (void) textdomain(TEXT_DOMAIN);
129 if (getuid() != 0) {
130 (void) fprintf(stderr, "Must be root to run syseventconfd\n");
131 exit(1);
134 if ((prog = strrchr(argv[0], '/')) == NULL) {
135 prog = argv[0];
136 } else {
137 prog++;
140 if ((c = getopt(argc, argv, "d:r:")) != EOF) {
141 switch (c) {
142 case 'd':
143 debug_level = atoi(optarg);
144 break;
145 case 'r':
147 * Private flag for suninstall to run
148 * daemon during install.
150 set_root_dir(optarg);
151 break;
152 case '?':
153 default:
154 usage();
159 if (fork()) {
160 exit(0);
163 (void) chdir("/");
165 (void) setsid();
166 if (debug_level <= 1) {
167 closefrom(0);
168 fd = open("/dev/null", 0);
169 (void) dup2(fd, 1);
170 (void) dup2(fd, 2);
173 openlog("syseventconfd", LOG_PID, LOG_DAEMON);
175 printmsg(1,
176 "syseventconfd started, debug level = %d\n", debug_level);
179 * Block all signals to all threads include the main thread.
180 * The sigwait_thr thread will catch and process all signals.
182 (void) sigfillset(&set);
183 (void) thr_sigsetmask(SIG_BLOCK, &set, NULL);
185 /* Create signal catching thread */
186 if (thr_create(NULL, NULL, (void *(*)(void *))sigwait_thr,
187 (void *)NULL, 0, NULL) < 0) {
188 syserrmsg(INIT_THR_CREATE_ERR, strerror(errno));
189 exit(2);
193 * Init mutex and list of cmds to be fork/exec'ed
194 * This is multi-threaded so the fork/exec can be
195 * done without blocking the door upcall.
197 cmd_list = NULL;
198 cmd_tail = NULL;
200 (void) mutex_init(&create_cnt_lock, USYNC_THREAD, NULL);
201 (void) mutex_init(&cmd_list_lock, USYNC_THREAD, NULL);
202 (void) cond_init(&cmd_list_cv, USYNC_THREAD, NULL);
205 * Open communication channel from sysevent_conf_mod
207 if (open_channel() == NULL) {
208 exit(1);
212 * main thread to wait for events to arrive and be placed
213 * on the queue. As events are queued, dequeue them
214 * here and invoke the associated fork/exec.
216 (void) mutex_lock(&cmd_list_lock);
217 for (;;) {
218 while (cmd_list == NULL)
219 (void) cond_wait(&cmd_list_cv, &cmd_list_lock);
221 cmd = cmd_list;
222 cmd_list = cmd->cmd_next;
223 if (cmd_list == NULL)
224 cmd_tail = NULL;
226 (void) mutex_unlock(&cmd_list_lock);
227 exec_cmd(cmd);
228 free_cmd(cmd);
229 (void) mutex_lock(&cmd_list_lock);
231 /* NOTREACHED */
232 return (0);
236 * Events sent via the door call from sysevent_conf_mod arrive
237 * here. Queue each event for the main thread to invoke, and
238 * return. We want to avoid doing the fork/exec while in the
239 * context of the door call.
241 /*ARGSUSED*/
242 static void
243 event_handler(sysevent_t *event)
245 nvlist_t *nvlist;
246 struct cmd *cmd;
248 nvlist = NULL;
249 if (sysevent_get_attr_list(event, &nvlist) != 0) {
250 syslog(LOG_ERR, NO_NVLIST_ERR);
251 return;
254 if ((cmd = alloc_cmd(nvlist)) != NULL) {
255 (void) mutex_lock(&cmd_list_lock);
256 if (cmd_list == NULL) {
257 cmd_list = cmd;
258 cmd_tail = cmd;
259 } else {
260 cmd_tail->cmd_next = cmd;
261 cmd_tail = cmd;
263 cmd->cmd_next = NULL;
264 (void) cond_signal(&cmd_list_cv);
265 (void) mutex_unlock(&cmd_list_lock);
268 nvlist_free(nvlist);
273 * Decode the command, build the exec args and fork/exec the command
274 * All command attributes are packed into the nvlist bundled with
275 * the delivered event.
277 static void
278 exec_cmd(struct cmd *cmd)
280 char *path;
281 char *cmdline;
282 uid_t uid;
283 gid_t gid;
284 char *file;
285 int line;
286 char *user;
287 arg_t *args;
288 pid_t pid;
289 char *lp;
290 char *p;
291 int i;
292 sigset_t set, prior_set;
294 if (nvlist_lookup_string(cmd->cmd_nvlist, "user", &user) != 0) {
295 syslog(LOG_ERR, NVLIST_FORMAT_ERR, "user");
296 return;
298 if (nvlist_lookup_string(cmd->cmd_nvlist, "file", &file) != 0) {
299 syslog(LOG_ERR, NVLIST_FORMAT_ERR, "file");
300 return;
303 if (nvlist_lookup_string(cmd->cmd_nvlist, "path", &path) != 0) {
304 syslog(LOG_ERR, NVLIST_FILE_LINE_FORMAT_ERR, "path");
305 return;
307 if (nvlist_lookup_string(cmd->cmd_nvlist, "cmd", &cmdline) != 0) {
308 syslog(LOG_ERR, NVLIST_FILE_LINE_FORMAT_ERR, "cmd");
309 return;
311 if (nvlist_lookup_int32(cmd->cmd_nvlist, "line", &line) != 0) {
312 syslog(LOG_ERR, NVLIST_FILE_LINE_FORMAT_ERR, "line");
313 return;
315 if (nvlist_lookup_int32(cmd->cmd_nvlist, "uid", (int *)&uid) == 0) {
316 if (nvlist_lookup_int32(cmd->cmd_nvlist,
317 "gid", (int *)&gid) != 0) {
318 syslog(LOG_ERR, NVLIST_FILE_LINE_FORMAT_ERR, "gid");
319 return;
321 } else {
322 uid = 0;
323 gid = 0;
326 args = init_arglist(32);
328 lp = cmdline;
329 while ((p = next_arg(&lp)) != NULL) {
330 if (add_arg(args, p)) {
331 free_arglist(args);
332 return;
336 if (debug_level >= DBG_EXEC) {
337 printmsg(DBG_EXEC, "path=%s\n", path);
338 printmsg(DBG_EXEC, "cmd=%s\n", cmdline);
341 if (debug_level >= DBG_EXEC_ARGS) {
342 for (i = 0; i < args->arg_nargs; i++) {
343 printmsg(DBG_EXEC_ARGS,
344 "arg[%d]: '%s'\n", i, args->arg_args[i]);
348 (void) sigprocmask(SIG_SETMASK, NULL, &set);
349 (void) sigaddset(&set, SIGCHLD);
350 (void) sigprocmask(SIG_SETMASK, &set, &prior_set);
352 again:
353 if ((pid = fork1()) == (pid_t)-1) {
354 if (errno == EINTR)
355 goto again;
356 syslog(LOG_ERR, CANNOT_FORK_ERR, strerror(errno));
357 free_arglist(args);
358 return;
360 if (pid != (pid_t)0) {
361 (void) sigprocmask(SIG_SETMASK, &prior_set, NULL);
362 free_arglist(args);
363 return;
367 * The child
369 (void) close(0);
370 (void) close(1);
371 (void) close(2);
372 (void) open("/dev/null", O_RDONLY);
373 (void) dup2(0, 1);
374 (void) dup2(0, 2);
376 if (uid != (uid_t)0) {
377 i = setgid(gid);
378 if (i == 0)
379 i = setuid(uid);
380 if (i != 0) {
381 syslog(LOG_ERR, SETUID_ERR,
382 file, line, user, strerror(errno));
383 _exit(0);
388 * Unblock all signals in the child
390 (void) sigprocmask(SIG_UNBLOCK, &prior_set, NULL);
392 if (execv(path, args->arg_args) == -1) {
393 syslog(LOG_ERR, CANNOT_EXEC_ERR,
394 path, strerror(errno));
395 _exit(0);
401 * Thread to handle in-coming signals
403 static void
404 sigwait_thr()
406 int sig;
407 sigset_t signal_set;
410 * SIGCHLD is ignored by default, and we need to handle this
411 * signal to reap the status of all children spawned by
412 * this daemon.
414 (void) sigset(SIGCHLD, reapchild);
416 for (;;) {
417 (void) sigfillset(&signal_set);
418 if (sigwait(&signal_set, &sig) == 0) {
420 * Block all signals until the signal handler completes
422 (void) sigfillset(&signal_set);
423 (void) thr_sigsetmask(SIG_BLOCK, &signal_set, NULL);
425 if (sig == SIGCHLD) {
426 reapchild(sig);
427 } else {
428 flt_handler(sig);
432 /* NOTREACHED */
438 * reapchild - reap the status of each child as it exits
440 /*ARGSUSED*/
441 static void
442 reapchild(int sig)
444 siginfo_t info;
445 char *signam;
446 int err;
448 for (;;) {
449 (void) memset(&info, 0, sizeof (info));
450 err = waitid(P_ALL, 0, &info, WNOHANG|WEXITED);
451 if (err == -1) {
452 if (errno != EINTR && errno != EAGAIN)
453 return;
454 } else if (info.si_pid == 0) {
455 return;
458 if (debug_level >= DBG_CHILD) {
459 printmsg(DBG_CHILD, CHILD_EXIT_STATUS_ERR,
460 info.si_pid, info.si_status);
463 if (info.si_status) {
464 if (info.si_code == CLD_EXITED) {
465 syserrmsg(CHILD_EXIT_STATUS_ERR,
466 info.si_pid, info.si_status);
467 } else {
468 signam = strsignal(info.si_status);
469 if (signam == NULL)
470 signam = "";
471 if (info.si_code == CLD_DUMPED) {
472 syserrmsg(
473 CHILD_EXIT_CORE_ERR,
474 info.si_pid, signam);
475 } else {
476 syserrmsg(
477 CHILD_EXIT_SIGNAL_ERR,
478 info.si_pid, signam);
487 * Fault handler for other signals caught
489 /*ARGSUSED*/
490 static void
491 flt_handler(int sig)
493 struct sigaction act;
495 (void) memset(&act, 0, sizeof (act));
496 act.sa_handler = SIG_DFL;
497 act.sa_flags = SA_RESTART;
498 (void) sigfillset(&act.sa_mask);
499 (void) sigaction(sig, &act, NULL);
501 switch (sig) {
502 case SIGINT:
503 case SIGSTOP:
504 case SIGTERM:
505 case SIGHUP:
506 exit(1);
507 /*NOTREACHED*/
512 static arg_t *
513 init_arglist(int hint)
515 arg_t *arglist;
517 if ((arglist = sc_malloc(sizeof (arg_t))) == NULL)
518 return (NULL);
519 arglist->arg_args = NULL;
520 arglist->arg_nargs = 0;
521 arglist->arg_alloc = 0;
522 arglist->arg_hint = hint;
523 return (arglist);
527 static void
528 free_arglist(arg_t *arglist)
530 if (arglist->arg_args) {
531 free(arglist->arg_args);
533 free(arglist);
537 static int
538 add_arg(arg_t *arglist, char *arg)
540 char **new_args;
541 int len;
543 len = arglist->arg_nargs + 2;
544 if (arglist->arg_alloc < len) {
545 arglist->arg_alloc = len + arglist->arg_hint;
546 new_args = (arglist->arg_nargs == 0) ?
547 sc_malloc(arglist->arg_alloc * sizeof (char **)) :
548 sc_realloc(arglist->arg_args,
549 arglist->arg_alloc * sizeof (char **));
550 if (new_args == NULL)
551 return (1);
552 arglist->arg_args = new_args;
555 arglist->arg_args[arglist->arg_nargs++] = arg;
556 arglist->arg_args[arglist->arg_nargs] = NULL;
558 return (0);
562 * next_arg() is used to break up a command line
563 * into the arguments for execv(2). Break up
564 * arguments separated by spaces, but respecting
565 * single/double quotes.
567 static char *
568 next_arg(char **cpp)
570 char *cp = *cpp;
571 char *start;
572 char quote;
574 while (*cp == ' ' || *cp == '\t')
575 cp++;
576 if (*cp == 0) {
577 *cpp = 0;
578 return (NULL);
580 start = cp;
581 while (*cp && *cp != ' ' && *cp != '\t') {
582 if (*cp == '"' || *cp == '\'') {
583 quote = *cp++;
584 while (*cp && *cp != quote) {
585 cp++;
587 if (*cp == 0) {
588 *cpp = 0;
589 return (NULL);
590 } else {
591 cp++;
593 } else {
594 cp++;
597 if (*cp != 0)
598 *cp++ = 0;
599 *cpp = cp;
600 return (start);
604 static struct cmd *
605 alloc_cmd(nvlist_t *nvlist)
607 struct cmd *cmd;
609 cmd = sc_malloc(sizeof (struct cmd));
610 if (cmd) {
611 if (nvlist_dup(nvlist, &cmd->cmd_nvlist, 0) != 0) {
612 syslog(LOG_ERR, OUT_OF_MEMORY_ERR);
613 free(cmd);
614 return (NULL);
617 return (cmd);
620 static void
621 free_cmd(struct cmd *cmd)
623 nvlist_free(cmd->cmd_nvlist);
624 free(cmd);
628 static void *
629 sc_malloc(size_t n)
631 void *p;
633 p = malloc(n);
634 if (p == NULL) {
635 syslog(LOG_ERR, OUT_OF_MEMORY_ERR);
637 return (p);
640 static void *
641 sc_realloc(void *p, size_t n)
643 p = realloc(p, n);
644 if (p == NULL) {
645 syslog(LOG_ERR, OUT_OF_MEMORY_ERR);
647 return (p);
653 * syserrsg - print error messages to the terminal if not
654 * yet daemonized or to syslog.
656 /*PRINTFLIKE1*/
657 static void
658 syserrmsg(char *message, ...)
660 va_list ap;
662 va_start(ap, message);
663 (void) vsyslog(LOG_ERR, message, ap);
664 va_end(ap);
668 * printmsg - print messages to the terminal or to syslog
669 * the following levels are implemented:
671 /*PRINTFLIKE2*/
672 static void
673 printmsg(int level, char *message, ...)
675 va_list ap;
677 if (level > debug_level) {
678 return;
681 va_start(ap, message);
682 (void) syslog(LOG_DEBUG, "%s[%ld]: ", prog, getpid());
683 (void) vsyslog(LOG_DEBUG, message, ap);
684 va_end(ap);
687 /* ARGSUSED */
688 static void *
689 create_door_thr(void *arg)
691 (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
692 (void) door_return(NULL, 0, NULL, 0);
693 return (NULL);
697 * Control creation of door server threads
699 * If first creation of server thread fails there is nothing
700 * we can do about. Doors would not work.
702 /* ARGSUSED */
703 static void
704 mk_thr_pool(door_info_t *dip)
706 (void) mutex_lock(&create_cnt_lock);
707 if (++cnt_servers > MAX_SERVER_THREADS) {
708 cnt_servers--;
709 (void) mutex_unlock(&create_cnt_lock);
710 return;
712 (void) mutex_unlock(&create_cnt_lock);
714 (void) thr_create(NULL, 0, create_door_thr, NULL,
715 THR_BOUND|THR_DETACHED, NULL);
718 static sysevent_handle_t *
719 open_channel()
721 char door_path[MAXPATHLEN];
722 const char *subclass_list;
723 sysevent_handle_t *handle;
725 if (snprintf(door_path, sizeof (door_path), "%s/%s",
726 root_dir, SYSEVENTCONFD_SERVICE_DOOR) >= sizeof (door_path)) {
727 syserrmsg(CHANNEL_OPEN_ERR);
728 return (NULL);
732 * Setup of door server create function to limit the
733 * amount of door servers
735 (void) door_server_create(mk_thr_pool);
737 handle = sysevent_open_channel_alt(door_path);
738 if (handle == NULL) {
739 syserrmsg(CHANNEL_OPEN_ERR);
740 return (NULL);
742 if (sysevent_bind_subscriber(handle, event_handler) != 0) {
743 syserrmsg(CHANNEL_BIND_ERR);
744 sysevent_close_channel(handle);
745 return (NULL);
747 subclass_list = EC_SUB_ALL;
748 if (sysevent_register_event(handle, EC_ALL, &subclass_list, 1)
749 != 0) {
750 syserrmsg(CHANNEL_BIND_ERR);
751 (void) sysevent_unbind_subscriber(handle);
752 (void) sysevent_close_channel(handle);
753 return (NULL);
755 return (handle);