3 * $Id: exec.c,v 1.9 2004/04/08 01:36:25 mdw Exp $
5 * Source and target for executable programs
7 * (c) 1999 Straylight/Edgeware
10 /*----- Licensing notice --------------------------------------------------*
12 * This file is part of the `fw' port forwarder.
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.
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.
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.
29 /*----- Header files ------------------------------------------------------*/
42 #include <sys/types.h>
49 # include <sys/resource.h>
53 extern char **environ
;
61 #include <mLib/alloc.h>
62 #include <mLib/dstr.h>
64 #include <mLib/fdflags.h>
65 #include <mLib/report.h>
67 #include <mLib/selbuf.h>
82 /*----- Data structures ---------------------------------------------------*/
84 /* --- Resource usage --- */
88 typedef struct xlimit
{
89 #define R(r, n) struct rlimit n;
95 /* --- Environment variable modifications --- */
107 /* --- Program options --- */
109 typedef struct xopts
{
118 #ifdef HAVE_SETRLIMIT
125 /* --- Executable program arguments --- */
127 typedef struct xargs
{
133 #define XARGS_SZ(n) (sizeof(xargs) + sizeof(char *) * (n))
135 /* --- Executable endpoints --- */
137 typedef struct xept
{
139 struct xept
*next
, *prev
;
149 #define XEF_CLOSE 16u
152 /* --- Executable program data --- */
154 typedef struct xdata
{
159 /* --- Executable source block --- */
161 typedef struct xsource
{
166 /* --- Executable target block --- */
168 typedef struct xtarget
{
173 /*----- Static variables --------------------------------------------------*/
175 static xopts exec_opts
= { 1, 0, 0, 0, -1, -1, 0, &exec_opts
.env
};
176 static xept
*xept_list
;
178 static sym_table env
;
180 /*----- Fiddling about with resource limits -------------------------------*/
182 #ifdef HAVE_SETRLIMIT
184 /* --- Table mapping user-level names to OS interface bits --- */
186 typedef struct rlimit_ent
{
193 static rlimit_ent rlimits
[] = {
194 #define R(r, n) { #n, #r, r, offsetof(xlimit, n) },
199 #define RLIMIT(xl, o) ((struct rlimit *)((char *)(xl) + (o)))
201 /* --- @rlimit_get@ --- *
203 * Arguments: @xlimit *xl@ = pointer to limit structure
207 * Use: Initializes a limit structure from the current limits.
210 static void rlimit_get(xlimit
*xl
)
214 for (r
= rlimits
; r
->name
; r
++) {
215 if (getrlimit(r
->r
, RLIMIT(xl
, r
->off
))) {
216 moan("couldn't read %s: %s", r
->rname
, strerror(errno
));
222 /* --- @rlimit_set@ --- *
224 * Arguments: @xlimit *xl@ = pointer to limit structure
228 * Use: Sets resource limits from the supplied limits structure.
231 static void rlimit_set(xlimit
*xl
)
235 for (r
= rlimits
; r
->name
; r
++) {
236 if (setrlimit(r
->r
, RLIMIT(xl
, r
->off
))) {
237 moan("couldn't set %s: %s", r
->rname
, strerror(errno
));
243 /* --- @rlimit_option@ --- */
245 static int rlimit_option(xlimit
*xl
, scanner
*sc
)
247 CONF_BEGIN(sc
, "rlimit", "resource limit")
248 enum { w_soft
, w_hard
, w_both
} which
= w_both
;
253 /* --- Find out which resource is being fiddled --- */
259 for (r
= rlimits
; r
->name
; r
++) {
260 if (strncmp(sc
->d
.buf
, r
->name
, sc
->d
.len
) == 0) {
261 if (r
->name
[sc
->d
.len
] == 0) {
265 error(sc
, "ambiguous resource limit name `%s'", sc
->d
.buf
);
273 rl
= RLIMIT(xl
, chosen
->off
);
276 /* --- Look for hard or soft restrictions --- */
282 if (sc
->t
== CTOK_WORD
) {
283 if ((i
= conf_enum(sc
, "soft,hard",
284 ENUM_ABBREV
| ENUM_NONE
, "limit type")) != -1)
289 /* --- Now read the new value --- */
293 if (sc
->t
!= CTOK_WORD
)
294 error(sc
, "parse error, expected limit value");
296 if (conf_enum(sc
, "unlimited,infinity",
297 ENUM_ABBREV
| ENUM_NONE
, "limit value") > -1)
302 v
= strtol(sc
->d
.buf
, &p
, 0);
304 error(sc
, "parse error, invalid limit value `%s'", sc
->d
.buf
);
305 switch (tolower((unsigned char)*p
)) {
307 case 'b': v
*= 512; break;
310 case 'k': v
*= 1024; break;
311 default: error(sc
, "parse error, invalid limit scale `%c'", *p
);
316 /* --- Store the limit value away --- */
324 if (v
> rl
->rlim_max
)
325 error(sc
, "soft limit %l exceeds hard limit %l for %s",
326 v
, rl
->rlim_max
, chosen
->rname
);
331 if (rl
->rlim_cur
> v
)
342 /*----- Environment fiddling ----------------------------------------------*/
344 /* --- @xenv_option@ --- *
346 * Arguments: @xopts *xo@ = pointer to options block
347 * @scanner *sc@ = pointer to scanner
349 * Returns: Nonzero if claimed
351 * Use: Parses environment variable assignments.
354 static int xenv_option(xopts
*xo
, scanner
*sc
)
356 CONF_BEGIN(sc
, "env", "environment")
359 /* --- Unset a variable --- */
361 if (strcmp(sc
->d
.buf
, "unset") == 0) {
363 if (sc
->t
!= CTOK_WORD
)
364 error(sc
, "parse error, expected environment variable name");
366 xe
->name
= xstrdup(sc
->d
.buf
);
373 /* --- Clear the entire environment --- */
375 if (strcmp(sc
->d
.buf
, "clear") == 0) {
382 /* --- Allow `set' to be omitted if there's a prefix --- */
384 if (strcmp(sc
->d
.buf
, "set") == 0)
389 /* --- Set a variable --- */
391 if (sc
->t
!= CTOK_WORD
)
392 error(sc
, "parse error, expected environment variable name");
394 xe
->name
= xstrdup(sc
->d
.buf
);
398 if (sc
->t
!= CTOK_WORD
)
399 error(sc
, "parse error, expected environment variable value");
400 xe
->value
= xstrdup(sc
->d
.buf
);
408 xo
->etail
= &xe
->next
;
411 /* --- Nothing else to try --- */
416 /* --- @xenv_apply@ --- *
418 * Arguments: @xenv *xe@ = pointer to a variable change list
422 * Use: Modifies the environment (in @env@) according to the list.
425 static void xenv_apply(xenv
*xe
)
430 env_put(&env
, xe
->name
, xe
->value
);
441 /* --- @xenv_destroy@ --- *
443 * Arguments: @xenv *xe@ = pointer to a variable change list
447 * Use: Frees the memory used by an environment variable change list.
450 static void xenv_destroy(xenv
*xe
)
462 /*----- Miscellaneous good things -----------------------------------------*/
464 /* --- @x_tidy@ --- *
466 * Arguments: @xargs *xa@ = pointer to an arguments block
467 * @xopts *xo@ = pointer to an options block
471 * Use: Releases a reference to argument and options blocks.
474 static void x_tidy(xargs
*xa
, xopts
*xo
)
482 xenv_destroy(xo
->env
);
487 /*----- Executable endpoints ----------------------------------------------*/
489 /* --- @attach@ --- */
491 static void xept_error(char */
*p*/
, size_t /*len*/, void */
*v*/
);
493 static void xept_attach(endpt
*e
, reffd
*in
, reffd
*out
)
495 xept
*xe
= (xept
*)e
;
499 /* --- Make a pipe for standard error --- */
502 fw_log(-1, "[%s] couldn't create pipe: %s", xe
->desc
, strerror(errno
));
505 fdflags(fd
[0], O_NONBLOCK
, O_NONBLOCK
, FD_CLOEXEC
, FD_CLOEXEC
);
507 /* --- Fork a child, and handle an error if there was one --- */
509 if ((kid
= fork()) == -1) {
510 fw_log(-1, "[%s] couldn't fork: %s", xe
->desc
, strerror(errno
));
516 /* --- Do the child thing --- */
521 /* --- Fiddle with the file descriptors --- *
523 * Attach the other endpoint's descriptors to standard input and output.
524 * Attach my pipe to standard error. Mark everything as blocking and
525 * not-to-be-closed-on-exec at this end.
529 if (dup2(in
->fd
, STDIN_FILENO
) < 0 ||
530 dup2(out
->fd
, STDOUT_FILENO
) < 0 ||
531 dup2(fd
[1], STDERR_FILENO
) < 0) {
532 moan("couldn't manipulate file descriptors: %s", strerror(errno
));
541 fdflags(STDIN_FILENO
, O_NONBLOCK
, 0, FD_CLOEXEC
, 0);
542 fdflags(STDOUT_FILENO
, O_NONBLOCK
, 0, FD_CLOEXEC
, 0);
543 fdflags(STDERR_FILENO
, O_NONBLOCK
, 0, FD_CLOEXEC
, 0);
545 /* --- First of all set the @chroot@ prison --- */
547 if (xo
->root
&& chroot(xo
->root
)) {
548 moan("couldn't set `%s' as filesystem root: %s",
549 xo
->root
, strerror(errno
));
553 /* --- Now set the current directory --- */
555 if (xo
->dir ?
chdir(xo
->dir
) : xo
->root ?
chdir("/") : 0) {
556 moan("couldn't set `%s' as current directory: %s",
557 xo
->dir ? xo
->dir
: "/", strerror(errno
));
561 /* --- Set the resource limits --- */
563 #ifdef HAVE_SETRLIMIT
567 /* --- Set group id --- */
569 if (xo
->gid
!= (gid_t
)-1) {
570 if (setgid(xo
->gid
)) {
571 moan("couldn't set gid %i: %s", xo
->gid
, strerror(errno
));
574 #ifdef HAVE_SETGROUPS
575 if (setgroups(1, &xo
->gid
))
576 moan("warning: couldn't set group list to %i: %s", xo
->gid
,
581 /* --- Set uid --- */
583 if (xo
->uid
!= (uid_t
)-1) {
584 if (setuid(xo
->uid
)) {
585 moan("couldn't set uid %i: %s", xo
->uid
, strerror(errno
));
590 /* --- Play with signal dispositions --- */
592 signal(SIGPIPE
, SIG_DFL
);
594 /* --- Fiddle with the environment --- */
596 xenv_apply(exec_opts
.env
);
597 xenv_apply(xe
->xo
->env
);
598 environ
= env_export(&env
);
600 /* --- Run the program --- */
602 execvp(xe
->xa
->file
, xe
->xa
->argv
);
603 moan("couldn't execute `%s': %s", xe
->xa
->file
, strerror(errno
));
607 /* --- The child's done; see to the parent --- */
610 selbuf_init(&xe
->err
, sel
, fd
[0], xept_error
, xe
);
612 xe
->next
= xept_list
;
615 xept_list
->prev
= xe
;
617 if (!(xe
->xo
->f
& XF_NOLOG
))
618 fw_log(-1, "[%s] started with pid %i", xe
->desc
, kid
);
623 /* --- @xept_file@ --- */
625 static void xept_file(endpt
*e
, endpt
*f
)
627 xept
*xe
= (xept
*)e
;
631 /* --- @xept_close@ --- */
633 static void xept_close(endpt
*e
)
635 xept
*xe
= (xept
*)e
;
638 xe
->f
->ops
->close(xe
->f
);
639 x_tidy(xe
->xa
, xe
->xo
);
645 /* --- @xept_destroy@ --- */
647 static void xept_destroy(xept
*xe
)
649 /* --- First emit the news about the process --- */
651 if (xe
->xo
->f
& XF_NOLOG
)
653 else if (WIFEXITED(xe
->st
)) {
654 if (WEXITSTATUS(xe
->st
) == 0)
655 fw_log(-1, "[%s] pid %i exited successfully", xe
->desc
, xe
->kid
);
657 fw_log(-1, "[%s] pid %i failed: status %i",
658 xe
->desc
, xe
->kid
, WEXITSTATUS(xe
->st
));
660 } else if (WIFSIGNALED(xe
->st
)) {
662 #ifdef HAVE_STRSIGNAL
663 s
= strsignal(WTERMSIG(xe
->st
));
664 #elif HAVE__SYS_SIGLIST
665 s
= _sys_siglist
[WTERMSIG(xe
->st
)];
668 sprintf(buf
, "signal %i", WTERMSIG(xe
->st
));
671 fw_log(-1, "[%s] pid %i failed: %s", xe
->desc
, xe
->kid
, s
);
673 fw_log(-1, "[%s] pid %i failed: unrecognized status", xe
->desc
, xe
->kid
);
675 /* --- Free up the parent-side resources --- */
678 xe
->next
->prev
= xe
->prev
;
680 xe
->prev
->next
= xe
->next
;
682 xept_list
= xe
->next
;
686 xe
->f
->ops
->close(xe
->f
);
687 x_tidy(xe
->xa
, xe
->xo
);
692 /* --- @xept_chld@ --- *
694 * Arguments: @int n@ = signal number
695 * @void *p@ = uninteresting pointer
699 * Use: Deals with child death situations.
702 static void xept_chld(int n
, void *p
)
707 while ((kid
= waitpid(-1, &st
, WNOHANG
)) > 0) {
708 xept
*xe
= xept_list
;
712 if (kid
== xxe
->kid
) {
714 xxe
->e
.f
|= XEF_EXIT
;
715 if (xxe
->e
.f
& XEF_CLOSE
)
723 /* --- @xept_error@ --- *
725 * Arguments: @char *p@ = pointer to string read from stderr
726 * @size_t len@ = length of the string
727 * @void *v@ = pointer to by endpoint
731 * Use: Handles error reports from a child process.
734 static void xept_error(char *p
, size_t len
, void *v
)
738 fw_log(-1, "[%s] pid %i: %s", xe
->desc
, xe
->kid
, p
);
740 close(xe
->err
.reader
.fd
);
741 selbuf_destroy(&xe
->err
);
742 xe
->e
.f
|= XEF_CLOSE
;
743 if (xe
->e
.f
& XEF_EXIT
)
748 /* --- Endpoint operations --- */
750 static endpt_ops xept_ops
= { xept_attach
, xept_file
, 0, xept_close
};
752 /*----- General operations on sources and targets -------------------------*/
754 /* --- @exec_init@ --- *
760 * Use: Initializes the executable problem source and target.
765 #ifdef HAVE_SETRLIMIT
766 rlimit_get(&exec_opts
.xl
);
768 sig_add(&xept_sig
, SIGCHLD
, xept_chld
, 0);
770 env_import(&env
, environ
);
773 /* --- @exec_option@ --- */
775 static int exec_option(xdata
*x
, scanner
*sc
)
777 xopts
*xo
= x ? x
->xo
: &exec_opts
;
779 CONF_BEGIN(sc
, "exec", "executable");
781 /* --- Logging settings --- */
783 if (strcmp(sc
->d
.buf
, "logging") == 0 ||
784 strcmp(sc
->d
.buf
, "log") == 0) {
788 if (conf_enum(sc
, "no,yes", ENUM_ABBREV
, "logging status"))
795 /* --- Current directory setting --- *
797 * Lots of possibilities to guard against possible brainoes.
800 if (strcmp(sc
->d
.buf
, "dir") == 0 ||
801 strcmp(sc
->d
.buf
, "cd") == 0 ||
802 strcmp(sc
->d
.buf
, "chdir") == 0 ||
803 strcmp(sc
->d
.buf
, "cwd") == 0) {
808 conf_name(sc
, '/', &d
);
809 xo
->dir
= xstrdup(d
.buf
);
814 /* --- Set a chroot prison --- */
816 if (strcmp(sc
->d
.buf
, "root") == 0 ||
817 strcmp(sc
->d
.buf
, "chroot") == 0) {
822 conf_name(sc
, '/', &d
);
823 xo
->root
= xstrdup(d
.buf
);
828 /* --- Set the target user id --- */
830 if (strcmp(sc
->d
.buf
, "uid") == 0 ||
831 strcmp(sc
->d
.buf
, "user") == 0) {
835 if (sc
->t
!= CTOK_WORD
)
836 error(sc
, "parse error, expected user name or uid");
837 if (isdigit((unsigned char)*sc
->d
.buf
))
838 xo
->uid
= atoi(sc
->d
.buf
);
840 struct passwd
*pw
= getpwnam(sc
->d
.buf
);
842 error(sc
, "unknown user name `%s'", sc
->d
.buf
);
843 xo
->uid
= pw
->pw_uid
;
849 /* --- Set the target group id --- */
851 if (strcmp(sc
->d
.buf
, "gid") == 0 ||
852 strcmp(sc
->d
.buf
, "group") == 0) {
856 if (sc
->t
!= CTOK_WORD
)
857 error(sc
, "parse error, expected group name or gid");
858 if (isdigit((unsigned char)*sc
->d
.buf
))
859 xo
->gid
= atoi(sc
->d
.buf
);
861 struct group
*gr
= getgrnam(sc
->d
.buf
);
863 error(sc
, "unknown user name `%s'", sc
->d
.buf
);
864 xo
->gid
= gr
->gr_gid
;
870 /* --- Now try resource limit settings --- */
872 #ifdef HAVE_SETRLIMIT
873 if (rlimit_option(&xo
->xl
, sc
))
877 /* --- And then environment settings --- */
879 if (xenv_option(xo
, sc
))
882 /* --- Nothing found --- */
887 /* --- @exec_desc@ --- */
889 static void exec_desc(xdata
*x
, dstr
*d
)
893 dstr_puts(d
, "exec ");
894 if (strcmp(x
->xa
->file
, x
->xa
->argv
[0]) != 0) {
895 dstr_puts(d
, x
->xa
->file
);
898 for (p
= x
->xa
->argv
; *p
; p
++) {
907 /* --- @exec_read@ --- */
909 static void exec_read(xdata
*x
, scanner
*sc
)
915 /* --- Read the first word --- *
917 * This is either a shell command or the actual program to run.
920 if (sc
->t
== CTOK_WORD
) {
921 dstr_putd(&d
, &sc
->d
); d
.len
++;
926 /* --- See if there's a list of arguments --- *
928 * If not, then the thing I saw was a shell command, so build the proper
929 * arguments for that.
935 error(sc
, "parse error, expected shell command or argument list");
936 xa
= xmalloc(XARGS_SZ(3) + 8 + 3 + d
.len
);
937 p
= (char *)(xa
->argv
+ 4);
940 xa
->argv
[0] = p
; memcpy(p
, "/bin/sh", 8); p
+= 8;
941 xa
->argv
[1] = p
; memcpy(p
, "-c", 3); p
+= 3;
942 xa
->argv
[2] = p
; memcpy(p
, d
.buf
, d
.len
); p
+= d
.len
;
946 /* --- Snarf in a list of arguments --- */
953 /* --- Strip off the leading `[' --- *
955 * Allow various handy filename characters to be entered without quoting.
958 conf_undelim(sc
, "=:/.", "=:/.");
961 /* --- Read a sequence of arguments --- */
963 while (sc
->t
== CTOK_WORD
) {
964 dstr_putd(&d
, &sc
->d
); d
.len
++;
968 conf_undelim(sc
, 0, 0);
970 /* --- Expect the closing `]' --- */
973 error(sc
, "parse error, missing `]'");
976 /* --- If there are no arguments, whinge --- */
979 error(sc
, "must specify at least one argument");
981 /* --- Allocate a lump of memory for the array --- */
983 xa
= xmalloc(XARGS_SZ(argc
) + d
.len
);
986 p
= (char *)(v
+ argc
+ 1);
987 memcpy(p
, d
.buf
, d
.len
);
992 /* --- Start dumping addresses into the @argv@ array --- */
996 while (*p
++ && p
< q
)
1004 /* --- Do some other setting up --- */
1008 x
->xo
= CREATE(xopts
);
1014 /* --- @exec_endpt@ --- */
1016 static endpt
*exec_endpt(xdata
*x
, const char *desc
)
1018 xept
*xe
= CREATE(xept
);
1019 xe
->e
.ops
= &xept_ops
;
1023 xe
->xa
= x
->xa
; xe
->xa
->ref
++;
1024 xe
->xo
= x
->xo
; xe
->xo
->ref
++;
1027 xe
->desc
= xstrdup(desc
);
1031 /* --- @exec_destroy@ --- */
1033 static void exec_destroy(xdata
*x
)
1035 x_tidy(x
->xa
, x
->xo
);
1038 /*----- Source definition -------------------------------------------------*/
1040 /* --- @option@ --- */
1042 static int xsource_option(source
*s
, scanner
*sc
)
1044 xsource
*xs
= (xsource
*)s
;
1045 return (exec_option(xs ?
&xs
->x
: 0, sc
));
1048 /* --- @read@ --- */
1050 static source
*xsource_read(scanner
*sc
)
1054 if (!conf_prefix(sc
, "exec"))
1056 xs
= CREATE(xsource
);
1057 xs
->s
.ops
= &xsource_ops
;
1059 exec_read(&xs
->x
, sc
);
1063 /* --- @attach@ --- */
1065 static void xsource_destroy(source */
*s*/
);
1067 static void xsource_attach(source
*s
, scanner
*sc
, target
*t
)
1069 xsource
*xs
= (xsource
*)s
;
1072 /* --- Set up the source description string --- */
1076 exec_desc(&xs
->x
, &d
);
1077 dstr_puts(&d
, " -> ");
1078 dstr_puts(&d
, t
->desc
);
1079 xs
->s
.desc
= xstrdup(d
.buf
);
1083 /* --- Create the endpoints --- */
1085 if ((ee
= t
->ops
->create(t
, xs
->s
.desc
)) == 0)
1087 if ((e
= exec_endpt(&xs
->x
, xs
->s
.desc
)) == 0) {
1093 /* --- Dispose of source and target --- */
1097 xsource_destroy(&xs
->s
);
1100 /* --- @destroy@ --- */
1102 static void xsource_destroy(source
*s
)
1104 xsource
*xs
= (xsource
*)s
;
1106 exec_destroy(&xs
->x
);
1110 /* --- Executable source operations --- */
1112 source_ops xsource_ops
= {
1114 xsource_option
, xsource_read
, xsource_attach
, xsource_destroy
1117 /*----- Exec target description -------------------------------------------*/
1119 /* --- @option@ --- */
1121 static int xtarget_option(target
*t
, scanner
*sc
)
1123 xtarget
*xt
= (xtarget
*)t
;
1124 return (exec_option(xt ?
&xt
->x
: 0, sc
));
1127 /* --- @read@ --- */
1129 static target
*xtarget_read(scanner
*sc
)
1134 if (!conf_prefix(sc
, "exec"))
1136 xt
= CREATE(xtarget
);
1137 xt
->t
.ops
= &xtarget_ops
;
1138 exec_read(&xt
->x
, sc
);
1139 exec_desc(&xt
->x
, &d
);
1140 xt
->t
.desc
= xstrdup(d
.buf
);
1145 /* --- @create@ --- */
1147 static endpt
*xtarget_create(target
*t
, const char *desc
)
1149 xtarget
*xt
= (xtarget
*)t
;
1150 endpt
*e
= exec_endpt(&xt
->x
, desc
);
1154 /* --- @destroy@ --- */
1156 static void xtarget_destroy(target
*t
)
1158 xtarget
*xt
= (xtarget
*)t
;
1160 exec_destroy(&xt
->x
);
1164 /* --- Exec target operations --- */
1166 target_ops xtarget_ops
= {
1168 xtarget_option
, xtarget_read
, 0, xtarget_create
, xtarget_destroy
1171 /*----- That's all, folks -------------------------------------------------*/