aboutsummaryrefslogtreecommitdiff
path: root/src/server_reqs.c
diff options
context:
space:
mode:
authorFranklin Wei <git@fwei.tk>2016-01-08 22:27:43 -0500
committerFranklin Wei <git@fwei.tk>2016-01-08 22:27:43 -0500
commit50134c46dc337c35b8ecf78b3c5b4308cf8fb791 (patch)
tree89d73bca1951129ec7e15b642e2fa6c85567f872 /src/server_reqs.c
parent2819d11ceeb1ac739ed5f17ccb0abab63f494299 (diff)
downloadnetcosm-50134c46dc337c35b8ecf78b3c5b4308cf8fb791.zip
netcosm-50134c46dc337c35b8ecf78b3c5b4308cf8fb791.tar.gz
netcosm-50134c46dc337c35b8ecf78b3c5b4308cf8fb791.tar.bz2
netcosm-50134c46dc337c35b8ecf78b3c5b4308cf8fb791.tar.xz
some stuff
Diffstat (limited to 'src/server_reqs.c')
-rw-r--r--src/server_reqs.c425
1 files changed, 425 insertions, 0 deletions
diff --git a/src/server_reqs.c b/src/server_reqs.c
new file mode 100644
index 0000000..1501434
--- /dev/null
+++ b/src/server_reqs.c
@@ -0,0 +1,425 @@
+#include "netcosm.h"
+
+static volatile sig_atomic_t num_acks_wanted, num_acks_recvd, inc_acks = 0;
+
+static void req_pass_msg(unsigned char *data, size_t datalen,
+ struct child_data *sender, struct child_data *child)
+{
+ (void) sender;
+
+ if(sender->pid != child->pid)
+ {
+ unsigned char cmd = REQ_BCASTMSG;
+ write(child->outpipe[1], &cmd, 1);
+ }
+
+ write(child->outpipe[1], data, datalen);
+ union sigval nothing;
+
+ if(sender->pid != child->pid)
+ {
+ sigqueue(child->pid, SIGRTMIN, nothing);
+ ++num_acks_wanted;
+ }
+}
+
+static void req_send_clientinfo(unsigned char *data, size_t datalen,
+ struct child_data *sender, struct child_data *child)
+{
+ (void) data;
+ (void) datalen;
+ char buf[128];
+ const char *state[] = {
+ "INIT",
+ "LOGIN SCREEN",
+ "CHECKING CREDENTIALS",
+ "LOGGED IN AS USER",
+ "LOGGED IN AS ADMIN",
+ "ACCESS DENIED",
+ };
+
+ if(child->user)
+ snprintf(buf, sizeof(buf), "Client %s PID %d [%s] USER %s",
+ inet_ntoa(child->addr), child->pid, state[child->state], child->user);
+ else
+ snprintf(buf, sizeof(buf), "Client %s PID %d [%s]",
+ inet_ntoa(child->addr), child->pid, state[child->state]);
+
+ if(sender->pid == child->pid)
+ strncat(buf, " [YOU]\n", sizeof(buf));
+ else
+ strncat(buf, "\n", sizeof(buf));
+
+ write(sender->outpipe[1], buf, strlen(buf));
+}
+
+static void req_change_state(unsigned char *data, size_t datalen,
+ struct child_data *sender, struct child_data *child)
+{
+ (void) data; (void) datalen; (void) child; (void) sender;
+ if(datalen == sizeof(sender->state))
+ {
+ sender->state = *((int*)data);
+ debugf("State changed to %d\n", sender->state);
+ }
+ else
+ {
+ debugf("State data is of the wrong size\n");
+ for(size_t i = 0; i < datalen; ++i)
+ debugf("%02x\n", data[i]);
+ }
+}
+
+static void req_change_user(unsigned char *data, size_t datalen,
+ struct child_data *sender, struct child_data *child)
+{
+ (void) data; (void) datalen; (void) child; (void) sender;
+ if(sender->user)
+ free(sender->user);
+ sender->user = strdup((char*)data);
+}
+
+//void req_hang(unsigned char *data, size_t datalen,
+// struct child_data *sender, struct child_data *child)
+//{
+// while(1);
+//}
+
+static void req_kick_client(unsigned char *data, size_t datalen,
+ struct child_data *sender, struct child_data *child)
+{
+ (void) data; (void) datalen; (void) child; (void) sender;
+ if(datalen >= sizeof(pid_t))
+ {
+ pid_t kicked_pid = *((pid_t*)data);
+ if(kicked_pid == child->pid)
+ {
+ unsigned char cmd = REQ_KICK;
+ write(child->outpipe[1], &cmd, 1);
+ write(child->outpipe[1], data + sizeof(pid_t), datalen - sizeof(pid_t));
+ union sigval nothing;
+ sigqueue(child->pid, SIGRTMIN, nothing);
+ }
+ }
+}
+
+static void req_wait(unsigned char *data, size_t datalen, struct child_data *sender)
+{
+ (void) data; (void) datalen; (void) sender;
+ sleep(10);
+}
+
+static void req_send_desc(unsigned char *data, size_t datalen, struct child_data *sender)
+{
+ (void) data; (void) datalen; (void) sender;
+ struct room_t *room = room_get(sender->room);
+ write(sender->outpipe[1], room->data.desc, strlen(room->data.desc) + 1);
+
+ char newline = '\n';
+ write(sender->outpipe[1], &newline, 1);
+}
+
+static void req_send_roomname(unsigned char *data, size_t datalen, struct child_data *sender)
+{
+ (void) data; (void) datalen; (void) sender;
+ struct room_t *room = room_get(sender->room);
+ write(sender->outpipe[1], room->data.name, strlen(room->data.name) + 1);
+
+ char newline = '\n';
+ write(sender->outpipe[1], &newline, 1);
+}
+
+static void child_set_room(struct child_data *child, room_id id)
+{
+ child->room = id;
+ room_user_add(id, child);
+}
+
+static void req_set_room(unsigned char *data, size_t datalen, struct child_data *sender)
+{
+ (void) data; (void) datalen; (void) sender;
+ room_id id = *((room_id*)data);
+
+ child_set_room(sender, id);
+}
+
+static void req_move_room(unsigned char *data, size_t datalen, struct child_data *sender)
+{
+ (void) data; (void) datalen; (void) sender;
+ enum direction_t dir = *((enum direction_t*)data);
+ struct room_t *current = room_get(sender->room);
+
+ room_user_del(sender->room, sender);
+
+ /* TODO: checking */
+ sig_debugf("Moving in direction %d\n", dir);
+ room_id new = current->adjacent[dir];
+ bool status;
+ if(new != ROOM_NONE)
+ {
+ child_set_room(sender, new);
+ status = true;
+ }
+ else
+ {
+ status = false;
+ }
+ write(sender->outpipe[1], &status, sizeof(status));
+}
+
+static void send_string(int fd, const char *s)
+{
+ size_t len = strlen(s);
+ write(fd, &len, sizeof(len));
+ write(fd, s, len);
+}
+
+static void req_send_user(unsigned char *data, size_t datalen, struct child_data *sender)
+{
+ if(datalen)
+ {
+ struct userdata_t *user = userdb_lookup((char*)data);
+
+ if(user)
+ {
+ bool confirm = true;
+ write(sender->outpipe[1], &confirm, sizeof(confirm));
+ send_string(sender->outpipe[1], user->username);
+ send_string(sender->outpipe[1], user->salt);
+ send_string(sender->outpipe[1], user->passhash);
+ write(sender->outpipe[1], &user->priv, sizeof(user->priv));
+ return;
+ }
+
+ sig_debugf("failure 2\n");
+ }
+
+ sig_debugf("failure 1\n");
+
+ bool fail = false;
+ write(sender->outpipe[1], &fail, sizeof(fail));
+}
+
+static const struct child_request {
+ unsigned char code;
+
+ bool havedata;
+
+ enum { CHILD_NONE, CHILD_SENDER, CHILD_ALL_BUT_SENDER, CHILD_ALL } which;
+
+ /* sender_pipe is the pipe to the sender of the request */
+ /* data points to bogus if havedata = false */
+ void (*handle_child)(unsigned char *data, size_t len,
+ struct child_data *sender, struct child_data *child);
+
+ void (*finalize)(unsigned char *data, size_t len, struct child_data *sender);
+
+ /* byte to write back to the sender */
+ unsigned char cmd_to_send;
+} requests[] = {
+ { REQ_NOP, false, CHILD_NONE, NULL, NULL, REQ_NOP },
+ { REQ_BCASTMSG, true, CHILD_ALL, req_pass_msg, NULL, REQ_BCASTMSG },
+ { REQ_LISTCLIENTS, false, CHILD_ALL, req_send_clientinfo, NULL, REQ_BCASTMSG },
+ { REQ_CHANGESTATE, true, CHILD_SENDER, req_change_state, NULL, REQ_NOP },
+ { REQ_CHANGEUSER, true, CHILD_SENDER, req_change_user, NULL, REQ_NOP },
+ { REQ_KICK, true, CHILD_ALL, req_kick_client, NULL, REQ_NOP },
+ { REQ_WAIT, false, CHILD_NONE, NULL, req_wait, REQ_NOP },
+ { REQ_GETROOMDESC, false, CHILD_NONE, NULL, req_send_desc, REQ_BCASTMSG },
+ { REQ_GETROOMNAME, false, CHILD_NONE, NULL, req_send_roomname, REQ_BCASTMSG },
+ { REQ_SETROOM, true, CHILD_NONE, NULL, req_set_room, REQ_NOP },
+ { REQ_MOVE, true, CHILD_NONE, NULL, req_move_room, REQ_MOVE },
+ { REQ_GETUSERDATA, true, CHILD_NONE, NULL, req_send_user, REQ_GETUSERDATA },
+ { REQ_DELUSERDATA, true, CHILD_NONE, NULL, req_del_user, REQ_DELUSERDATA },
+ { REQ_ADDUSERDATA, true, CHILD_NONE, NULL, req_add_user, REQ_ADDUSERDATA },
+ //{ REQ_ROOMMSG, true, CHILD_ALL, req_send_room_msg, NULL, REQ_BCASTMSG },
+};
+
+static SIMP_HASH(unsigned char, uchar_hash);
+static SIMP_EQUAL(unsigned char, uchar_equal);
+
+static void *request_map = NULL;
+
+void reqmap_init(void)
+{
+ request_map = hash_init(ARRAYLEN(requests), uchar_hash, uchar_equal);
+ for(unsigned i = 0; i < ARRAYLEN(requests); ++i)
+ hash_insert(request_map, &requests[i].code, requests + i);
+}
+
+void reqmap_free(void)
+{
+ if(request_map)
+ {
+ hash_free(request_map);
+ request_map = NULL;
+ }
+}
+
+/**
+ * Here's how child-parent requests work
+ * 1. Child writes its PID and length of request to the parent's pipe, followed
+ * by up to MSG_MAX bytes of data. If the length exceeds MSG_MAX bytes, the
+ * request will be ignored.
+ * 1.1 Child spins until parent response.
+ * 2. Parent handles the request.
+ * 3. Parent writes its PID and length of message back to the child(ren).
+ * 4. Parent signals child(ren) with SIGRTMIN
+ * 5. Child(ren) handle parent's message.
+ * 6. Child(ren) send the parent SIGRTMIN+1 to acknowledge receipt of message.
+ * 7. Parent spins until the needed number of signals is reached.
+ */
+
+bool handle_child_req(int in_fd)
+{
+ pid_t sender_pid;
+
+ if(read(in_fd, &sender_pid, sizeof(sender_pid)) != sizeof(sender_pid))
+ {
+ sig_debugf("Couldn't get sender PID\n");
+ return false;
+ }
+
+ sig_debugf("Got request from PID %d\n", sender_pid);
+
+ size_t msglen;
+ const struct child_request *req = NULL;
+ size_t datalen;
+
+ unsigned char cmd, msg[MSG_MAX + 1];
+
+ struct child_data *sender = hash_lookup(child_map, &sender_pid);
+
+ if(!sender)
+ {
+ sig_debugf("WARNING: got data from unknown PID, ignoring.\n");
+ goto fail;
+ }
+
+ if(read(in_fd, &msglen, sizeof(msglen)) != sizeof(msglen))
+ {
+ sig_debugf("Couldn't read message length, dropping.\n");
+ goto fail;
+ }
+
+ if(msglen < 1)
+ {
+ sig_debugf("message too short to be valid, ignoring.\n");
+ goto fail;
+ }
+ else if(msglen > MSG_MAX)
+ {
+ sig_debugf("message too long, ignoring.\n");
+ goto fail;
+ }
+
+ unsigned char *msgptr = msg;
+ size_t have = 0;
+ while(have < msglen)
+ {
+ ssize_t ret = read(sender->readpipe[0], msgptr, msglen - have);
+ if(ret < 0)
+ {
+ sig_debugf("unexpected EOF\n");
+ goto fail;
+ }
+ msgptr += ret;
+ have += ret;
+ }
+
+ cmd = msg[0];
+ msg[MSG_MAX] = '\0';
+
+ unsigned char *data = msg + 1;
+
+ datalen = msglen - 1;
+ req = hash_lookup(request_map, &cmd);
+
+ sigset_t old, block;
+
+ sigemptyset(&block);
+ sigaddset(&block, SIGRTMIN+1);
+ sigprocmask(SIG_BLOCK, &block, &old);
+
+ num_acks_wanted = 1;
+ num_acks_recvd = 0;
+ inc_acks = 1;
+
+ if(!req)
+ {
+ sig_debugf("Unknown request.\n");
+ goto fail;
+ }
+
+ write(sender->outpipe[1], &req->cmd_to_send, 1);
+
+ switch(req->which)
+ {
+ case CHILD_SENDER:
+ case CHILD_ALL:
+ req->handle_child(data, datalen, sender, sender);
+ if(req->which == CHILD_SENDER)
+ goto finish;
+ break;
+ case CHILD_NONE:
+ goto finish;
+ default:
+ break;
+ }
+
+ struct child_data *child = NULL;
+ void *ptr = child_map, *save;
+
+ do {
+ pid_t *key;
+ child = hash_iterate(ptr, &save, (void**)&key);
+ ptr = NULL;
+ if(!child)
+ break;
+ if(child->pid == sender->pid)
+ continue;
+
+ switch(req->which)
+ {
+ case CHILD_ALL:
+ case CHILD_ALL_BUT_SENDER:
+ req->handle_child(data, datalen, sender, child);
+ break;
+ default:
+ break;
+ }
+ } while(1);
+
+finish:
+
+ if(req && req->finalize)
+ req->finalize(data, datalen, sender);
+
+ union sigval junk;
+ if(sender)
+ sigqueue(sender->pid, SIGRTMIN, junk);
+ else
+ sig_debugf("Unknown PID sent request.\n");
+
+ while(num_acks_recvd < MIN(num_clients,num_acks_wanted))
+ {
+ sigsuspend(&old);
+ }
+
+ inc_acks = 0;
+
+ sigprocmask(SIG_SETMASK, &old, NULL);
+
+ return true;
+fail:
+ return true;
+}
+
+void master_ack_handler(int s, siginfo_t *info, void *v)
+{
+ (void) s;
+ (void) v;
+ if(inc_acks && hash_lookup(child_map, &info->si_pid))
+ {
+ ++num_acks_recvd;
+ }
+}