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