697 lines
15 KiB
C
697 lines
15 KiB
C
#include <u.h>
|
|
#include <libc.h>
|
|
#include <fcall.h>
|
|
#include <thread.h>
|
|
#include <9p.h>
|
|
#include <stdio.h>
|
|
#include <dirent.h>
|
|
#include <libString.h>
|
|
#include <string.h>
|
|
#include "config.h"
|
|
#include "err.h"
|
|
#include "command.h"
|
|
#include "util.h"
|
|
#include "aux.h"
|
|
#include "cart.h"
|
|
#include "universe.h"
|
|
#include "realm.h"
|
|
#include "user.h"
|
|
|
|
char version[] = "0.4";
|
|
|
|
int chatty9p = 0;
|
|
|
|
static Tree* tree;
|
|
|
|
static UserInfo users_table[MAX_USERS];
|
|
|
|
void helpme(char* arg0) {
|
|
printf("usage: %s [-m MOUNTPOINT | -s SOCKET | -v | -h|--help]\n", arg0);
|
|
printf("Serve xrxs game infrastructure over 9p.\n\n");
|
|
|
|
printf(" -m MOUNTPOINT: mount the 9p filesystem locally at MOUNTPOINT\n");
|
|
printf(" -s SOCKET: create a socket for the 9p filesystem with the "
|
|
"namespace SOCKET\n");
|
|
printf(" -v: print version information\n");
|
|
printf(" -h | --help: print this help text\n\n");
|
|
threadexits(0);
|
|
}
|
|
|
|
void xrxs_attach(Req* r) {
|
|
/* As it is, once the user detaches, they will stay in the table
|
|
* until the server restarts. We have to figure out some way
|
|
* to detect a detach to remove them... You can't delay respond()
|
|
* or the attach will never complete. We have logout() for now...
|
|
*/
|
|
int i = 0;
|
|
int vacancy = 0;
|
|
char* usr;
|
|
char* username = r->ifcall.uname;
|
|
|
|
for (i = 0; i < MAX_USERS; i++) {
|
|
usr = users_table[i].name;
|
|
if (scmp(usr, username)) {
|
|
respond(r, EUNAME);
|
|
return;
|
|
}
|
|
if (*usr == 0) {
|
|
scpy(username, usr, 32);
|
|
users_table[i].random = -1;
|
|
vacancy = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (vacancy) {
|
|
respond(r, nil);
|
|
} else {
|
|
respond(r, EUFULL);
|
|
}
|
|
}
|
|
|
|
void write_ctl(Req* r) {
|
|
char cmd[16] = {0};
|
|
char* c = r->ifcall.data;
|
|
int i;
|
|
int* code = ctl_code_handle(users_table, r->fid->uid);
|
|
|
|
for (i = 0; i < r->ifcall.count && *c != ' ' && *c != '\n'; i++) {
|
|
ccat(cmd, *c++);
|
|
}
|
|
if (*c == ' ')
|
|
c++;
|
|
scsw(c, '\n', 0);
|
|
|
|
fprintf(stderr, "%s(%s)\n", cmd, c);
|
|
uvlong const cmd_hashv = hash(cmd, 0);
|
|
switch (cmd_hashv) {
|
|
case LOGIN:
|
|
*code = login(users_table, r->fid->uid, c);
|
|
break;
|
|
case LOAD:
|
|
*code = load_cart(users_table, r->fid->uid, c);
|
|
break;
|
|
case CHUNK:
|
|
*code = get_chunk(users_table, r->fid->uid, c);
|
|
break;
|
|
case CREATE:
|
|
*code = create_realm(users_table, r->fid->uid, c) != nil ? 1 : 0;
|
|
break;
|
|
case PROTECT:
|
|
*code = protect_realm(users_table, r->fid->uid, c);
|
|
break;
|
|
case TRANSFER:
|
|
*code = transfer_realm(users_table, r->fid->uid, c);
|
|
break;
|
|
case DELETE:
|
|
*code = delete_realm(users_table, r->fid->uid, c);
|
|
break;
|
|
case ENTER:
|
|
*code = enter_realm(users_table, r->fid->uid, c);
|
|
break;
|
|
case LEAVE:
|
|
*code = leave_realm(users_table, r->fid->uid);
|
|
break;
|
|
case LOGOUT:
|
|
logout(users_table, r->fid->uid);
|
|
break;
|
|
case SAVE:
|
|
// save(r->fid->uid);
|
|
break;
|
|
case RESET:
|
|
// reset(r->fid->uid);
|
|
break;
|
|
case UNLOAD:
|
|
*code = unload_cart(users_table, r->fid->uid);
|
|
break;
|
|
}
|
|
r->ofcall.count = r->ifcall.count;
|
|
r->fid->file->dir.length = r->ifcall.count;
|
|
respond(r, nil);
|
|
}
|
|
|
|
void write_universe(Req* r) {
|
|
char key[16] = {0};
|
|
char value[64] = {0};
|
|
char buffer[1024] = {0};
|
|
UserInfo* u = find_user(users_table, r->fid->uid);
|
|
Atom* a;
|
|
|
|
scpy(r->ifcall.data, buffer, r->ifcall.count);
|
|
sscanf(buffer, "%15s = %63s", key, value);
|
|
|
|
/* for (i = 0; i < 15 && i < r->ifcall.count && *c != ' ' && *c != '\n'; i++)
|
|
{ ccat(key, *c++);
|
|
}
|
|
c++;
|
|
c++;
|
|
for (i = 0; i < 63 && i < r->ifcall.count && *c != ' ' && *c != '\n'; i++) {
|
|
ccat(value, *c++);
|
|
}
|
|
*/
|
|
if (u != nil && u->realm != nil && u->realm->universe != nil) {
|
|
a = get_atom(u->realm->universe, key);
|
|
if (a != nil) {
|
|
scpy(value, a->value, 64);
|
|
} else {
|
|
a = malloc(sizeof(Atom));
|
|
scpy(key, a->name, 16);
|
|
scpy(value, a->value, 64);
|
|
a->next = nil;
|
|
set_atom(u->realm->universe, a);
|
|
}
|
|
}
|
|
r->ofcall.count = r->ifcall.count;
|
|
r->fid->file->dir.length = r->ifcall.count;
|
|
respond(r, nil);
|
|
}
|
|
|
|
void write_scope(Req* r) {
|
|
String* scope = s_new();
|
|
char* c = r->ifcall.data;
|
|
UserInfo* u = find_user(users_table, r->fid->uid);
|
|
int i;
|
|
|
|
for (i = 0; i < r->ifcall.count; i++) {
|
|
s_putc(scope, *c++);
|
|
}
|
|
s_terminate(scope);
|
|
fprintf(stderr, scope->base);
|
|
|
|
if (u != nil && u->realm != nil && u->realm->universe != nil) {
|
|
u->scope = malloc(r->ifcall.count);
|
|
memcpy(u->scope, scope->base, r->ifcall.count);
|
|
}
|
|
s_free(scope);
|
|
r->ofcall.count = r->ifcall.count;
|
|
r->fid->file->dir.length = r->ifcall.count;
|
|
respond(r, nil);
|
|
}
|
|
|
|
void xrxs_write(Req* r) {
|
|
Aux* a = r->fid->file->aux;
|
|
switch (a->type) {
|
|
case CTL:
|
|
write_ctl(r);
|
|
break;
|
|
case UNIVERSE:
|
|
write_universe(r);
|
|
break;
|
|
case SCOPE:
|
|
write_scope(r);
|
|
break;
|
|
default:
|
|
respond(r, nil);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void read_users(Req* r) {
|
|
String* data = s_new();
|
|
int i;
|
|
for (i = 0; i < MAX_USERS; i++) {
|
|
if (scmp(users_table[i].name, r->fid->uid)) {
|
|
s_append(data, users_table[i].name);
|
|
s_putc(data, '\n');
|
|
break;
|
|
}
|
|
}
|
|
for (i = 0; i < MAX_USERS; i++) {
|
|
if (
|
|
scmp(users_table[i].name, "\0") ||
|
|
scmp(users_table[i].name, r->fid->uid)) {
|
|
continue;
|
|
}
|
|
s_append(data, users_table[i].name);
|
|
s_putc(data, '\n');
|
|
}
|
|
s_terminate(data);
|
|
readstr(r, data->base);
|
|
s_free(data);
|
|
respond(r, nil);
|
|
}
|
|
|
|
String** list_dir(char* path) {
|
|
int size = 128;
|
|
String** self = malloc(size * sizeof(String*));
|
|
DIR* dir;
|
|
struct dirent* ent;
|
|
int i = 0;
|
|
char* c;
|
|
if ((dir = opendir(path)) != NULL) {
|
|
while ((ent = readdir(dir)) != NULL) {
|
|
if (i == size) {
|
|
size *= 2;
|
|
self = realloc(self, size * sizeof(String*));
|
|
}
|
|
c = ent->d_name;
|
|
if (scmp(c, ".") || scmp(c, "..")) {
|
|
continue;
|
|
}
|
|
self[i] = s_new();
|
|
|
|
while (*c) {
|
|
s_putc(self[i], *c++);
|
|
}
|
|
s_terminate(self[i++]);
|
|
}
|
|
closedir(dir);
|
|
}
|
|
self[i] = nil;
|
|
return self;
|
|
}
|
|
|
|
void s_freemany(String** ss) {
|
|
String** s = ss;
|
|
while (*s != nil) {
|
|
s_free(*s++);
|
|
}
|
|
free(ss);
|
|
}
|
|
|
|
void read_ctl(Req* r) {
|
|
int* code = ctl_code_handle(users_table, r->fid->uid);
|
|
char buf[8] = {0};
|
|
if (code != nil) {
|
|
if (*code) {
|
|
strcat(buf, "1\n");
|
|
} else {
|
|
strcat(buf, "0\n");
|
|
}
|
|
} else {
|
|
strcat(buf, "-1\n");
|
|
}
|
|
readstr(r, buf);
|
|
respond(r, nil);
|
|
}
|
|
|
|
void read_carts(Req* r) {
|
|
String** carts = list_dir(DATA_DIR);
|
|
String** c = carts;
|
|
String* data = s_new();
|
|
while (*c != nil) {
|
|
s_append(data, (*c)->base);
|
|
s_putc(data, '\n');
|
|
c++;
|
|
}
|
|
s_terminate(data);
|
|
readstr(r, data->base);
|
|
respond(r, nil);
|
|
s_free(data);
|
|
s_freemany(carts);
|
|
}
|
|
|
|
void read_slot(Req* r) {
|
|
UserInfo* u = find_user(users_table, r->fid->uid);
|
|
if (u == nil)
|
|
goto end;
|
|
|
|
if (u->cart == nil)
|
|
goto end;
|
|
|
|
readbuf(r, u->cart->rom->data, u->cart->rom->length);
|
|
end:
|
|
respond(r, nil);
|
|
}
|
|
|
|
void read_data(Req* r, FileType t) {
|
|
UserInfo* u = find_user(users_table, r->fid->uid);
|
|
vlong offset = r->ifcall.offset;
|
|
long count = r->ifcall.count;
|
|
|
|
if (u->cart == nil)
|
|
goto end;
|
|
|
|
switch (t) {
|
|
case SPRITE_DATA:
|
|
if (
|
|
u->cart->sprite_data != nil && offset < u->cart->sprite_data->length) {
|
|
if (offset + count >= u->cart->sprite_data->length)
|
|
count = u->cart->sprite_data->length - offset;
|
|
readbuf(r, u->cart->sprite_data->data + offset, count);
|
|
}
|
|
break;
|
|
case AUDIO_DATA:
|
|
if (u->cart->audio_data != nil && offset < u->cart->audio_data->length) {
|
|
if (offset + count >= u->cart->audio_data->length)
|
|
count = u->cart->audio_data->length - offset;
|
|
readbuf(r, u->cart->audio_data->data + offset, count);
|
|
}
|
|
break;
|
|
case TEXT_DATA:
|
|
if (u->cart->txt_data != nil && offset < u->cart->txt_data->length) {
|
|
if (offset + count >= u->cart->txt_data->length)
|
|
count = u->cart->txt_data->length - offset;
|
|
readbuf(r, u->cart->txt_data->data + offset, count);
|
|
}
|
|
break;
|
|
}
|
|
end:
|
|
r->ofcall.count = count;
|
|
respond(r, nil);
|
|
}
|
|
|
|
void read_realms(Req* r) {
|
|
UserInfo* user = find_user(users_table, r->fid->uid);
|
|
char realm_path[256] = {0};
|
|
String** realms;
|
|
String** rr;
|
|
Realm* realm;
|
|
String* data = s_new();
|
|
int i, u, m, p;
|
|
char ubuf[8] = {0};
|
|
char mbuf[8] = {0};
|
|
char pbuf[2] = {0};
|
|
|
|
if (user->cart == nil)
|
|
respond(r, nil);
|
|
|
|
scat(realm_path, DATA_DIR);
|
|
scat(realm_path, user->cart->name);
|
|
scat(realm_path, "/realms");
|
|
|
|
realms = list_dir(realm_path);
|
|
rr = realms;
|
|
|
|
while (*rr != nil) {
|
|
s_append(data, (*rr)->base);
|
|
s_putc(data, ' ');
|
|
|
|
realm = parse_realm(user->cart->name, (*rr)->base);
|
|
m = realm->max;
|
|
p = realm->password ? 1 : 0;
|
|
|
|
for (i = u = 0; i < MAX_USERS; i++) {
|
|
if (
|
|
users_table[i].realm != nil &&
|
|
scmp(users_table[i].realm->name, realm->name))
|
|
u++;
|
|
}
|
|
itoa(u, ubuf, 10);
|
|
s_append(data, ubuf);
|
|
s_putc(data, ' ');
|
|
itoa(m, mbuf, 10);
|
|
s_append(data, mbuf);
|
|
s_putc(data, ' ');
|
|
itoa(p, pbuf, 10);
|
|
s_append(data, pbuf);
|
|
s_putc(data, '\n');
|
|
rr++;
|
|
}
|
|
s_terminate(data);
|
|
readstr(r, data->base);
|
|
respond(r, nil);
|
|
s_free(data);
|
|
s_freemany(realms);
|
|
}
|
|
|
|
void read_universe(Req* r) {
|
|
char* uname = r->fid->uid;
|
|
UserInfo* u = find_user(users_table, uname);
|
|
String* data = s_new();
|
|
Universe* universe;
|
|
Atom* a;
|
|
int i;
|
|
|
|
if (u == nil || u->realm == nil) {
|
|
fprintf(stderr, "realm nil, wtf\n");
|
|
respond(r, nil);
|
|
return;
|
|
}
|
|
|
|
universe = u->realm->universe;
|
|
if (universe == nil) {
|
|
fprintf(stderr, "no universe, oh noes!!\n");
|
|
respond(r, ENOUNI);
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < 256; i++) {
|
|
a = universe->atoms[i];
|
|
while (a != nil) {
|
|
s_append(data, a->name);
|
|
s_append(data, " = ");
|
|
s_append(data, a->value);
|
|
s_putc(data, '\n');
|
|
a = a->next;
|
|
}
|
|
}
|
|
s_terminate(data);
|
|
readstr(r, data->base);
|
|
respond(r, nil);
|
|
s_free(data);
|
|
}
|
|
|
|
void read_scope(Req* r) {
|
|
char* uname = r->fid->uid;
|
|
UserInfo* u = find_user(users_table, uname);
|
|
String* data = s_new();
|
|
Universe* universe;
|
|
Atom* a;
|
|
char key[16] = {0};
|
|
char* c;
|
|
|
|
if (u == nil || u->scope == nil || u->realm == nil) {
|
|
respond(r, nil);
|
|
return;
|
|
}
|
|
|
|
c = u->scope;
|
|
universe = u->realm->universe;
|
|
if (universe == nil) {
|
|
respond(r, ENOUNI);
|
|
return;
|
|
}
|
|
|
|
while (*c) {
|
|
while (*c != '\n') {
|
|
ccat(key, *c++);
|
|
}
|
|
a = get_atom(universe, key);
|
|
if (a != nil) {
|
|
s_append(data, a->value);
|
|
s_putc(data, '\n');
|
|
}
|
|
*key = '\0';
|
|
if (*c == '\0')
|
|
break;
|
|
c++;
|
|
}
|
|
|
|
s_terminate(data);
|
|
readstr(r, data->base);
|
|
respond(r, nil);
|
|
s_free(data);
|
|
}
|
|
|
|
void read_random(Req* r) {
|
|
char buf[8] = {0};
|
|
srand(rand());
|
|
sprintf(buf, "%d\n", rand() % 100);
|
|
readstr(r, buf);
|
|
respond(r, nil);
|
|
}
|
|
|
|
void read_grandom(Req* r) {
|
|
char buf[8] = {0};
|
|
int i;
|
|
int reset = 1;
|
|
int random;
|
|
UserInfo* u = find_user(users_table, r->fid->uid);
|
|
UserInfo** usrs = malloc(MAX_USERS * sizeof(UserInfo*));
|
|
UserInfo* p = users_table;
|
|
UserInfo** uu = usrs;
|
|
|
|
if (u->realm == nil) {
|
|
respond(r, nil);
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < MAX_USERS; i++) {
|
|
if (scmp(p->realm->name, u->realm->name)) {
|
|
*uu++ = p;
|
|
if (i < MAX_USERS - 1)
|
|
*uu = nil;
|
|
}
|
|
}
|
|
uu = usrs;
|
|
for (i = 0; i < MAX_USERS; i++) {
|
|
if ((*uu) != nil && (*uu)->random >= 0) {
|
|
reset = 0;
|
|
break;
|
|
}
|
|
uu++;
|
|
}
|
|
if (reset) {
|
|
srand(rand());
|
|
random = rand() % 100;
|
|
uu = usrs;
|
|
for (i = 0; i < MAX_USERS; i++) {
|
|
if ((*uu) != nil) {
|
|
(*uu)->random = random;
|
|
}
|
|
uu++;
|
|
}
|
|
}
|
|
sprintf(buf, "%d\n", u->random);
|
|
u->random = -1;
|
|
readstr(r, buf);
|
|
respond(r, nil);
|
|
}
|
|
|
|
void read_version(Req* r) {
|
|
char buf[16];
|
|
sprintf(buf, "%s\n", version);
|
|
readstr(r, buf);
|
|
respond(r, nil);
|
|
}
|
|
|
|
void xrxs_read(Req* r) {
|
|
Aux* a = r->fid->file->aux;
|
|
switch (a->type) {
|
|
case CTL:
|
|
read_ctl(r);
|
|
break;
|
|
case USERS:
|
|
read_users(r);
|
|
break;
|
|
case CARTS:
|
|
read_carts(r);
|
|
break;
|
|
case SLOT:
|
|
read_slot(r);
|
|
break;
|
|
case SPRITE_DATA:
|
|
case AUDIO_DATA:
|
|
case TEXT_DATA:
|
|
read_data(r, a->type);
|
|
break;
|
|
case REALMS:
|
|
read_realms(r);
|
|
break;
|
|
case UNIVERSE:
|
|
read_universe(r);
|
|
break;
|
|
case SCOPE:
|
|
read_scope(r);
|
|
break;
|
|
case RANDOM:
|
|
read_random(r);
|
|
break;
|
|
case GRANDOM:
|
|
read_grandom(r);
|
|
break;
|
|
case VERSION:
|
|
read_version(r);
|
|
break;
|
|
default:
|
|
respond(r, nil);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void fs_destroy_file(File* f) {
|
|
Aux* a = f->aux;
|
|
if (a && a->data) {
|
|
free(a->data);
|
|
free(a);
|
|
} else if (a) {
|
|
free(a);
|
|
}
|
|
}
|
|
|
|
Srv fs = {.attach = xrxs_attach, .read = xrxs_read, .write = xrxs_write};
|
|
|
|
int threadmaybackground(void) { return 1; }
|
|
|
|
void threadmain(int argc, char* argv[]) {
|
|
char* mtpt = nil;
|
|
char* usocket = nil;
|
|
int i;
|
|
|
|
/* if -h CMD is given, print the hash value of CMD */
|
|
if (argc == 3 && scmp(argv[1], "-h")) {
|
|
printf("%llu\n", hash(argv[2], 0));
|
|
return;
|
|
}
|
|
|
|
/* if -m PATH is supplied, mount on PATH */
|
|
/* if -s NAME is supplied, create a socket for the namespace */
|
|
/* otherwise, just use srv() (for wrapping with socat or inetd) */
|
|
|
|
if (argc >= 3) {
|
|
for (i = 0; i < argc; i++) {
|
|
if (scmp(argv[i], "-m")) {
|
|
mtpt = argv[++i];
|
|
printf("serving on %s", mtpt);
|
|
} else if (scmp(argv[i], "-s")) {
|
|
usocket = argv[++i];
|
|
printf("serving socket namespace %s", usocket);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < argc; i++) {
|
|
if (scmp(argv[i], "-d")) {
|
|
chatty9p = 1;
|
|
}
|
|
if (scmp(argv[i], "-v")) {
|
|
printf("xrxs v%s\n", version);
|
|
threadexits(0);
|
|
}
|
|
|
|
if (scmp(argv[i], "-h") || scmp(argv[i], "--help")) {
|
|
helpme(argv[0]);
|
|
}
|
|
}
|
|
|
|
fs.foreground = 1;
|
|
fs.tree = alloctree(nil, nil, DMDIR | 0777, fs_destroy_file);
|
|
tree = fs.tree;
|
|
|
|
closefile(createfile(tree->root, "version", nil, 0400, create_aux(VERSION)));
|
|
closefile(
|
|
createfile(tree->root, "ctl", nil, DMAPPEND | 0600, create_aux(CTL)));
|
|
closefile(createfile(tree->root, "carts", nil, 0400, create_aux(CARTS)));
|
|
closefile(createfile(tree->root, "users", nil, 0400, create_aux(USERS)));
|
|
closefile(createfile(tree->root, "slot", nil, 0400, create_aux(SLOT)));
|
|
closefile(createfile(tree->root, "data", nil, DMDIR | 0500, nil));
|
|
closefile(createfile(
|
|
walkfile(tree->root, "data"),
|
|
"sprite",
|
|
nil,
|
|
0400,
|
|
create_aux(SPRITE_DATA)));
|
|
closefile(createfile(
|
|
walkfile(tree->root, "data"),
|
|
"audio",
|
|
nil,
|
|
0400,
|
|
create_aux(AUDIO_DATA)));
|
|
closefile(createfile(
|
|
walkfile(tree->root, "data"),
|
|
"text",
|
|
nil,
|
|
0400,
|
|
create_aux(TEXT_DATA)));
|
|
closefile(createfile(tree->root, "realms", nil, 0400, create_aux(REALMS)));
|
|
closefile(createfile(
|
|
tree->root,
|
|
"universe",
|
|
nil,
|
|
DMAPPEND | 0600,
|
|
create_aux(UNIVERSE)));
|
|
closefile(
|
|
createfile(tree->root, "scope", nil, DMAPPEND | 0600, create_aux(SCOPE)));
|
|
closefile(createfile(tree->root, "random", nil, 0400, create_aux(RANDOM)));
|
|
closefile(createfile(tree->root, "grandom", nil, 0400, create_aux(GRANDOM)));
|
|
|
|
if (argc >= 3) {
|
|
if (mtpt != nil && access(mtpt, AEXIST) < 0 && access(mtpt, AEXIST) < 0)
|
|
sysfatal("mountpoint %s does not exist", mtpt);
|
|
|
|
threadpostmountsrv(&fs, usocket, mtpt, MREPL | MCREATE);
|
|
threadexits(0);
|
|
} else {
|
|
helpme(argv[0]);
|
|
}
|
|
}
|