ref: 1fa21bad206a0b7fc447f8aaee81d54e09f73360
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"); tbuf[4] = len >> 8; tbuf[5] = len & 0xFF; len += 4; vtsend(conn, tbuf, len, 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 tag, int drop) { buf[0] = size>>8; buf[1] = size&0xFF; buf[2] = tag; 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) { u8int *score; VtArena arena; u64int addr; u8int blocks; u16int size; score = (u8int*)buf + 4; if(!vtreadlookup(score, &arena, &addr, &size, &blocks)) sysfatal("todo graceful read errors"); // Response: VtRread, msg tag, data readclump((uchar*)buf+4, arena, addr, blocks); vtsend(conn, buf, 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 safely hang up vtconns"); } 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 handleproc(void *hnd) { ClientHandle *h = hnd; char buf[MaxPacketSize]; VtConn conn; switch(setjmp(conn.bounce)){ case 0: break; case 1: fprint(2, "abandoning client: %r\n"); case 2: close(conn.fd); free(h); return; default: fprint(2, "internal error: unexpected bounce code\n"); break; } conn.fd = accept(h->ctl, h->dir); if(conn.fd < 0) vterr(conn, nil, "failed to accept connection: %r"); vthello(conn, buf); while(1){ vtrecv(conn, buf); if(!vtconnhandle(conn, buf)){ close(conn.fd); return; } } } 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"); }