#include #include #include #include #include <9p.h> #include #include #include #include #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.3"; 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]); } }