ref: 6c16dccd849e657c3d11d233541e7da3b2992c5e
dir: /server.c/
#include <u.h> #include <libc.h> #include <bio.h> #include <thread.h> #include "neoventi.h" static void vtsend(VtConn conn, char *buf, u16int size, u8int tag, int drop); // Handles an error on `conn` handling client request `tbuf` // Only the tag must be preserved in the buffer static void vterr(VtConn conn, char *tbuf, char *msg, ...) { u16int len; va_list args; va_start(args, msg); msg = vsmprint(msg, args); werrstr(msg); free(msg); va_end(args); if(tbuf != nil){ len = snprint(tbuf+6, 0x10000, "neoventi: %r"); U16PUT(tbuf+4, len); vtsend(conn, tbuf, len+4, VtRerror, 1); } longjmp(conn.bounce, 1); } static void vthangup(VtConn conn) { longjmp(conn.bounce, 2); } // Convenience function: reads a venti packet from conn into buf static void vtrecv(VtConn conn, char *buf) { u16int len; switch(read(conn.fd, buf, 2)){ case 0: vthangup(conn); case 1: vterr(conn, nil, "received a single byte for message size"); case 2: len = U16GET(buf); if(read(conn.fd, buf + 2, len) != len) vterr(conn, nil, "failed to read message: %r"); } } static void vtversion(VtConn conn) { char c; // Response is one line of unknown size; discard bytes until EOL if(fprint(conn.fd, "venti-02-neoventi\n") == 18) while(read(conn.fd, &c, 1) == 1) if(c == '\n') return; // If the handshake fails, make it clear there's a problem and give up fprint(conn.fd, "FUCK OFF\n"); vterr(conn, nil, "handshake failed"); } static void vtsend(VtConn conn, char *buf, u16int size, u8int type, int drop) { U16PUT(buf, size); buf[2] = type; // +2 because we need to send the u16 size as well! if(write(conn.fd, buf, size+2) != size+2){ if(drop) fprint(2, "failed to submit error packet: %r\n"); else vterr(conn, buf, "failed to write packet: %r"); } } static void vtread(VtConn conn, char *buf) { VtAddress addr; if(!vtreadlookup((u8int*)buf + 4, &addr)) sysfatal("todo graceful read errors"); readclump((uchar*)buf+4, addr); vtsend(conn, buf, addr.size+2, VtRread, 0); } static int vtconnhandle(VtConn conn, char *buf) { switch(buf[2]){ case VtTread: vtread(conn, buf); return 1; case VtTgoodbye: vthangup(conn); case VtTsync: vterr(conn, buf, "TODO: sync not supported yet"); default: vterr(conn, buf, "TODO unimplemented request type %d", buf[2]); } return 0; } static void vthello(VtConn conn, char *buf) { vtversion(conn); vtrecv(conn, buf); if(buf[2] != VtThello) vterr(conn, buf, "received message before hello: %d", buf[2]); if(buf[4] != 0 || buf[5] != 2 || buf[6] != '0' || buf[7] != '2') vterr(conn, buf, "unsupported protocol version requested in Thello: %d %d %d %d", buf[4], buf[5], buf[6], buf[7]); buf[6] = 'n'; buf[7] = 'o'; vtsend(conn, buf, 8, VtRhello, 0); } typedef struct { int ctl; char *dir; } ClientHandle; static void vtloop(VtConn conn, char *buf) { while(1){ vtrecv(conn, buf); vtconnhandle(conn, buf); } } static int inittrampoline(ClientHandle *h, VtConn *conn) { switch(setjmp(conn->bounce)){ case 0: return 1; case 1: fprint(2, "abandoning client: %r\n"); case 2: close(conn->fd); free(h); return 0; default: fprint(2, "internal error: unexpected bounce code\n"); return 0; } } static void handleproc(void *hnd) { char buf[MaxPacketSize]; ClientHandle *h = hnd; VtConn conn; if(!inittrampoline(h, &conn)) return; if((conn.fd = accept(h->ctl, h->dir)) < 0) vterr(conn, nil, "failed to accept connection: %r"); vthello(conn, buf); vtloop(conn, buf); } static void handle(int ctl, char *dir) { ClientHandle *handle = malloc(sizeof(ClientHandle)); handle->ctl = ctl; handle->dir = dir; proccreate(handleproc, handle, mainstacksize); } void serve(char *addr) { char adir[NETPATHLEN], dir[NETPATHLEN]; int fd, ctl; fd = announce(addr, adir); if(fd < 0) sysfatal("%r"); procsetname("neoventi/server"); for(ctl = listen(adir, dir); ctl >= 0; ctl = listen(adir, dir)){ handle(ctl, dir); close(ctl); } fprint(2, "server has died\n"); }