diff -r d124aa7084af man/man4/9import.4 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/man/man4/9import.4 Wed Jun 06 00:00:00 2012 +0200 @@ -0,0 +1,75 @@ +.TH IMPORT 4 +.SH NAME +import \- import a name space from a remote system +.SH SYNOPSIS +.B import +[ +.I options +] +.I system +.I file +[ +.I mountpoint +] +.SH DESCRIPTION +.I Import +allows an arbitrary +.I file +on a remote +.I system +to be imported into the local name space. +Usually +.I file +is a directory, so the complete +file tree under the directory is made available. +.PP +A process is started on the +remote machine, with authority of the user of +.IR import , +to perform work for the local machine using the +.IR exportfs (4) +service. +The default port used is TCP 17007. +If +.I mountpoint +is omitted +.I import +uses the name of the remote +.I file +as the local mount point. +.PP +The options are: +.TF "-s namexxx" +.PD +.TP +.B -A +Skip the authentication protocol. +This is useful for connecting to foreign systems like Inferno. +.TP +.B -k \fIkeypattern +Use +.I keypattern +to select a key to authenticate to the remote side +(see +.IR auth (2)). +.TP +.B -p +Push the +.IR aan (8) +filter onto the connection to protect against +temporary network outages. +.TP +.B -s \fIname +Post the connection's mountable file descriptor as +.BI /srv/ name\fR. +.SH SOURCE +.B \*9/src/cmd/9import.c +.SH SEE ALSO +.IR srv (4), +.IR aan (8), +.IR listen (8), +.B cs +in +.IR ndb (8) +.SH BUGS +Encryption is not implemented. diff -r d124aa7084af man/man8/import.8 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/man/man8/aan.8 Wed Jun 06 00:00:00 2012 +0200 @@ -0,0 +1,98 @@ +.TH AAN 8 +.SH NAME +aan \- always available network +.SH SYNOPSIS +.B aan +.B -c +[ +.B -d +] +[ +.B -m maxto +] +.I dialstring +.br +.B aan +[ +.B -d +] +[ +.B -m maxto +] +.I netdir +.SH DESCRIPTION +.I Aan +tunnels traffic between a client and a server through a persistent +network connection. If the connection breaks (voluntarily or +due to networking problems), the +.I aan +client re-establishes the connection by redialing the server. +.PP +.I Aan +uses a unique protocol to make sure no data is ever lost even +when the connection breaks. +After a reconnection, +.I aan +retransmits all unacknowledged data between client and server. +.PP +A connection can be broken voluntarily (e.g. by roaming over IP networks), +or a connection can break when the IP service is unreliable. +In either case, +.I aan +re-establishes the client's connection automatically. +.PP +When the server part has not heard from the client in +.I maxto +seconds, the server part of +.I aan +exits. +The default +.I maxto +is one day. +The client side (option +.BR -c ) +calls the server by its +.IR dialstring , +while the server side listens for connections in the +already-announced network directory +.IR netdir . +.PP +.I Aan +is usually run automatically through the +.B -p +option of +.IR import (4). +.SH EXAMPLE +Assume the server part of +.I aan +is encapsulated in +.I exportfs +on the machine +.B sob +and started through +.B aux/listen +as follows: +.IP +.EX +netdir=`{echo $3 | sed 's;/[0-9]+$;!*!0;'} +exec exportfs -a -A $netdir +.EE +.PP +Then machine +.BR astro6 's +name space can be imported through +.I aan +using this command: +.IP +.EX +import -p astro6 / /mnt/term +.EE +.SH FILES +.TF /sys/log/aan +.TP +.B /sys/log/aan +Log file +.SH SOURCE +.B /\*9/src/cmd/aan.c +.SH SEE ALSO +.IR import (4), diff -r d124aa7084af src/cmd/9import.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cmd/9import.c Wed Jun 06 00:00:00 2012 +0200 @@ -0,0 +1,239 @@ +#include +#include +#include +#include + +enum { + Encnone, + Encssl, + Enctls, +}; + +static char *encprotos[] = { + [Encnone] = "clear", + [Encssl] = "ssl", + [Enctls] = "tls", + nil, +}; + +char *keyspec = ""; +char *filterp; +char *ealgs = "rc4_256 sha1"; +int encproto = Encnone; +AuthInfo *ai; +int debug; +int doauth = 1; +int timedout; + +int connectez(char*, char*); +void sysfatal(char*, ...); +void usage(void); +int filter(int, char *, char *); + +int +catcher(void *v, char *msg) +{ + timedout = 1; + if(strcmp(msg, "alarm") == 0) + return 1; + return 0; +} + +static int +lookup(char *s, char *l[]) +{ + int i; + + for (i = 0; l[i] != 0; i++) + if (strcmp(l[i], s) == 0) + return i; + return -1; +} + +static char* +srvname(char *addr) +{ + int i; + + for(i=0; i +#include +#include +#include +#include + +#define NS(x) ((vlong)x) +#define US(x) (NS(x) * 1000LL) +#define MS(x) (US(x) * 1000LL) +#define S(x) (MS(x) * 1000LL) + +#define LOGNAME "aan" + +enum { + Synctime = S(8), + Nbuf = 10, + K = 1024, + Bufsize = 8 * K, + Stacksize = 8 * K, + Timer = 0, // Alt channels. + Unsent = 1, + Maxto = 24 * 3600, // A full day to reconnect. +}; + +typedef struct Endpoints Endpoints; +struct Endpoints { + char *lsys; + char *lserv; + char *rsys; + char *rserv; +}; + +typedef struct { + ulong nb; // Number of data bytes in this message + ulong msg; // Message number + ulong acked; // Number of messages acked +} Hdr; + +typedef struct t_Buf { + Hdr hdr; + uchar buf[Bufsize]; +} Buf; + +static char *progname; +static Channel *unsent; +static Channel *unacked; +static Channel *empty; +static int netfd; +static int inmsg; +static char *devdir; +static int debug; +static int done; +static char *dialstring; +static int maxto = Maxto; +static char *Logname = LOGNAME; +static int client; + +static Alt a[] = { + /* c v op */ + { nil, nil, CHANRCV }, // timer + { nil, nil, CHANRCV }, // unsent + { nil, nil, CHANEND }, +}; + +static void fromnet(void*); +static void fromclient(void*); +static void reconnect(void); +static void synchronize(void); +static void showmsg(int, char *, Buf *); +static int writen(int, uchar *, int); +static void dmessage(int, char *, ...); +static void timerproc(void *); +static Endpoints *getendpoints(char *); +static void freeendpoints(Endpoints *); + +static void +usage(void) +{ + fprint(2, "Usage: %s [-cd] [-m maxto] dialstring|netdir\n", progname); + threadexitsall("usage"); +} + +static int +catch(void *v, char *s) +{ + if (!strcmp(s, "alarm")) { + syslog(0, Logname, "Timed out while waiting for client on %s, exiting...", + devdir); + threadexitsall(nil); + } + return 0; +} + +void +threadmain(int argc, char **argv) +{ + int i, failed; + Buf *b; + Channel *timer; + vlong synctime; + + progname = argv[0]; + ARGBEGIN { + case 'c': + client++; + break; + case 'd': + debug++; + break; + case 'm': + maxto = (int)strtol(EARGF(usage()), (char **)nil, 0); + break; + default: + usage(); + } ARGEND; + + if (argc != 1) + usage(); + + if (!client) { + char *p; + + devdir = argv[0]; + if ((p = strstr(devdir, "/local")) != nil) + *p = '\0'; + } + else + dialstring = argv[0]; + + if (debug > 0) { + int fd = open("#c/cons", OWRITE|OCEXEC); + dup(fd, 2); + } + + fmtinstall('F', fcallfmt); + + atnotify(catch, 1); + + unsent = chancreate(sizeof(Buf *), Nbuf); + unacked = chancreate(sizeof(Buf *), Nbuf); + empty = chancreate(sizeof(Buf *), Nbuf); + timer = chancreate(sizeof(uchar *), 1); + + for (i = 0; i != Nbuf; i++) { + Buf *b = malloc(sizeof(Buf)); + sendp(empty, b); + } + + netfd = -1; + + if (proccreate(fromnet, nil, Stacksize) < 0) + sysfatal("%s; Cannot start fromnet; %r", progname); + + reconnect(); // Set up the initial connection. + synchronize(); + + if (proccreate(fromclient, nil, Stacksize) < 0) + sysfatal("%s; Cannot start fromclient; %r", progname); + + if (proccreate(timerproc, timer, Stacksize) < 0) + sysfatal("%s; Cannot start timerproc; %r", progname); + + a[Timer].c = timer; + a[Unsent].c = unsent; + a[Unsent].v = &b; + + synctime = nsec() + Synctime; + failed = 0; + while (!done) { + vlong now; + int delta; + + if (failed) { + // Wait for the netreader to die. + while (netfd >= 0) { + dmessage(1, "main; waiting for netreader to die\n"); + sleep(1000); + } + + // the reader died; reestablish the world. + reconnect(); + synchronize(); + failed = 0; + } + + now = nsec(); + delta = (synctime - nsec()) / MS(1); + + if (delta <= 0) { + Hdr hdr; + + hdr.nb = 0; + hdr.acked = inmsg; + hdr.msg = -1; + + if (writen(netfd, (uchar *)&hdr, sizeof(Hdr)) < 0) { + dmessage(2, "main; writen failed; %r\n"); + failed = 1; + continue; + } + synctime = nsec() + Synctime; + assert(synctime > now); + } + + switch (alt(a)) { + case Timer: + break; + + case Unsent: + sendp(unacked, b); + + b->hdr.acked = inmsg; + + if (writen(netfd, (uchar *)&b->hdr, sizeof(Hdr)) < 0) { + dmessage(2, "main; writen failed; %r\n"); + failed = 1; + } + + if (writen(netfd, b->buf, b->hdr.nb) < 0) { + dmessage(2, "main; writen failed; %r\n"); + failed = 1; + } + + if (b->hdr.nb == 0) + done = 1; + break; + } + } + syslog(0, Logname, "exiting..."); + threadexitsall(nil); +} + + +static void +fromclient(void *v) +{ + static int outmsg; + + for (;;) { + Buf *b; + + b = recvp(empty); + if ((int)(b->hdr.nb = read(0, b->buf, Bufsize)) <= 0) { + if ((int)b->hdr.nb < 0) + dmessage(2, "fromclient; Cannot read 9P message; %r\n"); + else + dmessage(2, "fromclient; Client terminated\n"); + b->hdr.nb = 0; + } + b->hdr.msg = outmsg++; + + showmsg(1, "fromclient", b); + sendp(unsent, b); + + if (b->hdr.nb == 0) + break; + } +} + +static void +fromnet(void *v) +{ + static int lastacked; + Buf *b; + + b = (Buf *)malloc(sizeof(Buf)); + assert(b); + + while (!done) { + int len, acked, i; + + while (netfd < 0) { + dmessage(1, "fromnet; waiting for connection... (inmsg %d)\n", + inmsg); + sleep(1000); + } + + // Read the header. + if ((len = readn(netfd, &b->hdr, sizeof(Hdr))) <= 0) { + if (len < 0) + dmessage(1, "fromnet; (hdr) network failure; %r\n"); + else + dmessage(1, "fromnet; (hdr) network closed\n"); + close(netfd); + netfd = -1; + continue; + } + dmessage(2, "fromnet: Got message, size %d, nb %d, msg %d\n", len, + b->hdr.nb, b->hdr.msg); + + if (b->hdr.nb == 0) { + if ((long)b->hdr.msg >= 0) { + dmessage(1, "fromnet; network closed\n"); + break; + } + continue; + } + + if ((len = readn(netfd, b->buf, b->hdr.nb)) <= 0 || len != b->hdr.nb) { + if (len == 0) + dmessage(1, "fromnet; network closed\n"); + else + dmessage(1, "fromnet; network failure; %r\n"); + close(netfd); + netfd = -1; + continue; + } + + if (b->hdr.msg < inmsg) { + dmessage(1, "fromnet; skipping message %d, currently at %d\n", + b->hdr.msg, inmsg); + continue; + } + + // Process the acked list. + acked = b->hdr.acked - lastacked; + for (i = 0; i != acked; i++) { + Buf *rb; + + rb = recvp(unacked); + if (rb->hdr.msg != lastacked + i) { + dmessage(1, "rb %p, msg %d, lastacked %d, i %d\n", + rb, rb? rb->hdr.msg: -2, lastacked, i); + assert(0); + } + rb->hdr.msg = -1; + sendp(empty, rb); + } + lastacked = b->hdr.acked; + + inmsg++; + + showmsg(1, "fromnet", b); + + if (writen(1, b->buf, len) < 0) + sysfatal("fromnet; cannot write to client; %r"); + } + done = 1; +} + +static void +reconnect(void) +{ + char ldir[40]; + int lcfd, fd; + + if (dialstring) { + syslog(0, Logname, "dialing %s", dialstring); + while ((fd = dial(dialstring, nil, nil, nil)) < 0) { + char err[32]; + + err[0] = '\0'; + errstr(err, sizeof err); + if (strstr(err, "connection refused")) { + dmessage(1, "reconnect; server died...\n"); + threadexitsall("server died..."); + } + dmessage(1, "reconnect: dialed %s; %s\n", dialstring, err); + sleep(1000); + } + syslog(0, Logname, "reconnected to %s", dialstring); + } + else { + Endpoints *ep; + + syslog(0, Logname, "waiting for connection on %s", devdir); + alarm(maxto * 1000); + if ((lcfd = listen(devdir, ldir)) < 0) + sysfatal("reconnect; cannot listen; %r"); + + if ((fd = accept(lcfd, ldir)) < 0) + sysfatal("reconnect; cannot accept; %r"); + alarm(0); + close(lcfd); + + ep = getendpoints(ldir); + dmessage(1, "rsys '%s'\n", ep->rsys); + syslog(0, Logname, "connected from %s", ep->rsys); + freeendpoints(ep); + } + + netfd = fd; // Wakes up the netreader. +} + +static void +synchronize(void) +{ + Channel *tmp; + Buf *b; + + // Ignore network errors here. If we fail during + // synchronization, the next alarm will pick up + // the error. + + tmp = chancreate(sizeof(Buf *), Nbuf); + while ((b = nbrecvp(unacked)) != nil) { + writen(netfd, (uchar *)b, sizeof(Hdr) + b->hdr.nb); + sendp(tmp, b); + } + chanfree(unacked); + unacked = tmp; +} + +static void +showmsg(int level, char *s, Buf *b) +{ + if (b == nil) { + dmessage(level, "%s; b == nil\n", s); + return; + } + + dmessage(level, + "%s; (len %d) %X %X %X %X %X %X %X %X %X (%p)\n", s, + b->hdr.nb, + b->buf[0], b->buf[1], b->buf[2], + b->buf[3], b->buf[4], b->buf[5], + b->buf[6], b->buf[7], b->buf[8], b); +} + +static int +writen(int fd, uchar *buf, int nb) +{ + int len = nb; + + while (nb > 0) { + int n; + + if (fd < 0) + return -1; + + if ((n = write(fd, buf, nb)) < 0) { + dmessage(1, "writen; Write failed; %r\n"); + return -1; + } + dmessage(2, "writen: wrote %d bytes\n", n); + + buf += n; + nb -= n; + } + return len; +} + +static void +timerproc(void *x) +{ + Channel *timer = x; + while (!done) { + sleep((Synctime / MS(1)) >> 1); + sendp(timer, "timer"); + } +} + +static void +dmessage(int level, char *fmt, ...) +{ + va_list arg; + + if (level > debug) + return; + + va_start(arg, fmt); + vfprint(2, fmt, arg); + va_end(arg); +} + +static void +getendpoint(char *dir, char *file, char **sysp, char **servp) +{ + int fd, n; + char buf[128]; + char *sys, *serv; + + sys = serv = 0; + + snprint(buf, sizeof buf, "%s/%s", dir, file); + fd = open(buf, OREAD); + if(fd >= 0){ + n = read(fd, buf, sizeof(buf)-1); + if(n>0){ + buf[n-1] = 0; + serv = strchr(buf, '!'); + if(serv){ + *serv++ = 0; + serv = strdup(serv); + } + sys = strdup(buf); + } + close(fd); + } + if(serv == 0) + serv = strdup("unknown"); + if(sys == 0) + sys = strdup("unknown"); + *servp = serv; + *sysp = sys; +} + +static Endpoints * +getendpoints(char *dir) +{ + Endpoints *ep; + + ep = malloc(sizeof(*ep)); + getendpoint(dir, "local", &ep->lsys, &ep->lserv); + getendpoint(dir, "remote", &ep->rsys, &ep->rserv); + return ep; +} + +static void +freeendpoints(Endpoints *ep) +{ + free(ep->lsys); + free(ep->rsys); + free(ep->lserv); + free(ep->rserv); + free(ep); +} +