chiark / gitweb /
Added copyright messages.
[userv-utils.git] / ipif / service.c
index 33ba2df28a362962a09a886bbc37da43d41bea95..24ea45ba137e7f6826d2a822a14383bb76413c89 100644 (file)
@@ -15,7 +15,7 @@
  *      <peer-addr> is the address of the point-to-point peer.
  *  <prefix>/<mask>,<prefix>/<mask>,...
  *      List of additional routes to add for this interface.
- *      May be the empty argument.
+ *      May be the empty argument, or `-' if this is problematic.
  *
  * <config> is either
  *    <gid>,<prefix>/<len>[,<junk>]
  * 
  * Should be run from userv with no-disconnect-hup.
  */
+/*
+ * Copyright (C) 1999 Ian Jackson
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with userv-utils; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * $Id$
+ */
 
 #include <stdio.h>
 #include <string.h>
@@ -49,7 +68,7 @@
 
 #define NARGS 4
 #define MAXEXROUTES 5
-#define ATXTLEN 12
+#define ATXTLEN 16
 
 static const unsigned long gidmaxval= (unsigned long)((gid_t)-2);
 static const char *const protos_ok[]= { "slip", "cslip", "adaptive", 0 };
@@ -75,7 +94,7 @@ static struct pplace {
 } *cpplace;
 
 
-static int slpipe[2], ptmaster;
+static int slpipe[2], ptmaster, undoslattach;
 static const char *ifname;
 static const char *ptyname;
 
@@ -88,13 +107,36 @@ static union {
 sigset_t emptyset, fullset;
 
 
+static int cleantask(void) {
+  pid_t pid;
+
+  pid= fork();
+  if (!pid) return 1;
+  if (pid == (pid_t)-1)
+    perror("userv-ipif: fork for undo slattach failed - cannot clean up properly");
+  return 0;
+}
+
 static void terminate(int estatus) {
   int i, status;
   pid_t pid;
   
   for (i=0; i<NPIDS; i++)
     if (pids.bynumber[i]) kill(pids.bynumber[i], SIGTERM);
-  
+
+  if (undoslattach) {
+    if (cleantask()) {
+      execlp("slattach", "slattach", "-p", "tty", ptyname, (char*)0);
+      perror("userv-ipif: exec slattach for undo slattach failed");
+      exit(-1);
+    }
+    if (ifname && cleantask()) {
+      execlp("ifconfig", "ifconfig", ifname, "down", (char*)0);
+      perror("userv-ipif: exec ifconfig for undo ifconfig failed");
+      exit(-1);
+    }
+  }
+
   for (;;) {
     pid= waitpid(-1,&status,0);
     if (pid == (pid_t)-1) break;
@@ -124,9 +166,9 @@ static void sysfatal(const char *fmt, ...) {
   e= errno;
   va_start(al,fmt);
 
-  fputs("userv-ipif service: fatal system error",stderr);
+  fputs("userv-ipif service: fatal system error",stderr);
   vfprintf(stderr, fmt, al);
-  fprintf(stderr,"%s\n", strerror(e));
+  fprintf(stderr,"%s\n", strerror(e));
   terminate(12);
 }
 
@@ -179,13 +221,13 @@ static unsigned long eat_number(const char **argp, const char *what,
   char *ep;
   int endchar;
 
-  if (!*argp) { badusage("missing number %s\n",what); }
+  if (!*argp) { badusage("missing number %s",what); }
   rv= strtoul(*argp,&ep,0);
   if ((endchar= *ep)) {
-    if (!endchars) badusage("junk after number %s\n",what);
+    if (!endchars) badusage("junk after number %s",what);
     if (!strchr(endchars,endchar))
       badusage("invalid character or delimiter `%c' in or after number, %s:"
