chiark / gitweb /
@@ -2,6 +2,7 @@
[userv-utils.git] / ipif / service.c
1 /*
2  * userv service (or standalone program) for per-user IP subranges.
3  *
4  * When invoked appropriately, it creates a point-to-point network
5  * interface with specified parameters.  It arranges for packets sent out
6  * via that interface by the kernel to appear on its own stdout in SLIP or
7  * CSLIP encoding, and packets injected into its own stdin to be given to
8  * the kernel as if received on that interface.  Optionally, additional
9  * routes can be set up to arrange for traffic for other address ranges to
10  * be routed through the new interface.
11  *
12  * This is the service program, which is invoked as root from userv (or may
13  * be invoked firectly).
14  *
15  * Its arguments are supposed to be, in order, as follows:
16  *
17  *  The first two arguments are usually supplied by the userv
18  *  configuration.  See the file `ipif/ipif' in the source tree, which
19  *  is installed in /etc/userv/services.d/ipif by `make install':
20  *
21  *  <config>
22  *
23  *      Specifies address ranges and gids which own them.  The default
24  *      configuration supplies /etc/userv/ipif-networks, which is then read
25  *      for a list of entries, one per line.
26  *
27  *  --
28  *      Serves to separate the user-supplied and therefore untrusted
29  *      arguments from the trusted first argument.
30  *
31  *  The remaining arguments are supplied by the (untrusted) caller:
32  *
33  *  <local-addr>,<peer-addr>,<mtu>,<proto>
34  *
35  *      As for slattach.  Supported protocols are slip, cslip, and
36  *      adaptive.  Alternatively, set to `debug' to print debugging info
37  *      and exit.  <local-addr> is address of the interface to be created
38  *      on the local system; <peer-addr> is the address of the
39  *      point-to-point peer.  They must be actual addresses (not
40  *      hostnames).
41  *
42  *  <prefix>/<mask>,<prefix>/<mask>,...
43  *
44  *      List of additional routes to add for this interface.  routes will
45  *      be set up on the local system arranging for packets for those
46  *      networks to be sent via the created interface.  <prefix> must be an
47  *      IPv4 address, and mask must be an integer (dotted-quad masks are
48  *      not supported).  If no additional routes are to be set up, use `-'
49  *      or supply an empty argument.
50  *
51  * Each <config> item - whether a line file such as
52  * /etc/userv/ipif-networks, or supplied on the service program
53  * command line - is one of:
54  *
55  *   /<config-file-name>
56  *   ./<config-file-name>
57  *   ../<config-file-name>
58  *
59  *      Reads a file which contains lines which are each <config>
60  *      items.
61  *
62  *   <gid>,[=][-|+]<prefix>/<len>(-|+<prefix>/<len>...)[,<junk>]
63  *
64  *      Indicates that <gid> may allocate addresses in the relevant address
65  *      range (<junk> is ignored).  <gid> must be numeric.  To specify a
66  *      single host address, you must specify a mask of /32.  If `=' is
67  *      specified then the specific subrange is only allowed for the local
68  *      endpoint address, but not for remote addresses.
69  *
70  *      More than one range may be given, with each range prefixed
71  *      by + or -.  In this case each address range in the rule will
72  *      scanned in order, and the first range in the rule that matches
73  *      any desired rule will count: if that first matching range is
74  *      prefixed by `+' (or nothing) then the rule applies, if it
75  *      is prefixed by `-' (or nothing matches), the rule does not.
76  *
77  *   *
78  *      Means that anything is to be permitted.  This should not appear in
79  *      /etc/userv/ipif-networks, as that would permit any user on the
80  *      system to create any interfaces with any addresses and routes
81  *      attached.  It is provided so that root can usefully invoke the ipif
82  *      service program directly (not via userv), without needing to set up
83  *      permissions in /etc/userv/ipif-networks.
84  *
85  *   #...
86  *
87  *      Comment.  Blank lines are also ignored.
88  *
89  *   NB: Permission is granted if _any_ config entry matches the request.
90  *
91  * The service program should be run from userv with no-disconnect-hup.
92  */
93 /*
94  * Copyright (C) 1999-2000 Ian Jackson
95  *
96  * This is free software; you can redistribute it and/or modify it
97  * under the terms of the GNU General Public License as published by
98  * the Free Software Foundation; either version 2 of the License, or
99  * (at your option) any later version.
100  *
101  * This program is distributed in the hope that it will be useful, but
102  * WITHOUT ANY WARRANTY; without even the implied warranty of
103  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
104  * General Public License for more details.
105  *
106  * You should have received a copy of the GNU General Public License
107  * along with userv-utils; if not, write to the Free Software
108  * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
109  *
110  * $Id$
111  */
112
113 #include <stdio.h>
114 #include <string.h>
115 #include <stdlib.h>
116 #include <assert.h>
117 #include <errno.h>
118 #include <stdarg.h>
119 #include <ctype.h>
120 #include <limits.h>
121 #include <signal.h>
122 #include <unistd.h>
123
124 #include <sys/types.h>
125 #include <sys/wait.h>
126 #include <sys/stat.h>
127
128 #define NARGS 4
129 #define MAXEXROUTES 50
130 #define ATXTLEN 16
131
132 static const unsigned long gidmaxval= (unsigned long)((gid_t)-2);
133 static const char *const protos_ok[]= { "slip", "cslip", "adaptive", 0 };
134 static const int signals[]= { SIGHUP, SIGINT, SIGTERM, 0 };
135
136 static const char *configstr, *proto;
137 static unsigned long localaddr, peeraddr, mtu;
138 static int localpming, peerpming;
139 static int localallow, peerallow, allallow;
140 static int nexroutes;
141 static struct exroute {
142   unsigned long prefix, mask;
143   int allow, pming;
144   char prefixtxt[ATXTLEN], masktxt[ATXTLEN];
145 } exroutes[MAXEXROUTES];
146
147 static char localtxt[ATXTLEN];
148 static char peertxt[ATXTLEN];
149
150 static struct pplace {
151   struct pplace *parent;
152   const char *filename;
153   int lineno;
154 } *cpplace;
155
156
157 static int slpipe[2], ptmaster, undoslattach;
158 static const char *ifname;
159 static const char *ptyname;
160
161 #define NPIDS 4
162
163 static union {
164   struct { pid_t sl, cout, cin, task; } byname;
165   pid_t bynumber[NPIDS];
166 } pids;
167 sigset_t emptyset, fullset;
168
169
170 static int cleantask(void) {
171   pid_t pid;
172
173   pid= fork();
174   if (!pid) return 1;
175   if (pid == (pid_t)-1)
176     perror("userv-ipif: fork for undo slattach failed - cannot clean up properly");
177   return 0;
178 }
179
180 static void terminate(int estatus) {
181   int i, status;
182   pid_t pid;
183   
184   for (i=0; i<NPIDS; i++)
185     if (pids.bynumber[i]) kill(pids.bynumber[i], SIGTERM);
186
187   if (undoslattach) {
188     if (cleantask()) {
189       execlp("slattach", "slattach", "-p", "tty", ptyname, (char*)0);
190       perror("userv-ipif: exec slattach for undo slattach failed");
191       exit(-1);
192     }
193     if (ifname && cleantask()) {
194       execlp("ifconfig", "ifconfig", ifname, "down", (char*)0);
195       perror("userv-ipif: exec ifconfig for undo ifconfig failed");
196       exit(-1);
197     }
198   }
199
200   for (;;) {
201     pid= waitpid(-1,&status,0);
202     if (pid == (pid_t)-1) break;
203   }
204   exit(estatus);
205 }
206
207
208 static void fatal(const char *fmt, ...)
209      __attribute__((format(printf,1,2)));
210 static void fatal(const char *fmt, ...) {
211   va_list al;
212   va_start(al,fmt);
213
214   fputs("userv-ipif service: fatal error: ",stderr);
215   vfprintf(stderr, fmt, al);
216   putc('\n',stderr);
217   terminate(8);
218 }
219   
220 static void sysfatal(const char *fmt, ...)
221      __attribute__((format(printf,1,2)));
222 static void sysfatal(const char *fmt, ...) {
223   va_list al;
224   int e;
225
226   e= errno;
227   va_start(al,fmt);
228
229   fputs("userv-ipif service: fatal system error: ",stderr);
230   vfprintf(stderr, fmt, al);
231   fprintf(stderr,": %s\n", strerror(e));
232   terminate(12);
233 }
234
235
236 static void badusage(const char *fmt, ...)
237      __attribute__((format(printf,1,2)));
238 static void badusage(const char *fmt, ...) {
239   va_list al;
240   struct pplace *cpp;
241   
242   if (cpplace) {
243     fprintf(stderr,
244             "userv-ipif service: %s:%d: ",
245             cpplace->filename, cpplace->lineno);
246   } else {
247     fputs("userv-ipif service: invalid usage: ",stderr);
248   }
249   va_start(al,fmt);
250   vfprintf(stderr, fmt, al);
251   putc('\n',stderr);
252
253   if (cpplace) {
254     for (cpp=cpplace->parent; cpp; cpp=cpp->parent) {
255       fprintf(stderr,
256               "userv-ipif service: %s:%d: ... in file included from here\n",
257               cpp->filename, cpp->lineno);
258     }
259   }
260   terminate(16);
261 }
262
263 static char *ip2txt(unsigned long addr, char *buf) {
264   sprintf(buf, "%lu.%lu.%lu.%lu",
265           (addr>>24) & 0x0ff,
266           (addr>>16) & 0x0ff,
267           (addr>>8) & 0x0ff,
268           (addr) & 0x0ff);
269   return buf;
270 }
271
272 static unsigned long eat_number(const char **argp, const char *what,
273                                 unsigned long min, unsigned long max,
274                                 const char *endchars, int *endchar_r) {
275   /* If !endchars then the endchar must be a nul, otherwise it may be
276    * a nul (resulting in *argp set to 0) or something else (*argp set
277    * to point to after delim, *endchar_r set to delim).
278    * *endchar_r may be 0.
279    */
280   unsigned long rv;
281   char *ep;
282   int endchar;
283
284   if (!*argp) { badusage("missing number %s",what); }
285   rv= strtoul(*argp,&ep,0);
286   if ((endchar= *ep)) {
287     if (!endchars) badusage("junk after number %s",what);
288     if (!strchr(endchars,endchar))
289       badusage("invalid character or delimiter `%c' in or after number, %s:"
290                " expected %s (or none?)", endchar,what,endchars);
291     *argp= ep+1;
292   } else {
293     *argp= 0;
294   }
295   if (endchar_r) *endchar_r= endchar;
296   if (rv < min || rv > max) badusage("number %s value %lu out of range %lu..%lu",
297                                      what, rv, min, max);
298   return rv;
299 }
300
301 static int addrnet_overlap(unsigned long p1, unsigned long m1,
302                            unsigned long p2, unsigned long m2) {
303   unsigned long mask;
304
305   mask= m1&m2;
306   return (p1 & mask) == (p2 & mask);
307 }
308
309 static void addrnet_mustdiffer(const char *w1, unsigned long p1, unsigned long m1,
310                                const char *w2, unsigned long p2, unsigned long m2) {
311   if (!addrnet_overlap(p1,m1,p2,m2)) return;
312   badusage("%s %08lx/%08lx overlaps/clashes with %s %08lx/%08lx",
313            w1,p1,m1, w2,p2,m2);
314 }
315   
316 static unsigned long eat_addr(const char **argp, const char *what,
317                               const char *endchars, int *endchar_r) {
318   char whatbuf[100];
319   unsigned long rv;
320   int i;
321
322   for (rv=0, i=0;
323        i<4;
324        i++) {
325     rv <<= 8;
326     sprintf(whatbuf,"%s byte #%d",what,i);
327     rv |= eat_number(argp,whatbuf, 0,255, i<3 ? "." : endchars, endchar_r);
328   }
329
330   return rv;
331 }
332
333 static void eat_prefixmask(const char **argp, const char *what,
334                            const char *endchars, int *endchar_r,
335                            unsigned long *prefix_r, unsigned long *mask_r, int *len_r) {
336   /* mask_r and len_r may be 0 */
337   char whatbuf[100];
338   int len;
339   unsigned long prefix, mask;
340
341   prefix= eat_addr(argp,what, "/",0);
342   sprintf(whatbuf,"%s length",what);
343   len= eat_number(argp,whatbuf, 0,32, endchars,endchar_r);
344
345   mask= len ? (~0UL << (32-len)) : 0UL;
346   if (prefix & ~mask) badusage("%s prefix %08lx not fully contained in mask %08lx",
347                                what,prefix,mask);
348   *prefix_r= prefix;
349   if (mask_r) *mask_r= mask;
350   if (len_r) *len_r= len;
351 }
352
353 static int addrnet_isin(unsigned long prefix, unsigned long mask,
354                         unsigned long mprefix, unsigned long mmask) {
355   return  !(~mask & mmask)  &&  (prefix & mmask) == mprefix;
356 }
357
358 /* Totally hideous algorithm for parsing the config file lines.
359  * For each config file line, we first see if its gid applies.  If not
360  * we skip it.  Otherwise, we do
361  *  permit_begin
362  *     which sets <foo>pming to 1
363  * for each range.  <foo>pming may be 0 if we've determined that
364  * this line does not apply to <foo>.
365  *  permit_range
366  *     which calls permit_range_thing for each <foo>
367  *        which checks to see if <foo> is inside the relevant
368  *        range (for +) or overlaps it (for -) and updates
369  *        <foo>allow and <foo>pming.
370  */
371
372 static void permit_begin(void) {
373   int i;
374   
375   localpming= peerpming= 1;
376   for (i=0; i<nexroutes; i++) exroutes[i].pming= 1;
377 }
378
379 static void permit_range_thing(unsigned long tprefix, unsigned long tmask,
380                                const char *what, int *tallow, int *tpming,
381                                unsigned long pprefix, unsigned long pmask,
382                                int plus, int *any) {
383   if (plus) {
384     if (!addrnet_isin(tprefix,tmask, pprefix,pmask)) return;
385     if (*tpming) *tallow= 1;
386   } else {
387     if (!addrnet_overlap(tprefix,tmask, pprefix,pmask)) return;
388     *tpming= 0;
389   }
390   if (!proto) printf(" %c%s", plus?'+':'-', what);
391   *any= 1;
392 }
393
394 static void permit_range(unsigned long prefix, unsigned long mask,
395                          int plus, int localonly) {
396   int i, any;
397   char idbuf[40];
398   
399   assert(!(prefix & ~mask));
400   any= 0;
401
402   permit_range_thing(localaddr,~0UL,"local", &localallow,&localpming,
403                      prefix,mask, plus,&any);
404
405   if (!localonly) {
406     permit_range_thing(peeraddr,~0UL, "peer-addr", &peerallow,&peerpming,
407                        prefix,mask, plus,&any);
408     for (i=0; i<nexroutes; i++) {
409       sprintf(idbuf,"route#%d",i);
410       permit_range_thing(exroutes[i].prefix,exroutes[i].mask, idbuf,
411                          &exroutes[i].allow,&exroutes[i].pming,
412                          prefix,mask, plus,&any);
413     }
414   }
415   if (!proto)
416     if (!any) fputs(" nothing",stdout);
417 }
418
419 static void pconfig(const char *configstr, int truncated);
420
421 static void pfile(const char *filename) {
422   FILE *file;
423   char buf[PATH_MAX];
424   int l, truncated, c;
425   struct pplace npp, *cpp;
426
427   for (cpp=cpplace; cpp; cpp=cpp->parent) {
428     if (!strcmp(cpp->filename,filename))
429       badusage("recursive configuration file `%s'",filename);
430   }
431
432   file= fopen(filename,"r");
433   if (!file)
434     badusage("cannot open configuration file `%s': %s", filename, strerror(errno));
435
436   if (!proto) printf("config file `%s':\n",filename);
437
438   npp.parent= cpplace;
439   npp.filename= filename;
440   npp.lineno= 0;
441   cpplace= &npp;
442
443   while (fgets(buf, sizeof(buf), file)) {
444     npp.lineno++;
445     l= strlen(buf);
446     if (!l) continue;
447
448     truncated= (buf[l-1] != '\n');
449     while (l>0 && isspace((unsigned char) buf[l-1])) l--;
450     if (!l) continue;
451     buf[l]= 0;
452
453     if (truncated) {
454       while ((c= getc(file)) != EOF && c != '\n');
455       if (c == EOF) break;
456     }
457
458     pconfig(buf,truncated);
459   }
460   if (ferror(file))
461     badusage("failed while reading configuration file: %s", strerror(errno));
462
463   cpplace= npp.parent;
464 }
465
466 static void pconfig(const char *configstr, int truncated) {
467   unsigned long fgid, tgid, pprefix, pmask;
468   int plen, localonly, plus, rangeix, delim;
469   char ptxt[ATXTLEN];
470   char whattxt[100];
471   const char *gidlist;
472   
473   switch (configstr[0]) {
474   case '*':
475     permit_begin();
476     permit_range(0UL,0UL,1,0);
477     return;
478     
479   case '#':
480     return;
481     
482   case '/': case '.':
483     if (truncated) badusage("filename too long (`%.100s...')",configstr);
484     pfile(configstr);
485     return;
486     
487   default:
488     if (!isdigit((unsigned char)configstr[0]))
489       badusage("unknown configuration directive");
490     
491     fgid= eat_number(&configstr,"gid", 0,gidmaxval, ",",0);
492
493     if (!proto) printf(" %5lu", fgid);
494
495     gidlist= getenv("USERV_GID");
496     if (!gidlist) fatal("USERV_GID not set");
497     for (;;) {
498       if (!gidlist) {
499         if (!proto) printf(" no matching gid\n");
500         return;
501       }
502       tgid= eat_number(&gidlist,"userv-gid", 0,gidmaxval, " ",0);
503       if (tgid == fgid) break;
504     }
505
506     if (configstr[0] == '=') {
507       localonly= 1;
508       configstr++;
509     } else {
510       localonly= 0;
511     }
512
513     permit_begin();
514
515     rangeix= 0;
516     plus= 1;
517     switch (configstr[0]) {
518     case '-': plus= 0;     /* fall through */
519     case '+': configstr++;
520     default:;
521     }
522
523     for (;;) {
524       sprintf(whattxt, "%s-prefix#%d",
525               plus ? "permitted" : "notpermitted",
526               rangeix);
527       eat_prefixmask(&configstr,whattxt, ",+-",&delim,
528                      &pprefix,&pmask,&plen);
529       if (!configstr && truncated)
530         badusage("gid,prefix/len,... spec too long");
531
532       if (!proto)
533         printf("  %c%s/%d:", plus?'+':'-',ip2txt(pprefix,ptxt), plen);
534
535       permit_range(pprefix,pmask,plus,localonly);
536       if (delim==',') break;
537
538       plus= delim=='-' ? 0 : 1;
539       rangeix++;
540     }
541
542     putchar('\n');
543     return;
544   }
545 }
546
547 static void checkallow(int allow, const char *what,
548                        const char *prefixtxt, const char *masktxt) {
549   if (allow) return;
550   fprintf(stderr,"userv-ipif service: access denied for %s, %s/%s\n",
551           what, prefixtxt, masktxt);
552   allallow= 0;
553 }
554
555 static void parseargs(int argc, const char *const *argv) {
556   unsigned long routeaddr, routemask;
557   const char *carg;
558   const char *const *cprotop;
559   int i;
560   char erwhatbuf[100], erwhatbuf2[100];
561   
562   if (argc < NARGS+1) { badusage("too few arguments"); }
563   if (argc > NARGS+1) { badusage("too many arguments"); }
564
565   configstr= *++argv;
566   
567   carg= *++argv;
568   if (strcmp(carg,"--")) badusage("separator argument `--' not found, got `%s'",carg);
569
570   carg= *++argv;
571   localaddr= eat_addr(&carg,"local-addr", ",",0);
572   peeraddr= eat_addr(&carg,"peer-addr", ",",0);
573   mtu= eat_number(&carg,"mtu", 576,65536, ",",0);
574   localallow= peerallow= 0;
575   
576   if (!strcmp(carg,"debug")) {
577     proto= 0;
578   } else {
579     for (cprotop= protos_ok;
580          (proto= *cprotop) && strcmp(proto,carg);
581          cprotop++);
582     if (!proto) fatal("invalid protocol");
583   }
584   
585   addrnet_mustdiffer("local-addr",localaddr,~0UL, "peer-addr",peeraddr,~0UL);
586   
587   carg= *++argv;
588   if (strcmp(carg,"-")) {
589     for (nexroutes=0;
590          carg && *carg;
591          nexroutes++) {
592       if (nexroutes == MAXEXROUTES)
593         fatal("too many extra routes (only %d allowed)",MAXEXROUTES);
594       sprintf(erwhatbuf,"route#%d",nexroutes);
595     
596       eat_prefixmask(&carg,erwhatbuf, ",",0, &routeaddr,&routemask,0);
597       if (routemask == ~0UL) {
598         addrnet_mustdiffer(erwhatbuf,routeaddr,routemask, "local-addr",localaddr,~0UL);
599         addrnet_mustdiffer(erwhatbuf,routeaddr,routemask, "peer-addr",peeraddr,~0UL);
600       }
601       for (i=0; i<nexroutes; i++) {
602         sprintf(erwhatbuf2,"route#%d",i);
603         addrnet_mustdiffer(erwhatbuf,routeaddr,routemask,
604                            erwhatbuf2,exroutes[i].prefix,exroutes[i].mask);
605       }
606       exroutes[nexroutes].prefix= routeaddr;
607       exroutes[nexroutes].mask= routemask;
608       exroutes[nexroutes].allow= 0;
609       ip2txt(routeaddr,exroutes[nexroutes].prefixtxt);
610       ip2txt(routemask,exroutes[nexroutes].masktxt);
611     }
612   }
613
614   ip2txt(localaddr,localtxt);
615   ip2txt(peeraddr,peertxt);
616 }
617
618 static void checkpermit(void) {
619   int i;
620   char erwhatbuf[100];
621   
622   allallow= 1;
623   checkallow(localallow,"local-addr", localtxt,"32");
624   checkallow(peerallow,"peer-addr", peertxt,"32");
625   for (i=0; i<nexroutes; i++) {
626     sprintf(erwhatbuf, "route#%d", i);
627     checkallow(exroutes[i].allow, erwhatbuf, exroutes[i].prefixtxt, exroutes[i].masktxt);
628   }
629   if (!allallow) fatal("access denied");
630 }
631
632 static void dumpdebug(void) __attribute__((noreturn));
633 static void dumpdebug(void) {
634   int i;
635   char erwhatbuf[100];
636   
637   printf("protocol: debug\n"
638          "local:    %08lx == %s\n"
639          "peer:     %08lx == %s\n"
640          "mtu:      %ld\n"
641          "routes:   %d\n",
642          localaddr, localtxt,
643          peeraddr, peertxt,
644          mtu,
645          nexroutes);
646   for (i=0; i<nexroutes; i++) {
647     sprintf(erwhatbuf, "route#%d:", i);
648     printf("%-9s %08lx/%08lx == %s/%s\n",
649            erwhatbuf,
650            exroutes[i].prefix, exroutes[i].mask,
651            exroutes[i].prefixtxt, exroutes[i].masktxt);
652   }
653   if (ferror(stdout) || fclose(stdout)) sysfatal("flush stdout");
654   exit(0);
655 }
656
657
658 static void setsigmask(const sigset_t *ss) {
659   int r;
660   
661   r= sigprocmask(SIG_SETMASK, ss, 0);
662   if (r) sysfatal("[un]block signals");
663 }  
664
665 static void setsignals(void (*handler)(int), struct sigaction *sa, int chldflags) {
666   const int *signalp;
667   int r, sig;
668   
669   sa->sa_handler= handler;
670   sa->sa_flags= 0; 
671   for (signalp=signals; (sig=*signalp); signalp++) {
672     r= sigaction(sig, sa, 0);  if (r) sysfatal("uncatch signal");
673   }
674   sa->sa_flags= chldflags;
675   r= sigaction(SIGCHLD, sa, 0);  if (r) sysfatal("uncatch children");
676 }
677
678 static void infork(void) {
679   struct sigaction sa;
680
681   memset(&pids,0,sizeof(pids));
682   sigemptyset(&sa.sa_mask);
683   setsignals(SIG_DFL,&sa,0);
684   setsigmask(&emptyset);
685   undoslattach= 0;
686 }
687
688 static pid_t makesubproc(void (*entry)(void)) {
689   pid_t pid;
690
691   pid= fork();  if (pid == (pid_t)-1) sysfatal("fork for subprocess");
692   if (pid) return pid;
693
694   infork();
695   entry();
696   abort();
697 }
698
699 static int task(void) {
700   pid_t pid;
701
702   pid= fork();
703   if (pid == (pid_t)-1) sysfatal("fork for task");
704   if (!pid) { infork(); return 1; }
705
706   pids.byname.task= pid;
707   while (pids.byname.task) sigsuspend(&emptyset);
708   return 0;
709 }
710
711 static void mdup2(int fd1, int fd2, const char *what) {
712   int r;
713
714   for (;;) {
715     r= dup2(fd1,fd2); if (r==fd2) return;
716     if (r!=-1) fatal("dup2 in %s gave wrong answer %d instead of %d",what,r,fd2);
717     if (errno != EINTR) sysfatal("dup2 failed in %s",what);
718   }
719 }
720
721 static void sl_entry(void) {
722   mdup2(slpipe[1],1,"slattach child");
723   execlp("slattach", "slattach", "-v", "-L", "-p",proto, ptyname, (char*)0);
724   sysfatal("cannot exec slattach");
725 }
726
727 static void cin_entry(void) {
728   mdup2(ptmaster,1,"cat input child");
729   execlp("cat", "cat", (char*)0);
730   sysfatal("cannot exec cat input");
731 }
732
733 static void cout_entry(void) {
734   mdup2(ptmaster,0,"cat output child");
735   execlp("cat", "cat", (char*)0);
736   sysfatal("cannot exec cat output");
737 }
738
739 static void sighandler(int signum) {
740   pid_t pid;
741   int estatus, status;
742   const char *taskfail;
743
744   estatus= 4;
745   
746   if (signum == SIGCHLD) {
747     for (;;) {
748       pid= waitpid(-1,&status,WNOHANG);
749       if (!pid || pid == (pid_t)-1) return;
750
751       if (pid == pids.byname.task) {
752         pids.byname.task= 0;
753         if (!status) return;
754         taskfail= "task";
755       } else if (pid == pids.byname.cin) {
756         pids.byname.cin= 0;
757         if (status) {
758           taskfail= "input cat";
759         } else {
760           taskfail= 0;
761           estatus= 0;
762         }
763       } else if (pid == pids.byname.cout) {
764         pids.byname.cout= 0;
765         taskfail= "output cat";
766       } else if (pid == pids.byname.sl) {
767         pids.byname.sl= 0;
768         taskfail= "slattach";
769       } else {
770         continue;
771       }
772       break;
773     }
774     if (taskfail) {
775       if (WIFEXITED(status)) {
776         fprintf(stderr,
777                 "userv-ipif service: %s unexpectedly exited with exit status %d\n",
778                 taskfail, WEXITSTATUS(status));
779       } else if (WIFSIGNALED(status)) {
780         fprintf(stderr,
781                 "userv-ipif service: %s unexpectedly killed by signal %s%s\n",
782                 taskfail, strsignal(WTERMSIG(status)),
783                 WCOREDUMP(status) ? " (core dumped)" : "");
784       } else {
785         fprintf(stderr, "userv-ipif service: %s unexpectedly terminated"
786                 " with unknown status code %d\n", taskfail, status);
787       }
788     }
789   } else {
790     fprintf(stderr,
791             "userv-ipif service: received signal %d, terminating\n",
792             signum);
793   }
794
795   terminate(estatus);
796 }
797
798 static void startup(void) {
799   int r;
800   struct sigaction sa;
801   
802   sigfillset(&fullset);
803   sigemptyset(&emptyset);
804
805   ptmaster= getpt();  if (ptmaster==-1) sysfatal("allocate pty master");
806   r= grantpt(ptmaster);  if (r) sysfatal("grab/grant pty slave");
807   ptyname= ptsname(ptmaster);  if (!ptyname) sysfatal("get pty slave name");
808   r= chmod(ptyname,0600);  if (r) sysfatal("chmod pty slave");
809   r= unlockpt(ptmaster);  if (r) sysfatal("unlock pty");
810
811   sigfillset(&sa.sa_mask);
812   setsignals(sighandler,&sa,SA_NOCLDSTOP);
813   setsigmask(&fullset);
814 }
815
816 static void startslattach(void) {
817   static char ifnbuf[200];
818
819   FILE *piper;
820   int r, l, k;
821
822   r= pipe(slpipe);  if (r) sysfatal("create pipe");
823   piper= fdopen(slpipe[0],"r");  if (!piper) sysfatal("fdopen pipe");
824
825   undoslattach= 1;
826   pids.byname.sl= makesubproc(sl_entry);
827
828   close(slpipe[1]);
829   setsigmask(&emptyset);
830   if (!fgets(ifnbuf,sizeof(ifnbuf),piper)) {
831     if (ferror(piper)) sysfatal("cannot read ifname from slattach");
832     else fatal("cannot read ifname from slattach");
833   }
834   setsigmask(&fullset);
835   l= strlen(ifnbuf);
836   if (l<=0 || ifnbuf[l-1] != '\n') fatal("slattach gave strange output `%s'",ifnbuf);
837   ifnbuf[l-1]= 0;
838   for (k=l; k>0 && ifnbuf[k-1]!=' '; k--);
839   ifname= ifnbuf+k;
840 }
841
842 static void netconfigure(void) {
843   char mtutxt[100];
844   int i;
845
846   if (task()) {
847     sprintf(mtutxt,"%lu",mtu);
848   
849     execlp("ifconfig", "ifconfig", ifname, localtxt,
850            "netmask","255.255.255.255", "-broadcast", "pointopoint",peertxt,
851            "mtu",mtutxt, "up", (char*)0);
852     sysfatal("cannot exec ifconfig");
853   }
854
855   for (i=0; i<nexroutes; i++) {
856     if (task()) {
857       execlp("route","route", "add", "-net",exroutes[i].prefixtxt,
858              "netmask",exroutes[i].masktxt,
859              "gw",peertxt, "dev",ifname, (char*)0);
860       sysfatal("cannot exec route (for route)");
861     }
862   }
863 }
864
865 static void copydata(void) __attribute__((noreturn));
866 static void copydata(void) {
867   int r;
868   
869   pids.byname.cin= makesubproc(cin_entry);
870   for (;;) {
871     r= write(1, "\300", 1); if (r==1) break;
872     assert(r==-1);  if (errno != EINTR) sysfatal("send initial delim to confirm");
873   }
874   pids.byname.cout= makesubproc(cout_entry);
875
876   for (;;) sigsuspend(&emptyset);
877 }
878
879 int main(int argc, const char *const *argv) {
880   parseargs(argc,argv);
881   pconfig(configstr,0);
882   checkpermit();
883   if (!proto) dumpdebug();
884
885   startup();
886   startslattach();
887   netconfigure();
888   copydata();
889 }