chiark / gitweb /
Compiles and getting there for full use.
authorian <ian>
Sun, 19 Sep 1999 18:15:50 +0000 (18:15 +0000)
committerian <ian>
Sun, 19 Sep 1999 18:15:50 +0000 (18:15 +0000)
ipif/Makefile
ipif/service.c

index fd69b4a..2bcea8c 100644 (file)
@@ -1,6 +1,6 @@
 #
 
 CFLAGS=                -Wall -Wmissing-prototypes -Wstrict-prototypes -Wpointer-arith \
-               -Wwrite-strings -g
+               -Wwrite-strings -g -D_GNU_SOURCE
 
 all:           service
index b770623..33ba2df 100644 (file)
 #include <stdarg.h>
 #include <ctype.h>
 #include <limits.h>
+#include <signal.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
 
 #define NARGS 4
 #define MAXEXROUTES 5
@@ -47,6 +53,7 @@
 
 static const unsigned long gidmaxval= (unsigned long)((gid_t)-2);
 static const char *const protos_ok[]= { "slip", "cslip", "adaptive", 0 };
+static const int signals[]= { SIGHUP, SIGINT, SIGTERM, 0 };
 
 static const char *configstr, *proto;
 static unsigned long localaddr, peeraddr, mtu;
@@ -67,6 +74,35 @@ static struct pplace {
   int lineno;
 } *cpplace;
 
+
+static int slpipe[2], ptmaster;
+static const char *ifname;
+static const char *ptyname;
+
+#define NPIDS 4
+
+static union {
+  struct { pid_t sl, cout, cin, task; } byname;
+  pid_t bynumber[NPIDS];
+} pids;
+sigset_t emptyset, fullset;
+
+
+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);
+  
+  for (;;) {
+    pid= waitpid(-1,&status,0);
+    if (pid == (pid_t)-1) break;
+  }
+  exit(estatus);
+}
+
+
 static void fatal(const char *fmt, ...)
      __attribute__((format(printf,1,2)));
 static void fatal(const char *fmt, ...) {
@@ -76,7 +112,7 @@ static void fatal(const char *fmt, ...) {
   fputs("userv-ipif service: fatal error: ",stderr);
   vfprintf(stderr, fmt, al);
   putc('\n',stderr);
-  exit(8);
+  terminate(8);
 }
   
 static void sysfatal(const char *fmt, ...)
@@ -91,7 +127,7 @@ static void sysfatal(const char *fmt, ...) {
   fputs("userv-ipif service: fatal system error",stderr);
   vfprintf(stderr, fmt, al);
   fprintf(stderr,"%s\n", strerror(e));
-  exit(12);
+  terminate(12);
 }
 
 
@@ -119,7 +155,7 @@ static void badusage(const char *fmt, ...) {
              cpp->filename, cpp->lineno);
     }
   }