-              " expected %s (or none?)\n", endchar,what,endchars);
+              " expected %s (or none?)", endchar,what,endchars);
     *argp= ep+1;
   } else {
     *argp= 0;
@@ -236,7 +278,7 @@ static void eat_prefixmask(const char **argp, const char *what,
   len= eat_number(argp,whatbuf, 0,32, endchars,endchar_r);
 
   mask= (~0UL << (32-len));
-  if (prefix & ~mask) badusage("%s prefix %08lx not fully contained in mask %08lx\n",
+  if (prefix & ~mask) badusage("%s prefix %08lx not fully contained in mask %08lx",
                               what,prefix,mask);
   *prefix_r= prefix;
   if (mask_r) *mask_r= mask;
@@ -253,6 +295,7 @@ static void permit(unsigned long pprefix, unsigned long pmask) {
   int i, any;
   
   assert(!(pprefix & ~pmask));
+  any= 0;
 
   if (!proto) fputs("permits",stdout);
   if (addrnet_isin(localaddr,~0UL, pprefix,pmask)) {
@@ -270,7 +313,7 @@ static void permit(unsigned long pprefix, unsigned long pmask) {
     }
   }
   if (!proto) {
-    if (!any) fputs(" nothing!",stderr);
+    if (!any) fputs(" nothing",stdout);
     putchar('\n');
   }
 }
@@ -408,29 +451,32 @@ static void parseargs(int argc, const char *const *argv) {
   addrnet_mustdiffer("local-addr",localaddr,~0UL, "peer-addr",peeraddr,~0UL);
   
   carg= *++argv;
-  for (nexroutes=0;
-       carg;
-       nexroutes++) {
-    if (nexroutes == MAXEXROUTES)
-      fatal("too many extra routes (only %d allowed)",MAXEXROUTES);
-    sprintf(erwhatbuf,"route#%d",nexroutes);
+  if (strcmp(carg,"-")) {
+    for (nexroutes=0;
+        carg && *carg;
+        nexroutes++) {
+      if (nexroutes == MAXEXROUTES)
+       fatal("too many extra routes (only %d allowed)",MAXEXROUTES);
+      sprintf(erwhatbuf,"route#%d",nexroutes);
     
-    eat_prefixmask(&carg,erwhatbuf, ",",0, &routeaddr,&routemask,0);
-    if (routemask == ~0UL) {
-      addrnet_mustdiffer(erwhatbuf,routeaddr,routemask, "local-addr",localaddr,~0UL);
-      addrnet_mustdiffer(erwhatbuf,routeaddr,routemask, "peer-addr",peeraddr,~0UL);
-    }
-    for (i=0; i<nexroutes; i++) {
-      sprintf(erwhatbuf2,"route#%d",i);
-      addrnet_mustdiffer(erwhatbuf,routeaddr,routemask,
-                        erwhatbuf2,exroutes[i].prefix,exroutes[i].mask);
+      eat_prefixmask(&carg,erwhatbuf, ",",0, &routeaddr,&routemask,0);
+      if (routemask == ~0UL) {
+       addrnet_mustdiffer(erwhatbuf,routeaddr,routemask, "local-addr",localaddr,~0UL);
+       addrnet_mustdiffer(erwhatbuf,routeaddr,routemask, "peer-addr",peeraddr,~0UL);
+      }
+      for (i=0; i<nexroutes; i++) {
+       sprintf(erwhatbuf2,"route#%d",i);
+       addrnet_mustdiffer(erwhatbuf,routeaddr,routemask,
+                          erwhatbuf2,exroutes[i].prefix,exroutes[i].mask);
+      }
+      exroutes[nexroutes].prefix= routeaddr;
+      exroutes[nexroutes].mask= routemask;
+      exroutes[nexroutes].allow= 0;
+      ip2txt(routeaddr,exroutes[nexroutes].prefixtxt);
+      ip2txt(routemask,exroutes[nexroutes].masktxt);
     }
-    exroutes[nexroutes].prefix= routeaddr;
-    exroutes[nexroutes].mask= routemask;
-    exroutes[nexroutes].allow= 0;
-    ip2txt(routeaddr,exroutes[nexroutes].prefixtxt);
-    ip2txt(routemask,exroutes[nexroutes].masktxt);
   }
+
   ip2txt(localaddr,localtxt);
   ip2txt(peeraddr,peertxt);
 }
@@ -474,6 +520,14 @@ static void dumpdebug(void) {
   exit(0);
 }
 
+
+static void setsigmask(const sigset_t *ss) {
+  int r;
+  
+  r= sigprocmask(SIG_SETMASK, ss, 0);
+  if (r) sysfatal("[un]block signals");
+}  
+
 static void setsignals(void (*handler)(int), struct sigaction *sa, int chldflags) {
   const int *signalp;
   int r, sig;
@@ -487,13 +541,6 @@ static void setsignals(void (*handler)(int), struct sigaction *sa, int chldflags
   r= sigaction(SIGCHLD, sa, 0);  if (r) sysfatal("uncatch children");
 }
 
-static void setsigmask(const sigset_t *ss) {
-  int r;
-  
-  r= sigprocmask(SIG_SETMASK, ss, 0);
-  if (r) sysfatal("[un]block signals");
-}  
-
 static void infork(void) {
   struct sigaction sa;
 
@@ -501,6 +548,7 @@ static void infork(void) {
   sigemptyset(&sa.sa_mask);
   setsignals(SIG_DFL,&sa,0);
   setsigmask(&emptyset);
+  undoslattach= 0;
 }
 
 static pid_t makesubproc(void (*entry)(void)) {
@@ -518,28 +566,38 @@ static int task(void) {
   pid_t pid;
 
   pid= fork();
-  if (pids.byname.task == (pid_t)-1) sysfatal("fork for task");
-  if (!pids.byname.task) { infork(); return 1; }
+  if (pid == (pid_t)-1) sysfatal("fork for task");
+  if (!pid) { infork(); return 1; }
 
   pids.byname.task= pid;
   while (pids.byname.task) sigsuspend(&emptyset);
   return 0;
 }
 
+static void mdup2(int fd1, int fd2, const char *what) {
+  int r;
+
+  for (;;) {
+    r= dup2(fd1,fd2); if (r==fd2) return;
+    if (r!=-1) fatal("dup2 in %s gave wrong answer %d instead of %d",what,r,fd2);
+    if (errno != EINTR) sysfatal("dup2 failed in %s",what);
+  }
+}
+
 static void sl_entry(void) {
-  if (dup2(slpipe[1],1) != 1) sysfatal("dup2 stdout in slattach child");
+  mdup2(slpipe[1],1,"slattach child");
   execlp("slattach", "slattach", "-v", "-L", "-p",proto, ptyname, (char*)0);
   sysfatal("cannot exec slattach");
 }
 
 static void cin_entry(void) {
-  if (dup2(ptmaster,1) != 1) sysfatal("dup2 stdout in cat input child");
+  mdup2(ptmaster,1,"cat input child");
   execlp("cat", "cat", (char*)0);
   sysfatal("cannot exec cat input");
 }
 
 static void cout_entry(void) {
-  if (dup2(ptmaster,0) != 1) sysfatal("dup2 stdin in cat output child");
+  mdup2(ptmaster,0,"cat output child");
   execlp("cat", "cat", (char*)0);
   sysfatal("cannot exec cat output");
 }
@@ -577,11 +635,22 @@ static void sighandler(int signum) {
       } else {
        continue;
       }
+      break;
     }
     if (taskfail) {
-      fprintf(stderr,
-             "userv-ipif service: %s unexpected terminated with status code %d\n",
-             taskfail, status);
+      if (WIFEXITED(status)) {
+       fprintf(stderr,
+               "userv-ipif service: %s unexpectedly exited with exit status %d\n",
+               taskfail, WEXITSTATUS(status));
+      } else if (WIFSIGNALED(status)) {
+       fprintf(stderr,
+               "userv-ipif service: %s unexpectedly killed by signal %s%s\n",
+               taskfail, strsignal(WTERMSIG(status)),
+               WCOREDUMP(status) ? " (core dumped)" : "");
+      } else {
+       fprintf(stderr, "userv-ipif service: %s unexpectedly terminated"
+               " with unknown status code %d\n", taskfail, status);
+      }
     }
   } else {
     fprintf(stderr,
@@ -603,6 +672,7 @@ static void startup(void) {
   r= grantpt(ptmaster);  if (r) sysfatal("grab/grant pty slave");
   ptyname= ptsname(ptmaster);  if (!ptyname) sysfatal("get pty slave name");
   r= chmod(ptyname,0600);  if (r) sysfatal("chmod pty slave");
+  r= unlockpt(ptmaster);  if (r) sysfatal("unlock pty");
 
   sigfillset(&sa.sa_mask);
   setsignals(sighandler,&sa,SA_NOCLDSTOP);
@@ -610,13 +680,15 @@ static void startup(void) {
 }
 
 static void startslattach(void) {
+  static char ifnbuf[200];
+
   FILE *piper;
-  char ifnbuf[200];
   int r, l, k;
 
   r= pipe(slpipe);  if (r) sysfatal("create pipe");
   piper= fdopen(slpipe[0],"r");  if (!piper) sysfatal("fdopen pipe");
 
+  undoslattach= 1;
   pids.byname.sl= makesubproc(sl_entry);
 
   close(slpipe[1]);
@@ -627,7 +699,7 @@ static void startslattach(void) {
   }
   setsigmask(&fullset);
   l= strlen(ifnbuf);
-  if (l<0 || ifnbuf[l-1] != '\n') fatal("slattach gave strange output `%s'",ifnbuf);
+  if (l<=0 || ifnbuf[l-1] != '\n') fatal("slattach gave strange output `%s'",ifnbuf);
   ifnbuf[l-1]= 0;
   for (k=l; k>0 && ifnbuf[k-1]!=' '; k--);
   ifname= ifnbuf+k;
@@ -646,21 +718,9 @@ static void netconfigure(void) {
     sysfatal("cannot exec ifconfig");
   }
 
-  if (task()) {
-    execlp("route","route", "-host",localtxt, "netmask","255.255.255.255",
-          "dev",ifname, (char*)0);
-    sysfatal("cannot exec route (for local)");
-  }
-
-  if (task()) {
-    execlp("route","route", "-host",peertxt, "netmask","255.255.255.255",
-          "dev",ifname, (char*)0);
-    sysfatal("cannot exec route (for peer)");
-  }
-
   for (i=0; i<nexroutes; i++) {
     if (task()) {
-      execlp("route","route", "-net",exroutes[i].prefixtxt,
+      execlp("route","route", "add", "-net",exroutes[i].prefixtxt,
             "netmask",exroutes[i].masktxt,
             "gw",peertxt, "dev",ifname, (char*)0);
       sysfatal("cannot exec route (for route)");
@@ -669,8 +729,14 @@ static void netconfigure(void) {
 }
 
 static void copydata(void) __attribute__((noreturn));
-static void copydata(void) {  
+static void copydata(void) {
+  int r;
+  
   pids.byname.cin= makesubproc(cin_entry);
+  for (;;) {
+    r= write(1, "\300", 1); if (r==1) break;
+    assert(r==-1);  if (errno != EINTR) sysfatal("send initial delim to confirm");
+  }
   pids.byname.cout= makesubproc(cout_entry);
 
   for (;;) sigsuspend(&emptyset);