1*0Sstevel@tonic-gate /* 2*0Sstevel@tonic-gate * CDDL HEADER START 3*0Sstevel@tonic-gate * 4*0Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*0Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*0Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*0Sstevel@tonic-gate * with the License. 8*0Sstevel@tonic-gate * 9*0Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*0Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*0Sstevel@tonic-gate * See the License for the specific language governing permissions 12*0Sstevel@tonic-gate * and limitations under the License. 13*0Sstevel@tonic-gate * 14*0Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*0Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*0Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*0Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*0Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*0Sstevel@tonic-gate * 20*0Sstevel@tonic-gate * CDDL HEADER END 21*0Sstevel@tonic-gate */ 22*0Sstevel@tonic-gate /* 23*0Sstevel@tonic-gate * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24*0Sstevel@tonic-gate * Use is subject to license terms. 25*0Sstevel@tonic-gate */ 26*0Sstevel@tonic-gate 27*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 28*0Sstevel@tonic-gate 29*0Sstevel@tonic-gate #include <time.h> 30*0Sstevel@tonic-gate #include <stdio.h> 31*0Sstevel@tonic-gate #include <assert.h> 32*0Sstevel@tonic-gate #include <string.h> 33*0Sstevel@tonic-gate #include <stdlib.h> 34*0Sstevel@tonic-gate #include <unistd.h> 35*0Sstevel@tonic-gate #include <sys/types.h> 36*0Sstevel@tonic-gate #include <sys/stat.h> 37*0Sstevel@tonic-gate #include <sys/wait.h> 38*0Sstevel@tonic-gate #include <signal.h> 39*0Sstevel@tonic-gate #include <fcntl.h> 40*0Sstevel@tonic-gate #include <dhcpmsg.h> 41*0Sstevel@tonic-gate #include "script_handler.h" 42*0Sstevel@tonic-gate 43*0Sstevel@tonic-gate /* 44*0Sstevel@tonic-gate * scripts are directly managed by a script helper process. dhcpagent creates 45*0Sstevel@tonic-gate * the helper process and it, in turn, creates a process to run the script 46*0Sstevel@tonic-gate * dhcpagent owns one end of a pipe and the helper process owns the other end 47*0Sstevel@tonic-gate * the helper process calls waitpid to wait for the script to exit. an alarm 48*0Sstevel@tonic-gate * is set for SCRIPT_TIMEOUT seconds. If the alarm fires, SIGTERM is sent to 49*0Sstevel@tonic-gate * the script process and a second alarm is set for SCRIPT_TIMEOUT_GRACE. if 50*0Sstevel@tonic-gate * the second alarm fires, SIGKILL is sent to forcefully kill the script. when 51*0Sstevel@tonic-gate * script exits, the helper process notifies dhcpagent by closing its end 52*0Sstevel@tonic-gate * of the pipe. 53*0Sstevel@tonic-gate */ 54*0Sstevel@tonic-gate 55*0Sstevel@tonic-gate unsigned int script_count; 56*0Sstevel@tonic-gate 57*0Sstevel@tonic-gate /* 58*0Sstevel@tonic-gate * the signal to send to the script process. it is a global variable 59*0Sstevel@tonic-gate * to this file as sigterm_handler needs it. 60*0Sstevel@tonic-gate */ 61*0Sstevel@tonic-gate 62*0Sstevel@tonic-gate static int script_signal = SIGTERM; 63*0Sstevel@tonic-gate 64*0Sstevel@tonic-gate /* 65*0Sstevel@tonic-gate * script's absolute timeout value. the first timeout is set to SCRIPT_TIMEOUT 66*0Sstevel@tonic-gate * seconds from the time it is started. SIGTERM is sent on the first timeout 67*0Sstevel@tonic-gate * the second timeout is set to SCRIPT_TIMEOUT_GRACE from the first timeout 68*0Sstevel@tonic-gate * and SIGKILL is sent on the second timeout. 69*0Sstevel@tonic-gate */ 70*0Sstevel@tonic-gate static time_t timeout; 71*0Sstevel@tonic-gate 72*0Sstevel@tonic-gate /* 73*0Sstevel@tonic-gate * sigalarm_handler(): signal handler for SIGARLM 74*0Sstevel@tonic-gate * 75*0Sstevel@tonic-gate * input: int: signal the handler was called with 76*0Sstevel@tonic-gate * output: void 77*0Sstevel@tonic-gate */ 78*0Sstevel@tonic-gate 79*0Sstevel@tonic-gate /* ARGSUSED */ 80*0Sstevel@tonic-gate static void 81*0Sstevel@tonic-gate sigalarm_handler(int sig) 82*0Sstevel@tonic-gate { 83*0Sstevel@tonic-gate time_t now; 84*0Sstevel@tonic-gate 85*0Sstevel@tonic-gate /* set a another alarm if it fires too early */ 86*0Sstevel@tonic-gate now = time(NULL); 87*0Sstevel@tonic-gate if (now < timeout) 88*0Sstevel@tonic-gate (void) alarm(timeout - now); 89*0Sstevel@tonic-gate } 90*0Sstevel@tonic-gate 91*0Sstevel@tonic-gate /* 92*0Sstevel@tonic-gate * sigterm_handler(): signal handler for SIGTERM, fired when dhcpagent wants 93*0Sstevel@tonic-gate * to stop the script 94*0Sstevel@tonic-gate * input: int: signal the handler was called with 95*0Sstevel@tonic-gate * output: void 96*0Sstevel@tonic-gate */ 97*0Sstevel@tonic-gate 98*0Sstevel@tonic-gate /* ARGSUSED */ 99*0Sstevel@tonic-gate static void 100*0Sstevel@tonic-gate sigterm_handler(int sig) 101*0Sstevel@tonic-gate { 102*0Sstevel@tonic-gate if (script_signal != SIGKILL) { 103*0Sstevel@tonic-gate /* send SIGKILL SCRIPT_TIMEOUT_GRACE seconds from now */ 104*0Sstevel@tonic-gate script_signal = SIGKILL; 105*0Sstevel@tonic-gate timeout = time(NULL) + SCRIPT_TIMEOUT_GRACE; 106*0Sstevel@tonic-gate (void) alarm(SCRIPT_TIMEOUT_GRACE); 107*0Sstevel@tonic-gate } 108*0Sstevel@tonic-gate } 109*0Sstevel@tonic-gate 110*0Sstevel@tonic-gate /* 111*0Sstevel@tonic-gate * run_script(): it forks a process to execute the script 112*0Sstevel@tonic-gate * 113*0Sstevel@tonic-gate * input: struct ifslist *: the interface 114*0Sstevel@tonic-gate * const char *: the event name 115*0Sstevel@tonic-gate * int: the pipe end owned by the script helper process 116*0Sstevel@tonic-gate * output: void 117*0Sstevel@tonic-gate */ 118*0Sstevel@tonic-gate static void 119*0Sstevel@tonic-gate run_script(struct ifslist *ifsp, const char *event, int fd) 120*0Sstevel@tonic-gate { 121*0Sstevel@tonic-gate int n; 122*0Sstevel@tonic-gate char c; 123*0Sstevel@tonic-gate char *name; 124*0Sstevel@tonic-gate pid_t pid; 125*0Sstevel@tonic-gate time_t now; 126*0Sstevel@tonic-gate extern int errno; 127*0Sstevel@tonic-gate 128*0Sstevel@tonic-gate if ((pid = fork()) == -1) { 129*0Sstevel@tonic-gate return; 130*0Sstevel@tonic-gate } 131*0Sstevel@tonic-gate if (pid == 0) { 132*0Sstevel@tonic-gate name = strrchr(SCRIPT_PATH, '/') + 1; 133*0Sstevel@tonic-gate 134*0Sstevel@tonic-gate /* close all files */ 135*0Sstevel@tonic-gate closefrom(0); 136*0Sstevel@tonic-gate 137*0Sstevel@tonic-gate /* redirect stdin, stdout and stderr to /dev/null */ 138*0Sstevel@tonic-gate if ((n = open("/dev/null", O_RDWR)) < 0) 139*0Sstevel@tonic-gate exit(-1); 140*0Sstevel@tonic-gate 141*0Sstevel@tonic-gate (void) dup2(n, STDOUT_FILENO); 142*0Sstevel@tonic-gate (void) dup2(n, STDERR_FILENO); 143*0Sstevel@tonic-gate 144*0Sstevel@tonic-gate (void) execl(SCRIPT_PATH, name, ifsp->if_name, event, NULL); 145*0Sstevel@tonic-gate _exit(127); 146*0Sstevel@tonic-gate } 147*0Sstevel@tonic-gate 148*0Sstevel@tonic-gate /* 149*0Sstevel@tonic-gate * the first timeout fires SCRIPT_TIMEOUT seconds from now. 150*0Sstevel@tonic-gate */ 151*0Sstevel@tonic-gate timeout = time(NULL) + SCRIPT_TIMEOUT; 152*0Sstevel@tonic-gate (void) sigset(SIGALRM, sigalarm_handler); 153*0Sstevel@tonic-gate (void) alarm(SCRIPT_TIMEOUT); 154*0Sstevel@tonic-gate 155*0Sstevel@tonic-gate /* 156*0Sstevel@tonic-gate * pass script's pid to dhcpagent. 157*0Sstevel@tonic-gate */ 158*0Sstevel@tonic-gate (void) write(fd, &pid, sizeof (pid)); 159*0Sstevel@tonic-gate 160*0Sstevel@tonic-gate for (;;) { 161*0Sstevel@tonic-gate if (waitpid(pid, NULL, 0) >= 0) { 162*0Sstevel@tonic-gate /* script has exited */ 163*0Sstevel@tonic-gate c = SCRIPT_OK; 164*0Sstevel@tonic-gate break; 165*0Sstevel@tonic-gate } 166*0Sstevel@tonic-gate 167*0Sstevel@tonic-gate if (errno != EINTR) { 168*0Sstevel@tonic-gate return; 169*0Sstevel@tonic-gate } 170*0Sstevel@tonic-gate 171*0Sstevel@tonic-gate now = time(NULL); 172*0Sstevel@tonic-gate if (now >= timeout) { 173*0Sstevel@tonic-gate (void) kill(pid, script_signal); 174*0Sstevel@tonic-gate if (script_signal == SIGKILL) { 175*0Sstevel@tonic-gate c = SCRIPT_KILLED; 176*0Sstevel@tonic-gate break; 177*0Sstevel@tonic-gate } 178*0Sstevel@tonic-gate 179*0Sstevel@tonic-gate script_signal = SIGKILL; 180*0Sstevel@tonic-gate timeout = now + SCRIPT_TIMEOUT_GRACE; 181*0Sstevel@tonic-gate (void) alarm(SCRIPT_TIMEOUT_GRACE); 182*0Sstevel@tonic-gate } 183*0Sstevel@tonic-gate } 184*0Sstevel@tonic-gate 185*0Sstevel@tonic-gate (void) write(fd, &c, 1); 186*0Sstevel@tonic-gate } 187*0Sstevel@tonic-gate 188*0Sstevel@tonic-gate /* 189*0Sstevel@tonic-gate * script_cleanup(): cleanup helper function 190*0Sstevel@tonic-gate * 191*0Sstevel@tonic-gate * input: struct ifslist *: the interface 192*0Sstevel@tonic-gate * output: void 193*0Sstevel@tonic-gate */ 194*0Sstevel@tonic-gate 195*0Sstevel@tonic-gate static void 196*0Sstevel@tonic-gate script_cleanup(struct ifslist *ifsp) 197*0Sstevel@tonic-gate { 198*0Sstevel@tonic-gate ifsp->if_script_helper_pid = -1; 199*0Sstevel@tonic-gate ifsp->if_script_pid = -1; 200*0Sstevel@tonic-gate 201*0Sstevel@tonic-gate if (ifsp->if_script_fd != -1) { 202*0Sstevel@tonic-gate assert(ifsp->if_script_event_id != -1); 203*0Sstevel@tonic-gate assert(ifsp->if_script_callback != NULL); 204*0Sstevel@tonic-gate 205*0Sstevel@tonic-gate (void) iu_unregister_event(eh, ifsp->if_script_event_id, NULL); 206*0Sstevel@tonic-gate (void) close(ifsp->if_script_fd); 207*0Sstevel@tonic-gate ifsp->if_script_event_id = -1; 208*0Sstevel@tonic-gate ifsp->if_script_fd = -1; 209*0Sstevel@tonic-gate ifsp->if_script_callback(ifsp, ifsp->if_callback_msg); 210*0Sstevel@tonic-gate ifsp->if_script_callback = NULL; 211*0Sstevel@tonic-gate ifsp->if_script_event = NULL; 212*0Sstevel@tonic-gate ifsp->if_callback_msg = NULL; 213*0Sstevel@tonic-gate 214*0Sstevel@tonic-gate (void) release_ifs(ifsp); 215*0Sstevel@tonic-gate script_count--; 216*0Sstevel@tonic-gate } 217*0Sstevel@tonic-gate } 218*0Sstevel@tonic-gate 219*0Sstevel@tonic-gate /* 220*0Sstevel@tonic-gate * script_exit(): does cleanup and invokes callback when script exits 221*0Sstevel@tonic-gate * 222*0Sstevel@tonic-gate * input: eh_t *: unused 223*0Sstevel@tonic-gate * int: the end of pipe owned by dhcpagent 224*0Sstevel@tonic-gate * short: unused 225*0Sstevel@tonic-gate * eh_event_id_t: unused 226*0Sstevel@tonic-gate * void *: the interface 227*0Sstevel@tonic-gate * output: void 228*0Sstevel@tonic-gate */ 229*0Sstevel@tonic-gate 230*0Sstevel@tonic-gate /* ARGSUSED */ 231*0Sstevel@tonic-gate static void 232*0Sstevel@tonic-gate script_exit(iu_eh_t *ehp, int fd, short events, iu_event_id_t id, void *arg) 233*0Sstevel@tonic-gate { 234*0Sstevel@tonic-gate char c; 235*0Sstevel@tonic-gate 236*0Sstevel@tonic-gate if (read(fd, &c, 1) <= 0) { 237*0Sstevel@tonic-gate c = SCRIPT_FAILED; 238*0Sstevel@tonic-gate } 239*0Sstevel@tonic-gate 240*0Sstevel@tonic-gate if (c == SCRIPT_OK) { 241*0Sstevel@tonic-gate dhcpmsg(MSG_DEBUG, "script ok"); 242*0Sstevel@tonic-gate } else if (c == SCRIPT_KILLED) { 243*0Sstevel@tonic-gate dhcpmsg(MSG_DEBUG, "script killed"); 244*0Sstevel@tonic-gate } else { 245*0Sstevel@tonic-gate dhcpmsg(MSG_DEBUG, "script failed"); 246*0Sstevel@tonic-gate } 247*0Sstevel@tonic-gate 248*0Sstevel@tonic-gate script_cleanup(arg); 249*0Sstevel@tonic-gate } 250*0Sstevel@tonic-gate 251*0Sstevel@tonic-gate /* 252*0Sstevel@tonic-gate * script_start(): tries to run the script 253*0Sstevel@tonic-gate * 254*0Sstevel@tonic-gate * input: struct ifslist *: the interface 255*0Sstevel@tonic-gate * const char *: the event name 256*0Sstevel@tonic-gate * script_callback_t: callback function 257*0Sstevel@tonic-gate * void *: data to the callback function 258*0Sstevel@tonic-gate * output: int: 1 if script starts successfully 259*0Sstevel@tonic-gate * int *: the returned value of the callback function if script 260*0Sstevel@tonic-gate * starts unsuccessfully 261*0Sstevel@tonic-gate */ 262*0Sstevel@tonic-gate int 263*0Sstevel@tonic-gate script_start(struct ifslist *ifsp, const char *event, 264*0Sstevel@tonic-gate script_callback_t *callback, const char *msg, int *status) 265*0Sstevel@tonic-gate { 266*0Sstevel@tonic-gate int n; 267*0Sstevel@tonic-gate int fds[2]; 268*0Sstevel@tonic-gate pid_t pid; 269*0Sstevel@tonic-gate iu_event_id_t event_id; 270*0Sstevel@tonic-gate 271*0Sstevel@tonic-gate assert(callback != NULL); 272*0Sstevel@tonic-gate 273*0Sstevel@tonic-gate if (access(SCRIPT_PATH, X_OK) == -1) { 274*0Sstevel@tonic-gate /* script does not exist */ 275*0Sstevel@tonic-gate goto out; 276*0Sstevel@tonic-gate } 277*0Sstevel@tonic-gate if (ifsp->if_script_pid != -1) { 278*0Sstevel@tonic-gate /* script is running, stop it */ 279*0Sstevel@tonic-gate dhcpmsg(MSG_ERROR, "script_start: stop script"); 280*0Sstevel@tonic-gate script_stop(ifsp); 281*0Sstevel@tonic-gate } 282*0Sstevel@tonic-gate 283*0Sstevel@tonic-gate /* 284*0Sstevel@tonic-gate * dhcpagent owns one end of the pipe and script helper process 285*0Sstevel@tonic-gate * owns the other end. dhcpagent reads on the pipe; and the helper 286*0Sstevel@tonic-gate * process notifies it when the script exits. 287*0Sstevel@tonic-gate */ 288*0Sstevel@tonic-gate if (pipe(fds) < 0) { 289*0Sstevel@tonic-gate dhcpmsg(MSG_ERROR, "script_start: can't create pipe"); 290*0Sstevel@tonic-gate goto out; 291*0Sstevel@tonic-gate } 292*0Sstevel@tonic-gate 293*0Sstevel@tonic-gate if ((pid = fork()) < 0) { 294*0Sstevel@tonic-gate dhcpmsg(MSG_ERROR, "script_start: can't fork"); 295*0Sstevel@tonic-gate (void) close(fds[0]); 296*0Sstevel@tonic-gate (void) close(fds[1]); 297*0Sstevel@tonic-gate goto out; 298*0Sstevel@tonic-gate } 299*0Sstevel@tonic-gate 300*0Sstevel@tonic-gate if (pid == 0) { 301*0Sstevel@tonic-gate /* 302*0Sstevel@tonic-gate * SIGCHLD is ignored in dhcpagent, the helper process 303*0Sstevel@tonic-gate * needs it. it calls waitpid to wait for the script to exit. 304*0Sstevel@tonic-gate */ 305*0Sstevel@tonic-gate (void) close(fds[0]); 306*0Sstevel@tonic-gate (void) sigset(SIGCHLD, SIG_DFL); 307*0Sstevel@tonic-gate (void) sigset(SIGTERM, sigterm_handler); 308*0Sstevel@tonic-gate run_script(ifsp, event, fds[1]); 309*0Sstevel@tonic-gate exit(0); 310*0Sstevel@tonic-gate } 311*0Sstevel@tonic-gate 312*0Sstevel@tonic-gate (void) close(fds[1]); 313*0Sstevel@tonic-gate 314*0Sstevel@tonic-gate /* get the script's pid */ 315*0Sstevel@tonic-gate if (read(fds[0], &ifsp->if_script_pid, sizeof (pid_t)) != 316*0Sstevel@tonic-gate sizeof (pid_t)) { 317*0Sstevel@tonic-gate (void) kill(pid, SIGKILL); 318*0Sstevel@tonic-gate ifsp->if_script_pid = -1; 319*0Sstevel@tonic-gate (void) close(fds[0]); 320*0Sstevel@tonic-gate goto out; 321*0Sstevel@tonic-gate } 322*0Sstevel@tonic-gate 323*0Sstevel@tonic-gate ifsp->if_script_helper_pid = pid; 324*0Sstevel@tonic-gate event_id = iu_register_event(eh, fds[0], POLLIN, script_exit, ifsp); 325*0Sstevel@tonic-gate if (event_id == -1) { 326*0Sstevel@tonic-gate (void) close(fds[0]); 327*0Sstevel@tonic-gate script_stop(ifsp); 328*0Sstevel@tonic-gate goto out; 329*0Sstevel@tonic-gate } 330*0Sstevel@tonic-gate 331*0Sstevel@tonic-gate script_count++; 332*0Sstevel@tonic-gate ifsp->if_script_event_id = event_id; 333*0Sstevel@tonic-gate ifsp->if_script_callback = callback; 334*0Sstevel@tonic-gate ifsp->if_script_event = event; 335*0Sstevel@tonic-gate ifsp->if_callback_msg = msg; 336*0Sstevel@tonic-gate ifsp->if_script_fd = fds[0]; 337*0Sstevel@tonic-gate hold_ifs(ifsp); 338*0Sstevel@tonic-gate return (1); 339*0Sstevel@tonic-gate 340*0Sstevel@tonic-gate out: 341*0Sstevel@tonic-gate /* callback won't be called in script_exit, so call it here */ 342*0Sstevel@tonic-gate n = callback(ifsp, msg); 343*0Sstevel@tonic-gate if (status != NULL) 344*0Sstevel@tonic-gate *status = n; 345*0Sstevel@tonic-gate 346*0Sstevel@tonic-gate return (0); 347*0Sstevel@tonic-gate } 348*0Sstevel@tonic-gate 349*0Sstevel@tonic-gate /* 350*0Sstevel@tonic-gate * script_stop(): stops the script if it is running 351*0Sstevel@tonic-gate * 352*0Sstevel@tonic-gate * input: struct ifslist *: the interface 353*0Sstevel@tonic-gate * output: void 354*0Sstevel@tonic-gate */ 355*0Sstevel@tonic-gate void 356*0Sstevel@tonic-gate script_stop(struct ifslist *ifsp) 357*0Sstevel@tonic-gate { 358*0Sstevel@tonic-gate if (ifsp->if_script_pid != -1) { 359*0Sstevel@tonic-gate assert(ifsp->if_script_helper_pid != -1); 360*0Sstevel@tonic-gate 361*0Sstevel@tonic-gate /* 362*0Sstevel@tonic-gate * sends SIGTERM to the script and asks the helper process 363*0Sstevel@tonic-gate * to send SIGKILL if it does not exit after 364*0Sstevel@tonic-gate * SCRIPT_TIMEOUT_GRACE seconds. 365*0Sstevel@tonic-gate */ 366*0Sstevel@tonic-gate (void) kill(ifsp->if_script_pid, SIGTERM); 367*0Sstevel@tonic-gate (void) kill(ifsp->if_script_helper_pid, SIGTERM); 368*0Sstevel@tonic-gate } 369*0Sstevel@tonic-gate 370*0Sstevel@tonic-gate script_cleanup(ifsp); 371*0Sstevel@tonic-gate } 372