-  exit(16);
+  terminate(16);
 }
 
 static char *ip2txt(unsigned long addr, char *buf) {
@@ -438,11 +474,216 @@ static void dumpdebug(void) {
   exit(0);
 }
 
+static void setsignals(void (*handler)(int), struct sigaction *sa, int chldflags) {
+  const int *signalp;
+  int r, sig;
+  
+  sa->sa_handler= handler;
+  sa->sa_flags= 0; 
+  for (signalp=signals; (sig=*signalp); signalp++) {
+    r= sigaction(sig, sa, 0);  if (r) sysfatal("uncatch signal");
+  }
+  sa->sa_flags= 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;
+
+  memset(&pids,0,sizeof(pids));
+  sigemptyset(&sa.sa_mask);
+  setsignals(SIG_DFL,&sa,0);
+  setsigmask(&emptyset);
+}
+
+static pid_t makesubproc(void (*entry)(void)) {
+  pid_t pid;
+
+  pid= fork();  if (pid == (pid_t)-1) sysfatal("fork for subprocess");
+  if (pid) return pid;
+
+  infork();
+  entry();
+  abort();
+}
+
+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; }
+
+  pids.byname.task= pid;
+  while (pids.byname.task) sigsuspend(&emptyset);
+  return 0;
+}
+
+static void sl_entry(void) {
+  if (dup2(slpipe[1],1) != 1) sysfatal("dup2 stdout in 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");
+  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");
+  execlp("cat", "cat", (char*)0);
+  sysfatal("cannot exec cat output");
+}
+
+static void sighandler(int signum) {
+  pid_t pid;
+  int estatus, status;
+  const char *taskfail;
+
+  estatus= 4;
+  
+  if (signum == SIGCHLD) {
+    for (;;) {
+      pid= waitpid(-1,&status,WNOHANG);
+      if (!pid || pid == (pid_t)-1) return;
+
+      if (pid == pids.byname.task) {
+       pids.byname.task= 0;
+       if (!status) return;
+       taskfail= "task";
+      } else if (pid == pids.byname.cin) {
+       pids.byname.cin= 0;
+       if (status) {
+         taskfail= "input cat";
+       } else {
+         taskfail= 0;
+         estatus= 0;
+       }
+      } else if (pid == pids.byname.cout) {
+       pids.byname.cout= 0;
+       taskfail= "output cat";
+      } else if (pid == pids.byname.sl) {
+       pids.byname.sl= 0;
+       taskfail= "slattach";
+      } else {
+       continue;
+      }
+    }
+    if (taskfail) {
+      fprintf(stderr,
+             "userv-ipif service: %s unexpected terminated with status code %d\n",
+             taskfail, status);
+    }
+  } else {
+    fprintf(stderr,
+           "userv-ipif service: received signal %d, terminating\n",
+           signum);
+  }
+
+  terminate(estatus);
+}
+
+static void startup(void) {
+  int r;
+  struct sigaction sa;
+  
+  sigfillset(&fullset);
+  sigemptyset(&emptyset);
+
+  ptmaster= getpt();  if (ptmaster==-1) sysfatal("allocate pty master");
+  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");
+
+  sigfillset(&sa.sa_mask);
+  setsignals(sighandler,&sa,SA_NOCLDSTOP);
+  setsigmask(&fullset);
+}
+
+static void startslattach(void) {
+  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");
+
+  pids.byname.sl= makesubproc(sl_entry);
+
+  close(slpipe[1]);
+  setsigmask(&emptyset);
+  if (!fgets(ifnbuf,sizeof(ifnbuf),piper)) {
+    if (ferror(piper)) sysfatal("cannot read ifname from slattach");
+    else fatal("cannot read ifname from slattach");
+  }
+  setsigmask(&fullset);
+  l= strlen(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;
+}
+
+static void netconfigure(void) {
+  char mtutxt[100];
+  int i;
+
+  if (task()) {
+    sprintf(mtutxt,"%lu",mtu);
+  
+    execlp("ifconfig", "ifconfig", ifname, localtxt,
+          "netmask","255.255.255.255", "-broadcast", "pointopoint",peertxt,
+          "mtu",mtutxt, "up", (char*)0);
+    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,
+            "netmask",exroutes[i].masktxt,
+            "gw",peertxt, "dev",ifname, (char*)0);
+      sysfatal("cannot exec route (for route)");
+    }
+  }
+}
+
+static void copydata(void) __attribute__((noreturn));
+static void copydata(void) {  
+  pids.byname.cin= makesubproc(cin_entry);
+  pids.byname.cout= makesubproc(cout_entry);
+
+  for (;;) sigsuspend(&emptyset);
+}
+
 int main(int argc, const char *const *argv) {
   parseargs(argc,argv);
   pconfig(configstr,0);
   checkpermit();
   if (!proto) dumpdebug();
 
-  abort();
+  startup();
+  startslattach();
+  netconfigure();
+  copydata();
 }