build: Actually compile the `blast' helper program.
[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 `fw' port forwarder.
11 *
12 * `fw' 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 * `fw' 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 `fw'; if not, write to the Free Software Foundation,
24 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 */
26
27 #include "fw.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
468 /* --- Fiddle with the file descriptors --- *
469 *
470 * Attach the other endpoint's descriptors to standard input and output.
471 * Attach my pipe to standard error. Mark everything as blocking and
472 * not-to-be-closed-on-exec at this end.
473 */
474
475 close(fd[0]);
476 if (dup2(in->fd, STDIN_FILENO) < 0 ||
477 dup2(out->fd, STDOUT_FILENO) < 0 ||
478 dup2(fd[1], STDERR_FILENO) < 0) {
479 moan("couldn't manipulate file descriptors: %s", strerror(errno));
480 _exit(1);
481 }
482
483 if (in->fd > 2)
484 close(in->fd);
485 if (out->fd > 2)
486 close(out->fd);
487
488 fdflags(STDIN_FILENO, O_NONBLOCK, 0, FD_CLOEXEC, 0);
489 fdflags(STDOUT_FILENO, O_NONBLOCK, 0, FD_CLOEXEC, 0);
490 fdflags(STDERR_FILENO, O_NONBLOCK, 0, FD_CLOEXEC, 0);
491
492 /* --- First of all set the @chroot@ prison --- */
493
494 if (xo->root && chroot(xo->root)) {
495 moan("couldn't set `%s' as filesystem root: %s",
496 xo->root, strerror(errno));
497 _exit(1);
498 }
499
500 /* --- Now set the current directory --- */
501
502 if (xo->dir ? chdir(xo->dir) : xo->root ? chdir("/") : 0) {
503 moan("couldn't set `%s' as current directory: %s",
504 xo->dir ? xo->dir : "/", strerror(errno));
505 _exit(1);
506 }
507
508 /* --- Set the resource limits --- */
509
510 #ifdef HAVE_SETRLIMIT
511 rlimit_set(&xo->xl);
512 #endif
513
514 /* --- Set group id --- */
515
516 if (xo->gid != (gid_t)-1) {
517 if (setgid(xo->gid)) {
518 moan("couldn't set gid %i: %s", xo->gid, strerror(errno));
519 _exit(1);
520 }
521 #ifdef HAVE_SETGROUPS
522 if (setgroups(1, &xo->gid))
523 moan("warning: couldn't set group list to %i: %s", xo->gid,
524 strerror(errno));
525 #endif
526 }
527
528 /* --- Set uid --- */
529
530 if (xo->uid != (uid_t)-1) {
531 if (setuid(xo->uid)) {
532 moan("couldn't set uid %i: %s", xo->uid, strerror(errno));
533 _exit(1);
534 }
535 }
536
537 /* --- Play with signal dispositions --- */
538
539 signal(SIGPIPE, SIG_DFL);
540
541 /* --- Fiddle with the environment --- */
542
543 xenv_apply(exec_opts.env);
544 xenv_apply(xe->xo->env);
545 environ = env_export(&env);
546
547 /* --- Run the program --- */
548
549 execvp(xe->xa->file, xe->xa->argv);
550 moan("couldn't execute `%s': %s", xe->xa->file, strerror(errno));
551 _exit(127);
552 }
553
554 /* --- The child's done; see to the parent --- */
555
556 xe->kid = kid;
557 selbuf_init(&xe->err, sel, fd[0], xept_error, xe);
558 close(fd[1]);
559 xe->next = xept_list;
560 xe->prev = 0;
561 if (xept_list)
562 xept_list->prev = xe;
563 xept_list = xe;
564 if (!(xe->xo->f & XF_NOLOG))
565 fw_log(-1, "[%s] started with pid %i", xe->desc, kid);
566 fw_inc();
567 return;
568 }
569
570 /* --- @xept_file@ --- */
571
572 static void xept_file(endpt *e, endpt *f)
573 {
574 xept *xe = (xept *)e;
575 xe->f = f;
576 }
577
578 /* --- @xept_close@ --- */
579
580 static void xept_close(endpt *e)
581 {
582 xept *xe = (xept *)e;
583 if (xe->kid == -1) {
584 if (xe->f)
585 xe->f->ops->close(xe->f);
586 x_tidy(xe->xa, xe->xo);
587 DESTROY(xe);
588 fw_dec();
589 }
590 }
591
592 /* --- @xept_destroy@ --- */
593
594 static void xept_destroy(xept *xe)
595 {
596 /* --- First emit the news about the process --- */
597
598 if (xe->xo->f & XF_NOLOG)
599 /* Nothin' doin' */;
600 else if (WIFEXITED(xe->st)) {
601 if (WEXITSTATUS(xe->st) == 0)
602 fw_log(-1, "[%s] pid %i exited successfully", xe->desc, xe->kid);
603 else {
604 fw_log(-1, "[%s] pid %i failed: status %i",
605 xe->desc, xe->kid, WEXITSTATUS(xe->st));
606 }
607 } else if (WIFSIGNALED(xe->st)) {
608 const char *s;
609 #ifdef HAVE_STRSIGNAL
610 s = strsignal(WTERMSIG(xe->st));
611 #elif HAVE__SYS_SIGLIST
612 s = _sys_siglist[WTERMSIG(xe->st)];
613 #else
614 char buf[32];
615 sprintf(buf, "signal %i", WTERMSIG(xe->st));
616 s = buf;
617 #endif
618 fw_log(-1, "[%s] pid %i failed: %s", xe->desc, xe->kid, s);
619 } else
620 fw_log(-1, "[%s] pid %i failed: unrecognized status", xe->desc, xe->kid);
621
622 /* --- Free up the parent-side resources --- */
623
624 if (xe->next)
625 xe->next->prev = xe->prev;
626 if (xe->prev)
627 xe->prev->next = xe->next;
628 else
629 xept_list = xe->next;
630
631 xfree(xe->desc);
632 if (xe->f)
633 xe->f->ops->close(xe->f);
634 x_tidy(xe->xa, xe->xo);
635 fw_dec();
636 DESTROY(xe);
637 }
638
639 /* --- @xept_chld@ --- *
640 *
641 * Arguments: @int n@ = signal number
642 * @void *p@ = uninteresting pointer
643 *
644 * Returns: ---
645 *
646 * Use: Deals with child death situations.
647 */
648
649 static void xept_chld(int n, void *p)
650 {
651 pid_t kid;
652 int st;
653
654 while ((kid = waitpid(-1, &st, WNOHANG)) > 0) {
655 xept *xe = xept_list;
656 while (xe) {
657 xept *xxe = xe;
658 xe = xe->next;
659 if (kid == xxe->kid) {
660 xxe->st = st;
661 xxe->e.f |= XEF_EXIT;
662 if (xxe->e.f & XEF_CLOSE)
663 xept_destroy(xxe);
664 break;
665 }
666 }
667 }
668 }
669
670 /* --- @xept_error@ --- *
671 *
672 * Arguments: @char *p@ = pointer to string read from stderr
673 * @size_t len@ = length of the string
674 * @void *v@ = pointer to by endpoint
675 *
676 * Returns: ---
677 *
678 * Use: Handles error reports from a child process.
679 */
680
681 static void xept_error(char *p, size_t len, void *v)
682 {
683 xept *xe = v;
684 if (p)
685 fw_log(-1, "[%s] pid %i: %s", xe->desc, xe->kid, p);
686 else {
687 close(xe->err.reader.fd);
688 selbuf_destroy(&xe->err);
689 xe->e.f |= XEF_CLOSE;
690 if (xe->e.f & XEF_EXIT)
691 xept_destroy(xe);
692 }
693 }
694
695 /* --- Endpoint operations --- */
696
697 static endpt_ops xept_ops = { xept_attach, xept_file, 0, xept_close };
698
699 /*----- General operations on sources and targets -------------------------*/
700
701 /* --- @exec_init@ --- *
702 *
703 * Arguments: ---
704 *
705 * Returns: ---
706 *
707 * Use: Initializes the executable problem source and target.
708 */
709
710 void exec_init(void)
711 {
712 #ifdef HAVE_SETRLIMIT
713 rlimit_get(&exec_opts.xl);
714 #endif
715 sig_add(&xept_sig, SIGCHLD, xept_chld, 0);
716 sym_create(&env);
717 env_import(&env, environ);
718 }
719
720 /* --- @exec_option@ --- */
721
722 static int exec_option(xdata *x, scanner *sc)
723 {
724 xopts *xo = x ? x->xo : &exec_opts;
725
726 CONF_BEGIN(sc, "exec", "executable");
727
728 /* --- Logging settings --- */
729
730 if (strcmp(sc->d.buf, "logging") == 0 ||
731 strcmp(sc->d.buf, "log") == 0) {
732 token(sc);
733 if (sc->t == '=')
734 token(sc);
735 if (conf_enum(sc, "no,yes", ENUM_ABBREV, "logging status"))
736 xo->f &= ~XF_NOLOG;
737 else
738 xo->f |= XF_NOLOG;
739 CONF_ACCEPT;
740 }
741
742 /* --- Current directory setting --- *
743 *
744 * Lots of possibilities to guard against possible brainoes.
745 */
746
747 if (strcmp(sc->d.buf, "dir") == 0 ||
748 strcmp(sc->d.buf, "cd") == 0 ||
749 strcmp(sc->d.buf, "chdir") == 0 ||
750 strcmp(sc->d.buf, "cwd") == 0) {
751 dstr d = DSTR_INIT;
752 token(sc);
753 if (sc->t == '=')
754 token(sc);
755 conf_name(sc, '/', &d);
756 xo->dir = xstrdup(d.buf);
757 dstr_destroy(&d);
758 CONF_ACCEPT;
759 }
760
761 /* --- Set a chroot prison --- */
762
763 if (strcmp(sc->d.buf, "root") == 0 ||
764 strcmp(sc->d.buf, "chroot") == 0) {
765 dstr d = DSTR_INIT;
766 token(sc);
767 if (sc->t == '=')
768 token(sc);
769 conf_name(sc, '/', &d);
770 xo->root = xstrdup(d.buf);
771 dstr_destroy(&d);
772 CONF_ACCEPT;
773 }
774
775 /* --- Set the target user id --- */
776
777 if (strcmp(sc->d.buf, "uid") == 0 ||
778 strcmp(sc->d.buf, "user") == 0) {
779 token(sc);
780 if (sc->t == '=')
781 token(sc);
782 if (sc->t != CTOK_WORD)
783 error(sc, "parse error, expected user name or uid");
784 if (isdigit((unsigned char)*sc->d.buf))
785 xo->uid = atoi(sc->d.buf);
786 else {
787 struct passwd *pw = getpwnam(sc->d.buf);
788 if (!pw)
789 error(sc, "unknown user name `%s'", sc->d.buf);
790 xo->uid = pw->pw_uid;
791 }
792 token(sc);
793 CONF_ACCEPT;
794 }
795
796 /* --- Set the target group id --- */
797
798 if (strcmp(sc->d.buf, "gid") == 0 ||
799 strcmp(sc->d.buf, "group") == 0) {
800 token(sc);
801 if (sc->t == '=')
802 token(sc);
803 if (sc->t != CTOK_WORD)
804 error(sc, "parse error, expected group name or gid");
805 if (isdigit((unsigned char)*sc->d.buf))
806 xo->gid = atoi(sc->d.buf);
807 else {
808 struct group *gr = getgrnam(sc->d.buf);
809 if (!gr)
810 error(sc, "unknown user name `%s'", sc->d.buf);
811 xo->gid = gr->gr_gid;
812 }
813 token(sc);
814 CONF_ACCEPT;
815 }
816
817 /* --- Now try resource limit settings --- */
818
819 #ifdef HAVE_SETRLIMIT
820 if (rlimit_option(&xo->xl, sc))
821 CONF_ACCEPT;
822 #endif
823
824 /* --- And then environment settings --- */
825
826 if (xenv_option(xo, sc))
827 CONF_ACCEPT;
828
829 /* --- Nothing found --- */
830
831 CONF_END;
832 }
833
834 /* --- @exec_desc@ --- */
835
836 static void exec_desc(xdata *x, dstr *d)
837 {
838 char **p;
839 char sep = '[';
840 dstr_puts(d, "exec ");
841 if (strcmp(x->xa->file, x->xa->argv[0]) != 0) {
842 dstr_puts(d, x->xa->file);
843 dstr_putc(d, ' ');
844 }
845 for (p = x->xa->argv; *p; p++) {
846 dstr_putc(d, sep);
847 dstr_puts(d, *p);
848 sep = ' ';
849 }
850 dstr_putc(d, ']');
851 dstr_putz(d);
852 }
853
854 /* --- @exec_read@ --- */
855
856 static void exec_read(xdata *x, scanner *sc)
857 {
858 size_t base = 0;
859 dstr d = DSTR_INIT;
860 xargs *xa;
861
862 /* --- Read the first word --- *
863 *
864 * This is either a shell command or the actual program to run.
865 */
866
867 if (sc->t == CTOK_WORD) {
868 dstr_putd(&d, &sc->d); d.len++;
869 base = d.len;
870 token(sc);
871 }
872
873 /* --- See if there's a list of arguments --- *
874 *
875 * If not, then the thing I saw was a shell command, so build the proper
876 * arguments for that.
877 */
878
879 if (sc->t != '[') {
880 char *p;
881 if (!base)
882 error(sc, "parse error, expected shell command or argument list");
883 xa = xmalloc(XARGS_SZ(3) + 8 + 3 + d.len);
884 p = (char *)(xa->argv + 4);
885 xa->ref = 1;
886 xa->file = p;
887 xa->argv[0] = p; memcpy(p, "/bin/sh", 8); p += 8;
888 xa->argv[1] = p; memcpy(p, "-c", 3); p += 3;
889 xa->argv[2] = p; memcpy(p, d.buf, d.len); p += d.len;
890 xa->argv[3] = 0;
891 }
892
893 /* --- Snarf in a list of arguments --- */
894
895 else {
896 int argc = 0;
897 char *p, *q;
898 char **v;
899
900 /* --- Strip off the leading `[' --- *
901 *
902 * Allow various handy filename characters to be entered without quoting.
903 */
904
905 conf_undelim(sc, "=:/.", "=:/.");
906 token(sc);
907
908 /* --- Read a sequence of arguments --- */
909
910 while (sc->t == CTOK_WORD) {
911 dstr_putd(&d, &sc->d); d.len++;
912 token(sc);
913 argc++;
914 }
915 conf_undelim(sc, 0, 0);
916
917 /* --- Expect the closing `]' --- */
918
919 if (sc->t != ']')
920 error(sc, "parse error, missing `]'");
921 token(sc);
922
923 /* --- If there are no arguments, whinge --- */
924
925 if (!argc)
926 error(sc, "must specify at least one argument");
927
928 /* --- Allocate a lump of memory for the array --- */
929
930 xa = xmalloc(XARGS_SZ(argc) + d.len);
931 xa->ref = 1;
932 v = xa->argv;
933 p = (char *)(v + argc + 1);
934 memcpy(p, d.buf, d.len);
935 q = p + d.len;
936 xa->file = p;
937 p += base;
938
939 /* --- Start dumping addresses into the @argv@ array --- */
940
941 for (;;) {
942 *v++ = p;
943 while (*p++ && p < q)
944 ;
945 if (p >= q)
946 break;
947 }
948 *v++ = 0;
949 }
950
951 /* --- Do some other setting up --- */
952
953 dstr_destroy(&d);
954 x->xa = xa;
955 x->xo = CREATE(xopts);
956 *x->xo = exec_opts;
957 x->xo->ref = 1;
958 return;
959 }
960
961 /* --- @exec_endpt@ --- */
962
963 static endpt *exec_endpt(xdata *x, const char *desc)
964 {
965 xept *xe = CREATE(xept);
966 xe->e.ops = &xept_ops;
967 xe->e.other = 0;
968 xe->e.t = 0;
969 xe->e.f = 0;
970 xe->xa = x->xa; xe->xa->ref++;
971 xe->xo = x->xo; xe->xo->ref++;
972 xe->kid = -1;
973 xe->f = 0;
974 xe->desc = xstrdup(desc);
975 return (&xe->e);
976 }
977
978 /* --- @exec_destroy@ --- */
979
980 static void exec_destroy(xdata *x)
981 {
982 x_tidy(x->xa, x->xo);
983 }
984
985 /*----- Source definition -------------------------------------------------*/
986
987 /* --- @option@ --- */
988
989 static int xsource_option(source *s, scanner *sc)
990 {
991 xsource *xs = (xsource *)s;
992 return (exec_option(xs ? &xs->x : 0, sc));
993 }
994
995 /* --- @read@ --- */
996
997 static source *xsource_read(scanner *sc)
998 {
999 xsource *xs;
1000
1001 if (!conf_prefix(sc, "exec"))
1002 return (0);
1003 xs = CREATE(xsource);
1004 xs->s.ops = &xsource_ops;
1005 xs->s.desc = 0;
1006 exec_read(&xs->x, sc);
1007 return (&xs->s);
1008 }
1009
1010 /* --- @attach@ --- */
1011
1012 static void xsource_destroy(source */*s*/);
1013
1014 static void xsource_attach(source *s, scanner *sc, target *t)
1015 {
1016 xsource *xs = (xsource *)s;
1017 endpt *e, *ee;
1018
1019 /* --- Set up the source description string --- */
1020
1021 {
1022 dstr d = DSTR_INIT;
1023 exec_desc(&xs->x, &d);
1024 dstr_puts(&d, " -> ");
1025 dstr_puts(&d, t->desc);
1026 xs->s.desc = xstrdup(d.buf);
1027 dstr_destroy(&d);
1028 }
1029
1030 /* --- Create the endpoints --- */
1031
1032 if ((ee = t->ops->create(t, xs->s.desc)) == 0)
1033 goto tidy;
1034 if ((e = exec_endpt(&xs->x, xs->s.desc)) == 0) {
1035 ee->ops->close(ee);
1036 goto tidy;
1037 }
1038 endpt_join(e, ee);
1039
1040 /* --- Dispose of source and target --- */
1041
1042 tidy:
1043 t->ops->destroy(t);
1044 xsource_destroy(&xs->s);
1045 }
1046
1047 /* --- @destroy@ --- */
1048
1049 static void xsource_destroy(source *s)
1050 {
1051 xsource *xs = (xsource *)s;
1052 xfree(xs->s.desc);
1053 exec_destroy(&xs->x);
1054 DESTROY(xs);
1055 }
1056
1057 /* --- Executable source operations --- */
1058
1059 source_ops xsource_ops = {
1060 "exec",
1061 xsource_option, xsource_read, xsource_attach, xsource_destroy
1062 };
1063
1064 /*----- Exec target description -------------------------------------------*/
1065
1066 /* --- @option@ --- */
1067
1068 static int xtarget_option(target *t, scanner *sc)
1069 {
1070 xtarget *xt = (xtarget *)t;
1071 return (exec_option(xt ? &xt->x : 0, sc));
1072 }
1073
1074 /* --- @read@ --- */
1075
1076 static target *xtarget_read(scanner *sc)
1077 {
1078 xtarget *xt;
1079 dstr d = DSTR_INIT;
1080
1081 if (!conf_prefix(sc, "exec"))
1082 return (0);
1083 xt = CREATE(xtarget);
1084 xt->t.ops = &xtarget_ops;
1085 exec_read(&xt->x, sc);
1086 exec_desc(&xt->x, &d);
1087 xt->t.desc = xstrdup(d.buf);
1088 dstr_destroy(&d);
1089 return (&xt->t);
1090 }
1091
1092 /* --- @create@ --- */
1093
1094 static endpt *xtarget_create(target *t, const char *desc)
1095 {
1096 xtarget *xt = (xtarget *)t;
1097 endpt *e = exec_endpt(&xt->x, desc);
1098 return (e);
1099 }
1100
1101 /* --- @destroy@ --- */
1102
1103 static void xtarget_destroy(target *t)
1104 {
1105 xtarget *xt = (xtarget *)t;
1106 xfree(xt->t.desc);
1107 exec_destroy(&xt->x);
1108 DESTROY(xt);
1109 }
1110
1111 /* --- Exec target operations --- */
1112
1113 target_ops xtarget_ops = {
1114 "exec",
1115 xtarget_option, xtarget_read, 0, xtarget_create, xtarget_destroy
1116 };
1117
1118 /*----- That's all, folks -------------------------------------------------*/