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