summaryrefslogtreecommitdiff
path: root/usr/src/cmd/cmd-inet/sbin/dhcpagent/script_handler.c
diff options
context:
space:
mode:
authorstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
committerstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
commit7c478bd95313f5f23a4c958a745db2134aa03244 (patch)
treec871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/cmd/cmd-inet/sbin/dhcpagent/script_handler.c
downloadillumos-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.c371
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);
+}