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");
}