add config.h, ctl status codes, and delete realm command

This commit is contained in:
Iris Lightshard 2021-08-03 00:53:16 -06:00
parent e9839b1663
commit 4fab56e77f
Signed by: Iris Lightshard
GPG key ID: 3B7FBC22144E6398
12 changed files with 220 additions and 66 deletions

View file

@ -8,16 +8,18 @@ The client is intended to be a specialized [uxn](https://wiki.xxiivv.com/site/ux
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.
@ -37,6 +39,13 @@ This is the working structure of the 9p filesystem:
* `/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
Each realm directory on the server should have the following files:

9
cart.c
View file

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

View file

@ -5,6 +5,7 @@ typedef enum {
CREATE = 7083959236,
PROTECT = 295480618573,
TRANSFER = 11311529048177,
DELETE = 7129299147,
ENTER = 195024746,
LEAVE = 207662601,
LOGOUT = 7702552890,

5
config.h Normal file
View file

@ -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/"

25
realm.c
View file

@ -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 @@ Realm* create_realm(UserInfo* table, char* uname, char* name) {
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 @@ Realm* create_realm(UserInfo* table, char* uname, char* name) {
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* create_realm(UserInfo* table, char* uname, char* name) {
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 @@ Realm* parse_realm(char* cart, char* name) {
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 @@ Realm* find_realm(UserInfo* table, char* name) {
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");

View file

@ -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 @@ Universe* create_universe() {
}
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 @@ Universe* parse_universe(char* cart, char* realm_name) {
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 @@ Universe* parse_universe(char* cart, char* realm_name) {
}
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);

View file

@ -4,7 +4,7 @@ typedef struct Atom Atom;
struct Atom {
char name[16];
char value[64];
char value[MAX_USERS];
Atom* next;
};

61
user.c
View file

@ -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,7 +13,7 @@ extern UserInfo users_table[64];
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++;
@ -19,6 +21,13 @@ UserInfo* find_user(UserInfo* table, char* uname) {
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;
}
int login(UserInfo* table, char* uname, char* password) {
UserInfo* u = find_user(table, uname);
@ -43,7 +52,7 @@ int logout(UserInfo* table, char* uname) {
u->scope = nil;
}
u->random = 0;
u->random = -1;
if (u->realm != nil)
leave_realm(table, uname);
@ -104,8 +113,7 @@ int enter_realm(UserInfo* table, char* uname, char* realm_name) {
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++;
}
@ -163,3 +171,48 @@ int unload_cart(UserInfo* table, char* uname) {
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;
}

3
user.h
View file

@ -7,9 +7,11 @@ typedef struct UserInfo {
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);

54
util.c
View file

@ -1,4 +1,5 @@
#include <u.h>
#include <sys/stat.h>
#include <libc.h>
#include <stdio.h>
#include "cart.h"
@ -149,6 +150,59 @@ Blob* read_chars(char* path) {
}
}
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) {
char aux;

1
util.h
View file

@ -17,3 +17,4 @@ char* ccat(char* dst, char c);
Blob* read_bytes(char* path);
Blob* read_chars(char* path);
void itoa(int, char*, int);
int rm_dir(char* path);

91
xrxs.c
View file

@ -7,6 +7,7 @@
#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 "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 @@ void xrxs_attach(Req* r) {
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 @@ void xrxs_attach(Req* r) {
}
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 @@ void write_ctl(Req* r) {
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 @@ void write_ctl(Req* r) {
// 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 xrxs_write(Req* r) {
}
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 @@ void s_freemany(String** ss) {
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 @@ end:
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 @@ void read_realms(Req* r) {
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 @@ void read_grandom(Req* r) {
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 @@ void read_grandom(Req* r) {
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 read_grandom(Req* r) {
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 @@ void threadmain(int argc, char* argv[]) {
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)));