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