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 -------------------------------------------------*/