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]
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.
64 #include <libsysevent.h>
67 #include <sys/modctl.h>
69 #include <sys/systeminfo.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 */
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
;
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;
101 (void) fprintf(stderr
, "usage: syseventconfd [-d <debug_level>]\n");
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
));
114 (void) strcpy(root_dir
, dir
);
119 main(int argc
, char **argv
)
126 (void) setlocale(LC_ALL
, "");
127 (void) textdomain(TEXT_DOMAIN
);
130 (void) fprintf(stderr
, "Must be root to run syseventconfd\n");
134 if ((prog
= strrchr(argv
[0], '/')) == NULL
) {
140 if ((c
= getopt(argc
, argv
, "d:r:")) != EOF
) {
143 debug_level
= atoi(optarg
);
147 * Private flag for suninstall to run
148 * daemon during install.
150 set_root_dir(optarg
);
166 if (debug_level
<= 1) {
168 fd
= open("/dev/null", 0);
173 openlog("syseventconfd", LOG_PID
, LOG_DAEMON
);
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
));
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.
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
) {
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
);
218 while (cmd_list
== NULL
)
219 (void) cond_wait(&cmd_list_cv
, &cmd_list_lock
);
222 cmd_list
= cmd
->cmd_next
;
223 if (cmd_list
== NULL
)
226 (void) mutex_unlock(&cmd_list_lock
);
229 (void) mutex_lock(&cmd_list_lock
);
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.
243 event_handler(sysevent_t
*event
)
249 if (sysevent_get_attr_list(event
, &nvlist
) != 0) {
250 syslog(LOG_ERR
, NO_NVLIST_ERR
);
254 if ((cmd
= alloc_cmd(nvlist
)) != NULL
) {
255 (void) mutex_lock(&cmd_list_lock
);
256 if (cmd_list
== NULL
) {
260 cmd_tail
->cmd_next
= cmd
;
263 cmd
->cmd_next
= NULL
;
264 (void) cond_signal(&cmd_list_cv
);
265 (void) mutex_unlock(&cmd_list_lock
);
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.
278 exec_cmd(struct cmd
*cmd
)
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");
298 if (nvlist_lookup_string(cmd
->cmd_nvlist
, "file", &file
) != 0) {
299 syslog(LOG_ERR
, NVLIST_FORMAT_ERR
, "file");
303 if (nvlist_lookup_string(cmd
->cmd_nvlist
, "path", &path
) != 0) {
304 syslog(LOG_ERR
, NVLIST_FILE_LINE_FORMAT_ERR
, "path");
307 if (nvlist_lookup_string(cmd
->cmd_nvlist
, "cmd", &cmdline
) != 0) {
308 syslog(LOG_ERR
, NVLIST_FILE_LINE_FORMAT_ERR
, "cmd");
311 if (nvlist_lookup_int32(cmd
->cmd_nvlist
, "line", &line
) != 0) {
312 syslog(LOG_ERR
, NVLIST_FILE_LINE_FORMAT_ERR
, "line");
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");
326 args
= init_arglist(32);
329 while ((p
= next_arg(&lp
)) != NULL
) {
330 if (add_arg(args
, p
)) {
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
);
353 if ((pid
= fork1()) == (pid_t
)-1) {
356 syslog(LOG_ERR
, CANNOT_FORK_ERR
, strerror(errno
));
360 if (pid
!= (pid_t
)0) {
361 (void) sigprocmask(SIG_SETMASK
, &prior_set
, NULL
);
372 (void) open("/dev/null", O_RDONLY
);
376 if (uid
!= (uid_t
)0) {
381 syslog(LOG_ERR
, SETUID_ERR
,
382 file
, line
, user
, strerror(errno
));
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
));
401 * Thread to handle in-coming signals
410 * SIGCHLD is ignored by default, and we need to handle this
411 * signal to reap the status of all children spawned by
414 (void) sigset(SIGCHLD
, reapchild
);
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
) {
438 * reapchild - reap the status of each child as it exits
449 (void) memset(&info
, 0, sizeof (info
));
450 err
= waitid(P_ALL
, 0, &info
, WNOHANG
|WEXITED
);
452 if (errno
!= EINTR
&& errno
!= EAGAIN
)
454 } else if (info
.si_pid
== 0) {
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
);
468 signam
= strsignal(info
.si_status
);
471 if (info
.si_code
== CLD_DUMPED
) {
474 info
.si_pid
, signam
);
477 CHILD_EXIT_SIGNAL_ERR
,
478 info
.si_pid
, signam
);
487 * Fault handler for other signals caught
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
);
513 init_arglist(int hint
)
517 if ((arglist
= sc_malloc(sizeof (arg_t
))) == NULL
)
519 arglist
->arg_args
= NULL
;
520 arglist
->arg_nargs
= 0;
521 arglist
->arg_alloc
= 0;
522 arglist
->arg_hint
= hint
;
528 free_arglist(arg_t
*arglist
)
530 if (arglist
->arg_args
) {
531 free(arglist
->arg_args
);
538 add_arg(arg_t
*arglist
, char *arg
)
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
)
552 arglist
->arg_args
= new_args
;
555 arglist
->arg_args
[arglist
->arg_nargs
++] = arg
;
556 arglist
->arg_args
[arglist
->arg_nargs
] = NULL
;
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.
574 while (*cp
== ' ' || *cp
== '\t')
581 while (*cp
&& *cp
!= ' ' && *cp
!= '\t') {
582 if (*cp
== '"' || *cp
== '\'') {
584 while (*cp
&& *cp
!= quote
) {
605 alloc_cmd(nvlist_t
*nvlist
)
609 cmd
= sc_malloc(sizeof (struct cmd
));
611 if (nvlist_dup(nvlist
, &cmd
->cmd_nvlist
, 0) != 0) {
612 syslog(LOG_ERR
, OUT_OF_MEMORY_ERR
);
621 free_cmd(struct cmd
*cmd
)
623 nvlist_free(cmd
->cmd_nvlist
);
635 syslog(LOG_ERR
, OUT_OF_MEMORY_ERR
);
641 sc_realloc(void *p
, size_t n
)
645 syslog(LOG_ERR
, OUT_OF_MEMORY_ERR
);
653 * syserrsg - print error messages to the terminal if not
654 * yet daemonized or to syslog.
658 syserrmsg(char *message
, ...)
662 va_start(ap
, message
);
663 (void) vsyslog(LOG_ERR
, message
, ap
);
668 * printmsg - print messages to the terminal or to syslog
669 * the following levels are implemented:
673 printmsg(int level
, char *message
, ...)
677 if (level
> debug_level
) {
681 va_start(ap
, message
);
682 (void) syslog(LOG_DEBUG
, "%s[%ld]: ", prog
, getpid());
683 (void) vsyslog(LOG_DEBUG
, message
, ap
);
689 create_door_thr(void *arg
)
691 (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE
, NULL
);
692 (void) door_return(NULL
, 0, NULL
, 0);
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.
704 mk_thr_pool(door_info_t
*dip
)
706 (void) mutex_lock(&create_cnt_lock
);
707 if (++cnt_servers
> MAX_SERVER_THREADS
) {
709 (void) mutex_unlock(&create_cnt_lock
);
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
*
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
);
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
);
742 if (sysevent_bind_subscriber(handle
, event_handler
) != 0) {
743 syserrmsg(CHANNEL_BIND_ERR
);
744 sysevent_close_channel(handle
);
747 subclass_list
= EC_SUB_ALL
;
748 if (sysevent_register_event(handle
, EC_ALL
, &subclass_list
, 1)
750 syserrmsg(CHANNEL_BIND_ERR
);
751 (void) sysevent_unbind_subscriber(handle
);
752 (void) sysevent_close_channel(handle
);