all repos — xrxs @ 4fab56e77f805cfecaf6be6deea189f365c0c4d6

experimental networked application/game server with 9p

add config.h, ctl status codes, and delete realm command
Iris Lightshard nilix@nilfm.cc
PGP Signature
-----BEGIN PGP SIGNATURE-----

iQIzBAABCAAdFiEEkFh6dA+k/6CXFXU4O3+8IhROY5gFAmEI59wACgkQO3+8IhRO
Y5gcmA/+M2ctnEsqoxzNAB6FBedKU1Of/4+MMUcUq+rmbYpJfvlMmFyQVl22E6uH
UdSTTZ/Q64Bh8Lhzijok3ESbspTfFaSDdHYQOogR/z69PyoZDz+YV6N81wYE2Zbg
4krw+tETJcQUzzdlGQNCvphlBR7jAQt5Y8Qe0QD249i3Xhu9SBZuna/C5qBK7qQj
g5ZOtQb2IRxFnoEsNP3vz4WY8MRsXq7uU8p3Csk5TkUpmvbGDyk6M+9y2LL81HN+
4H0+ELd3I6NMdDaxUGzNS8sNLmExaaua/UTV9SucdJfXThozH4qh+pUv6o3JZbmV
WavE1LcEXn+N9tn5K8Xcwa09lWXEsxpcvKVfV8kpf4FhJcAnOELeX3YDzHX38xqc
EaiQ5R5G9Y70CsZgJj+U7KzG/qLsW3W7lJojuc22btLxvaia6fsa2SjtiaqQmqeD
sTfFijsPIq0jBSzKHq8OS9tOFRYP3tTCWgFs3mat6x32NVYdOkqUa2Amn/evsNqD
RLUvE7D4PZB2CGVjktmiWCussUxjHoxxdD/A6mb8F78pQNEnQNcFFPow13W70AJM
6gKkNyLBGqavm/+ycYQ2BGCzivfOb62w5zHW0z+mOq1qSTK8+0Mm5EjYhmh+s2N2
7hQ7wBRN28fotn7h9zj00dzaAbs0ZDRGbbgsRCwLqicppWH0zAY=
=J6Xv
-----END PGP SIGNATURE-----
commit

4fab56e77f805cfecaf6be6deea189f365c0c4d6

parent

e9839b1663cb0052a845940a52e3c28ea90a1ff1

12 files changed, 220 insertions(+), 66 deletions(-)

jump to
M README.mdREADME.md

@@ -8,16 +8,18 @@ ## design

This is the working structure of the 9p filesystem: -* `/ctl`: Write-only control file for inputing system commands - * `login PW`: Authenticate with `xrxs` -- password is hashed against realm password hash - * `load CART`: Load a cartridge - * `chunk TYPE N`: Load data of type TYPE and chunk number N +* `/ctl`: Read/write control file for inputing system commands. Reading the file shows the status of the last input command: 1 for success, 0 for failure; `logout` is a special case, and the status code will be -1 if it was succesful. the following are valid command syntax: + * `login PW`: Authenticate with `xrxs` -- password is hashed against realm password hash. + * `logout`: Gracefully remove yourself from the users table. + * `load CART`: Load a cartridge. + * `chunk TYPE N`: Load data of type TYPE and chunk number N. * `create REALM`: Create a new realm (start a new game) -- must have a cartridge loaded * `protect PW`: Protect the curent realm with a password if you are the master. * `transfer USER`: Transfer ownership of the realm to another user. - * `enter REALM`: Join an existing realm - * `leave`: Leave the current realm - * `unload`: Unload the cartridge + * `delete REALM`: Delete the realm off the server if you are the master and it is empty. + * `enter REALM`: Join an existing realm. + * `leave`: Leave the current realm. + * `unload`: Unload the cartridge. * `/users`: Read-only; Self and others in the realm are readable from here, one per line. It contains only yourself before joining a realm. Your username on your machine is used as your username in `xrxs` -- if your name is taken, you will get an error on attaching.

@@ -36,6 +38,13 @@

