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