]> git.proxmox.com Git - ifupdown-pve.git/blame - execute.c
Squashed 'src/' content from commit c732260
[ifupdown-pve.git] / execute.c
CommitLineData
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
17extern char **environ;
18static char **localenv = NULL;
19
20static int check(const char *str) {
21 return str != NULL;
22}
23
24static 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
31static 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
54static 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
113int 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
159static 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
169static 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
191int 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
206int 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
221int 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
230int 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
267int 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
282int 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
291int iface_list(interface_defn *iface) {
292 printf("%s\n", iface->real_iface);
293
294 return 0;
295}
296
297int 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
304static 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
328static 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
440int 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
454int 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
463char *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
482bool 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
498bool 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
509bool 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
524static 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
585bool 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}