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