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