* `/random`: Read-only, get a random number from 0 to 99. * `/grandom`: Read-only, get a random number from 0 to 99 -- These are doled out on a per-realm basis, and the number stays the same until everyone in the realm has had a chance to read it. If you've already read it this round or aren't in a realm, it will be empty. + +## configuration + +`config.h` in the source contains the following configuration macros: + + * `MAX_USERS`: the maximum number of simultaneous users able to attach to the `xrxs` service + * `DATA_DIR`: the path to the root of the cartridge and realm storage; can be absolute or relative to the `xrxs` executable, but must have the trailing `/` ## realm format
M cart.ccart.c

@@ -1,18 +1,19 @@

#include <u.h> #include <libc.h> +#include "config.h" #include "util.h" #include "user.h" #include "cart.h" Cart* create_cart(char* name) { - char path[64] = {0}; - char file[64] = {0}; + char path[256] = {0}; + char file[256] = {0}; Cart* cart; Blob* cart_data; - scat(path, "carts/"); + scat(path, DATA_DIR); scat(path, name); - scpy(path, file, 64); + scpy(path, file, 256); ccat(file, '/'); scat(file, name); scat(file, ".rom");
M command.hcommand.h

@@ -5,6 +5,7 @@ CHUNK = 190974201,

CREATE = 7083959236, PROTECT = 295480618573, TRANSFER = 11311529048177, + DELETE = 7129299147, ENTER = 195024746, LEAVE = 207662601, LOGOUT = 7702552890,
A config.h

@@ -0,0 +1,5 @@

+/* Maximum number of users on the server */ +#define MAX_USERS 64 + +/* Path to cart/realm storage (must include trailing slash) */ +#define DATA_DIR "/home/nilix/src/xrxs/carts/"
M realm.crealm.c

@@ -1,6 +1,7 @@

#include <u.h> #include <libc.h> #include <stdio.h> +#include "config.h" #include "util.h" #include "user.h" #include "cart.h"

@@ -13,7 +14,7 @@ UserInfo* u = find_user(table, uname);

char cart[32]; int max = 4; char* n = name; - char path[128] = {0}; + char path[256] = {0}; if (u == nil || u->cart == nil) return 0;

@@ -28,7 +29,7 @@ n++;

max = atoi(n); } - scat(path, "carts/"); + scat(path, DATA_DIR); scat(path, cart); scat(path, "/realms/"); if (open(path, OREAD) < 0)

@@ -55,14 +56,14 @@

Realm* parse_realm(char* cart, char* name) { Realm* self; FILE* f; - char path[128] = {0}; - char file[128] = {0}; + char path[256] = {0}; + char file[256] = {0}; char buf[256] = {0}; - scat(path, "carts/"); + scat(path, DATA_DIR); scat(path, cart); scat(path, "/realms/"); scat(path, name); - scpy(path, file, 128); + scpy(path, file, 256); scat(file, "/realm");

@@ -85,7 +86,7 @@ } else {

return nil; } - scpy(path, file, 128); + scpy(path, file, 256); scat(file, "/universe"); self->universe = parse_universe(cart, name); return self;

