summaryrefslogtreecommitdiff
path: root/sys/src/cmd/9nfs/server.c
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/9nfs/server.c
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/9nfs/server.c')
-rwxr-xr-xsys/src/cmd/9nfs/server.c618
1 files changed, 618 insertions, 0 deletions
diff --git a/sys/src/cmd/9nfs/server.c b/sys/src/cmd/9nfs/server.c
new file mode 100755
index 000000000..fe70f1036
--- /dev/null
+++ b/sys/src/cmd/9nfs/server.c
@@ -0,0 +1,618 @@
+#include "all.h"
+#include <ndb.h>
+
+static int alarmflag;
+
+static int Iconv(Fmt*);
+static void openudp(int);
+static void cachereply(Rpccall*, void*, int);
+static int replycache(int, Rpccall*, long (*)(int, void*, long));
+static void udpserver(int, Progmap*);
+static void tcpserver(int, Progmap*);
+static void getendpoints(Udphdr*, char*);
+static long readtcp(int, void*, long);
+static long writetcp(int, void*, long);
+static int servemsg(int, long (*)(int, void*, long), long (*)(int, void*, long),
+ int, Progmap*);
+void (*rpcalarm)(void);
+int rpcdebug;
+int rejectall;
+int p9debug;
+
+int nocache;
+
+uchar buf[9000];
+uchar rbuf[9000];
+uchar resultbuf[9000];
+
+static int tcp;
+
+char *commonopts = "[-9CDrtv]"; /* for usage() messages */
+
+/*
+ * this recognises common, nominally rcp-related options.
+ * they may not take arguments.
+ */
+int
+argopt(int c)
+{
+ switch(c){
+ case '9':
+ ++p9debug;
+ return 0;
+ case 'C':
+ ++nocache;
+ return 0;
+ case 'D':
+ ++rpcdebug;
+ return 0;
+ case 'r':
+ ++rejectall;
+ return 0;
+ case 't':
+ tcp = 1;
+ return 0;
+ case 'v':
+ ++chatty;
+ return 0;
+ default:
+ return -1;
+ }
+}
+
+/*
+ * all option parsing is now done in (*pg->init)(), which can call back
+ * here to argopt for common options.
+ */
+void
+server(int argc, char **argv, int myport, Progmap *progmap)
+{
+ Progmap *pg;
+
+ fmtinstall('I', Iconv);
+ fmtinstall('F', fcallfmt);
+ fmtinstall('D', dirfmt);
+
+ switch(rfork(RFNOWAIT|RFENVG|RFNAMEG|RFNOTEG|RFFDG|RFPROC)){
+ case -1:
+ panic("fork");
+ default:
+ _exits(0);
+ case 0:
+ break;
+ }
+
+ switch(rfork(RFMEM|RFPROC)){
+ case 0:
+ for(;;){
+ sleep(30*1000);
+ alarmflag = 1;
+ }
+ case -1:
+ sysfatal("rfork: %r");
+ }
+
+ for(pg=progmap; pg->init; pg++)
+ (*pg->init)(argc, argv);
+ if(tcp)
+ tcpserver(myport, progmap);
+ else
+ udpserver(myport, progmap);
+}
+
+static void
+udpserver(int myport, Progmap *progmap)
+{
+ char service[128];
+ char data[128];
+ char devdir[40];
+ int ctlfd, datafd;
+
+ snprint(service, sizeof service, "udp!*!%d", myport);
+ ctlfd = announce(service, devdir);
+ if(ctlfd < 0)
+ panic("can't announce %s: %r\n", service);
+ if(fprint(ctlfd, "headers") < 0)
+ panic("can't set header mode: %r\n");
+
+ snprint(data, sizeof data, "%s/data", devdir);
+ datafd = open(data, ORDWR);
+ if(datafd < 0)
+ panic("can't open udp data: %r\n");
+ close(ctlfd);
+
+ chatsrv(0);
+ clog("%s: listening to port %d\n", argv0, myport);
+ while (servemsg(datafd, read, write, myport, progmap) >= 0)
+ continue;
+ exits(0);
+}
+
+static void
+tcpserver(int myport, Progmap *progmap)
+{
+ char adir[40];
+ char ldir[40];
+ char ds[40];
+ int actl, lctl, data;
+
+ snprint(ds, sizeof ds, "tcp!*!%d", myport);
+ chatsrv(0);
+ actl = -1;
+ for(;;){
+ if(actl < 0){
+ actl = announce(ds, adir);
+ if(actl < 0){
+ clog("%s: listening to tcp port %d\n",
+ argv0, myport);
+ clog("announcing: %r");
+ break;
+ }
+ }
+ lctl = listen(adir, ldir);
+ if(lctl < 0){
+ close(actl);
+ actl = -1;
+ continue;
+ }
+ switch(fork()){
+ case -1:
+ clog("%s!%d: %r\n", argv0, myport);
+ /* fall through */
+ default:
+ close(lctl);
+ continue;
+ case 0:
+ close(actl);
+ data = accept(lctl, ldir);
+ close(lctl);
+ if(data < 0)
+ exits(0);
+
+ /* pretend it's udp; fill in Udphdr */
+ getendpoints((Udphdr*)buf, ldir);
+
+ while (servemsg(data, readtcp, writetcp, myport,
+ progmap) >= 0)
+ continue;
+ close(data);
+ exits(0);
+ }
+ }
+ exits(0);
+}
+
+static int
+servemsg(int fd, long (*readmsg)(int, void*, long), long (*writemsg)(int, void*, long),
+ int myport, Progmap * progmap)
+{
+ int i, n, nreply;
+ Rpccall rcall, rreply;
+ int vlo, vhi;
+ Progmap *pg;
+ Procmap *pp;
+ char errbuf[ERRMAX];
+
+ if(alarmflag){
+ alarmflag = 0;
+ if(rpcalarm)
+ (*rpcalarm)();
+ }
+ n = (*readmsg)(fd, buf, sizeof buf);
+ if(n < 0){
+ errstr(errbuf, sizeof errbuf);
+ if(strcmp(errbuf, "interrupted") == 0)
+ return 0;
+ clog("port %d: error: %s\n", myport, errbuf);
+ return -1;
+ }
+ if(n == 0){
+ clog("port %d: EOF\n", myport);
+ return -1;
+ }
+ if(rpcdebug == 1)
+ fprint(2, "%s: rpc from %d.%d.%d.%d/%d\n",
+ argv0, buf[12], buf[13], buf[14], buf[15],
+ (buf[32]<<8)|buf[33]);
+ i = rpcM2S(buf, &rcall, n);
+ if(i != 0){
+ clog("udp port %d: message format error %d\n",
+ myport, i);
+ return 0;
+ }
+ if(rpcdebug > 1)
+ rpcprint(2, &rcall);
+ if(rcall.mtype != CALL)
+ return 0;
+ if(replycache(fd, &rcall, writemsg))
+ return 0;
+ nreply = 0;
+ rreply.host = rcall.host;
+ rreply.port = rcall.port;
+ rreply.lhost = rcall.lhost;
+ rreply.lport = rcall.lport;
+ rreply.xid = rcall.xid;
+ rreply.mtype = REPLY;
+ if(rcall.rpcvers != 2){
+ rreply.stat = MSG_DENIED;
+ rreply.rstat = RPC_MISMATCH;
+ rreply.rlow = 2;
+ rreply.rhigh = 2;
+ goto send_reply;
+ }
+ if(rejectall){
+ rreply.stat = MSG_DENIED;
+ rreply.rstat = AUTH_ERROR;
+ rreply.authstat = AUTH_TOOWEAK;
+ goto send_reply;
+ }
+ i = n - (((uchar *)rcall.args) - buf);
+ if(rpcdebug > 1)
+ fprint(2, "arg size = %d\n", i);
+ rreply.stat = MSG_ACCEPTED;
+ rreply.averf.flavor = 0;
+ rreply.averf.count = 0;
+ rreply.results = resultbuf;
+ vlo = 0x7fffffff;
+ vhi = -1;
+ for(pg=progmap; pg->pmap; pg++){
+ if(pg->progno != rcall.prog)
+ continue;
+ if(pg->vers == rcall.vers)
+ break;
+ if(pg->vers < vlo)
+ vlo = pg->vers;
+ if(pg->vers > vhi)
+ vhi = pg->vers;
+ }
+ if(pg->pmap == 0){
+ if(vhi < 0)
+ rreply.astat = PROG_UNAVAIL;
+ else{
+ rreply.astat = PROG_MISMATCH;
+ rreply.plow = vlo;
+ rreply.phigh = vhi;
+ }
+ goto send_reply;
+ }
+ for(pp = pg->pmap; pp->procp; pp++)
+ if(rcall.proc == pp->procno){
+ if(rpcdebug > 1)
+ fprint(2, "process %d\n", pp->procno);
+ rreply.astat = SUCCESS;
+ nreply = (*pp->procp)(i, &rcall, &rreply);
+ goto send_reply;
+ }
+ rreply.astat = PROC_UNAVAIL;
+send_reply:
+ if(nreply >= 0){
+ i = rpcS2M(&rreply, nreply, rbuf);
+ if(rpcdebug > 1)
+ rpcprint(2, &rreply);
+ (*writemsg)(fd, rbuf, i);
+ cachereply(&rreply, rbuf, i);
+ }
+ return 0;
+}
+
+static void
+getendpoint(char *dir, char *file, uchar *addr, uchar *port)
+{
+ 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");
+ parseip(addr, sys);
+ n = atoi(serv);
+ hnputs(port, n);
+}
+
+/* set Udphdr values from protocol dir local & remote files */
+static void
+getendpoints(Udphdr *ep, char *dir)
+{
+ getendpoint(dir, "local", ep->laddr, ep->lport);
+ getendpoint(dir, "remote", ep->raddr, ep->rport);
+}
+
+static long
+readtcp(int fd, void *vbuf, long blen)
+{
+ uchar mk[4];
+ int n, m, sofar;
+ ulong done;
+ char *buf;
+
+ buf = vbuf;
+ buf += Udphdrsize;
+ blen -= Udphdrsize;
+
+ done = 0;
+ for(sofar = 0; !done; sofar += n){
+ m = readn(fd, mk, 4);
+ if(m < 4)
+ return 0;
+ done = (mk[0]<<24)|(mk[1]<<16)|(mk[2]<<8)|mk[3];
+ m = done & 0x7fffffff;
+ done &= 0x80000000;
+ if(m > blen-sofar)
+ return -1;
+ n = readn(fd, buf+sofar, m);
+ if(m != n)
+ return 0;
+ }
+ return sofar + Udphdrsize;
+}
+
+static long
+writetcp(int fd, void *vbuf, long len)
+{
+ char *buf;
+
+ buf = vbuf;
+ buf += Udphdrsize;
+ len -= Udphdrsize;
+
+ buf -= 4;
+ buf[0] = 0x80 | (len>>24);
+ buf[1] = len>>16;
+ buf[2] = len>>8;
+ buf[3] = len;
+ len += 4;
+ return write(fd, buf, len);
+}
+/*
+ *long
+ *niwrite(int fd, void *buf, long count)
+ *{
+ * char errbuf[ERRLEN];
+ * long n;
+ *
+ * for(;;){
+ * n = write(fd, buf, count);
+ * if(n < 0){
+ * errstr(errbuf);
+ * if(strcmp(errbuf, "interrupted") == 0)
+ * continue;
+ * clog("niwrite error: %s\n", errbuf);
+ * werrstr(errbuf);
+ * }
+ * break;
+ * }
+ * return n;
+ *}
+ */
+long
+niwrite(int fd, void *buf, long n)
+{
+// int savalarm;
+
+// savalarm = alarm(0);
+ n = write(fd, buf, n);
+// if(savalarm > 0)
+// alarm(savalarm);
+ return n;
+}
+
+typedef struct Namecache Namecache;
+struct Namecache {
+ char dom[256];
+ ulong ipaddr;
+ Namecache *next;
+};
+
+Namecache *dnscache;
+
+static Namecache*
+domlookupl(void *name, int len)
+{
+ Namecache *n, **ln;
+
+ if(len >= sizeof(n->dom))
+ return nil;
+
+ for(ln=&dnscache, n=*ln; n; ln=&(*ln)->next, n=*ln) {
+ if(strncmp(n->dom, name, len) == 0 && n->dom[len] == 0) {
+ *ln = n->next;
+ n->next = dnscache;
+ dnscache = n;
+ return n;
+ }
+ }
+ return nil;
+}
+
+static Namecache*
+domlookup(void *name)
+{
+ return domlookupl(name, strlen(name));
+}
+
+static Namecache*
+iplookup(ulong ip)
+{
+ Namecache *n, **ln;
+
+ for(ln=&dnscache, n=*ln; n; ln=&(*ln)->next, n=*ln) {
+ if(n->ipaddr == ip) {
+ *ln = n->next;
+ n->next = dnscache;
+ dnscache = n;
+ return n;
+ }
+ }
+ return nil;
+}
+
+static Namecache*
+addcacheentry(void *name, int len, ulong ip)
+{
+ Namecache *n;
+
+ if(len >= sizeof(n->dom))
+ return nil;
+
+ n = malloc(sizeof(*n));
+ if(n == nil)
+ return nil;
+ strncpy(n->dom, name, len);
+ n->dom[len] = 0;
+ n->ipaddr = ip;
+ n->next = dnscache;
+ dnscache = n;
+ return nil;
+}
+
+int
+getdnsdom(ulong ip, char *name, int len)
+{
+ char buf[128];
+ Namecache *nc;
+ char *p;
+
+ if(nc=iplookup(ip)) {
+ strncpy(name, nc->dom, len);
+ name[len-1] = 0;
+ return 0;
+ }
+ clog("getdnsdom: %I\n", ip);
+ snprint(buf, sizeof buf, "%I", ip);
+ p = csgetvalue("/net", "ip", buf, "dom", nil);
+ if(p == nil)
+ return -1;
+ strncpy(name, p, len-1);
+ name[len] = 0;
+ free(p);
+ addcacheentry(name, strlen(name), ip);
+ return 0;
+}
+
+int
+getdom(ulong ip, char *dom, int len)
+{
+ int i;
+ static char *prefix[] = { "", "gate-", "fddi-", "u-", 0 };
+ char **pr;
+
+ if(getdnsdom(ip, dom, len)<0)
+ return -1;
+
+ for(pr=prefix; *pr; pr++){
+ i = strlen(*pr);
+ if(strncmp(dom, *pr, i) == 0) {
+ memmove(dom, dom+i, len-i);
+ break;
+ }
+ }
+ return 0;
+}
+
+#define MAXCACHE 64
+
+static Rpccache *head, *tail;
+static int ncache;
+
+static void
+cachereply(Rpccall *rp, void *buf, int len)
+{
+ Rpccache *cp;
+
+ if(nocache)
+ return;
+
+ if(ncache >= MAXCACHE){
+ if(rpcdebug)
+ fprint(2, "%s: drop %I/%ld, xid %uld, len %d\n",
+ argv0, tail->host,
+ tail->port, tail->xid, tail->n);
+ tail = tail->prev;
+ free(tail->next);
+ tail->next = 0;
+ --ncache;
+ }
+ cp = malloc(sizeof(Rpccache)+len-4);
+ if(cp == 0){
+ clog("cachereply: malloc %d failed\n", len);
+ return;
+ }
+ ++ncache;
+ cp->prev = 0;
+ cp->next = head;
+ if(head)
+ head->prev = cp;
+ else
+ tail = cp;
+ head = cp;
+ cp->host = rp->host;
+ cp->port = rp->port;
+ cp->xid = rp->xid;
+ cp->n = len;
+ memmove(cp->data, buf, len);
+ if(rpcdebug)
+ fprint(2, "%s: cache %I/%ld, xid %uld, len %d\n",
+ argv0, cp->host, cp->port, cp->xid, cp->n);
+}
+
+static int
+replycache(int fd, Rpccall *rp, long (*writemsg)(int, void*, long))
+{
+ Rpccache *cp;
+
+ for(cp=head; cp; cp=cp->next)
+ if(cp->host == rp->host &&
+ cp->port == rp->port &&
+ cp->xid == rp->xid)
+ break;
+ if(cp == 0)
+ return 0;
+ if(cp->prev){ /* move to front */
+ cp->prev->next = cp->next;
+ if(cp->next)
+ cp->next->prev = cp->prev;
+ else
+ tail = cp->prev;
+ cp->prev = 0;
+ cp->next = head;
+ head->prev = cp;
+ head = cp;
+ }
+ (*writemsg)(fd, cp->data, cp->n);
+ if(rpcdebug)
+ fprint(2, "%s: reply %I/%ld, xid %uld, len %d\n",
+ argv0, cp->host, cp->port, cp->xid, cp->n);
+ return 1;
+}
+
+static int
+Iconv(Fmt *f)
+{
+ char buf[16];
+ ulong h;
+
+ h = va_arg(f->args, ulong);
+ snprint(buf, sizeof buf, "%ld.%ld.%ld.%ld",
+ (h>>24)&0xff, (h>>16)&0xff,
+ (h>>8)&0xff, h&0xff);
+ return fmtstrcpy(f, buf);
+}