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