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