@@ -95,7 +96,7 @@ Realm* find_realm(UserInfo* table, char* name) {

UserInfo* u = table; int i; - for (i = 0; i < 64; i++) { + for (i = 0; i < MAX_USERS; i++) { if (slen(u->name) > 0 && u->realm != nil && scmp(u->realm->name, name)) return u->realm; u++;

@@ -105,13 +106,13 @@ }

void save_realm(char* cart, Realm* self) { FILE* f; - char path[128] = {0}; - char file[128] = {0}; - scat(path, "carts/"); + char path[256] = {0}; + char file[256] = {0}; + scat(path, DATA_DIR); scat(path, cart); scat(path, "/realms/"); scat(path, self->name); - scpy(path, file, 128); + scpy(path, file, 256); scat(file, "/realm"); f = fopen(file, "w");
M universe.cuniverse.c

@@ -1,6 +1,7 @@

#include <u.h> #include <libc.h> #include <stdio.h> +#include "config.h" #include "util.h" #include "universe.h"

@@ -14,7 +15,7 @@ return self;

} Universe* parse_universe(char* cart, char* realm_name) { - char path[64] = {0}; + char path[256] = {0}; char buf[256] = {0}; char name[16] = {0}; char value[64] = {0};

@@ -22,7 +23,7 @@ FILE* f;

Atom* a; Universe* self; - scat(path, "carts/"); + scat(path, DATA_DIR); scat(path, cart); scat(path, "/realms/"); scat(path, realm_name);

@@ -52,12 +53,12 @@ return nil;

} void save_universe(char* cart, Universe* self, char* realm_name) { - char path[64] = {0}; + char path[256] = {0}; FILE* f; Atom* a; int i; - scat(path, "carts/"); + scat(path, DATA_DIR); scat(path, cart); scat(path, "/realms/"); scat(path, realm_name);
M universe.huniverse.h

@@ -4,7 +4,7 @@ typedef struct Atom Atom;

struct Atom { char name[16]; - char value[64]; + char value[MAX_USERS]; Atom* next; };
M user.cuser.c

@@ -1,6 +1,8 @@

#include <u.h> +#include <sys/stat.h> #include <libc.h> #include <stdio.h> +#include "config.h" #include "util.h" #include "cart.h" #include "realm.h"

@@ -11,11 +13,18 @@

UserInfo* find_user(UserInfo* table, char* uname) { UserInfo* u = table; int i; - for (i = 0; i < 64; i++) { + for (i = 0; i < MAX_USERS; i++) { if (scmp(uname, u->name)) return u; u++; } + return nil; +} + +int* ctl_code_handle(UserInfo* table, char* uname) { + UserInfo* u = find_user(table, uname); + if (u != nil) + return &(u->ctl_code); return nil; }

@@ -43,7 +52,7 @@ free(u->scope);

u->scope = nil; } - u->random = 0; + u->random = -1; if (u->realm != nil) leave_realm(table, uname);

@@ -104,8 +113,7 @@ fprintf(stderr, "no cart\n");

return 0; } - for (i = 0; i < 64; i++) { - fprintf(stderr, "iterating through users table: slot %d\n", i); + for (i = 0; i < MAX_USERS; i++) { if (table[i].realm != nil && scmp(table[i].realm->name, realm_name)) j++; }

@@ -162,4 +170,49 @@

destroy_cart(u->cart); u->cart = nil; return 1; +} + +int delete_realm(UserInfo* table, char* uname, char* realm) { + UserInfo* u = find_user(table, uname); + char path[256] = {0}; + char file[256] = {0}; + Blob* realm_data; + char garbage[32] = {0}; + char master[32] = {0}; + int i; + if (u == nil || u->cart == nil) { + fprintf(stderr, "no user/cart\n"); + return 0; + } + + for (i = 0; i < MAX_USERS; i++) { + if (table[i].realm != nil && scmp(table[i].realm->name, realm)) { + fprintf(stderr, "realm not empty\n"); + return 0; + } + } + + scat(path, DATA_DIR); + scat(path, u->cart->name); + scat(path, "/realms/"); + scat(path, realm); + scpy(path, file, 256); + ccat(file, '/'); + scat(file, "realm"); + + fprintf(stderr, "%s\n", file); + realm_data = read_chars(file); + if (realm_data == nil) + return 0; + + sscanf( + realm_data->data, + "%u %32s %llu", + (uint*)garbage, + master, + (uvlong*)garbage); + if (scmp(master, uname) && rm_dir(path)) + return 1; + else + return 0; }
M user.huser.h

@@ -7,9 +7,11 @@ Cart* cart;

Realm* realm; char* scope; int random; + int ctl_code; } UserInfo; UserInfo* find_user(UserInfo* table, char* uname); +int* ctl_code_handle(UserInfo* table, char* uname); int login(UserInfo* table, char* uname, char* password); int logout(UserInfo* table, char* uname); int protect_realm(UserInfo* table, char* uname, char* password);

@@ -18,3 +20,4 @@ int load_cart(UserInfo* table, char* uname, char* cart_name);

int enter_realm(UserInfo* table, char* uname, char* realm_name); int leave_realm(UserInfo* table, char* uname); int unload_cart(UserInfo* table, char* uname); +int delete_realm(UserInfo* table, char* uname, char* realm);
M util.cutil.c

@@ -1,4 +1,5 @@

#include <u.h> +#include <sys/stat.h> #include <libc.h> #include <stdio.h> #include "cart.h"

@@ -147,6 +148,59 @@ self->data = buf;

self->length = count; return self; } +} + +int issymlink(char* name) { + struct stat s; + return lstat(name, &s) >= 0 && S_ISLNK(s.st_mode); +} + +int rm_dir(char* f) { + char* name; + int fd, i, j, n, ndir, nname; + Dir* dirbuf; + int fail = 0; + + fd = open(f, OREAD); + if (fd < 0) { + return 0; + } + n = dirreadall(fd, &dirbuf); + close(fd); + if (n < 0) { + return 0; + } + + nname = strlen(f) + 1 + STATMAX + 1; /* plenty! */ + name = malloc(nname); + if (name == 0) { + return 0; + } + + ndir = 0; + for (i = 0; i < n; i++) { + snprint(name, nname, "%s/%s", f, dirbuf[i].name); + if (remove(name) != -1 || issymlink(name)) + dirbuf[i].qid.type = QTFILE; /* so we won't recurse */ + else { + if (dirbuf[i].qid.type & QTDIR) + ndir++; + else + fail = 1; + } + } + if (ndir) + for (j = 0; j < n; j++) + if (dirbuf[j].qid.type & QTDIR) { + snprint(name, nname, "%s/%s", f, dirbuf[j].name); + rmdir(name); + } + if (remove(f) == -1) + fail = 1; + + free(name); + free(dirbuf); + return fail ? 0 : 1; } void strreverse(char* begin, char* end) {
M util.hutil.h

@@ -16,4 +16,5 @@ int ssin(char* str, char* substr);

char* ccat(char* dst, char c); Blob* read_bytes(char* path); Blob* read_chars(char* path); -void itoa(int, char*, int);+void itoa(int, char*, int); +int rm_dir(char* path);
M xrxs.cxrxs.c

@@ -7,6 +7,7 @@ #include <stdio.h>

#include <dirent.h> #include <libString.h> #include <string.h> +#include "config.h" #include "err.h" #include "command.h" #include "util.h"

@@ -16,14 +17,11 @@ #include "universe.h"

#include "realm.h" #include "user.h" -#define CARTSPATH "./carts" -#define REALMSPATH "./realms" - int chatty9p = 1; static Tree* tree; -static UserInfo users_table[64]; +static UserInfo users_table[MAX_USERS]; void xrxs_attach(Req* r) { /* As it is, once the user detaches, they will stay in the table

@@ -36,7 +34,7 @@ int vacancy = 0;

char* usr; char* username = r->ifcall.uname; - for (i = 0; i < 64; i++) { + for (i = 0; i < MAX_USERS; i++) { usr = users_table[i].name; if (scmp(usr, username)) { respond(r, EUNAME);

@@ -44,6 +42,7 @@ return;

} if (*usr == 0) { scpy(username, usr, 32); + users_table[i].random = -1; vacancy = 1; break; }

@@ -59,6 +58,7 @@ 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++);

@@ -71,28 +71,31 @@ fprintf(stderr, "%s(%s)\n", cmd, c);

uvlong const cmd_hashv = hash(cmd, 0); switch (cmd_hashv) { case LOGIN: - login(users_table, r->fid->uid, c); + *code = login(users_table, r->fid->uid, c); break; case LOAD: - load_cart(users_table, r->fid->uid, c); + *code = load_cart(users_table, r->fid->uid, c); break; case CHUNK: - get_chunk(users_table, r->fid->uid, c); + *code = get_chunk(users_table, r->fid->uid, c); break; case CREATE: - create_realm(users_table, r->fid->uid, c); + *code = create_realm(users_table, r->fid->uid, c) != nil ? 1 : 0; break; case PROTECT: - protect_realm(users_table, r->fid->uid, c); + *code = protect_realm(users_table, r->fid->uid, c); break; case TRANSFER: - transfer_realm(users_table, r->fid->uid, c); + *code = transfer_realm(users_table, r->fid->uid, c); + break; + case DELETE: + *code = delete_realm(users_table, r->fid->uid, c); break; case ENTER: - enter_realm(users_table, r->fid->uid, c); + *code = enter_realm(users_table, r->fid->uid, c); break; case LEAVE: - leave_realm(users_table, r->fid->uid); + *code = leave_realm(users_table, r->fid->uid); break; case LOGOUT: logout(users_table, r->fid->uid);

@@ -104,7 +107,7 @@ case RESET:

// reset(r->fid->uid); break; case UNLOAD: - unload_cart(users_table, r->fid->uid); + *code = unload_cart(users_table, r->fid->uid); break; } r->ofcall.count = r->ifcall.count;

@@ -189,26 +192,27 @@ }

} void read_users(Req* r) { - char buf[2113] = {0}; + String* data = s_new(); int i; - for (i = 0; i < 64; i++) { + for (i = 0; i < MAX_USERS; i++) { if (scmp(users_table[i].name, r->fid->uid)) { - scat(buf, users_table[i].name); - ccat(buf, '\n'); + s_append(data, users_table[i].name); + s_putc(data, '\n'); break; } } - for (i = 0; i < 64; i++) { + for (i = 0; i < MAX_USERS; i++) { if ( scmp(users_table[i].name, "\0") || scmp(users_table[i].name, r->fid->uid)) { continue; } - scat(buf, users_table[i].name); - ccat(buf, '\n'); + s_append(data, users_table[i].name); + s_putc(data, '\n'); } - ccat(buf, 0); - readstr(r, buf); + s_terminate(data); + readstr(r, data->base); + s_free(data); respond(r, nil); }

@@ -250,8 +254,24 @@ }

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(CARTSPATH); + String** carts = list_dir(DATA_DIR); String** c = carts; String* data = s_new(); while (*c != nil) {

@@ -318,7 +338,7 @@ }

void read_realms(Req* r) { UserInfo* user = find_user(users_table, r->fid->uid); - char realm_path[128] = {0}; + char realm_path[256] = {0}; String** realms; String** rr; Realm* realm;

@@ -346,7 +366,7 @@ realm = parse_realm(user->cart->name, (*rr)->base);

m = realm->max; p = realm->password ? 1 : 0; - for (i = u = 0; i < 64; i++) { + for (i = u = 0; i < MAX_USERS; i++) { if ( users_table[i].realm != nil && scmp(users_table[i].realm->name, realm->name))

@@ -463,22 +483,24 @@ int i;

int reset = 1; int random; UserInfo* u = find_user(users_table, r->fid->uid); - UserInfo** usrs = malloc(64 * sizeof(UserInfo*)); + UserInfo** usrs = malloc(MAX_USERS * sizeof(UserInfo*)); UserInfo* p = users_table; UserInfo** uu = usrs; - if (u->realm == nil) + if (u->realm == nil) { + respond(r, nil); return; + } - for (i = 0; i < 64; i++) { + for (i = 0; i < MAX_USERS; i++) { if (scmp(p->realm->name, u->realm->name)) { *uu++ = p; - if (i < 64) + if (i < MAX_USERS - 1) *uu = nil; } } uu = usrs; - for (i = 0; i < 64; i++) { + for (i = 0; i < MAX_USERS; i++) { if ((*uu) != nil && (*uu)->random >= 0) { reset = 0; break;

@@ -489,7 +511,7 @@ if (reset) {

srand(rand()); random = rand() % 100; uu = usrs; - for (i = 0; i < 64; i++) { + for (i = 0; i < MAX_USERS; i++) { if ((*uu) != nil) { (*uu)->random = random; }

@@ -505,6 +527,9 @@

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;

@@ -586,7 +611,7 @@ fs.tree = alloctree(nil, nil, DMDIR | 0777, fs_destroy_file);

tree = fs.tree; closefile( - createfile(tree->root, "ctl", nil, DMAPPEND | 0300, create_aux(CTL))); + 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)));