Commit | Line | Data |
---|---|---|
48bb1f76 | 1 | /* -*-c-*- |
2 | * | |
48bb1f76 | 3 | * Source and target for executable programs |
4 | * | |
5 | * (c) 1999 Straylight/Edgeware | |
6 | */ | |
7 | ||
206212ca | 8 | /*----- Licensing notice --------------------------------------------------* |
48bb1f76 | 9 | * |
9155ea97 | 10 | * This file is part of the `fwd' port forwarder. |
48bb1f76 | 11 | * |
9155ea97 | 12 | * `fwd' is free software; you can redistribute it and/or modify |
48bb1f76 | 13 | * it under the terms of the GNU General Public License as published by |
14 | * the Free Software Foundation; either version 2 of the License, or | |
15 | * (at your option) any later version. | |
206212ca | 16 | * |
9155ea97 | 17 | * `fwd' is distributed in the hope that it will be useful, |
48bb1f76 | 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
20 | * GNU General Public License for more details. | |
206212ca | 21 | * |
48bb1f76 | 22 | * You should have received a copy of the GNU General Public License |
9155ea97 | 23 | * along with `fwd'; if not, write to the Free Software Foundation, |
48bb1f76 | 24 | * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
25 | */ | |
26 | ||
9155ea97 | 27 | #include "fwd.h" |
48bb1f76 | 28 | |
29 | /*----- Data structures ---------------------------------------------------*/ | |
30 | ||
31 | /* --- Resource usage --- */ | |
32 | ||
33 | #ifdef HAVE_SETRLIMIT | |
34 | ||
35 | typedef struct xlimit { | |
36 | #define R(r, n) struct rlimit n; | |
37 | #include "rlimits.h" | |
38 | } xlimit; | |
39 | ||
40 | #endif | |
41 | ||
42 | /* --- Environment variable modifications --- */ | |
43 | ||
44 | typedef struct xenv { | |
45 | struct xenv *next; | |
46 | unsigned act; | |
47 | char *name; | |
48 | char *value; | |
49 | } xenv; | |
50 | ||
51 | #define XEA_SET 0u | |
52 | #define XEA_CLEAR 1u | |
53 | ||
54 | /* --- Program options --- */ | |
55 | ||
56 | typedef struct xopts { | |
57 | unsigned ref; | |
58 | unsigned f; | |
59 | const char *dir; | |
60 | const char *root; | |
61 | uid_t uid; | |
62 | gid_t gid; | |
63 | xenv *env; | |
64 | xenv **etail; | |
65 | #ifdef HAVE_SETRLIMIT | |
66 | xlimit xl; | |
67 | #endif | |
68 | } xopts; | |
69 | ||
70 | #define XF_NOLOG 1u | |
71 | ||
72 | /* --- Executable program arguments --- */ | |
73 | ||
74 | typedef struct xargs { | |
75 | unsigned ref; | |
76 | char *file; | |
77 | char *argv[1]; | |
78 | } xargs; | |
79 | ||
80 | #define XARGS_SZ(n) (sizeof(xargs) + sizeof(char *) * (n)) | |
81 | ||
82 | /* --- Executable endpoints --- */ | |
83 | ||
84 | typedef struct xept { | |
85 | endpt e; | |
86 | struct xept *next, *prev; | |
87 | pid_t kid; | |
28d2517c | 88 | endpt *f; |
372a98e2 | 89 | char *desc; |
48bb1f76 | 90 | int st; |
91 | xargs *xa; | |
92 | xopts *xo; | |
93 | selbuf err; | |
94 | } xept; | |
95 | ||
96 | #define XEF_CLOSE 16u | |
97 | #define XEF_EXIT 32u | |
98 | ||
99 | /* --- Executable program data --- */ | |
100 | ||
101 | typedef struct xdata { | |
102 | xargs *xa; | |
103 | xopts *xo; | |
104 | } xdata; | |
105 | ||
106 | /* --- Executable source block --- */ | |
107 | ||
108 | typedef struct xsource { | |
109 | source s; | |
110 | xdata x; | |
111 | } xsource; | |
112 | ||
113 | /* --- Executable target block --- */ | |
114 | ||
115 | typedef struct xtarget { | |
116 | target t; | |
117 | xdata x; | |
118 | } xtarget; | |
119 | ||
120 | /*----- Static variables --------------------------------------------------*/ | |
121 | ||
122 | static xopts exec_opts = { 1, 0, 0, 0, -1, -1, 0, &exec_opts.env }; | |
123 | static xept *xept_list; | |
124 | static sig xept_sig; | |
125 | static sym_table env; | |
126 | ||
127 | /*----- Fiddling about with resource limits -------------------------------*/ | |
128 | ||
129 | #ifdef HAVE_SETRLIMIT | |
130 | ||
131 | /* --- Table mapping user-level names to OS interface bits --- */ | |
132 | ||
133 | typedef struct rlimit_ent { | |
134 | const char *name; | |
135 | const char *rname; | |
136 | int r; | |
137 | size_t off; | |
138 | } rlimit_ent; | |
139 | ||
140 | static rlimit_ent rlimits[] = { | |
141 | #define R(r, n) { #n, #r, r, offsetof(xlimit, n) }, | |
142 | #include "rlimits.h" | |
143 | { 0, 0, 0, 0 } | |
144 | }; | |
145 | ||
146 | #define RLIMIT(xl, o) ((struct rlimit *)((char *)(xl) + (o))) | |
147 | ||
148 | /* --- @rlimit_get@ --- * | |
149 | * | |
150 | * Arguments: @xlimit *xl@ = pointer to limit structure | |
151 | * | |
152 | * Returns: --- | |
153 | * | |
154 | * Use: Initializes a limit structure from the current limits. | |
155 | */ | |
156 | ||
157 | static void rlimit_get(xlimit *xl) | |
158 | { | |
159 | rlimit_ent *r; | |
160 | ||
161 | for (r = rlimits; r->name; r++) { | |
162 | if (getrlimit(r->r, RLIMIT(xl, r->off))) { | |
163 | moan("couldn't read %s: %s", r->rname, strerror(errno)); | |
164 | _exit(1); | |
165 | } | |
166 | } | |
167 | } | |
168 | ||
169 | /* --- @rlimit_set@ --- * | |
170 | * | |
171 | * Arguments: @xlimit *xl@ = pointer to limit structure | |
172 | * | |
173 | * Returns: --- | |
174 | * | |
175 | * Use: Sets resource limits from the supplied limits structure. | |
176 | */ | |
177 | ||
178 | static void rlimit_set(xlimit *xl) | |
179 | { | |
180 | rlimit_ent *r; | |
181 | ||
182 | for (r = rlimits; r->name; r++) { | |
183 | if (setrlimit(r->r, RLIMIT(xl, r->off))) { | |
184 | moan("couldn't set %s: %s", r->rname, strerror(errno)); | |
185 | _exit(1); | |
186 | } | |
187 | } | |
188 | } | |
189 | ||
190 | /* --- @rlimit_option@ --- */ | |
191 | ||
192 | static int rlimit_option(xlimit *xl, scanner *sc) | |
193 | { | |
194 | CONF_BEGIN(sc, "rlimit", "resource limit") | |
195 | enum { w_soft, w_hard, w_both } which = w_both; | |
196 | rlimit_ent *chosen; | |
197 | struct rlimit *rl; | |
198 | long v; | |
199 | ||
200 | /* --- Find out which resource is being fiddled --- */ | |
201 | ||
202 | { | |
203 | rlimit_ent *r; | |
204 | ||
205 | chosen = 0; | |
206 | for (r = rlimits; r->name; r++) { | |
207 | if (strncmp(sc->d.buf, r->name, sc->d.len) == 0) { | |
208 | if (r->name[sc->d.len] == 0) { | |
209 | chosen = r; | |
210 | break; | |
211 | } else if (chosen) | |
212 | error(sc, "ambiguous resource limit name `%s'", sc->d.buf); | |
213 | else if (CONF_QUAL) | |
214 | chosen = r; | |
215 | } | |
216 | } | |
217 | if (!chosen) | |
218 | CONF_REJECT; | |
219 | token(sc); | |
220 | rl = RLIMIT(xl, chosen->off); | |
221 | } | |
222 | ||
223 | /* --- Look for hard or soft restrictions --- */ | |
224 | ||
225 | { | |
226 | int i; | |
227 | if (sc->t == '.') | |
228 | token(sc); | |
229 | if (sc->t == CTOK_WORD) { | |
230 | if ((i = conf_enum(sc, "soft,hard", | |
231 | ENUM_ABBREV | ENUM_NONE, "limit type")) != -1) | |
232 | which = i; | |
233 | } | |
234 | } | |
235 | ||
236 | /* --- Now read the new value --- */ | |
237 | ||
238 | if (sc->t == '=') | |
239 | token(sc); | |
240 | if (sc->t != CTOK_WORD) | |
241 | error(sc, "parse error, expected limit value"); | |
242 | ||
243 | if (conf_enum(sc, "unlimited,infinity", | |
244 | ENUM_ABBREV | ENUM_NONE, "limit value") > -1) | |
245 | v = RLIM_INFINITY; | |
246 | else { | |
247 | char *p; | |
248 | ||
249 | v = strtol(sc->d.buf, &p, 0); | |
250 | if (p == sc->d.buf) | |
251 | error(sc, "parse error, invalid limit value `%s'", sc->d.buf); | |
252 | switch (tolower((unsigned char)*p)) { | |
253 | case 0: break; | |
254 | case 'b': v *= 512; break; | |
255 | case 'g': v *= 1024; | |
256 | case 'm': v *= 1024; | |
257 | case 'k': v *= 1024; break; | |
258 | default: error(sc, "parse error, invalid limit scale `%c'", *p); | |
259 | } | |
260 | token(sc); | |
261 | } | |
262 | ||
263 | /* --- Store the limit value away --- */ | |
264 | ||
265 | switch (which) { | |
266 | case w_both: | |
267 | rl->rlim_cur = v; | |
268 | rl->rlim_max = v; | |
269 | break; | |
270 | case w_soft: | |
271 | if (v > rl->rlim_max) | |
272 | error(sc, "soft limit %l exceeds hard limit %l for %s", | |
273 | v, rl->rlim_max, chosen->rname); | |
274 | rl->rlim_cur = v; | |
275 | break; | |
276 | case w_hard: | |
277 | rl->rlim_max = v; | |
278 | if (rl->rlim_cur > v) | |
279 | rl->rlim_cur = v; | |
280 | break; | |
281 | } | |
282 | ||
283 | CONF_ACCEPT; | |
284 | CONF_END; | |
285 | } | |
286 | ||
287 | #endif | |
288 | ||
289 | /*----- Environment fiddling ----------------------------------------------*/ | |
290 | ||
291 | /* --- @xenv_option@ --- * | |
292 | * | |
293 | * Arguments: @xopts *xo@ = pointer to options block | |
294 | * @scanner *sc@ = pointer to scanner | |
295 | * | |
296 | * Returns: Nonzero if claimed | |
297 | * | |
298 | * Use: Parses environment variable assignments. | |
299 | */ | |
300 | ||
301 | static int xenv_option(xopts *xo, scanner *sc) | |
302 | { | |
303 | CONF_BEGIN(sc, "env", "environment") | |
304 | xenv *xe; | |
305 | ||
306 | /* --- Unset a variable --- */ | |
307 | ||
308 | if (strcmp(sc->d.buf, "unset") == 0) { | |
309 | token(sc); | |
310 | if (sc->t != CTOK_WORD) | |
311 | error(sc, "parse error, expected environment variable name"); | |
312 | xe = CREATE(xenv); | |
313 | xe->name = xstrdup(sc->d.buf); | |
314 | xe->value = 0; | |
315 | xe->act = XEA_SET; | |
316 | token(sc); | |
317 | goto link; | |
318 | } | |
319 | ||
320 | /* --- Clear the entire environment --- */ | |
321 | ||
322 | if (strcmp(sc->d.buf, "clear") == 0) { | |
323 | token(sc); | |
324 | xe = CREATE(xenv); | |
325 | xe->act = XEA_CLEAR; | |
326 | goto link; | |
327 | } | |
328 | ||
329 | /* --- Allow `set' to be omitted if there's a prefix --- */ | |
330 | ||
331 | if (strcmp(sc->d.buf, "set") == 0) | |
332 | token(sc); | |
333 | else if (!CONF_QUAL) | |
334 | CONF_REJECT; | |
335 | ||
336 | /* --- Set a variable --- */ | |
337 | ||
338 | if (sc->t != CTOK_WORD) | |
339 | error(sc, "parse error, expected environment variable name"); | |
340 | xe = CREATE(xenv); | |
341 | xe->name = xstrdup(sc->d.buf); | |
342 | token(sc); | |
343 | if (sc->t == '=') | |
344 | token(sc); | |
345 | if (sc->t != CTOK_WORD) | |
346 | error(sc, "parse error, expected environment variable value"); | |
347 | xe->value = xstrdup(sc->d.buf); | |
348 | xe->act = XEA_SET; | |
349 | token(sc); | |
350 | goto link; | |
351 | ||
352 | link: | |
353 | xe->next = 0; | |
354 | *xo->etail = xe; | |
355 | xo->etail = &xe->next; | |
356 | CONF_ACCEPT; | |
357 | ||
358 | /* --- Nothing else to try --- */ | |
359 | ||
360 | CONF_END; | |
361 | } | |
362 | ||
363 | /* --- @xenv_apply@ --- * | |
364 | * | |
365 | * Arguments: @xenv *xe@ = pointer to a variable change list | |
366 | * | |
367 | * Returns: --- | |
368 | * | |
369 | * Use: Modifies the environment (in @env@) according to the list. | |
370 | */ | |
371 | ||
372 | static void xenv_apply(xenv *xe) | |
373 | { | |
374 | while (xe) { | |
375 | switch (xe->act) { | |
376 | case XEA_SET: | |
377 | env_put(&env, xe->name, xe->value); | |
378 | break; | |
379 | case XEA_CLEAR: | |
380 | env_destroy(&env); | |
381 | sym_create(&env); | |
382 | break; | |
383 | } | |
384 | xe = xe->next; | |
385 | } | |
386 | } | |
387 | ||
388 | /* --- @xenv_destroy@ --- * | |
389 | * | |
390 | * Arguments: @xenv *xe@ = pointer to a variable change list | |
391 | * | |
392 | * Returns: --- | |
393 | * | |
394 | * Use: Frees the memory used by an environment variable change list. | |
395 | */ | |
396 | ||
397 | static void xenv_destroy(xenv *xe) | |
398 | { | |
399 | while (xe) { | |
400 | xenv *xxe = xe; | |
401 | xe = xe->next; | |
b0805b27 | 402 | xfree(xxe->name); |
48bb1f76 | 403 | if (xxe->value) |
b0805b27 | 404 | xfree(xxe->value); |
48bb1f76 | 405 | DESTROY(xxe); |
406 | } | |
407 | } | |
408 | ||
409 | /*----- Miscellaneous good things -----------------------------------------*/ | |
410 | ||
411 | /* --- @x_tidy@ --- * | |
412 | * | |
413 | * Arguments: @xargs *xa@ = pointer to an arguments block | |
414 | * @xopts *xo@ = pointer to an options block | |
415 | * | |
416 | * Returns: --- | |
417 | * | |
418 | * Use: Releases a reference to argument and options blocks. | |
419 | */ | |
420 | ||
421 | static void x_tidy(xargs *xa, xopts *xo) | |
422 | { | |
423 | xa->ref--; | |
424 | if (!xa->ref) | |
b0805b27 | 425 | xfree(xa); |
48bb1f76 | 426 | |
427 | xo->ref--; | |
428 | if (!xo->ref) { | |
429 | xenv_destroy(xo->env); | |
430 | DESTROY(xo); | |
431 | } | |
432 | } | |
433 | ||
434 | /*----- Executable endpoints ----------------------------------------------*/ | |
435 | ||
436 | /* --- @attach@ --- */ | |
437 | ||
f00786b9 | 438 | static void xept_error(char */*p*/, size_t /*len*/, void */*v*/); |
48bb1f76 | 439 | |
440 | static void xept_attach(endpt *e, reffd *in, reffd *out) | |
441 | { | |
442 | xept *xe = (xept *)e; | |
443 | pid_t kid; | |
444 | int fd[2]; | |
445 | ||
446 | /* --- Make a pipe for standard error --- */ | |
447 | ||
448 | if (pipe(fd)) { | |
449 | fw_log(-1, "[%s] couldn't create pipe: %s", xe->desc, strerror(errno)); | |
450 | return; | |
451 | } | |
452 | fdflags(fd[0], O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC); | |
453 | ||
454 | /* --- Fork a child, and handle an error if there was one --- */ | |
455 | ||
456 | if ((kid = fork()) == -1) { | |
457 | fw_log(-1, "[%s] couldn't fork: %s", xe->desc, strerror(errno)); | |
458 | close(fd[0]); | |
459 | close(fd[1]); | |
460 | return; | |
461 | } | |
462 | ||
463 | /* --- Do the child thing --- */ | |
464 | ||
465 | if (kid == 0) { | |
466 | xopts *xo = xe->xo; | |
76654703 | 467 | mdup_fd md[3]; |
48bb1f76 | 468 | |
469 | /* --- Fiddle with the file descriptors --- * | |
470 | * | |
471 | * Attach the other endpoint's descriptors to standard input and output. | |
472 | * Attach my pipe to standard error. Mark everything as blocking and | |
473 | * not-to-be-closed-on-exec at this end. | |
474 | */ | |
475 | ||
476 | close(fd[0]); | |
76654703 MW |
477 | md[0].cur = in->fd; md[0].want = STDIN_FILENO; |
478 | md[1].cur = out->fd; md[1].want = STDOUT_FILENO; | |
479 | md[2].cur = fd[1]; md[2].want = STDERR_FILENO; | |
480 | if (mdup(md, 3)) { | |
48bb1f76 | 481 | moan("couldn't manipulate file descriptors: %s", strerror(errno)); |
482 | _exit(1); | |
483 | } | |
484 | ||
48bb1f76 | 485 | fdflags(STDIN_FILENO, O_NONBLOCK, 0, FD_CLOEXEC, 0); |
486 | fdflags(STDOUT_FILENO, O_NONBLOCK, 0, FD_CLOEXEC, 0); | |
487 | fdflags(STDERR_FILENO, O_NONBLOCK, 0, FD_CLOEXEC, 0); | |
488 | ||
489 | /* --- First of all set the @chroot@ prison --- */ | |
490 | ||
491 | if (xo->root && chroot(xo->root)) { | |
492 | moan("couldn't set `%s' as filesystem root: %s", | |
493 | xo->root, strerror(errno)); | |
494 | _exit(1); | |
495 | } | |
496 | ||
497 | /* --- Now set the current directory --- */ | |
498 | ||
499 | if (xo->dir ? chdir(xo->dir) : xo->root ? chdir("/") : 0) { | |
500 | moan("couldn't set `%s' as current directory: %s", | |
501 | xo->dir ? xo->dir : "/", strerror(errno)); | |
502 | _exit(1); | |
503 | } | |
504 | ||
505 | /* --- Set the resource limits --- */ | |
506 | ||
507 | #ifdef HAVE_SETRLIMIT | |
508 | rlimit_set(&xo->xl); | |
509 | #endif | |
510 | ||
511 | /* --- Set group id --- */ | |
512 | ||
71db0fbd | 513 | if (xo->gid != (gid_t)-1) { |
48bb1f76 | 514 | if (setgid(xo->gid)) { |
515 | moan("couldn't set gid %i: %s", xo->gid, strerror(errno)); | |
516 | _exit(1); | |
517 | } | |
518 | #ifdef HAVE_SETGROUPS | |
519 | if (setgroups(1, &xo->gid)) | |
520 | moan("warning: couldn't set group list to %i: %s", xo->gid, | |
521 | strerror(errno)); | |
522 | #endif | |
523 | } | |
524 | ||
525 | /* --- Set uid --- */ | |
526 | ||
71db0fbd | 527 | if (xo->uid != (uid_t)-1) { |
48bb1f76 | 528 | if (setuid(xo->uid)) { |
529 | moan("couldn't set uid %i: %s", xo->uid, strerror(errno)); | |
530 | _exit(1); | |
531 | } | |
532 | } | |
533 | ||
534 | /* --- Play with signal dispositions --- */ | |
535 | ||
536 | signal(SIGPIPE, SIG_DFL); | |
537 | ||
538 | /* --- Fiddle with the environment --- */ | |
539 | ||
540 | xenv_apply(exec_opts.env); | |
541 | xenv_apply(xe->xo->env); | |
542 | environ = env_export(&env); | |
543 | ||
544 | /* --- Run the program --- */ | |
545 | ||
546 | execvp(xe->xa->file, xe->xa->argv); | |
547 | moan("couldn't execute `%s': %s", xe->xa->file, strerror(errno)); | |
548 | _exit(127); | |
549 | } | |
550 | ||
551 | /* --- The child's done; see to the parent --- */ | |
552 | ||
553 | xe->kid = kid; | |
554 | selbuf_init(&xe->err, sel, fd[0], xept_error, xe); | |
555 | close(fd[1]); | |
556 | xe->next = xept_list; | |
557 | xe->prev = 0; | |
558 | if (xept_list) | |
559 | xept_list->prev = xe; | |
560 | xept_list = xe; | |
561 | if (!(xe->xo->f & XF_NOLOG)) | |
562 | fw_log(-1, "[%s] started with pid %i", xe->desc, kid); | |
563 | fw_inc(); | |
564 | return; | |
565 | } | |
566 | ||
28d2517c | 567 | /* --- @xept_file@ --- */ |
568 | ||
569 | static void xept_file(endpt *e, endpt *f) | |
570 | { | |
571 | xept *xe = (xept *)e; | |
572 | xe->f = f; | |
573 | } | |
574 | ||
48bb1f76 | 575 | /* --- @xept_close@ --- */ |
576 | ||
577 | static void xept_close(endpt *e) | |
578 | { | |
579 | xept *xe = (xept *)e; | |
580 | if (xe->kid == -1) { | |
28d2517c | 581 | if (xe->f) |
582 | xe->f->ops->close(xe->f); | |
48bb1f76 | 583 | x_tidy(xe->xa, xe->xo); |
584 | DESTROY(xe); | |
585 | fw_dec(); | |
586 | } | |
587 | } | |
588 | ||
589 | /* --- @xept_destroy@ --- */ | |
590 | ||
591 | static void xept_destroy(xept *xe) | |
592 | { | |
593 | /* --- First emit the news about the process --- */ | |
594 | ||
595 | if (xe->xo->f & XF_NOLOG) | |
596 | /* Nothin' doin' */; | |
597 | else if (WIFEXITED(xe->st)) { | |
598 | if (WEXITSTATUS(xe->st) == 0) | |
599 | fw_log(-1, "[%s] pid %i exited successfully", xe->desc, xe->kid); | |
600 | else { | |
601 | fw_log(-1, "[%s] pid %i failed: status %i", | |
602 | xe->desc, xe->kid, WEXITSTATUS(xe->st)); | |
603 | } | |
604 | } else if (WIFSIGNALED(xe->st)) { | |
605 | const char *s; | |
606 | #ifdef HAVE_STRSIGNAL | |
607 | s = strsignal(WTERMSIG(xe->st)); | |
608 | #elif HAVE__SYS_SIGLIST | |
609 | s = _sys_siglist[WTERMSIG(xe->st)]; | |
610 | #else | |
611 | char buf[32]; | |
612 | sprintf(buf, "signal %i", WTERMSIG(xe->st)); | |
613 | s = buf; | |
614 | #endif | |
615 | fw_log(-1, "[%s] pid %i failed: %s", xe->desc, xe->kid, s); | |
616 | } else | |
617 | fw_log(-1, "[%s] pid %i failed: unrecognized status", xe->desc, xe->kid); | |
618 | ||
619 | /* --- Free up the parent-side resources --- */ | |
620 | ||
621 | if (xe->next) | |
622 | xe->next->prev = xe->prev; | |
623 | if (xe->prev) | |
624 | xe->prev->next = xe->next; | |
625 | else | |
626 | xept_list = xe->next; | |
627 | ||
b0805b27 | 628 | xfree(xe->desc); |
28d2517c | 629 | if (xe->f) |
630 | xe->f->ops->close(xe->f); | |
48bb1f76 | 631 | x_tidy(xe->xa, xe->xo); |
632 | fw_dec(); | |
633 | DESTROY(xe); | |
634 | } | |
635 | ||
636 | /* --- @xept_chld@ --- * | |
637 | * | |
638 | * Arguments: @int n@ = signal number | |
639 | * @void *p@ = uninteresting pointer | |
640 | * | |
641 | * Returns: --- | |
642 | * | |
643 | * Use: Deals with child death situations. | |
644 | */ | |
645 | ||
646 | static void xept_chld(int n, void *p) | |
647 | { | |
648 | pid_t kid; | |
649 | int st; | |
650 | ||
651 | while ((kid = waitpid(-1, &st, WNOHANG)) > 0) { | |
652 | xept *xe = xept_list; | |
653 | while (xe) { | |
654 | xept *xxe = xe; | |
655 | xe = xe->next; | |
656 | if (kid == xxe->kid) { | |
657 | xxe->st = st; | |
658 | xxe->e.f |= XEF_EXIT; | |
659 | if (xxe->e.f & XEF_CLOSE) | |
660 | xept_destroy(xxe); | |
661 | break; | |
662 | } | |
663 | } | |
664 | } | |
665 | } | |
666 | ||
667 | /* --- @xept_error@ --- * | |
668 | * | |
669 | * Arguments: @char *p@ = pointer to string read from stderr | |
f00786b9 | 670 | * @size_t len@ = length of the string |
48bb1f76 | 671 | * @void *v@ = pointer to by endpoint |
672 | * | |
673 | * Returns: --- | |
674 | * | |
675 | * Use: Handles error reports from a child process. | |
676 | */ | |
677 | ||
f00786b9 | 678 | static void xept_error(char *p, size_t len, void *v) |
48bb1f76 | 679 | { |
680 | xept *xe = v; | |
681 | if (p) | |
682 | fw_log(-1, "[%s] pid %i: %s", xe->desc, xe->kid, p); | |
683 | else { | |
48bb1f76 | 684 | close(xe->err.reader.fd); |
e68b88bf | 685 | selbuf_destroy(&xe->err); |
48bb1f76 | 686 | xe->e.f |= XEF_CLOSE; |
687 | if (xe->e.f & XEF_EXIT) | |
688 | xept_destroy(xe); | |
689 | } | |
690 | } | |
691 | ||
692 | /* --- Endpoint operations --- */ | |
693 | ||
28d2517c | 694 | static endpt_ops xept_ops = { xept_attach, xept_file, 0, xept_close }; |
48bb1f76 | 695 | |
696 | /*----- General operations on sources and targets -------------------------*/ | |
697 | ||
698 | /* --- @exec_init@ --- * | |
699 | * | |
700 | * Arguments: --- | |
701 | * | |
702 | * Returns: --- | |
703 | * | |
704 | * Use: Initializes the executable problem source and target. | |
705 | */ | |
706 | ||
707 | void exec_init(void) | |
708 | { | |
71db0fbd | 709 | #ifdef HAVE_SETRLIMIT |
48bb1f76 | 710 | rlimit_get(&exec_opts.xl); |
71db0fbd | 711 | #endif |
48bb1f76 | 712 | sig_add(&xept_sig, SIGCHLD, xept_chld, 0); |
713 | sym_create(&env); | |
714 | env_import(&env, environ); | |
715 | } | |
716 | ||
717 | /* --- @exec_option@ --- */ | |
718 | ||
719 | static int exec_option(xdata *x, scanner *sc) | |
720 | { | |
721 | xopts *xo = x ? x->xo : &exec_opts; | |
722 | ||
723 | CONF_BEGIN(sc, "exec", "executable"); | |
724 | ||
725 | /* --- Logging settings --- */ | |
726 | ||
727 | if (strcmp(sc->d.buf, "logging") == 0 || | |
728 | strcmp(sc->d.buf, "log") == 0) { | |
729 | token(sc); | |
730 | if (sc->t == '=') | |
731 | token(sc); | |
732 | if (conf_enum(sc, "no,yes", ENUM_ABBREV, "logging status")) | |
733 | xo->f &= ~XF_NOLOG; | |
734 | else | |
735 | xo->f |= XF_NOLOG; | |
736 | CONF_ACCEPT; | |
737 | } | |
738 | ||
739 | /* --- Current directory setting --- * | |
740 | * | |
741 | * Lots of possibilities to guard against possible brainoes. | |
742 | */ | |
743 | ||
744 | if (strcmp(sc->d.buf, "dir") == 0 || | |
745 | strcmp(sc->d.buf, "cd") == 0 || | |
746 | strcmp(sc->d.buf, "chdir") == 0 || | |
747 | strcmp(sc->d.buf, "cwd") == 0) { | |
748 | dstr d = DSTR_INIT; | |
749 | token(sc); | |
750 | if (sc->t == '=') | |
751 | token(sc); | |
35a142ca | 752 | conf_fname(sc, &d); |
48bb1f76 | 753 | xo->dir = xstrdup(d.buf); |
754 | dstr_destroy(&d); | |
755 | CONF_ACCEPT; | |
756 | } | |
757 | ||
758 | /* --- Set a chroot prison --- */ | |
759 | ||
760 | if (strcmp(sc->d.buf, "root") == 0 || | |
206212ca | 761 | strcmp(sc->d.buf, "chroot") == 0) { |
48bb1f76 | 762 | dstr d = DSTR_INIT; |
763 | token(sc); | |
764 | if (sc->t == '=') | |
765 | token(sc); | |
35a142ca | 766 | conf_fname(sc, &d); |
48bb1f76 | 767 | xo->root = xstrdup(d.buf); |
768 | dstr_destroy(&d); | |
769 | CONF_ACCEPT; | |
770 | } | |
771 | ||
772 | /* --- Set the target user id --- */ | |
773 | ||
774 | if (strcmp(sc->d.buf, "uid") == 0 || | |
775 | strcmp(sc->d.buf, "user") == 0) { | |
776 | token(sc); | |
777 | if (sc->t == '=') | |
778 | token(sc); | |
779 | if (sc->t != CTOK_WORD) | |
780 | error(sc, "parse error, expected user name or uid"); | |
781 | if (isdigit((unsigned char)*sc->d.buf)) | |
782 | xo->uid = atoi(sc->d.buf); | |
783 | else { | |
784 | struct passwd *pw = getpwnam(sc->d.buf); | |
785 | if (!pw) | |
786 | error(sc, "unknown user name `%s'", sc->d.buf); | |
787 | xo->uid = pw->pw_uid; | |
788 | } | |
789 | token(sc); | |
790 | CONF_ACCEPT; | |
791 | } | |
792 | ||
793 | /* --- Set the target group id --- */ | |
794 | ||
795 | if (strcmp(sc->d.buf, "gid") == 0 || | |
796 | strcmp(sc->d.buf, "group") == 0) { | |
797 | token(sc); | |
798 | if (sc->t == '=') | |
799 | token(sc); | |
800 | if (sc->t != CTOK_WORD) | |
801 | error(sc, "parse error, expected group name or gid"); | |
802 | if (isdigit((unsigned char)*sc->d.buf)) | |
803 | xo->gid = atoi(sc->d.buf); | |
804 | else { | |
805 | struct group *gr = getgrnam(sc->d.buf); | |
806 | if (!gr) | |
807 | error(sc, "unknown user name `%s'", sc->d.buf); | |
808 | xo->gid = gr->gr_gid; | |
809 | } | |
810 | token(sc); | |
811 | CONF_ACCEPT; | |
812 | } | |
813 | ||
814 | /* --- Now try resource limit settings --- */ | |
815 | ||
71db0fbd | 816 | #ifdef HAVE_SETRLIMIT |
48bb1f76 | 817 | if (rlimit_option(&xo->xl, sc)) |
818 | CONF_ACCEPT; | |
71db0fbd | 819 | #endif |
48bb1f76 | 820 | |
821 | /* --- And then environment settings --- */ | |
822 | ||
823 | if (xenv_option(xo, sc)) | |
824 | CONF_ACCEPT; | |
825 | ||
826 | /* --- Nothing found --- */ | |
827 | ||
828 | CONF_END; | |
829 | } | |
830 | ||
831 | /* --- @exec_desc@ --- */ | |
832 | ||
833 | static void exec_desc(xdata *x, dstr *d) | |
834 | { | |
835 | char **p; | |
836 | char sep = '['; | |
837 | dstr_puts(d, "exec "); | |
838 | if (strcmp(x->xa->file, x->xa->argv[0]) != 0) { | |
839 | dstr_puts(d, x->xa->file); | |
840 | dstr_putc(d, ' '); | |
841 | } | |
842 | for (p = x->xa->argv; *p; p++) { | |
843 | dstr_putc(d, sep); | |
844 | dstr_puts(d, *p); | |
845 | sep = ' '; | |
846 | } | |
847 | dstr_putc(d, ']'); | |
848 | dstr_putz(d); | |
849 | } | |
850 | ||
851 | /* --- @exec_read@ --- */ | |
852 | ||
853 | static void exec_read(xdata *x, scanner *sc) | |
854 | { | |
855 | size_t base = 0; | |
856 | dstr d = DSTR_INIT; | |
857 | xargs *xa; | |
858 | ||
859 | /* --- Read the first word --- * | |
860 | * | |
861 | * This is either a shell command or the actual program to run. | |
862 | */ | |
863 | ||
864 | if (sc->t == CTOK_WORD) { | |
865 | dstr_putd(&d, &sc->d); d.len++; | |
866 | base = d.len; | |
867 | token(sc); | |
868 | } | |
869 | ||
870 | /* --- See if there's a list of arguments --- * | |
871 | * | |
872 | * If not, then the thing I saw was a shell command, so build the proper | |
873 | * arguments for that. | |
874 | */ | |
875 | ||
876 | if (sc->t != '[') { | |
877 | char *p; | |
878 | if (!base) | |
879 | error(sc, "parse error, expected shell command or argument list"); | |
880 | xa = xmalloc(XARGS_SZ(3) + 8 + 3 + d.len); | |
881 | p = (char *)(xa->argv + 4); | |
882 | xa->ref = 1; | |
883 | xa->file = p; | |
884 | xa->argv[0] = p; memcpy(p, "/bin/sh", 8); p += 8; | |
885 | xa->argv[1] = p; memcpy(p, "-c", 3); p += 3; | |
886 | xa->argv[2] = p; memcpy(p, d.buf, d.len); p += d.len; | |
887 | xa->argv[3] = 0; | |
888 | } | |
889 | ||
890 | /* --- Snarf in a list of arguments --- */ | |
891 | ||
892 | else { | |
893 | int argc = 0; | |
894 | char *p, *q; | |
895 | char **v; | |
896 | ||
71db0fbd | 897 | /* --- Strip off the leading `[' --- * |
898 | * | |
899 | * Allow various handy filename characters to be entered without quoting. | |
900 | */ | |
48bb1f76 | 901 | |
71db0fbd | 902 | conf_undelim(sc, "=:/.", "=:/."); |
48bb1f76 | 903 | token(sc); |
904 | ||
905 | /* --- Read a sequence of arguments --- */ | |
906 | ||
907 | while (sc->t == CTOK_WORD) { | |
908 | dstr_putd(&d, &sc->d); d.len++; | |
909 | token(sc); | |
910 | argc++; | |
911 | } | |
71db0fbd | 912 | conf_undelim(sc, 0, 0); |
48bb1f76 | 913 | |
914 | /* --- Expect the closing `]' --- */ | |
915 | ||
916 | if (sc->t != ']') | |
917 | error(sc, "parse error, missing `]'"); | |
918 | token(sc); | |
919 | ||
920 | /* --- If there are no arguments, whinge --- */ | |
921 | ||
922 | if (!argc) | |
923 | error(sc, "must specify at least one argument"); | |
924 | ||
925 | /* --- Allocate a lump of memory for the array --- */ | |
926 | ||
927 | xa = xmalloc(XARGS_SZ(argc) + d.len); | |
928 | xa->ref = 1; | |
929 | v = xa->argv; | |
930 | p = (char *)(v + argc + 1); | |
931 | memcpy(p, d.buf, d.len); | |
932 | q = p + d.len; | |
933 | xa->file = p; | |
934 | p += base; | |
935 | ||
936 | /* --- Start dumping addresses into the @argv@ array --- */ | |
937 | ||
938 | for (;;) { | |
939 | *v++ = p; | |
940 | while (*p++ && p < q) | |
941 | ; | |
942 | if (p >= q) | |
943 | break; | |
944 | } | |
945 | *v++ = 0; | |
946 | } | |
947 | ||
948 | /* --- Do some other setting up --- */ | |
949 | ||
950 | dstr_destroy(&d); | |
951 | x->xa = xa; | |
952 | x->xo = CREATE(xopts); | |
953 | *x->xo = exec_opts; | |
954 | x->xo->ref = 1; | |
955 | return; | |
956 | } | |
957 | ||
958 | /* --- @exec_endpt@ --- */ | |
959 | ||
960 | static endpt *exec_endpt(xdata *x, const char *desc) | |
961 | { | |
962 | xept *xe = CREATE(xept); | |
963 | xe->e.ops = &xept_ops; | |
964 | xe->e.other = 0; | |
965 | xe->e.t = 0; | |
966 | xe->e.f = 0; | |
967 | xe->xa = x->xa; xe->xa->ref++; | |
968 | xe->xo = x->xo; xe->xo->ref++; | |
969 | xe->kid = -1; | |
28d2517c | 970 | xe->f = 0; |
372a98e2 | 971 | xe->desc = xstrdup(desc); |
48bb1f76 | 972 | return (&xe->e); |
973 | } | |
974 | ||
975 | /* --- @exec_destroy@ --- */ | |
976 | ||
977 | static void exec_destroy(xdata *x) | |
978 | { | |
979 | x_tidy(x->xa, x->xo); | |
980 | } | |
981 | ||
982 | /*----- Source definition -------------------------------------------------*/ | |
983 | ||
984 | /* --- @option@ --- */ | |
985 | ||
986 | static int xsource_option(source *s, scanner *sc) | |
987 | { | |
988 | xsource *xs = (xsource *)s; | |
989 | return (exec_option(xs ? &xs->x : 0, sc)); | |
990 | } | |
991 | ||
992 | /* --- @read@ --- */ | |
993 | ||
994 | static source *xsource_read(scanner *sc) | |
995 | { | |
996 | xsource *xs; | |
997 | ||
998 | if (!conf_prefix(sc, "exec")) | |
999 | return (0); | |
1000 | xs = CREATE(xsource); | |
1001 | xs->s.ops = &xsource_ops; | |
1002 | xs->s.desc = 0; | |
1003 | exec_read(&xs->x, sc); | |
1004 | return (&xs->s); | |
1005 | } | |
1006 | ||
1007 | /* --- @attach@ --- */ | |
1008 | ||
1009 | static void xsource_destroy(source */*s*/); | |
1010 | ||
1011 | static void xsource_attach(source *s, scanner *sc, target *t) | |
1012 | { | |
1013 | xsource *xs = (xsource *)s; | |
1014 | endpt *e, *ee; | |
1015 | ||
1016 | /* --- Set up the source description string --- */ | |
1017 | ||
1018 | { | |
1019 | dstr d = DSTR_INIT; | |
1020 | exec_desc(&xs->x, &d); | |
1021 | dstr_puts(&d, " -> "); | |
1022 | dstr_puts(&d, t->desc); | |
1023 | xs->s.desc = xstrdup(d.buf); | |
1024 | dstr_destroy(&d); | |
1025 | } | |
1026 | ||
1027 | /* --- Create the endpoints --- */ | |
1028 | ||
1029 | if ((ee = t->ops->create(t, xs->s.desc)) == 0) | |
1030 | goto tidy; | |
1031 | if ((e = exec_endpt(&xs->x, xs->s.desc)) == 0) { | |
1032 | ee->ops->close(ee); | |
1033 | goto tidy; | |
1034 | } | |
1035 | endpt_join(e, ee); | |
1036 | ||
1037 | /* --- Dispose of source and target --- */ | |
1038 | ||
1039 | tidy: | |
1040 | t->ops->destroy(t); | |
1041 | xsource_destroy(&xs->s); | |
1042 | } | |
1043 | ||
1044 | /* --- @destroy@ --- */ | |
1045 | ||
1046 | static void xsource_destroy(source *s) | |
1047 | { | |
1048 | xsource *xs = (xsource *)s; | |
b0805b27 | 1049 | xfree(xs->s.desc); |
48bb1f76 | 1050 | exec_destroy(&xs->x); |
1051 | DESTROY(xs); | |
1052 | } | |
1053 | ||
1054 | /* --- Executable source operations --- */ | |
1055 | ||
1056 | source_ops xsource_ops = { | |
1057 | "exec", | |
1058 | xsource_option, xsource_read, xsource_attach, xsource_destroy | |
1059 | }; | |
1060 | ||
1061 | /*----- Exec target description -------------------------------------------*/ | |
1062 | ||
1063 | /* --- @option@ --- */ | |
1064 | ||
1065 | static int xtarget_option(target *t, scanner *sc) | |
1066 | { | |
1067 | xtarget *xt = (xtarget *)t; | |
1068 | return (exec_option(xt ? &xt->x : 0, sc)); | |
1069 | } | |
1070 | ||
1071 | /* --- @read@ --- */ | |
1072 | ||
1073 | static target *xtarget_read(scanner *sc) | |
1074 | { | |
1075 | xtarget *xt; | |
1076 | dstr d = DSTR_INIT; | |
1077 | ||
1078 | if (!conf_prefix(sc, "exec")) | |
1079 | return (0); | |
1080 | xt = CREATE(xtarget); | |
1081 | xt->t.ops = &xtarget_ops; | |
1082 | exec_read(&xt->x, sc); | |
1083 | exec_desc(&xt->x, &d); | |
1084 | xt->t.desc = xstrdup(d.buf); | |
1085 | dstr_destroy(&d); | |
1086 | return (&xt->t); | |
1087 | } | |
1088 | ||
1089 | /* --- @create@ --- */ | |
1090 | ||
1091 | static endpt *xtarget_create(target *t, const char *desc) | |
1092 | { | |
1093 | xtarget *xt = (xtarget *)t; | |
1094 | endpt *e = exec_endpt(&xt->x, desc); | |
1095 | return (e); | |
1096 | } | |
1097 | ||
1098 | /* --- @destroy@ --- */ | |
1099 | ||
1100 | static void xtarget_destroy(target *t) | |
1101 | { | |
1102 | xtarget *xt = (xtarget *)t; | |
b0805b27 | 1103 | xfree(xt->t.desc); |
48bb1f76 | 1104 | exec_destroy(&xt->x); |
1105 | DESTROY(xt); | |
1106 | } | |
1107 | ||
1108 | /* --- Exec target operations --- */ | |
1109 | ||
1110 | target_ops xtarget_ops = { | |
1111 | "exec", | |
ee599f55 | 1112 | xtarget_option, xtarget_read, 0, xtarget_create, xtarget_destroy |
48bb1f76 | 1113 | }; |
1114 | ||
1115 | /*----- That's all, folks -------------------------------------------------*/ |