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