3 * Source and target for executable programs
5 * (c) 1999 Straylight/Edgeware
8 /*----- Licensing notice --------------------------------------------------*
10 * This file is part of the `fwd' port forwarder.
12 * `fwd' is free software; you can redistribute it and/or modify
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.
17 * `fwd' is distributed in the hope that it will be useful,
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.
22 * You should have received a copy of the GNU General Public License
23 * along with `fwd'; if not, write to the Free Software Foundation,
24 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29 /*----- Data structures ---------------------------------------------------*/
31 /* --- Resource usage --- */
35 typedef struct xlimit {
36 #define XLIMIT_ENTRY(name, constant) struct rlimit name;
43 /* --- Environment variable modifications --- */
55 /* --- Program options --- */
57 typedef struct xopts {
73 /* --- Executable program arguments --- */
75 typedef struct xargs {
81 #define XARGS_SZ(n) (sizeof(xargs) + sizeof(char *) * (n))
83 /* --- Executable endpoints --- */
87 struct xept *next, *prev;
100 /* --- Executable program data --- */
102 typedef struct xdata {
107 /* --- Executable source block --- */
109 typedef struct xsource {
114 /* --- Executable target block --- */
116 typedef struct xtarget {
121 /*----- Static variables --------------------------------------------------*/
123 static xopts exec_opts = { 1, 0, 0, 0, -1, -1, 0, &exec_opts.env };
124 static xept *xept_list;
126 static sym_table env;
128 /*----- Fiddling about with resource limits -------------------------------*/
130 #ifdef HAVE_SETRLIMIT
132 /* --- Table mapping user-level names to OS interface bits --- */
134 typedef struct rlimit_ent {
141 static rlimit_ent rlimits[] = {
142 #define TABLE_ENTRY(name, constant) \
143 { #name, #constant, constant, offsetof(xlimit, name) },
149 #define RLIMIT(xl, o) ((struct rlimit *)((char *)(xl) + (o)))
151 /* --- @rlimit_get@ --- *
153 * Arguments: @xlimit *xl@ = pointer to limit structure
157 * Use: Initializes a limit structure from the current limits.
160 static void rlimit_get(xlimit *xl)
164 for (r = rlimits; r->name; r++) {
165 if (getrlimit(r->r, RLIMIT(xl, r->off))) {
166 moan("couldn't read %s: %s", r->rname, strerror(errno));
172 /* --- @rlimit_set@ --- *
174 * Arguments: @xlimit *xl@ = pointer to limit structure
178 * Use: Sets resource limits from the supplied limits structure.
181 static void rlimit_set(xlimit *xl)
185 for (r = rlimits; r->name; r++) {
186 if (setrlimit(r->r, RLIMIT(xl, r->off))) {
187 moan("couldn't set %s: %s", r->rname, strerror(errno));
193 /* --- @rlimit_option@ --- */
195 static int rlimit_option(xlimit *xl, scanner *sc)
197 CONF_BEGIN(sc, "rlimit", "resource limit")
198 enum { w_soft, w_hard, w_both } which = w_both;
203 /* --- Find out which resource is being fiddled --- */
209 for (r = rlimits; r->name; r++) {
210 if (strncmp(sc->d.buf, r->name, sc->d.len) == 0) {
211 if (r->name[sc->d.len] == 0) {
215 error(sc, "ambiguous resource limit name `%s'", sc->d.buf);
223 rl = RLIMIT(xl, chosen->off);
226 /* --- Look for hard or soft restrictions --- */
232 if (sc->t == CTOK_WORD) {
233 if ((i = conf_enum(sc, "soft,hard",
234 ENUM_ABBREV | ENUM_NONE, "limit type")) != -1)
239 /* --- Now read the new value --- */
243 if (sc->t != CTOK_WORD)
244 error(sc, "parse error, expected limit value");
246 if (conf_enum(sc, "unlimited,infinity",
247 ENUM_ABBREV | ENUM_NONE, "limit value") > -1)
252 v = strtol(sc->d.buf, &p, 0);
254 error(sc, "parse error, invalid limit value `%s'", sc->d.buf);
255 switch (tolower((unsigned char)*p)) {
257 case 'b': v *= 512; break;
260 case 'k': v *= 1024; break;
261 default: error(sc, "parse error, invalid limit scale `%c'", *p);
266 /* --- Store the limit value away --- */
274 if (v > rl->rlim_max)
275 error(sc, "soft limit %l exceeds hard limit %l for %s",
276 v, rl->rlim_max, chosen->rname);
281 if (rl->rlim_cur > v)
292 /*----- Environment fiddling ----------------------------------------------*/
294 /* --- @xenv_option@ --- *
296 * Arguments: @xopts *xo@ = pointer to options block
297 * @scanner *sc@ = pointer to scanner
299 * Returns: Nonzero if claimed
301 * Use: Parses environment variable assignments.
304 static int xenv_option(xopts *xo, scanner *sc)
306 CONF_BEGIN(sc, "env", "environment")
309 /* --- Unset a variable --- */
311 if (strcmp(sc->d.buf, "unset") == 0) {
313 if (sc->t != CTOK_WORD)
314 error(sc, "parse error, expected environment variable name");
316 xe->name = xstrdup(sc->d.buf);
323 /* --- Clear the entire environment --- */
325 if (strcmp(sc->d.buf, "clear") == 0) {
332 /* --- Allow `set' to be omitted if there's a prefix --- */
334 if (strcmp(sc->d.buf, "set") == 0)
339 /* --- Set a variable --- */
341 if (sc->t != CTOK_WORD)
342 error(sc, "parse error, expected environment variable name");
344 xe->name = xstrdup(sc->d.buf);
348 if (sc->t != CTOK_WORD)
349 error(sc, "parse error, expected environment variable value");
350 xe->value = xstrdup(sc->d.buf);
358 xo->etail = &xe->next;
361 /* --- Nothing else to try --- */
366 /* --- @xenv_apply@ --- *
368 * Arguments: @xenv *xe@ = pointer to a variable change list
372 * Use: Modifies the environment (in @env@) according to the list.
375 static void xenv_apply(xenv *xe)
380 env_put(&env, xe->name, xe->value);
391 /* --- @xenv_destroy@ --- *
393 * Arguments: @xenv *xe@ = pointer to a variable change list
397 * Use: Frees the memory used by an environment variable change list.
400 static void xenv_destroy(xenv *xe)
412 /*----- Miscellaneous good things -----------------------------------------*/
414 /* --- @x_tidy@ --- *
416 * Arguments: @xargs *xa@ = pointer to an arguments block
417 * @xopts *xo@ = pointer to an options block
421 * Use: Releases a reference to argument and options blocks.
424 static void x_tidy(xargs *xa, xopts *xo)
432 xenv_destroy(xo->env);
437 /*----- Executable endpoints ----------------------------------------------*/
439 /* --- @attach@ --- */
441 static void xept_error(char */*p*/, size_t /*len*/, void */*v*/);
443 static void xept_attach(endpt *e, reffd *in, reffd *out)
445 xept *xe = (xept *)e;
449 /* --- Make a pipe for standard error --- */
452 fw_log(-1, "[%s] couldn't create pipe: %s", xe->desc, strerror(errno));
455 fdflags(fd[0], O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
457 /* --- Fork a child, and handle an error if there was one --- */
459 if ((kid = fork()) == -1) {
460 fw_log(-1, "[%s] couldn't fork: %s", xe->desc, strerror(errno));
466 /* --- Do the child thing --- */
472 /* --- Fiddle with the file descriptors --- *
474 * Attach the other endpoint's descriptors to standard input and output.
475 * Attach my pipe to standard error. Mark everything as blocking and
476 * not-to-be-closed-on-exec at this end.
480 md[0].cur = in->fd; md[0].want = STDIN_FILENO;
481 md[1].cur = out->fd; md[1].want = STDOUT_FILENO;
482 md[2].cur = fd[1]; md[2].want = STDERR_FILENO;
484 moan("couldn't manipulate file descriptors: %s", strerror(errno));
488 fdflags(STDIN_FILENO, O_NONBLOCK, 0, FD_CLOEXEC, 0);
489 fdflags(STDOUT_FILENO, O_NONBLOCK, 0, FD_CLOEXEC, 0);
490 fdflags(STDERR_FILENO, O_NONBLOCK, 0, FD_CLOEXEC, 0);
492 /* --- First of all set the @chroot@ prison --- */
494 if (xo->root && chroot(xo->root)) {
495 moan("couldn't set `%s' as filesystem root: %s",
496 xo->root, strerror(errno));
500 /* --- Now set the current directory --- */
502 if (xo->dir ? chdir(xo->dir) : xo->root ? chdir("/") : 0) {
503 moan("couldn't set `%s' as current directory: %s",
504 xo->dir ? xo->dir : "/", strerror(errno));
508 /* --- Set the resource limits --- */
510 #ifdef HAVE_SETRLIMIT
514 /* --- Set group id --- */
516 if (xo->gid != (gid_t)-1) {
517 if (setgid(xo->gid)) {
518 moan("couldn't set gid %i: %s", xo->gid, strerror(errno));
521 #ifdef HAVE_SETGROUPS
522 if (setgroups(1, &xo->gid))
523 moan("warning: couldn't set group list to %i: %s", xo->gid,
528 /* --- Set uid --- */
530 if (xo->uid != (uid_t)-1) {
531 if (setuid(xo->uid)) {
532 moan("couldn't set uid %i: %s", xo->uid, strerror(errno));
537 /* --- Play with signal dispositions --- */
539 signal(SIGPIPE, SIG_DFL);
541 /* --- Fiddle with the environment --- */
543 xenv_apply(exec_opts.env);
544 xenv_apply(xe->xo->env);
545 environ = env_export(&env);
547 /* --- Run the program --- */
549 execvp(xe->xa->file, xe->xa->argv);
550 moan("couldn't execute `%s': %s", xe->xa->file, strerror(errno));
554 /* --- The child's done; see to the parent --- */
557 selbuf_init(&xe->err, sel, fd[0], xept_error, xe);
559 xe->next = xept_list;
562 xept_list->prev = xe;
564 if (!(xe->xo->f & XF_NOLOG))
565 fw_log(-1, "[%s] started with pid %i", xe->desc, kid);
570 /* --- @xept_file@ --- */
572 static void xept_file(endpt *e, endpt *f)
574 xept *xe = (xept *)e;
578 /* --- @xept_close@ --- */
580 static void xept_close(endpt *e)
582 xept *xe = (xept *)e;
585 xe->f->ops->close(xe->f);
586 x_tidy(xe->xa, xe->xo);
592 /* --- @xept_destroy@ --- */
594 static void xept_destroy(xept *xe)
596 /* --- First emit the news about the process --- */
598 if (xe->xo->f & XF_NOLOG)
600 else if (WIFEXITED(xe->st)) {
601 if (WEXITSTATUS(xe->st) == 0)
602 fw_log(-1, "[%s] pid %i exited successfully", xe->desc, xe->kid);
604 fw_log(-1, "[%s] pid %i failed: status %i",
605 xe->desc, xe->kid, WEXITSTATUS(xe->st));
607 } else if (WIFSIGNALED(xe->st)) {
609 #ifdef HAVE_STRSIGNAL
610 s = strsignal(WTERMSIG(xe->st));
611 #elif HAVE__SYS_SIGLIST
612 s = _sys_siglist[WTERMSIG(xe->st)];
615 sprintf(buf, "signal %i", WTERMSIG(xe->st));
618 fw_log(-1, "[%s] pid %i failed: %s", xe->desc, xe->kid, s);
620 fw_log(-1, "[%s] pid %i failed: unrecognized status", xe->desc, xe->kid);
622 /* --- Free up the parent-side resources --- */
625 xe->next->prev = xe->prev;
627 xe->prev->next = xe->next;
629 xept_list = xe->next;
633 xe->f->ops->close(xe->f);
634 x_tidy(xe->xa, xe->xo);
639 /* --- @xept_chld@ --- *
641 * Arguments: @int n@ = signal number
642 * @void *p@ = uninteresting pointer
646 * Use: Deals with child death situations.
649 static void xept_chld(int n, void *p)
654 while ((kid = waitpid(-1, &st, WNOHANG)) > 0) {
655 xept *xe = xept_list;
659 if (kid == xxe->kid) {
661 xxe->e.f |= XEF_EXIT;
662 if (xxe->e.f & XEF_CLOSE)
670 /* --- @xept_error@ --- *
672 * Arguments: @char *p@ = pointer to string read from stderr
673 * @size_t len@ = length of the string
674 * @void *v@ = pointer to by endpoint
678 * Use: Handles error reports from a child process.
681 static void xept_error(char *p, size_t len, void *v)
685 fw_log(-1, "[%s] pid %i: %s", xe->desc, xe->kid, p);
687 close(xe->err.reader.fd);
688 selbuf_destroy(&xe->err);
689 xe->e.f |= XEF_CLOSE;
690 if (xe->e.f & XEF_EXIT)
695 /* --- Endpoint operations --- */
697 static endpt_ops xept_ops = { xept_attach, xept_file, 0, xept_close };
699 /*----- General operations on sources and targets -------------------------*/
701 /* --- @exec_init@ --- *
707 * Use: Initializes the executable problem source and target.
712 #ifdef HAVE_SETRLIMIT
713 rlimit_get(&exec_opts.xl);
715 sig_add(&xept_sig, SIGCHLD, xept_chld, 0);
717 env_import(&env, environ);
720 /* --- @exec_option@ --- */
722 static int exec_option(xdata *x, scanner *sc)
724 xopts *xo = x ? x->xo : &exec_opts;
726 CONF_BEGIN(sc, "exec", "executable");
728 /* --- Logging settings --- */
730 if (strcmp(sc->d.buf, "logging") == 0 ||
731 strcmp(sc->d.buf, "log") == 0) {
735 if (conf_enum(sc, "no,yes", ENUM_ABBREV, "logging status"))
742 /* --- Current directory setting --- *
744 * Lots of possibilities to guard against possible brainoes.
747 if (strcmp(sc->d.buf, "dir") == 0 ||
748 strcmp(sc->d.buf, "cd") == 0 ||
749 strcmp(sc->d.buf, "chdir") == 0 ||
750 strcmp(sc->d.buf, "cwd") == 0) {
756 xo->dir = xstrdup(d.buf);
761 /* --- Set a chroot prison --- */
763 if (strcmp(sc->d.buf, "root") == 0 ||
764 strcmp(sc->d.buf, "chroot") == 0) {
770 xo->root = xstrdup(d.buf);
775 /* --- Set the target user id --- */
777 if (strcmp(sc->d.buf, "uid") == 0 ||
778 strcmp(sc->d.buf, "user") == 0) {
782 if (sc->t != CTOK_WORD)
783 error(sc, "parse error, expected user name or uid");
784 if (isdigit((unsigned char)*sc->d.buf))
785 xo->uid = atoi(sc->d.buf);
787 struct passwd *pw = getpwnam(sc->d.buf);
789 error(sc, "unknown user name `%s'", sc->d.buf);
790 xo->uid = pw->pw_uid;
796 /* --- Set the target group id --- */
798 if (strcmp(sc->d.buf, "gid") == 0 ||
799 strcmp(sc->d.buf, "group") == 0) {
803 if (sc->t != CTOK_WORD)
804 error(sc, "parse error, expected group name or gid");
805 if (isdigit((unsigned char)*sc->d.buf))
806 xo->gid = atoi(sc->d.buf);
808 struct group *gr = getgrnam(sc->d.buf);
810 error(sc, "unknown user name `%s'", sc->d.buf);
811 xo->gid = gr->gr_gid;
817 /* --- Now try resource limit settings --- */
819 #ifdef HAVE_SETRLIMIT
820 if (rlimit_option(&xo->xl, sc))
824 /* --- And then environment settings --- */
826 if (xenv_option(xo, sc))
829 /* --- Nothing found --- */
834 /* --- @exec_desc@ --- */
836 static void exec_desc(xdata *x, dstr *d)
840 dstr_puts(d, "exec ");
841 if (strcmp(x->xa->file, x->xa->argv[0]) != 0) {
842 dstr_puts(d, x->xa->file);
845 for (p = x->xa->argv; *p; p++) {
854 /* --- @exec_read@ --- */
856 static void exec_read(xdata *x, scanner *sc)
862 /* --- Read the first word --- *
864 * This is either a shell command or the actual program to run.
867 if (sc->t == CTOK_WORD) {
868 dstr_putd(&d, &sc->d); d.len++;
873 /* --- See if there's a list of arguments --- *
875 * If not, then the thing I saw was a shell command, so build the proper
876 * arguments for that.
882 error(sc, "parse error, expected shell command or argument list");
883 xa = xmalloc(XARGS_SZ(3) + 8 + 3 + d.len);
884 p = (char *)(xa->argv + 4);
887 xa->argv[0] = p; memcpy(p, "/bin/sh", 8); p += 8;
888 xa->argv[1] = p; memcpy(p, "-c", 3); p += 3;
889 xa->argv[2] = p; memcpy(p, d.buf, d.len); p += d.len;
893 /* --- Snarf in a list of arguments --- */
900 /* --- Strip off the leading `[' --- *
902 * Allow various handy filename characters to be entered without quoting.
905 conf_undelim(sc, "=:/.", "=:/.");
908 /* --- Read a sequence of arguments --- */
910 while (sc->t == CTOK_WORD) {
911 dstr_putd(&d, &sc->d); d.len++;
915 conf_undelim(sc, 0, 0);
917 /* --- Expect the closing `]' --- */
920 error(sc, "parse error, missing `]'");
923 /* --- If there are no arguments, whinge --- */
926 error(sc, "must specify at least one argument");
928 /* --- Allocate a lump of memory for the array --- */
930 xa = xmalloc(XARGS_SZ(argc) + d.len);
933 p = (char *)(v + argc + 1);
934 memcpy(p, d.buf, d.len);
939 /* --- Start dumping addresses into the @argv@ array --- */
943 while (*p++ && p < q)
951 /* --- Do some other setting up --- */
955 x->xo = CREATE(xopts);
961 /* --- @exec_endpt@ --- */
963 static endpt *exec_endpt(xdata *x, const char *desc)
965 xept *xe = CREATE(xept);
966 xe->e.ops = &xept_ops;
970 xe->xa = x->xa; xe->xa->ref++;
971 xe->xo = x->xo; xe->xo->ref++;
974 xe->desc = xstrdup(desc);
978 /* --- @exec_destroy@ --- */
980 static void exec_destroy(xdata *x)
982 x_tidy(x->xa, x->xo);
985 /*----- Source definition -------------------------------------------------*/
987 /* --- @option@ --- */
989 static int xsource_option(source *s, scanner *sc)
991 xsource *xs = (xsource *)s;
992 return (exec_option(xs ? &xs->x : 0, sc));
997 static source *xsource_read(scanner *sc)
1001 if (!conf_prefix(sc, "exec"))
1003 xs = CREATE(xsource);
1004 xs->s.ops = &xsource_ops;
1006 exec_read(&xs->x, sc);
1010 /* --- @attach@ --- */
1012 static void xsource_destroy(source */*s*/);
1014 static void xsource_attach(source *s, scanner *sc, target *t)
1016 xsource *xs = (xsource *)s;
1019 /* --- Set up the source description string --- */
1023 exec_desc(&xs->x, &d);
1024 dstr_puts(&d, " -> ");
1025 dstr_puts(&d, t->desc);
1026 xs->s.desc = xstrdup(d.buf);
1030 /* --- Create the endpoints --- */
1032 if ((ee = t->ops->create(t, xs->s.desc)) == 0)
1034 if ((e = exec_endpt(&xs->x, xs->s.desc)) == 0) {
1038 endpt_join(e, ee, xs->s.desc);
1040 /* --- Dispose of source and target --- */
1044 xsource_destroy(&xs->s);
1047 /* --- @destroy@ --- */
1049 static void xsource_destroy(source *s)
1051 xsource *xs = (xsource *)s;
1053 exec_destroy(&xs->x);
1057 /* --- Executable source operations --- */
1059 source_ops xsource_ops = {
1061 xsource_option, xsource_read, xsource_attach, xsource_destroy
1064 /*----- Exec target description -------------------------------------------*/
1066 /* --- @option@ --- */
1068 static int xtarget_option(target *t, scanner *sc)
1070 xtarget *xt = (xtarget *)t;
1071 return (exec_option(xt ? &xt->x : 0, sc));
1074 /* --- @read@ --- */
1076 static target *xtarget_read(scanner *sc)
1081 if (!conf_prefix(sc, "exec"))
1083 xt = CREATE(xtarget);
1084 xt->t.ops = &xtarget_ops;
1085 exec_read(&xt->x, sc);
1086 exec_desc(&xt->x, &d);
1087 xt->t.desc = xstrdup(d.buf);
1092 /* --- @create@ --- */
1094 static endpt *xtarget_create(target *t, const char *desc)
1096 xtarget *xt = (xtarget *)t;
1097 endpt *e = exec_endpt(&xt->x, desc);
1101 /* --- @destroy@ --- */
1103 static void xtarget_destroy(target *t)
1105 xtarget *xt = (xtarget *)t;
1107 exec_destroy(&xt->x);
1111 /* --- Exec target operations --- */
1113 target_ops xtarget_ops = {
1115 xtarget_option, xtarget_read, 0, xtarget_create, xtarget_destroy
1118 /*----- That's all, folks -------------------------------------------------*/