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