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