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/* Fill dest with the text of option 'option'. */ 66static void fill_options(char *dest, unsigned char *option, struct dhcp_option *type_p) 67{ 68 int type, optlen; 69 u_int16_t val_u16; 70 int16_t val_s16; 71 u_int32_t val_u32; 72 int32_t val_s32; 73 int len = option[OPT_LEN - 2]; 74 75 dest += sprintf(dest, "%s=", type_p->name); 76 77 type = type_p->flags & TYPE_MASK; 78 79 /* foxconn added start by EricHuang, 12/20/2007 */ 80 if ( strcmp(type_p->name, "rfc3442") == 0 ) 81 { 82 FILE *fp=0; 83 84 /*foxconn added start, water, 03/08/10, @option249*/ 85 /*fp = fopen("/tmp/udhcpc.routes", "w+");*/ 86 fp = fopen("/tmp/udhcpc.routes", "a+"); 87 /*foxconn added end, water, 03/08/10*/ 88 if (fp) 89 { 90 fwrite(option, 1, len, fp); 91 fclose(fp); 92 } 93 94 dest += sprintf(dest, "%d", len); /* only save the length here ... */ 95 return; 96 } 97 /* foxconn added end by EricHuang, 12/20/2007 */ 98 99 /* foxconn added start by EricHuang, 03/12/2008 */ 100 if ( strcmp(type_p->name, "sroute") == 0 ) 101 { 102 FILE *fp=0; 103 104 fp = fopen("/tmp/udhcpc.routes2", "w+"); 105 if (fp) 106 { 107 fwrite(option, 1, len, fp); 108 fclose(fp); 109 } 110 111 dest += sprintf(dest, "%d", len); /* only save the length here ... */ 112 return; 113 } 114 /* foxconn added end by EricHuang, 03/12/2008 */ 115 116 117 optlen = option_lengths[type]; 118 for(;;) { 119 switch (type) { 120 case OPTION_IP_PAIR: 121 dest += sprintip(dest, "", option); 122 *(dest++) = '/'; 123 option += 4; 124 optlen = 4; 125 case OPTION_IP: /* Works regardless of host byte order. */ 126 dest += sprintip(dest, "", option); 127 break; 128 case OPTION_BOOLEAN: 129 dest += sprintf(dest, *option ? "yes " : "no "); 130 break; 131 case OPTION_U8: 132 dest += sprintf(dest, "%u ", *option); 133 break; 134 case OPTION_U16: 135 memcpy(&val_u16, option, 2); 136 dest += sprintf(dest, "%u ", ntohs(val_u16)); 137 break; 138 case OPTION_S16: 139 memcpy(&val_s16, option, 2); 140 dest += sprintf(dest, "%d ", ntohs(val_s16)); 141 break; 142 case OPTION_U32: 143 memcpy(&val_u32, option, 4); 144 dest += sprintf(dest, "%lu ", (unsigned long) ntohl(val_u32)); 145 break; 146 case OPTION_S32: 147 memcpy(&val_s32, option, 4); 148 dest += sprintf(dest, "%ld ", (long) ntohl(val_s32)); 149 break; 150 case OPTION_STRING: 151 memcpy(dest, option, len); 152 dest[len] = '\0'; 153 return; /* Short circuit this case */ 154 } 155 option += optlen; 156 len -= optlen; 157 if (len <= 0) break; 158 } 159} 160 161 162static char *find_env(const char *prefix, char *defaultstr) 163{ 164 extern char **environ; 165 char **ptr; 166 const int len = strlen(prefix); 167 168 for (ptr = environ; *ptr != NULL; ptr++) { 169 if (strncmp(prefix, *ptr, len) == 0) 170 return *ptr; 171 } 172 return defaultstr; 173} 174 175 176/* put all the paramaters into an environment */ 177static char **fill_envp(struct dhcpMessage *packet) 178{ 179 int num_options = 0; 180 int i, j; 181 char **envp; 182 unsigned char *temp; 183 char over = 0; 184 185 if (packet == NULL) 186 num_options = 0; 187 else { 188 for (i = 0; options[i].code; i++) 189 if (get_option(packet, options[i].code)) 190 num_options++; 191 if (packet->siaddr) num_options++; 192 if ((temp = get_option(packet, DHCP_OPTION_OVER))) 193 over = *temp; 194 if (!(over & FILE_FIELD) && packet->file[0]) num_options++; 195 if (!(over & SNAME_FIELD) && packet->sname[0]) num_options++; 196 } 197 198 envp = xmalloc((num_options + 5) * sizeof(char *)); 199 envp[0] = xmalloc(sizeof("interface=") + strlen(client_config.interface)); 200 sprintf(envp[0], "interface=%s", client_config.interface); 201 envp[1] = find_env("PATH", "PATH=/bin:/usr/bin:/sbin:/usr/sbin"); 202 envp[2] = find_env("HOME", "HOME=/"); 203 204 if (packet == NULL) { 205 envp[3] = NULL; 206 return envp; 207 } 208 209 envp[3] = xmalloc(sizeof("ip=255.255.255.255")); 210 sprintip(envp[3], "ip=", (unsigned char *) &packet->yiaddr); 211 for (i = 0, j = 4; options[i].code; i++) { 212 if ((temp = get_option(packet, options[i].code))) { 213 envp[j] = xmalloc(upper_length(temp[OPT_LEN - 2], &options[i]) + strlen(options[i].name) + 2); 214 fill_options(envp[j], temp, &options[i]); 215 j++; 216 } 217 } 218 if (packet->siaddr) { 219 envp[j] = xmalloc(sizeof("siaddr=255.255.255.255")); 220 sprintip(envp[j++], "siaddr=", (unsigned char *) &packet->siaddr); 221 } 222 if (!(over & FILE_FIELD) && packet->file[0]) { 223 /* watch out for invalid packets */ 224 packet->file[sizeof(packet->file) - 1] = '\0'; 225 envp[j] = xmalloc(sizeof("boot_file=") + strlen(packet->file)); 226 sprintf(envp[j++], "boot_file=%s", packet->file); 227 } 228 if (!(over & SNAME_FIELD) && packet->sname[0]) { 229 /* watch out for invalid packets */ 230 packet->sname[sizeof(packet->sname) - 1] = '\0'; 231 envp[j] = xmalloc(sizeof("sname=") + strlen(packet->sname)); 232 sprintf(envp[j++], "sname=%s", packet->sname); 233 } 234 envp[j] = NULL; 235 return envp; 236} 237 238 239/* Call a script with a par file and env vars */ 240void run_script(struct dhcpMessage *packet, const char *name) 241{ 242 int pid; 243 char **envp; 244 245 if (client_config.script == NULL) 246 return; 247 248 /* call script */ 249 pid = fork(); 250 if (pid) { 251 waitpid(pid, NULL, 0); 252 return; 253 } else if (pid == 0) { 254 envp = fill_envp(packet); 255 256 /* close fd's? */ 257 258 /* exec script */ 259 DEBUG(LOG_INFO, "execle'ing %s", client_config.script); 260 execle(client_config.script, client_config.script, 261 name, NULL, envp); 262 LOG(LOG_ERR, "script %s failed: %s", 263 client_config.script, strerror(errno)); 264 exit(1); 265 } 266} 267