diff options
author | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
---|---|---|
committer | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
commit | 7c478bd95313f5f23a4c958a745db2134aa03244 (patch) | |
tree | c871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/cmd/cmd-inet/sbin/dhcpagent/script_handler.c | |
download | illumos-gate-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz |
OpenSolaris Launch
Diffstat (limited to 'usr/src/cmd/cmd-inet/sbin/dhcpagent/script_handler.c')
-rw-r--r-- | usr/src/cmd/cmd-inet/sbin/dhcpagent/script_handler.c | 371 |
1 files changed, 371 insertions, 0 deletions
diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/script_handler.c b/usr/src/cmd/cmd-inet/sbin/dhcpagent/script_handler.c new file mode 100644 index 0000000000..9dd9690748 --- /dev/null +++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/script_handler.c @@ -0,0 +1,371 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <time.h> +#include <stdio.h> +#include <assert.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <signal.h> +#include <fcntl.h> +#include <dhcpmsg.h> +#include "script_handler.h" + +/* + * scripts are directly managed by a script helper process. dhcpagent creates + * the helper process and it, in turn, creates a process to run the script + * dhcpagent owns one end of a pipe and the helper process owns the other end + * the helper process calls waitpid to wait for the script to exit. an alarm + * is set for SCRIPT_TIMEOUT seconds. If the alarm fires, SIGTERM is sent to + * the script process and a second alarm is set for SCRIPT_TIMEOUT_GRACE. if + * the second alarm fires, SIGKILL is sent to forcefully kill the script. when + * script exits, the helper process notifies dhcpagent by closing its end + * of the pipe. + */ + +unsigned int script_count; + +/* + * the signal to send to the script process. it is a global variable + * to this file as sigterm_handler needs it. + */ + +static int script_signal = SIGTERM; + +/* + * script's absolute timeout value. the first timeout is set to SCRIPT_TIMEOUT + * seconds from the time it is started. SIGTERM is sent on the first timeout + * the second timeout is set to SCRIPT_TIMEOUT_GRACE from the first timeout + * and SIGKILL is sent on the second timeout. + */ +static time_t timeout; + +/* + * sigalarm_handler(): signal handler for SIGARLM + * + * input: int: signal the handler was called with + * output: void + */ + +/* ARGSUSED */ +static void +sigalarm_handler(int sig) +{ + time_t now; + + /* set a another alarm if it fires too early */ + now = time(NULL); + if (now < timeout) + (void) alarm(timeout - now); +} + +/* + * sigterm_handler(): signal handler for SIGTERM, fired when dhcpagent wants + * to stop the script + * input: int: signal the handler was called with + * output: void + */ + +/* ARGSUSED */ +static void +sigterm_handler(int sig) +{ + if (script_signal != SIGKILL) { + /* send SIGKILL SCRIPT_TIMEOUT_GRACE seconds from now */ + script_signal = SIGKILL; + timeout = time(NULL) + SCRIPT_TIMEOUT_GRACE; + (void) alarm(SCRIPT_TIMEOUT_GRACE); + } +} + +/* + * run_script(): it forks a process to execute the script + * + * input: struct ifslist *: the interface + * const char *: the event name + * int: the pipe end owned by the script helper process + * output: void + */ +static void +run_script(struct ifslist *ifsp, const char *event, int fd) +{ + int n; + char c; + char *name; + pid_t pid; + time_t now; + extern int errno; + + if ((pid = fork()) == -1) { + return; + } + if (pid == 0) { + name = strrchr(SCRIPT_PATH, '/') + 1; + + /* close all files */ + closefrom(0); + + /* redirect stdin, stdout and stderr to /dev/null */ + if ((n = open("/dev/null", O_RDWR)) < 0) + exit(-1); + + (void) dup2(n, STDOUT_FILENO); + (void) dup2(n, STDERR_FILENO); + + (void) execl(SCRIPT_PATH, name, ifsp->if_name, event, NULL); + _exit(127); + } + + /* + * the first timeout fires SCRIPT_TIMEOUT seconds from now. + */ + timeout = time(NULL) + SCRIPT_TIMEOUT; + (void) sigset(SIGALRM, sigalarm_handler); + (void) alarm(SCRIPT_TIMEOUT); + + /* + * pass script's pid to dhcpagent. + */ + (void) write(fd, &pid, sizeof (pid)); + + for (;;) { + if (waitpid(pid, NULL, 0) >= 0) { + /* script has exited */ + c = SCRIPT_OK; + break; + } + + if (errno != EINTR) { + return; + } + + now = time(NULL); + if (now >= timeout) { + (void) kill(pid, script_signal); + if (script_signal == SIGKILL) { + c = SCRIPT_KILLED; + break; + } + + script_signal = SIGKILL; + timeout = now + SCRIPT_TIMEOUT_GRACE; + (void) alarm(SCRIPT_TIMEOUT_GRACE); + } + } + + (void) write(fd, &c, 1); +} + +/* + * script_cleanup(): cleanup helper function + * + * input: struct ifslist *: the interface + * output: void + */ + +static void +script_cleanup(struct ifslist *ifsp) +{ + ifsp->if_script_helper_pid = -1; + ifsp->if_script_pid = -1; + + if (ifsp->if_script_fd != -1) { + assert(ifsp->if_script_event_id != -1); + assert(ifsp->if_script_callback != NULL); + + (void) iu_unregister_event(eh, ifsp->if_script_event_id, NULL); + (void) close(ifsp->if_script_fd); + ifsp->if_script_event_id = -1; + ifsp->if_script_fd = -1; + ifsp->if_script_callback(ifsp, ifsp->if_callback_msg); + ifsp->if_script_callback = NULL; + ifsp->if_script_event = NULL; + ifsp->if_callback_msg = NULL; + + (void) release_ifs(ifsp); + script_count--; + } +} + +/* + * script_exit(): does cleanup and invokes callback when script exits + * + * input: eh_t *: unused + * int: the end of pipe owned by dhcpagent + * short: unused + * eh_event_id_t: unused + * void *: the interface + * output: void + */ + +/* ARGSUSED */ +static void +script_exit(iu_eh_t *ehp, int fd, short events, iu_event_id_t id, void *arg) +{ + char c; + + if (read(fd, &c, 1) <= 0) { + c = SCRIPT_FAILED; + } + + if (c == SCRIPT_OK) { + dhcpmsg(MSG_DEBUG, "script ok"); + } else if (c == SCRIPT_KILLED) { + dhcpmsg(MSG_DEBUG, "script killed"); + } else { + dhcpmsg(MSG_DEBUG, "script failed"); + } + + script_cleanup(arg); +} + +/* + * script_start(): tries to run the script + * + * input: struct ifslist *: the interface + * const char *: the event name + * script_callback_t: callback function + * void *: data to the callback function + * output: int: 1 if script starts successfully + * int *: the returned value of the callback function if script + * starts unsuccessfully + */ +int +script_start(struct ifslist *ifsp, const char *event, + script_callback_t *callback, const char *msg, int *status) +{ + int n; + int fds[2]; + pid_t pid; + iu_event_id_t event_id; + + assert(callback != NULL); + + if (access(SCRIPT_PATH, X_OK) == -1) { + /* script does not exist */ + goto out; + } + if (ifsp->if_script_pid != -1) { + /* script is running, stop it */ + dhcpmsg(MSG_ERROR, "script_start: stop script"); + script_stop(ifsp); + } + + /* + * dhcpagent owns one end of the pipe and script helper process + * owns the other end. dhcpagent reads on the pipe; and the helper + * process notifies it when the script exits. + */ + if (pipe(fds) < 0) { + dhcpmsg(MSG_ERROR, "script_start: can't create pipe"); + goto out; + } + + if ((pid = fork()) < 0) { + dhcpmsg(MSG_ERROR, "script_start: can't fork"); + (void) close(fds[0]); + (void) close(fds[1]); + goto out; + } + + if (pid == 0) { + /* + * SIGCHLD is ignored in dhcpagent, the helper process + * needs it. it calls waitpid to wait for the script to exit. + */ + (void) close(fds[0]); + (void) sigset(SIGCHLD, SIG_DFL); + (void) sigset(SIGTERM, sigterm_handler); + run_script(ifsp, event, fds[1]); + exit(0); + } + + (void) close(fds[1]); + + /* get the script's pid */ + if (read(fds[0], &ifsp->if_script_pid, sizeof (pid_t)) != + sizeof (pid_t)) { + (void) kill(pid, SIGKILL); + ifsp->if_script_pid = -1; + (void) close(fds[0]); + goto out; + } + + ifsp->if_script_helper_pid = pid; + event_id = iu_register_event(eh, fds[0], POLLIN, script_exit, ifsp); + if (event_id == -1) { + (void) close(fds[0]); + script_stop(ifsp); + goto out; + } + + script_count++; + ifsp->if_script_event_id = event_id; + ifsp->if_script_callback = callback; + ifsp->if_script_event = event; + ifsp->if_callback_msg = msg; + ifsp->if_script_fd = fds[0]; + hold_ifs(ifsp); + return (1); + +out: + /* callback won't be called in script_exit, so call it here */ + n = callback(ifsp, msg); + if (status != NULL) + *status = n; + + return (0); +} + +/* + * script_stop(): stops the script if it is running + * + * input: struct ifslist *: the interface + * output: void + */ +void +script_stop(struct ifslist *ifsp) +{ + if (ifsp->if_script_pid != -1) { + assert(ifsp->if_script_helper_pid != -1); + + /* + * sends SIGTERM to the script and asks the helper process + * to send SIGKILL if it does not exit after + * SCRIPT_TIMEOUT_GRACE seconds. + */ + (void) kill(ifsp->if_script_pid, SIGTERM); + (void) kill(ifsp->if_script_helper_pid, SIGTERM); + } + + script_cleanup(ifsp); +} |