#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
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;
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, ...) {
fputs("userv-ipif service: fatal error: ",stderr);
vfprintf(stderr, fmt, al);
putc('\n',stderr);
- exit(8);
+ terminate(8);
}
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);
}
cpp->filename, cpp->lineno);
}
}
- exit(16);
+ terminate(16);
}
static char *ip2txt(unsigned long addr, char *buf) {
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();
}