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