]>
Commit | Line | Data |
---|---|---|
6f248ce1 TL |
1 | #define _GNU_SOURCE |
2 | ||
3 | #include <stdio.h> | |
4 | #include <ctype.h> | |
5 | #include <stdlib.h> | |
6 | #include <string.h> | |
7 | #include <assert.h> | |
8 | #include <errno.h> | |
9 | #include <stdarg.h> | |
10 | #include <unistd.h> | |
11 | #include <sys/wait.h> | |
12 | #include <signal.h> | |
13 | #include <err.h> | |
14 | ||
15 | #include "header.h" | |
16 | ||
17 | extern char **environ; | |
18 | static char **localenv = NULL; | |
19 | ||
20 | static int check(const char *str) { | |
21 | return str != NULL; | |
22 | } | |
23 | ||
24 | static char *setlocalenv_nomangle(char *format, char *name, char *value) { | |
25 | char *result; | |
26 | if(asprintf(&result, format, name, value) == -1) | |
27 | err(1, "asprintf"); | |
28 | return result; | |
29 | } | |
30 | ||
31 | static char *setlocalenv(char *format, char *name, char *value) { | |
32 | char *result = setlocalenv_nomangle(format, name, value); | |
33 | ||
34 | char *here, *there; | |
35 | ||
36 | for (here = there = result; *there != '=' && *there; there++) { | |
37 | if (*there == '-') | |
38 | *there = '_'; | |
39 | ||
40 | if (isalpha(*there)) | |
41 | *there = toupper(*there); | |
42 | ||
43 | if (isalnum(*there) || *there == '_') { | |
44 | *here = *there; | |
45 | here++; | |
46 | } | |
47 | } | |
48 | ||
49 | memmove(here, there, strlen(there) + 1); | |
50 | ||
51 | return result; | |
52 | } | |
53 | ||
54 | static void set_environ(interface_defn *iface, char *mode, char *phase) { | |
55 | if (localenv != NULL) { | |
56 | for (char **ppch = localenv; *ppch; ppch++) | |
57 | free(*ppch); | |
58 | ||
59 | free(localenv); | |
60 | } | |
61 | ||
62 | int n_recursion = 0; | |
63 | for(char **envp = environ; *envp; envp++) | |
64 | if(strncmp(*envp, "IFUPDOWN_", 9) == 0) | |
65 | n_recursion++; | |
66 | ||
67 | const int n_env_entries = iface->n_options + 12 + n_recursion; | |
68 | localenv = malloc(sizeof *localenv * (n_env_entries + 1 /* for final NULL */ )); | |
69 | ||
70 | char **ppch = localenv; | |
71 | ||
72 | for (int i = 0; i < iface->n_options; i++) { | |
73 | if (strcmp(iface->option[i].name, "pre-up") == 0 || strcmp(iface->option[i].name, "up") == 0 || strcmp(iface->option[i].name, "down") == 0 || strcmp(iface->option[i].name, "post-down") == 0) | |
74 | continue; | |
75 | ||
76 | *ppch++ = setlocalenv("IF_%s=%s", iface->option[i].name, iface->option[i].value ? iface->option[i].value : ""); | |
77 | } | |
78 | ||
79 | for(char **envp = environ; *envp; envp++) | |
80 | if(strncmp(*envp, "IFUPDOWN_", 9) == 0) | |
81 | *ppch++ = strdup(*envp); | |
82 | ||
83 | /* Do we have a parent interface? */ | |
84 | char piface[80]; | |
85 | strncpy(piface, iface->real_iface, sizeof piface); | |
86 | piface[sizeof piface - 1] = '\0'; | |
87 | char *pch = strchr(piface, '.'); | |
88 | if (pch) { | |
89 | /* If so, declare that we have locked it, but don't overwrite an existing environment variable. */ | |
90 | *pch = '\0'; | |
91 | char envname[160]; | |
92 | snprintf(envname, sizeof envname, "IFUPDOWN_%s", piface); | |
93 | sanitize_env_name(envname + 9); | |
94 | ||
95 | if (!getenv(envname)) | |
96 | *ppch++ = setlocalenv_nomangle("IFUPDOWN_%s=%s", piface, "parent-lock"); | |
97 | } | |
98 | ||
99 | *ppch++ = setlocalenv_nomangle("IFUPDOWN_%s=%s", iface->real_iface, phase); | |
100 | *ppch++ = setlocalenv("%s=%s", "IFACE", iface->real_iface); | |
101 | *ppch++ = setlocalenv("%s=%s", "LOGICAL", iface->logical_iface); | |
102 | *ppch++ = setlocalenv("%s=%s", "ADDRFAM", iface->address_family->name); | |
103 | *ppch++ = setlocalenv("%s=%s", "METHOD", iface->method->name); | |
104 | *ppch++ = setlocalenv("%s=%s", "MODE", mode); | |
105 | *ppch++ = setlocalenv("%s=%s", "PHASE", phase); | |
106 | *ppch++ = setlocalenv("%s=%s", "VERBOSITY", verbose ? "1" : "0"); | |
107 | *ppch++ = setlocalenv("%s=%s", "PATH", "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"); | |
108 | if (allow_class || do_all) | |
109 | *ppch++ = setlocalenv("%s=%s", "CLASS", allow_class ? allow_class : "auto"); | |
110 | *ppch = NULL; | |
111 | } | |
112 | ||
113 | int doit(const char *str) { | |
114 | if (interrupted) | |
115 | return 0; | |
116 | ||
117 | assert(str); | |
118 | bool ignore_status = false; | |
119 | ||
120 | if (*str == '-') { | |
121 | ignore_status = true; | |
122 | str++; | |
123 | } | |
124 | ||
125 | if (verbose || no_act) | |
126 | fprintf(stderr, "%s\n", str); | |
127 | ||
128 | if (!no_act_commands) { | |
129 | pid_t child; | |
130 | int status; | |
131 | ||
132 | fflush(NULL); | |
133 | setpgid(0, 0); | |
134 | ||
135 | switch (child = fork()) { | |
136 | case -1: /* failure */ | |
137 | err(1, "fork"); | |
138 | ||
139 | case 0: /* child */ | |
140 | execle("/bin/sh", "/bin/sh", "-c", str, NULL, localenv); | |
141 | err(127, "executing '%s' failed", str); | |
142 | ||
143 | default: /* parent */ | |
144 | break; | |
145 | } | |
146 | ||
147 | waitpid(child, &status, 0); | |
148 | ||
149 | if (ignore_status || ignore_failures) | |
150 | return 1; | |
151 | ||
152 | if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) | |
153 | return 0; | |
154 | } | |
155 | ||
156 | return 1; | |
157 | } | |
158 | ||
159 | static int execute_options(interface_defn *ifd, execfn *exec, char *opt) { | |
160 | for (int i = 0; i < ifd->n_options; i++) | |
161 | if (strcmp(ifd->option[i].name, opt) == 0) | |
162 | if (interrupted || !(*exec) (ifd->option[i].value)) | |
163 | if (!ignore_failures) | |
164 | return 0; | |
165 | ||
166 | return 1; | |
167 | } | |
168 | ||
169 | static int execute_scripts(interface_defn *ifd, execfn *exec, char *opt) { | |
170 | if (interrupted) | |
171 | return 1; | |
172 | ||
173 | if (!run_scripts) | |
174 | return 1; | |
175 | ||
176 | if (no_scripts_ints && match_patterns(ifd->logical_iface, no_scripts_ints, no_scripts_int)) | |
177 | return 1; | |
178 | ||
179 | ||
180 | char *command; | |
181 | if(asprintf(&command, "/bin/run-parts %s%s/etc/network/if-%s.d", ignore_failures ? "" : "--exit-on-error ", verbose ? "--verbose " : "", opt) == -1) | |
182 | err(1, "asprintf"); | |
183 | ||
184 | int result = (*exec) (command); | |
185 | ||
186 | free(command); | |
187 | ||
188 | return ignore_failures ? 1 : result; | |
189 | } | |
190 | ||
191 | int iface_preup(interface_defn *iface) { | |
192 | set_environ(iface, "start", "pre-up"); | |
193 | ||
194 | if (!iface->method->up(iface, check)) | |
195 | return -1; | |
196 | ||
197 | if (!execute_options(iface, doit, "pre-up")) | |
198 | return 0; | |
199 | ||
200 | if (!execute_scripts(iface, doit, "pre-up")) | |
201 | return 0; | |
202 | ||
203 | return 1; | |
204 | } | |
205 | ||
206 | int iface_postup(interface_defn *iface) { | |
207 | set_environ(iface, "start", "post-up"); | |
208 | ||
209 | if (!iface->method->up(iface, doit)) | |
210 | return 0; | |
211 | ||
212 | if (!execute_options(iface, doit, "up")) | |
213 | return 0; | |
214 | ||
215 | if (!execute_scripts(iface, doit, "up")) | |
216 | return 0; | |
217 | ||
218 | return 1; | |
219 | } | |
220 | ||
221 | int iface_up(interface_defn *iface) { | |
222 | int result = iface_preup(iface); | |
223 | ||
224 | if (result != 1) | |
225 | return result; | |
226 | ||
227 | return iface_postup(iface); | |
228 | } | |
229 | ||
230 | int iface_predown(interface_defn *iface) { | |
231 | if (!no_act) { | |
232 | char *pidfilename = make_pidfile_name("ifup", iface); | |
233 | ||
234 | FILE *pidfile = fopen(pidfilename, "r"); | |
235 | ||
236 | if (pidfile) { | |
237 | int pid; | |
238 | ||
239 | if (fscanf(pidfile, "%d", &pid) == 1) { | |
240 | if (verbose) | |
241 | warnx("terminating ifup (pid %d)", pid); | |
242 | ||
243 | kill((pid_t) - pid, SIGTERM); | |
244 | } | |
245 | ||
246 | fclose(pidfile); | |
247 | unlink(pidfilename); | |
248 | } | |
249 | ||
250 | free(pidfilename); | |
251 | } | |
252 | ||
253 | set_environ(iface, "stop", "pre-down"); | |
254 | ||
255 | if (!iface->method->down(iface, check)) | |
256 | return -1; | |
257 | ||
258 | if (!execute_scripts(iface, doit, "down")) | |
259 | return 0; | |
260 | ||
261 | if (!execute_options(iface, doit, "down")) | |
262 | return 0; | |
263 | ||
264 | return 1; | |
265 | } | |
266 | ||
267 | int iface_postdown(interface_defn *iface) { | |
268 | if (!iface->method->down(iface, doit)) | |
269 | return 0; | |
270 | ||
271 | set_environ(iface, "stop", "post-down"); | |
272 | ||
273 | if (!execute_scripts(iface, doit, "post-down")) | |
274 | return 0; | |
275 | ||
276 | if (!execute_options(iface, doit, "post-down")) | |
277 | return 0; | |
278 | ||
279 | return 1; | |
280 | } | |
281 | ||
282 | int iface_down(interface_defn *iface) { | |
283 | int result = iface_predown(iface); | |
284 | ||
285 | if (result != 1) | |
286 | return result; | |
287 | ||
288 | return iface_postdown(iface); | |
289 | } | |
290 | ||
291 | int iface_list(interface_defn *iface) { | |
292 | printf("%s\n", iface->real_iface); | |
293 | ||
294 | return 0; | |
295 | } | |
296 | ||
297 | int iface_query(interface_defn *iface) { | |
298 | for (int i = 0; i < iface->n_options; i++) | |
299 | printf("%s: %s\n", iface->option[i].name, iface->option[i].value); | |
300 | ||
301 | return 1; | |
302 | } | |
303 | ||
304 | static void addstr(char **buf, size_t *len, size_t *pos, const char *str, size_t strlen) { | |
305 | assert(*len >= *pos); | |
306 | assert(*len == 0 || (*buf)[*pos] == '\0'); | |
307 | ||
308 | if (*pos + strlen >= *len) { | |
309 | char *newbuf; | |
310 | ||
311 | newbuf = realloc(*buf, *len * 2 + strlen + 1); | |
312 | if (!newbuf) | |
313 | err(1, "realloc"); | |
314 | ||
315 | *buf = newbuf; | |
316 | *len = *len * 2 + strlen + 1; | |
317 | } | |
318 | ||
319 | while (strlen-- >= 1) { | |
320 | (*buf)[(*pos)++] = *str; | |
321 | str++; | |
322 | } | |
323 | ||
324 | (*buf)[*pos] = '\0'; | |
325 | } | |
326 | ||
327 | ||
328 | static char *parse(const char *command, interface_defn *ifd) { | |
329 | char *result = NULL; | |
330 | size_t pos = 0, len = 0; | |
331 | size_t old_pos[MAX_OPT_DEPTH] = { 0 }; | |
332 | int okay[MAX_OPT_DEPTH] = { 1 }; | |
333 | int opt_depth = 1; | |
334 | ||
335 | while (*command) { | |
336 | switch (*command) { | |
337 | default: | |
338 | addstr(&result, &len, &pos, command, 1); | |
339 | command++; | |
340 | break; | |
341 | ||
342 | case '\\': | |
343 | if (command[1]) { | |
344 | addstr(&result, &len, &pos, command + 1, 1); | |
345 | command += 2; | |
346 | } else { | |
347 | addstr(&result, &len, &pos, command, 1); | |
348 | command++; | |
349 | } | |
350 | break; | |
351 | ||
352 | case '[': | |
353 | if (command[1] == '[' && opt_depth < MAX_OPT_DEPTH) { | |
354 | old_pos[opt_depth] = pos; | |
355 | okay[opt_depth] = 1; | |
356 | opt_depth++; | |
357 | command += 2; | |
358 | } else { | |
359 | addstr(&result, &len, &pos, "[", 1); | |
360 | command++; | |
361 | } | |
362 | break; | |
363 | ||
364 | case ']': | |
365 | if (command[1] == ']' && opt_depth > 1) { | |
366 | opt_depth--; | |
367 | if (!okay[opt_depth]) { | |
368 | pos = old_pos[opt_depth]; | |
369 | result[pos] = '\0'; | |
370 | } | |
371 | command += 2; | |
372 | } else { | |
373 | addstr(&result, &len, &pos, "]", 1); | |
374 | command++; | |
375 | } | |
376 | break; | |
377 | ||
378 | case '%': | |
379 | { | |
380 | char *nextpercent; | |
381 | size_t namelen; | |
382 | char pat = 0, rep = 0; | |
383 | char *varvalue; | |
384 | ||
385 | command++; | |
386 | nextpercent = strchr(command, '%'); | |
387 | namelen = nextpercent - command; | |
388 | ||
389 | if (!nextpercent) { | |
390 | errno = EUNBALPER; | |
391 | free(result); | |
392 | return NULL; | |
393 | } | |
394 | ||
395 | /* %var/p/r% */ | |
396 | if (*(nextpercent - 4) == '/') { | |
397 | pat = *(nextpercent - 3); | |
398 | rep = *(nextpercent - 1); | |
399 | namelen -= 4; | |
400 | } | |
401 | ||
402 | varvalue = get_var(command, namelen, ifd); | |
403 | ||
404 | if (varvalue) { | |
405 | for (char *position = varvalue; *position; position++) | |
406 | if (*position == pat) | |
407 | *position = rep; | |
408 | ||
409 | addstr(&result, &len, &pos, varvalue, strlen(varvalue)); | |
410 | free(varvalue); | |
411 | } else { | |
412 | if (opt_depth == 1) | |
413 | warnx("missing required variable: %.*s", (int)namelen, command); | |
414 | ||
415 | okay[opt_depth - 1] = 0; | |
416 | } | |
417 | ||
418 | command = nextpercent + 1; | |
419 | ||
420 | break; | |
421 | } | |
422 | } | |
423 | } | |
424 | ||
425 | if (opt_depth > 1) { | |
426 | errno = EUNBALBRACK; | |
427 | free(result); | |
428 | return NULL; | |
429 | } | |
430 | ||
431 | if (!okay[0]) { | |
432 | errno = EUNDEFVAR; | |
433 | free(result); | |
434 | return NULL; | |
435 | } | |
436 | ||
437 | return result; | |
438 | } | |
439 | ||
440 | int execute(const char *command, interface_defn *ifd, execfn *exec) { | |
441 | char *out; | |
442 | int ret; | |
443 | ||
444 | out = parse(command, ifd); | |
445 | if (!out) | |
446 | return 0; | |
447 | ||
448 | ret = (*exec) (out); | |
449 | free(out); | |
450 | ||
451 | return ret; | |
452 | } | |
453 | ||
454 | int strncmpz(const char *l, const char *r, size_t llen) { | |
455 | int i = strncmp(l, r, llen); | |
456 | ||
457 | if (i == 0) | |
458 | return -r[llen]; | |
459 | else | |
460 | return i; | |
461 | } | |
462 | ||
463 | char *get_var(const char *id, size_t idlen, interface_defn *ifd) { | |
464 | if (strncmpz(id, "iface", idlen) == 0) | |
465 | return strdup(ifd->real_iface); | |
466 | ||
467 | for (int i = 0; i < ifd->n_options; i++) { | |
468 | if (strncmpz(id, ifd->option[i].name, idlen) == 0) { | |
469 | if (!ifd->option[i].value) | |
470 | return NULL; | |
471 | ||
472 | if (strlen(ifd->option[i].value) > 0) | |
473 | return strdup(ifd->option[i].value); | |
474 | else | |
475 | return NULL; | |
476 | } | |
477 | } | |
478 | ||
479 | return NULL; | |
480 | } | |
481 | ||
482 | bool var_true(const char *id, interface_defn *ifd) { | |
483 | char *varvalue = get_var(id, strlen(id), ifd); | |
484 | ||
485 | if (varvalue) { | |
486 | if (atoi(varvalue) || strcasecmp(varvalue, "on") == 0 || strcasecmp(varvalue, "true") == 0 || strcasecmp(varvalue, "yes") == 0) { | |
487 | free(varvalue); | |
488 | return true; | |
489 | } else { | |
490 | free(varvalue); | |
491 | return false; | |
492 | } | |
493 | } else { | |
494 | return false; | |
495 | } | |
496 | } | |
497 | ||
498 | bool var_set(const char *id, interface_defn *ifd) { | |
499 | char *varvalue = get_var(id, strlen(id), ifd); | |
500 | ||
501 | if (varvalue) { | |
502 | free(varvalue); | |
503 | return true; | |
504 | } else { | |
505 | return false; | |
506 | } | |
507 | } | |
508 | ||
509 | bool var_set_anywhere(const char *id, interface_defn *ifd) { | |
510 | for (interface_defn *currif = defn->ifaces; currif; currif = currif->next) { | |
511 | if (strcmp(ifd->logical_iface, currif->logical_iface) == 0) { | |
512 | char *varvalue = get_var(id, strlen(id), currif); | |
513 | ||
514 | if (varvalue) { | |
515 | free(varvalue); | |
516 | return true; | |
517 | } | |
518 | } | |
519 | } | |
520 | ||
521 | return false; | |
522 | } | |
523 | ||
524 | static int popen2(FILE **in, FILE **out, char *command, ...) { | |
525 | va_list ap; | |
526 | char *argv[11] = { command }; | |
527 | int argc; | |
528 | int infd[2], outfd[2]; | |
529 | pid_t pid; | |
530 | ||
531 | argc = 1; | |
532 | va_start(ap, command); | |
533 | ||
534 | while ((argc < 10) && (argv[argc] = va_arg(ap, char *))) | |
535 | argc++; | |
536 | ||
537 | argv[argc] = NULL; /* make sure */ | |
538 | va_end(ap); | |
539 | ||
540 | if (pipe(infd) != 0) | |
541 | return 0; | |
542 | ||
543 | if (pipe(outfd) != 0) { | |
544 | close(infd[0]); | |
545 | close(infd[1]); | |
546 | return 0; | |
547 | } | |
548 | ||
549 | fflush(NULL); | |
550 | ||
551 | switch (pid = fork()) { | |
552 | case -1: /* failure */ | |
553 | close(infd[0]); | |
554 | close(infd[1]); | |
555 | close(outfd[0]); | |
556 | close(outfd[1]); | |
557 | return 0; | |
558 | ||
559 | case 0: /* child */ | |
560 | /* release the current directory */ | |
561 | if(chdir("/") == -1) | |
562 | // VERY unlikely, but if this fails we probably don't want to continue anyway. | |
563 | err(127, "could not chdir to /"); | |
564 | ||
565 | dup2(infd[0], 0); | |
566 | dup2(outfd[1], 1); | |
567 | close(infd[0]); | |
568 | close(infd[1]); | |
569 | close(outfd[0]); | |
570 | close(outfd[1]); | |
571 | execvp(command, argv); | |
572 | err(127, "executing \"%s\" failed", command); | |
573 | ||
574 | default: /* parent */ | |
575 | *in = fdopen(infd[1], "w"); | |
576 | *out = fdopen(outfd[0], "r"); | |
577 | close(infd[0]); | |
578 | close(outfd[1]); | |
579 | return pid; | |
580 | } | |
581 | ||
582 | /* unreached */ | |
583 | } | |
584 | ||
585 | bool run_mapping(const char *physical, char *logical, int len, mapping_defn *map) { | |
586 | FILE *in, *out; | |
587 | int status; | |
588 | pid_t pid; | |
589 | bool result = false; | |
590 | ||
591 | pid = popen2(&in, &out, map->script, physical, NULL); | |
592 | if (pid == 0) { | |
593 | warn("could not execute mapping script %s on %s", map->script, physical); | |
594 | return false; | |
595 | } | |
596 | ||
597 | signal(SIGPIPE, SIG_IGN); | |
598 | ||
599 | for (int i = 0; i < map->n_mappings; i++) | |
600 | fprintf(in, "%s\n", map->mapping[i]); | |
601 | ||
602 | fclose(in); | |
603 | ||
604 | signal(SIGPIPE, SIG_DFL); | |
605 | ||
606 | waitpid(pid, &status, 0); | |
607 | ||
608 | if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { | |
609 | if (fgets(logical, len, out)) { | |
610 | char *pch = logical + strlen(logical) - 1; | |
611 | ||
612 | while (pch >= logical && isspace(*pch)) | |
613 | *(pch--) = '\0'; | |
614 | ||
615 | result = true; | |
616 | } else { | |
617 | warnx("no output from mapping script %s on %s", map->script, physical); | |
618 | } | |
619 | } else { | |
620 | warnx("error trying to executing mapping script %s on %s", map->script, physical); | |
621 | } | |
622 | ||
623 | fclose(out); | |
624 | ||
625 | return result; | |
626 | } |