1/* script.c 2 * 3 * Functions to call the DHCP client notification scripts 4 * 5 * Russ Dill <Russ.Dill@asu.edu> July 2001 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; either version 2 of the License, or 10 * (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 */ 21 22#include <string.h> 23#include <unistd.h> 24#include <stdio.h> 25#include <stdlib.h> 26#include <sys/socket.h> 27#include <netinet/in.h> 28#include <arpa/inet.h> 29#include <sys/types.h> 30#include <sys/wait.h> 31#include <errno.h> 32 33#include "options.h" 34#include "dhcpd.h" 35#include "dhcpc.h" 36#include "packet.h" 37#include "options.h" 38#include "debug.h" 39 40/* get a rough idea of how long an option will be (rounding up...) */ 41static int max_option_length[] = { 42 [OPTION_IP] = sizeof("255.255.255.255 "), 43 [OPTION_IP_PAIR] = sizeof("255.255.255.255 ") * 2, 44 [OPTION_STRING] = 1, 45 [OPTION_BOOLEAN] = sizeof("yes "), 46 [OPTION_U8] = sizeof("255 "), 47 [OPTION_U16] = sizeof("65535 "), 48 [OPTION_S16] = sizeof("-32768 "), 49 [OPTION_U32] = sizeof("4294967295 "), 50 [OPTION_S32] = sizeof("-2147483684 "), 51}; 52 53 54static int upper_length(int length, struct dhcp_option *option) 55{ 56 return max_option_length[option->flags & TYPE_MASK] * 57 (length / option_lengths[option->flags & TYPE_MASK]); 58} 59 60 61static int sprintip(char *dest, char *pre, unsigned char *ip) { 62 return sprintf(dest, "%s%d.%d.%d.%d ", pre, ip[0], ip[1], ip[2], ip[3]); 63} 64 65 66/* Fill dest with the text of option 'option'. */ 67static void fill_options(char *dest, unsigned char *option, struct dhcp_option *type_p) 68{ 69 int type, optlen; 70 u_int16_t val_u16; 71 int16_t val_s16; 72 u_int32_t val_u32; 73 int32_t val_s32; 74 int len = option[OPT_LEN - 2]; 75 76 dest += sprintf(dest, "%s=", type_p->name); 77 78 type = type_p->flags & TYPE_MASK; 79 optlen = option_lengths[type]; 80 for(;;) { 81 switch (type) { 82 case OPTION_IP_PAIR: 83 dest += sprintip(dest, "", option); 84 *(dest++) = '/'; 85 option += 4; 86 optlen = 4; 87 case OPTION_IP: /* Works regardless of host byte order. */ 88 dest += sprintip(dest, "", option); 89 break; 90 case OPTION_BOOLEAN: 91 dest += sprintf(dest, *option ? "yes " : "no "); 92 break; 93 case OPTION_U8: 94 dest += sprintf(dest, "%u ", *option); 95 break; 96 case OPTION_U16: 97 memcpy(&val_u16, option, 2); 98 dest += sprintf(dest, "%u ", ntohs(val_u16)); 99 break; 100 case OPTION_S16: 101 memcpy(&val_s16, option, 2); 102 dest += sprintf(dest, "%d ", ntohs(val_s16)); 103 break; 104 case OPTION_U32: 105 memcpy(&val_u32, option, 4); 106 dest += sprintf(dest, "%lu ", (unsigned long) ntohl(val_u32)); 107 break; 108 case OPTION_S32: 109 memcpy(&val_s32, option, 4); 110 dest += sprintf(dest, "%ld ", (long) ntohl(val_s32)); 111 break; 112 case OPTION_STRING: 113 memcpy(dest, option, len); 114 dest[len] = '\0'; 115 return; /* Short circuit this case */ 116 } 117 option += optlen; 118 len -= optlen; 119 if (len <= 0) break; 120 } 121} 122 123 124static char *find_env(const char *prefix, char *defaultstr) 125{ 126 extern char **environ; 127 char **ptr; 128 const int len = strlen(prefix); 129 130 for (ptr = environ; *ptr != NULL; ptr++) { 131 if (strncmp(prefix, *ptr, len) == 0) 132 return *ptr; 133 } 134 return defaultstr; 135} 136 137 138/* put all the paramaters into an environment */ 139static char **fill_envp(struct dhcpMessage *packet) 140{ 141 int num_options = 0; 142 int i, j; 143 char **envp; 144 unsigned char *temp; 145 char over = 0; 146 147 if (packet == NULL) 148 num_options = 0; 149 else { 150 for (i = 0; options[i].code; i++) 151 if (get_option(packet, options[i].code)) 152 num_options++; 153 if (packet->siaddr) num_options++; 154 if ((temp = get_option(packet, DHCP_OPTION_OVER))) 155 over = *temp; 156 if (!(over & FILE_FIELD) && packet->file[0]) num_options++; 157 if (!(over & SNAME_FIELD) && packet->sname[0]) num_options++; 158 } 159 160 envp = xmalloc((num_options + 5) * sizeof(char *)); 161 envp[0] = xmalloc(sizeof("interface=") + strlen(client_config.interface)); 162 sprintf(envp[0], "interface=%s", client_config.interface); 163 envp[1] = find_env("PATH", "PATH=/bin:/usr/bin:/sbin:/usr/sbin"); 164 envp[2] = find_env("HOME", "HOME=/"); 165 166 if (packet == NULL) { 167 envp[3] = NULL; 168 return envp; 169 } 170 171 envp[3] = xmalloc(sizeof("ip=255.255.255.255")); 172 sprintip(envp[3], "ip=", (unsigned char *) &packet->yiaddr); 173 for (i = 0, j = 4; options[i].code; i++) { 174 if ((temp = get_option(packet, options[i].code))) { 175 envp[j] = xmalloc(upper_length(temp[OPT_LEN - 2], &options[i]) + strlen(options[i].name) + 2); 176 fill_options(envp[j], temp, &options[i]); 177 j++; 178 } 179 } 180 if (packet->siaddr) { 181 envp[j] = xmalloc(sizeof("siaddr=255.255.255.255")); 182 sprintip(envp[j++], "siaddr=", (unsigned char *) &packet->siaddr); 183 } 184 if (!(over & FILE_FIELD) && packet->file[0]) { 185 /* watch out for invalid packets */ 186 packet->file[sizeof(packet->file) - 1] = '\0'; 187 envp[j] = xmalloc(sizeof("boot_file=") + strlen(packet->file)); 188 sprintf(envp[j++], "boot_file=%s", packet->file); 189 } 190 if (!(over & SNAME_FIELD) && packet->sname[0]) { 191 /* watch out for invalid packets */ 192 packet->sname[sizeof(packet->sname) - 1] = '\0'; 193 envp[j] = xmalloc(sizeof("sname=") + strlen(packet->sname)); 194 sprintf(envp[j++], "sname=%s", packet->sname); 195 } 196 envp[j] = NULL; 197 return envp; 198} 199 200 201/* Call a script with a par file and env vars */ 202void run_script(struct dhcpMessage *packet, const char *name) 203{ 204 int pid; 205 char **envp; 206 207 if (client_config.script == NULL) 208 return; 209 210 /* call script */ 211 pid = fork(); 212 if (pid) { 213 waitpid(pid, NULL, 0); 214 return; 215 } else if (pid == 0) { 216 envp = fill_envp(packet); 217 218 /* close fd's? */ 219 220 /* exec script */ 221 DEBUG(LOG_INFO, "execle'ing %s", client_config.script); 222 execle(client_config.script, client_config.script, 223 name, NULL, envp); 224 LOG(LOG_ERR, "script %s failed: %s", 225 client_config.script, strerror(errno)); 226 exit(1); 227 } 228} 229