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.
28 * syseventconfd - The sysevent conf daemon
30 * This daemon is a companion to the sysevent_conf_mod module.
32 * The sysevent_conf_mod module receives events from syseventd,
33 * and compares those events against event specs in the
34 * sysevent.conf files. For each matching event spec, the
35 * specified command is invoked.
37 * This daemon manages the fork/exec's on behalf of sysevent_conf_mod.
38 * The events and associated nvlist are delivered via a door upcall
39 * from sysevent_conf_mod. Arriving events are queued, and the
40 * main thread of this daemon dequeues events one by one, and
41 * builds the necessary arguments to fork/exec the command.
43 * Since sysevent_conf_mod is running in the context of syseventd,
44 * invoking the fork/exec from that module blocks the door upcalls
45 * from the kernel delivering events to syseventd. We avoid a
46 * major performance bottleneck in this fashion.
62 #include <libsysevent.h>
65 #include <sys/modctl.h>
67 #include <sys/systeminfo.h>
70 #include "syseventconfd.h"
71 #include "syseventconfd_door.h"
72 #include "message_confd.h"
76 static int debug_level
= 0;
77 static char *root_dir
= ""; /* Relative root for lock and door */
80 static struct cmd
*cmd_list
;
81 static struct cmd
*cmd_tail
;
83 static mutex_t cmd_list_lock
;
84 static cond_t cmd_list_cv
;
89 * Support for door server thread handling
91 #define MAX_SERVER_THREADS 1
93 static mutex_t create_cnt_lock
;
94 static int cnt_servers
= 0;
99 (void) fprintf(stderr
, "usage: syseventconfd [-d <debug_level>]\n");
105 set_root_dir(char *dir
)
107 root_dir
= malloc(strlen(dir
) + 1);
108 if (root_dir
== NULL
) {
109 syserrmsg(INIT_ROOT_DIR_ERR
, strerror(errno
));
112 (void) strcpy(root_dir
, dir
);
117 main(int argc
, char **argv
)
124 (void) setlocale(LC_ALL
, "");
125 (void) textdomain(TEXT_DOMAIN
);
128 (void) fprintf(stderr
, "Must be root to run syseventconfd\n");
132 if ((prog
= strrchr(argv
[0], '/')) == NULL
) {
138 if ((c
= getopt(argc
, argv
, "d:r:")) != EOF
) {
141 debug_level
= atoi(optarg
);
145 * Private flag for suninstall to run
146 * daemon during install.
148 set_root_dir(optarg
);
164 if (debug_level
<= 1) {
166 fd
= open("/dev/null", O_RDONLY
);
171 openlog("syseventconfd", LOG_PID
, LOG_DAEMON
);
174 "syseventconfd started, debug level = %d\n", debug_level
);
177 * Block all signals to all threads include the main thread.
178 * The sigwait_thr thread will catch and process all signals.
180 (void) sigfillset(&set
);
181 (void) thr_sigsetmask(SIG_BLOCK
, &set
, NULL
);
183 /* Create signal catching thread */
184 if (thr_create(NULL
, 0, (void *(*)(void *))sigwait_thr
,
185 NULL
, 0, NULL
) < 0) {
186 syserrmsg(INIT_THR_CREATE_ERR
, strerror(errno
));
191 * Init mutex and list of cmds to be fork/exec'ed
192 * This is multi-threaded so the fork/exec can be
193 * done without blocking the door upcall.
198 (void) mutex_init(&create_cnt_lock
, USYNC_THREAD
, NULL
);
199 (void) mutex_init(&cmd_list_lock
, USYNC_THREAD
, NULL
);
200 (void) cond_init(&cmd_list_cv
, USYNC_THREAD
, NULL
);
203 * Open communication channel from sysevent_conf_mod
205 if (open_channel() == NULL
) {
210 * main thread to wait for events to arrive and be placed
211 * on the queue. As events are queued, dequeue them
212 * here and invoke the associated fork/exec.
214 (void) mutex_lock(&cmd_list_lock
);
216 while (cmd_list
== NULL
)
217 (void) cond_wait(&cmd_list_cv
, &cmd_list_lock
);
220 cmd_list
= cmd
->cmd_next
;
221 if (cmd_list
== NULL
)
224 (void) mutex_unlock(&cmd_list_lock
);
227 (void) mutex_lock(&cmd_list_lock
);
234 * Events sent via the door call from sysevent_conf_mod arrive
235 * here. Queue each event for the main thread to invoke, and
236 * return. We want to avoid doing the fork/exec while in the
237 * context of the door call.
241 event_handler(sysevent_t
*event
)
247 if (sysevent_get_attr_list(event
, &nvlist
) != 0) {
248 syslog(LOG_ERR
, NO_NVLIST_ERR
);
252 if ((cmd
= alloc_cmd(nvlist
)) != NULL
) {
253 (void) mutex_lock(&cmd_list_lock
);
254 if (cmd_list
== NULL
) {
258 cmd_tail
->cmd_next
= cmd
;
261 cmd
->cmd_next
= NULL
;
262 (void) cond_signal(&cmd_list_cv
);
263 (void) mutex_unlock(&cmd_list_lock
);
271 * Decode the command, build the exec args and fork/exec the command
272 * All command attributes are packed into the nvlist bundled with
273 * the delivered event.
276 exec_cmd(struct cmd
*cmd
)
290 sigset_t set
, prior_set
;
292 if (nvlist_lookup_string(cmd
->cmd_nvlist
, "user", &user
) != 0) {
293 syslog(LOG_ERR
, NVLIST_FORMAT_ERR
, "user");
296 if (nvlist_lookup_string(cmd
->cmd_nvlist
, "file", &file
) != 0) {
297 syslog(LOG_ERR
, NVLIST_FORMAT_ERR
, "file");
301 if (nvlist_lookup_string(cmd
->cmd_nvlist
, "path", &path
) != 0) {
302 syslog(LOG_ERR
, NVLIST_FILE_LINE_FORMAT_ERR
, "path");
305 if (nvlist_lookup_string(cmd
->cmd_nvlist
, "cmd", &cmdline
) != 0) {
306 syslog(LOG_ERR
, NVLIST_FILE_LINE_FORMAT_ERR
, "cmd");
309 if (nvlist_lookup_int32(cmd
->cmd_nvlist
, "line", &line
) != 0) {
310 syslog(LOG_ERR
, NVLIST_FILE_LINE_FORMAT_ERR
, "line");
313 if (nvlist_lookup_int32(cmd
->cmd_nvlist
, "uid", (int *)&uid
) == 0) {
314 if (nvlist_lookup_int32(cmd
->cmd_nvlist
,
315 "gid", (int *)&gid
) != 0) {
316 syslog(LOG_ERR
, NVLIST_FILE_LINE_FORMAT_ERR
, "gid");
324 args
= init_arglist(32);
327 while ((p
= next_arg(&lp
)) != NULL
) {
328 if (add_arg(args
, p
)) {
334 if (debug_level
>= DBG_EXEC
) {
335 printmsg(DBG_EXEC
, "path=%s\n", path
);
336 printmsg(DBG_EXEC
, "cmd=%s\n", cmdline
);
339 if (debug_level
>= DBG_EXEC_ARGS
) {
340 for (i
= 0; i
< args
->arg_nargs
; i
++) {
341 printmsg(DBG_EXEC_ARGS
,
342 "arg[%d]: '%s'\n", i
, args
->arg_args
[i
]);
346 (void) sigprocmask(SIG_SETMASK
, NULL
, &set
);
347 (void) sigaddset(&set
, SIGCHLD
);
348 (void) sigprocmask(SIG_SETMASK
, &set
, &prior_set
);
351 if ((pid
= fork1()) == (pid_t
)-1) {
354 syslog(LOG_ERR
, CANNOT_FORK_ERR
, strerror(errno
));
358 if (pid
!= (pid_t
)0) {
359 (void) sigprocmask(SIG_SETMASK
, &prior_set
, NULL
);
370 (void) open("/dev/null", O_RDONLY
);
374 if (uid
!= (uid_t
)0) {
379 syslog(LOG_ERR
, SETUID_ERR
,
380 file
, line
, user
, strerror(errno
));
386 * Unblock all signals in the child
388 (void) sigprocmask(SIG_UNBLOCK
, &prior_set
, NULL
);
390 if (execv(path
, args
->arg_args
) == -1) {
391 syslog(LOG_ERR
, CANNOT_EXEC_ERR
,
392 path
, strerror(errno
));
399 * Thread to handle in-coming signals
408 * SIGCHLD is ignored by default, and we need to handle this
409 * signal to reap the status of all children spawned by
412 (void) sigset(SIGCHLD
, reapchild
);
415 (void) sigfillset(&signal_set
);
416 if (sigwait(&signal_set
, &sig
) == 0) {
418 * Block all signals until the signal handler completes
420 (void) sigfillset(&signal_set
);
421 (void) thr_sigsetmask(SIG_BLOCK
, &signal_set
, NULL
);
423 if (sig
== SIGCHLD
) {
436 * reapchild - reap the status of each child as it exits
447 (void) memset(&info
, 0, sizeof (info
));
448 err
= waitid(P_ALL
, 0, &info
, WNOHANG
|WEXITED
);
450 if (errno
!= EINTR
&& errno
!= EAGAIN
)
452 } else if (info
.si_pid
== 0) {
456 if (debug_level
>= DBG_CHILD
) {
457 printmsg(DBG_CHILD
, CHILD_EXIT_STATUS_ERR
,
458 info
.si_pid
, info
.si_status
);
461 if (info
.si_status
) {
462 if (info
.si_code
== CLD_EXITED
) {
463 syserrmsg(CHILD_EXIT_STATUS_ERR
,
464 info
.si_pid
, info
.si_status
);
466 signam
= strsignal(info
.si_status
);
469 if (info
.si_code
== CLD_DUMPED
) {
472 info
.si_pid
, signam
);
475 CHILD_EXIT_SIGNAL_ERR
,
476 info
.si_pid
, signam
);
485 * Fault handler for other signals caught
491 struct sigaction act
;
493 (void) memset(&act
, 0, sizeof (act
));
494 act
.sa_handler
= SIG_DFL
;
495 act
.sa_flags
= SA_RESTART
;
496 (void) sigfillset(&act
.sa_mask
);
497 (void) sigaction(sig
, &act
, NULL
);
511 init_arglist(int hint
)
515 if ((arglist
= sc_malloc(sizeof (arg_t
))) == NULL
)
517 arglist
->arg_args
= NULL
;
518 arglist
->arg_nargs
= 0;
519 arglist
->arg_alloc
= 0;
520 arglist
->arg_hint
= hint
;
526 free_arglist(arg_t
*arglist
)
528 if (arglist
->arg_args
) {
529 free(arglist
->arg_args
);
536 add_arg(arg_t
*arglist
, char *arg
)
541 len
= arglist
->arg_nargs
+ 2;
542 if (arglist
->arg_alloc
< len
) {
543 arglist
->arg_alloc
= len
+ arglist
->arg_hint
;
544 new_args
= (arglist
->arg_nargs
== 0) ?
545 sc_malloc(arglist
->arg_alloc
* sizeof (char **)) :
546 sc_realloc(arglist
->arg_args
,
547 arglist
->arg_alloc
* sizeof (char **));
548 if (new_args
== NULL
)
550 arglist
->arg_args
= new_args
;
553 arglist
->arg_args
[arglist
->arg_nargs
++] = arg
;
554 arglist
->arg_args
[arglist
->arg_nargs
] = NULL
;
560 * next_arg() is used to break up a command line
561 * into the arguments for execv(2). Break up
562 * arguments separated by spaces, but respecting
563 * single/double quotes.
572 while (*cp
== ' ' || *cp
== '\t')
579 while (*cp
&& *cp
!= ' ' && *cp
!= '\t') {
580 if (*cp
== '"' || *cp
== '\'') {
582 while (*cp
&& *cp
!= quote
) {
603 alloc_cmd(nvlist_t
*nvlist
)
607 cmd
= sc_malloc(sizeof (struct cmd
));
609 if (nvlist_dup(nvlist
, &cmd
->cmd_nvlist
, 0) != 0) {
610 syslog(LOG_ERR
, OUT_OF_MEMORY_ERR
);
619 free_cmd(struct cmd
*cmd
)
621 nvlist_free(cmd
->cmd_nvlist
);
633 syslog(LOG_ERR
, OUT_OF_MEMORY_ERR
);
639 sc_realloc(void *p
, size_t n
)
643 syslog(LOG_ERR
, OUT_OF_MEMORY_ERR
);
651 * syserrsg - print error messages to the terminal if not
652 * yet daemonized or to syslog.
656 syserrmsg(char *message
, ...)
660 va_start(ap
, message
);
661 (void) vsyslog(LOG_ERR
, message
, ap
);
666 * printmsg - print messages to the terminal or to syslog
667 * the following levels are implemented:
671 printmsg(int level
, char *message
, ...)
675 if (level
> debug_level
) {
679 va_start(ap
, message
);
680 (void) syslog(LOG_DEBUG
, "%s[%ld]: ", prog
, getpid());
681 (void) vsyslog(LOG_DEBUG
, message
, ap
);
687 create_door_thr(void *arg
)
689 (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE
, NULL
);
690 (void) door_return(NULL
, 0, NULL
, 0);
695 * Control creation of door server threads
697 * If first creation of server thread fails there is nothing
698 * we can do about. Doors would not work.
702 mk_thr_pool(door_info_t
*dip
)
704 (void) mutex_lock(&create_cnt_lock
);
705 if (++cnt_servers
> MAX_SERVER_THREADS
) {
707 (void) mutex_unlock(&create_cnt_lock
);
710 (void) mutex_unlock(&create_cnt_lock
);
712 (void) thr_create(NULL
, 0, create_door_thr
, NULL
,
713 THR_BOUND
|THR_DETACHED
, NULL
);
716 static sysevent_handle_t
*
719 char door_path
[MAXPATHLEN
];
720 const char *subclass_list
;
721 sysevent_handle_t
*handle
;
723 if (snprintf(door_path
, sizeof (door_path
), "%s/%s",
724 root_dir
, SYSEVENTCONFD_SERVICE_DOOR
) >= sizeof (door_path
)) {
725 syserrmsg(CHANNEL_OPEN_ERR
);
730 * Setup of door server create function to limit the
731 * amount of door servers
733 (void) door_server_create(mk_thr_pool
);
735 handle
= sysevent_open_channel_alt(door_path
);
736 if (handle
== NULL
) {
737 syserrmsg(CHANNEL_OPEN_ERR
);
740 if (sysevent_bind_subscriber(handle
, event_handler
) != 0) {
741 syserrmsg(CHANNEL_BIND_ERR
);
742 sysevent_close_channel(handle
);
745 subclass_list
= EC_SUB_ALL
;
746 if (sysevent_register_event(handle
, EC_ALL
, &subclass_list
, 1)
748 syserrmsg(CHANNEL_BIND_ERR
);
749 (void) sysevent_unbind_subscriber(handle
);
750 (void) sysevent_close_channel(handle
);