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