chiark / gitweb /
Fix whitespace throughout.
[fwd] / exec.c
1 /* -*-c-*-
2  *
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
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
86 typedef 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
95 typedef 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
107 typedef 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
125 typedef 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
135 typedef struct xept {
136   endpt e;
137   struct xept *next, *prev;
138   pid_t kid;
139   endpt *f;
140   char *desc;
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
152 typedef struct xdata {
153   xargs *xa;
154   xopts *xo;
155 } xdata;
156
157 /* --- Executable source block --- */
158
159 typedef struct xsource {
160   source s;
161   xdata x;
162 } xsource;
163
164 /* --- Executable target block --- */
165
166 typedef struct xtarget {
167   target t;
168   xdata x;
169 } xtarget;
170
171 /*----- Static variables --------------------------------------------------*/
172
173 static xopts exec_opts = { 1, 0, 0, 0, -1, -1, 0, &exec_opts.env };
174 static xept *xept_list;
175 static sig xept_sig;
176 static 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
184 typedef struct rlimit_ent {
185   const char *name;
186   const char *rname;
187   int r;
188   size_t off;
189 } rlimit_ent;
190
191 static 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
208 static 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
229 static 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
243 static 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
352 static 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
403 link:
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
423 static 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
448 static void xenv_destroy(xenv *xe)
449 {
450   while (xe) {
451     xenv *xxe = xe;
452     xe = xe->next;
453     xfree(xxe->name);
454     if (xxe->value)
455       xfree(xxe->value);
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
472 static void x_tidy(xargs *xa, xopts *xo)
473 {
474   xa->ref--;
475   if (!xa->ref)
476     xfree(xa);
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
489 static void xept_error(char */*p*/, size_t /*len*/, void */*v*/);
490
491 static 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
567     if (xo->gid != (gid_t)-1) {
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
581     if (xo->uid != (uid_t)-1) {
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
621 /* --- @xept_file@ --- */
622
623 static void xept_file(endpt *e, endpt *f)
624 {
625   xept *xe = (xept *)e;
626   xe->f = f;
627 }
628
629 /* --- @xept_close@ --- */
630
631 static void xept_close(endpt *e)
632 {
633   xept *xe = (xept *)e;
634   if (xe->kid == -1) {
635     if (xe->f)
636       xe->f->ops->close(xe->f);
637     x_tidy(xe->xa, xe->xo);
638     DESTROY(xe);
639     fw_dec();
640   }
641 }
642
643 /* --- @xept_destroy@ --- */
644
645 static 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
682   xfree(xe->desc);
683   if (xe->f)
684     xe->f->ops->close(xe->f);
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
700 static 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
724  *              @size_t len@ = length of the string
725  *              @void *v@ = pointer to by endpoint
726  *
727  * Returns:     ---
728  *
729  * Use:         Handles error reports from a child process.
730  */
731
732 static void xept_error(char *p, size_t len, void *v)
733 {
734   xept *xe = v;
735   if (p)
736     fw_log(-1, "[%s] pid %i: %s", xe->desc, xe->kid, p);
737   else {
738     close(xe->err.reader.fd);
739     selbuf_destroy(&xe->err);
740     xe->e.f |= XEF_CLOSE;
741     if (xe->e.f & XEF_EXIT)
742       xept_destroy(xe);
743   }
744 }
745
746 /* --- Endpoint operations --- */
747
748 static endpt_ops xept_ops = { xept_attach, xept_file, 0, xept_close };
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
761 void exec_init(void)
762 {
763 #ifdef HAVE_SETRLIMIT
764   rlimit_get(&exec_opts.xl);
765 #endif
766   sig_add(&xept_sig, SIGCHLD, xept_chld, 0);
767   sym_create(&env);
768   env_import(&env, environ);
769 }
770
771 /* --- @exec_option@ --- */
772
773 static 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
870 #ifdef HAVE_SETRLIMIT
871   if (rlimit_option(&xo->xl, sc))
872     CONF_ACCEPT;
873 #endif
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
887 static 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
907 static 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
951     /* --- Strip off the leading `[' --- *
952      *
953      * Allow various handy filename characters to be entered without quoting.
954      */
955
956     conf_undelim(sc, "=:/.", "=:/.");
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     }
966     conf_undelim(sc, 0, 0);
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
1014 static 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;
1024   xe->f = 0;
1025   xe->desc = xstrdup(desc);
1026   return (&xe->e);
1027 }
1028
1029 /* --- @exec_destroy@ --- */
1030
1031 static void exec_destroy(xdata *x)
1032 {
1033   x_tidy(x->xa, x->xo);
1034 }
1035
1036 /*----- Source definition -------------------------------------------------*/
1037
1038 /* --- @option@ --- */
1039
1040 static 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
1048 static 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
1063 static void xsource_destroy(source */*s*/);
1064
1065 static 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
1093 tidy:
1094   t->ops->destroy(t);
1095   xsource_destroy(&xs->s);
1096 }
1097
1098 /* --- @destroy@ --- */
1099
1100 static void xsource_destroy(source *s)
1101 {
1102   xsource *xs = (xsource *)s;
1103   xfree(xs->s.desc);
1104   exec_destroy(&xs->x);
1105   DESTROY(xs);
1106 }
1107
1108 /* --- Executable source operations --- */
1109
1110 source_ops xsource_ops = {
1111   "exec",
1112   xsource_option, xsource_read, xsource_attach, xsource_destroy
1113 };
1114
1115 /*----- Exec target description -------------------------------------------*/
1116
1117 /* --- @option@ --- */
1118
1119 static 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
1127 static 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
1145 static 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
1154 static void xtarget_destroy(target *t)
1155 {
1156   xtarget *xt = (xtarget *)t;
1157   xfree(xt->t.desc);
1158   exec_destroy(&xt->x);
1159   DESTROY(xt);
1160 }
1161
1162 /* --- Exec target operations --- */
1163
1164 target_ops xtarget_ops = {
1165   "exec",
1166   xtarget_option, xtarget_read, 0, xtarget_create, xtarget_destroy
1167 };
1168
1169 /*----- That's all, folks -------------------------------------------------*/