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