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