ref: 1dccb7eb6d6eca49acddd9f2ed95d2e11880a339
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 = (buf[0] << 8 | buf[1]);
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");
}