+
+static int task(void) {
+ pid_t pid;
+
+ pid= fork();
+ 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) {
+ 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) {
+ mdup2(ptmaster,1,"cat input child");
+ execlp("cat", "cat", (char*)0);
+ sysfatal("cannot exec cat input");
+}
+
+static void cout_entry(void) {
+ mdup2(ptmaster,0,"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;
+ }
+ break;
+ }
+ if (taskfail) {
+ 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,
+ "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");
+ r= unlockpt(ptmaster); if (r) sysfatal("unlock pty");
+
+ sigfillset(&sa.sa_mask);
+ setsignals(sighandler,&sa,SA_NOCLDSTOP);
+ setsigmask(&fullset);
+}
+
+static void startslattach(void) {
+ static char ifnbuf[200];
+
+ FILE *piper;
+ 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]);
+ 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");
+ }
+
+ for (i=0; i<nexroutes; i++) {
+ if (task()) {
+ execlp("route","route", "add", "-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) {
+ 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);
+}
+
+int main(int argc, const char *const *argv) {
+ parseargs(argc,argv);
+ pconfig(configstr,0);
+ checkpermit();
+ if (!proto) dumpdebug();
+
+ startup();
+ startslattach();
+ netconfigure();
+ copydata();
+}