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