aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorFranklin Wei <git@fwei.tk>2015-12-26 21:02:54 -0500
committerFranklin Wei <git@fwei.tk>2015-12-26 21:03:15 -0500
commit0a2f9197058cc5248ec8e4bed7c361397c8d1c79 (patch)
treeb4ac31dcaa64bdfded9cdb2450bf936904802d08 /src
parentf7041112f179aa79b6e315e7d57afbf76d3cd8bb (diff)
downloadnetcosm-0a2f9197058cc5248ec8e4bed7c361397c8d1c79.zip
netcosm-0a2f9197058cc5248ec8e4bed7c361397c8d1c79.tar.gz
netcosm-0a2f9197058cc5248ec8e4bed7c361397c8d1c79.tar.bz2
netcosm-0a2f9197058cc5248ec8e4bed7c361397c8d1c79.tar.xz
stuff mostly works, need to fix error on client exit
Diffstat (limited to 'src')
-rw-r--r--src/auth.c24
-rw-r--r--src/client.c79
-rw-r--r--src/hash.c65
-rw-r--r--src/hash.h23
-rw-r--r--src/netcosm.h25
-rw-r--r--src/room.c66
-rw-r--r--src/server.c326
-rw-r--r--src/telnet.c18
-rw-r--r--src/telnet.h18
-rw-r--r--src/util.c18
10 files changed, 506 insertions, 156 deletions
diff --git a/src/auth.c b/src/auth.c
index c771747..5041ef4 100644
--- a/src/auth.c
+++ b/src/auth.c
@@ -1,8 +1,27 @@
+/*
+ * NetCosm - a MUD server
+ * Copyright (C) 2015 Franklin Wei
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
#include "netcosm.h"
#define SALT_LEN 12
#define ALGO GCRY_MD_SHA512
-#define HASH_ITERS 500000
+//#define HASH_ITERS 500000
+#define HASH_ITERS 1
static bool valid_login_name(const char *name);
@@ -307,9 +326,11 @@ struct authinfo_t auth_check(const char *name2, const char *pass2)
}
good:
printf("Successful authentication.\n");
+ fclose(f);
return ret;
bad:
sleep(2);
+ fclose(f);
printf("Failed authentication.\n");
return ret;
}
@@ -328,6 +349,7 @@ void auth_user_list(void)
if(getline(&line, &len, f) < 0)
{
free(line);
+ fclose(f);
return;
}
char *user = strdup(strtok_r(line, ":\r\n", &save));
diff --git a/src/client.c b/src/client.c
index b50e71d..090ffa1 100644
--- a/src/client.c
+++ b/src/client.c
@@ -1,3 +1,21 @@
+/*
+ * NetCosm - a MUD server
+ * Copyright (C) 2015 Franklin Wei
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
#include "netcosm.h"
static int client_fd, to_parent, from_parent;
@@ -35,35 +53,33 @@ void __attribute__((format(printf,1,2))) out(const char *fmt, ...)
static volatile sig_atomic_t request_complete;
-static void signal_master(void)
+void send_master(unsigned char cmd, const void *data, size_t sz)
{
request_complete = 0;
- sigset_t block, old;
+ sigset_t block, old;
sigemptyset(&block);
- sigaddset(&block, SIGUSR2);
+ sigaddset(&block, SIGRTMIN);
sigprocmask(SIG_BLOCK, &block, &old);
- kill(getppid(), SIGUSR1);
+ pid_t our_pid = getpid();
+ size_t total_len = (data?sz:0) + 1;
- /* wait for a signal */
- printf("Waiting for signal.\n");
+ char header[sizeof(pid_t) + sizeof(size_t) + 1];
- sigsuspend(&old);
- sigprocmask(SIG_SETMASK, &old, NULL);
+ memcpy(header, &our_pid, sizeof(pid_t));
+ memcpy(header + sizeof(pid_t), &total_len, sizeof(size_t));
+ memcpy(header + sizeof(pid_t) + sizeof(size_t), &cmd, 1);
- errno = 0;
+ write(to_parent, header, sizeof(header));
- /* spin until we're done handling the request */
- while(!request_complete) usleep(1);
-}
-
-void send_master(unsigned char cmd, const void *data, size_t sz)
-{
- write(to_parent, &cmd, 1);
if(data)
write(to_parent, data, sz);
- signal_master();
+
+ sigsuspend(&old);
+ sigprocmask(SIG_SETMASK, &old, NULL);
+
+ while(!request_complete) usleep(1);
}
#define BUFSZ 128
@@ -92,7 +108,7 @@ tryagain:
return buf;
}
-/* still not encrypted, but a bit more secure than echoing the pass */
+/* still not encrypted, but a bit more secure than echoing the password! */
char *client_read_password(void)
{
telnet_echo_off();
@@ -111,17 +127,16 @@ void all_upper(char *s)
}
}
-void sigusr2_handler(int s, siginfo_t *info, void *vp)
+void sig_rt_0_handler(int s, siginfo_t *info, void *v)
{
(void) s;
- (void) vp;
-
- sig_printf("PID %d got SIGUSR2\n", getpid());
+ (void) v;
+ sig_printf("PID %d got SIGRTMIN+1\n", getpid());
/* we only listen to requests from our parent */
if(info->si_pid != getppid())
{
- sig_printf("Unknown PID sent SIGUSR2\n");
+ sig_printf("Unknown PID sent SIGRTMIN+1\n");
return;
}
@@ -145,6 +160,9 @@ void sigusr2_handler(int s, siginfo_t *info, void *vp)
ssize_t len = read(from_parent, buf, MSG_MAX);
buf[MSG_MAX] = '\0';
out_raw(buf, len);
+ union sigval junk;
+ /* the master still expects an ACK */
+ sigqueue(getppid(), SIGRTMIN+1, junk);
exit(EXIT_SUCCESS);
}
case REQ_MOVE:
@@ -165,6 +183,10 @@ void sigusr2_handler(int s, siginfo_t *info, void *vp)
sig_printf("Client finishes handling request.\n");
request_complete = 1;
+
+ /* signal the master that we're done */
+ union sigval junk;
+ sigqueue(getppid(), SIGRTMIN+1, junk);
}
static void client_change_state(int state)
@@ -246,15 +268,6 @@ void client_main(int fd, struct sockaddr_in *addr, int total, int to, int from)
output_locked = 0;
- struct sigaction sa;
-
- sigemptyset(&sa.sa_mask);
-
- sa.sa_flags = SA_RESTART | SA_SIGINFO;
- sa.sa_sigaction = sigusr2_handler;
- if(sigaction(SIGUSR2, &sa, NULL) < 0)
- error("sigaction");
-
telnet_init();
char *ip = inet_ntoa(addr->sin_addr);
@@ -334,7 +347,7 @@ auth:
char *tok = strtok_r(cmd, WSPACE, &save);
if(!tok)
- continue;
+ goto next_cmd;
all_upper(tok);
if(admin)
diff --git a/src/hash.c b/src/hash.c
index 8e4888a..035236c 100644
--- a/src/hash.c
+++ b/src/hash.c
@@ -1,3 +1,21 @@
+/*
+ * NetCosm - a MUD server
+ * Copyright (C) 2015 Franklin Wei
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
#include "hash.h"
#include <stdlib.h>
#include <string.h>
@@ -13,6 +31,8 @@ struct hash_map {
int (*compare)(const void *a, const void *b);
struct hash_node **table;
size_t table_sz;
+ void (*free_key)(void *key);
+ void (*free_data)(void *data);
};
unsigned hash_djb(const void *ptr)
@@ -46,21 +66,40 @@ void *hash_init(size_t sz, unsigned (*hash_fn)(const void*),
return ret;
}
-void hash_free(void *ptr)
+void hash_setfreekey_cb(void *ptr, void (*cb)(void *key))
+{
+ struct hash_map *map = ptr;
+ map->free_key = cb;
+}
+
+void hash_setfreedata_cb(void *ptr, void (*cb)(void *data))
{
struct hash_map *map = ptr;
- for(unsigned i = 0; i < map->table_sz; ++i)
+ map->free_data = cb;
+}
+
+void hash_free(void *ptr)
+{
+ if(ptr)
{
- struct hash_node *node = map->table[i];
- while(node)
+ struct hash_map *map = ptr;
+ for(unsigned i = 0; i < map->table_sz; ++i)
{
- struct hash_node *next = node->next;
- free(node);
- node = next;
+ struct hash_node *node = map->table[i];
+ while(node)
+ {
+ struct hash_node *next = node->next;
+ if(map->free_data)
+ map->free_data((void*)node->data);
+ if(map->free_key)
+ map->free_key((void*)node->key);
+ free(node);
+ node = next;
+ }
}
+ free(map->table);
+ free(map);
}
- free(map->table);
- free(map);
}
void *hash_iterate(void *map, void **saveptr, void **keyptr)
@@ -95,7 +134,7 @@ void *hash_iterate(void *map, void **saveptr, void **keyptr)
if(saved->node)
{
if(keyptr)
- *keyptr = saved->node->key;
+ *keyptr = (void*)saved->node->key;
return (void*)saved->node->data;
}
} while(saved->node);
@@ -163,7 +202,7 @@ void hash_insert_pairs(void *ptr, const struct hash_pair *pairs,
}
}
-bool hash_remove(void *ptr, void *key)
+bool hash_remove(void *ptr, const void *key)
{
struct hash_map *map = ptr;
unsigned hash = map->hash(key) % map->table_sz;
@@ -178,6 +217,10 @@ bool hash_remove(void *ptr, void *key)
last->next = iter->next;
else
map->table[hash] = iter->next;
+ if(map->free_key)
+ map->free_key((void*)iter->key);
+ if(map->free_data)
+ map->free_data((void*)iter->data);
free(iter);
return true;
}
diff --git a/src/hash.h b/src/hash.h
index 23586f4..3abba9a 100644
--- a/src/hash.h
+++ b/src/hash.h
@@ -1,3 +1,21 @@
+/*
+ * NetCosm - a MUD server
+ * Copyright (C) 2015 Franklin Wei
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
#include <stdbool.h>
#include <stddef.h>
@@ -9,6 +27,9 @@ int compare_strings(const void*, const void*);
void *hash_init(size_t tabsz, unsigned (*hash_fn)(const void*),
int (*compare_key)(const void*, const void*));
+void hash_setfreedata_cb(void*, void (*cb)(void *data));
+void hash_setfreekey_cb(void*, void (*cb)(void *key));
+
void hash_free(void*);
/* insert a pair, returns null if not already found, otherwise
@@ -18,7 +39,7 @@ void *hash_insert(void*, const void *key, const void *data);
/* returns NULL if not found */
void *hash_lookup(void*, const void *key);
-bool hash_remove(void *ptr, void *key);
+bool hash_remove(void *ptr, const void *key);
/* use like you would strtok_r */
/* allocates a buffer that's freed once all elements are processed */
diff --git a/src/netcosm.h b/src/netcosm.h
index c9c4a0f..d0d4dea 100644
--- a/src/netcosm.h
+++ b/src/netcosm.h
@@ -1,3 +1,21 @@
+/*
+ * NetCosm - a MUD server
+ * Copyright (C) 2015 Franklin Wei
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
#include <arpa/inet.h>
#include <ctype.h>
#include <errno.h>
@@ -5,6 +23,7 @@
#include <gcrypt.h>
#include <netdb.h>
#include <netinet/in.h>
+#include <poll.h>
#include <signal.h>
#include <stdarg.h>
#include <stdbool.h>
@@ -122,8 +141,10 @@ struct child_data {
struct roomdata_t {
/* the non-const pointers can be modified by the world module */
const char * const uniq_id;
- const char *name;
- const char *desc;
+
+ /* mutable properties */
+ char *name;
+ char *desc;
const char * const adjacent[NUM_DIRECTIONS];
diff --git a/src/room.c b/src/room.c
index 18fd37e..80447ac 100644
--- a/src/room.c
+++ b/src/room.c
@@ -1,3 +1,21 @@
+/*
+ * NetCosm - a MUD server
+ * Copyright (C) 2015 Franklin Wei
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
#include "netcosm.h"
/* processed world data */
@@ -9,11 +27,6 @@ struct room_t *room_get(room_id id)
return world + id;
}
-void world_free(void)
-{
- free(world);
-}
-
bool room_user_add(room_id id, struct child_data *child)
{
struct room_t *room = room_get(id);
@@ -82,7 +95,7 @@ room_id read_roomid(int fd)
return ret;
}
-const char *read_string(int fd)
+char *read_string(int fd)
{
size_t sz;
read(fd, &sz, sizeof(sz));
@@ -106,7 +119,7 @@ void world_save(const char *fname)
//write_string(fd, world[i].data.uniq_id);
write_string(fd, world[i].data.name);
write_string(fd, world[i].data.desc);
- /* adjacency strings are only used when loading the world for the 1st time */
+ /* adjacency strings not serialized, only adjacent IDs */
/* callbacks are static, so are not serialized */
@@ -115,6 +128,31 @@ void world_save(const char *fname)
close(fd);
}
+static void room_free(struct room_t *room)
+{
+ while(room->users)
+ {
+ struct user_t *old = room->users;
+ room->users = room->users->next;
+ free(old);
+ }
+ free(room->data.name);
+ free(room->data.desc);
+}
+
+void world_free(void)
+{
+ if(world)
+ {
+ for(unsigned i = 0; i < world_sz; ++i)
+ {
+ room_free(world + i);
+ }
+ free(world);
+ world = NULL;
+ }
+}
+
bool world_load(const char *fname, const struct roomdata_t *data, size_t data_sz)
{
int fd = open(fname, O_RDONLY);
@@ -127,9 +165,7 @@ bool world_load(const char *fname, const struct roomdata_t *data, size_t data_sz
read(fd, &world_sz, sizeof(world_sz));
if(world)
- /* possible memory leak here as strings allocated inside of world
- aren't freed. avoided by loading only one world per instance */
- free(world);
+ world_free();
if(world_sz != data_sz)
return false;
@@ -166,6 +202,11 @@ void world_init(const struct roomdata_t *data, size_t sz)
{
world[i].id = i;
memcpy(&world[i].data, &data[i], sizeof(data[i]));
+
+ /* have to strdup these strings so they can be freed later */
+ //world[i].uniq_id = strdup(world[i].uniq_id);
+ world[i].data.name = strdup(world[i].data.name);
+ world[i].data.desc = strdup(world[i].data.desc);
printf("Loading room '%s'\n", world[i].data.uniq_id);
if(hash_insert(map, world[i].data.uniq_id, world + i))
@@ -197,7 +238,8 @@ void world_init(const struct roomdata_t *data, size_t sz)
if(room)
world[i].adjacent[dir] = room->id;
else
- error("unknown room '%s' referenced from '%s'", adjacent_room, world[i].data.uniq_id);
+ error("unknown room '%s' referenced from '%s'",
+ adjacent_room, world[i].data.uniq_id);
}
else
world[i].adjacent[dir] = ROOM_NONE;
@@ -240,7 +282,7 @@ void world_init(const struct roomdata_t *data, size_t sz)
enum direction_t *opp = hash_lookup(dir_map, &j);
struct room_t *adj = room_get(world[i].adjacent[j]);
if(adj->adjacent[*opp] != i)
- printf("WARNING: Rooms '%s' and '%s' are not connected correctly.\n",
+ printf("WARNING: Rooms '%s' and '%s' are one-way\n",
world[i].data.uniq_id, adj->data.uniq_id);
}
}
diff --git a/src/server.c b/src/server.c
index 2ea5d1d..193f811 100644
--- a/src/server.c
+++ b/src/server.c
@@ -37,6 +37,14 @@ void __attribute__((noreturn)) error(const char *fmt, ...)
volatile int num_clients = 0;
void *child_map = NULL;
+static void free_child_data(void *ptr)
+{
+ struct child_data *child = ptr;
+ if(child->user)
+ free(child->user);
+ free(ptr);
+}
+
static void sigchld_handler(int s, siginfo_t *info, void *vp)
{
(void) s;
@@ -50,9 +58,7 @@ static void sigchld_handler(int s, siginfo_t *info, void *vp)
pid_t pid;
while((pid = waitpid(-1, NULL, WNOHANG)) > 0)
{
- void *key = hash_getkeyptr(child_map, &pid);
hash_remove(child_map, &pid);
- free(key);
}
errno = saved_errno;
@@ -70,12 +76,17 @@ static void handle_client(int fd, struct sockaddr_in *addr,
int server_socket;
+static void *request_map = NULL;
+
static void serv_cleanup(void)
{
- write(STDOUT_FILENO, "Shutdown server.\n", strlen("Shutdown server.\n"));
+ sig_printf("Shutdown server.\n");
if(shutdown(server_socket, SHUT_RDWR) > 0)
error("shutdown");
close(server_socket);
+ world_free();
+ hash_free(request_map);
+ hash_free(child_map);
}
static void sigint_handler(int s)
@@ -84,6 +95,8 @@ static void sigint_handler(int s)
serv_cleanup();
}
+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)
{
@@ -96,9 +109,13 @@ static void req_pass_msg(unsigned char *data, size_t datalen,
}
write(child->outpipe[1], data, datalen);
+ union sigval nothing;
if(sender->pid != child->pid)
- kill(child->pid, SIGUSR2);
+ {
+ sigqueue(child->pid, SIGRTMIN, nothing);
+ ++num_acks_wanted;
+ }
}
static void req_send_clientinfo(unsigned char *data, size_t datalen,
@@ -107,7 +124,6 @@ static void req_send_clientinfo(unsigned char *data, size_t datalen,
(void) data;
(void) datalen;
char buf[128];
- int len;
const char *state[] = {
"INIT",
"LOGIN SCREEN",
@@ -118,18 +134,18 @@ static void req_send_clientinfo(unsigned char *data, size_t datalen,
};
if(child->user)
- len = snprintf(buf, sizeof(buf), "Client %s PID %d [%s] USER %s",
- inet_ntoa(child->addr), child->pid, state[child->state], 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
- len = snprintf(buf, sizeof(buf), "Client %s PID %d [%s]",
- inet_ntoa(child->addr), child->pid, state[child->state]);
+ 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, len);
+ write(sender->outpipe[1], buf, strlen(buf));
}
static void req_change_state(unsigned char *data, size_t datalen,
@@ -176,7 +192,9 @@ static void req_kick_client(unsigned char *data, size_t datalen,
unsigned char cmd = REQ_KICK;
write(child->outpipe[1], &cmd, 1);
write(child->outpipe[1], data + sizeof(pid_t), datalen - sizeof(pid_t));
- kill(child->pid, SIGUSR2);
+ union sigval nothing;
+ sigqueue(child->pid, SIGRTMIN, nothing);
+ ++num_acks_wanted;
}
}
}
@@ -276,8 +294,6 @@ static const struct child_request {
//{ REQ_ROOMMSG, true, CHILD_ALL, req_send_room_msg, NULL, REQ_BCASTMSG },
};
-static void *request_map = NULL;
-
static SIMP_HASH(unsigned char, uchar_hash);
static SIMP_EQUAL(unsigned char, uchar_equal);
@@ -301,38 +317,87 @@ void sig_printf(const char *fmt, ...)
va_end(ap);
}
-/* SIGUSR1 is used by children to communicate with the master process */
-/* the master handles commands that involve multiple children, i.e. message passing, listing clients, etc. */
-static void sigusr1_handler(int s, siginfo_t *info, void *vp)
+/**
+ * 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.
+ * 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.
+ */
+
+static bool handle_child_req(int in_fd)
{
- (void) s;
- (void) vp;
- pid_t sender_pid = info->si_pid;
+ pid_t sender_pid;
+ if(read(in_fd, &sender_pid, sizeof(sender_pid)) != sizeof(pid_t))
+ return false;
sig_printf("PID %d sends a client request\n", sender_pid);
- unsigned char cmd, data[MSG_MAX + 1];
- const struct child_request *req = NULL;
- size_t datalen = 0;
+ size_t msglen;
+ if(read(in_fd, &msglen, sizeof(msglen)) != sizeof(msglen))
+ return false;
+
+ if(msglen > MSG_MAX)
+ {
+ sig_printf("message data too long, dropping\n");
+ return false;
+ }
+ else if(msglen < 1)
+ {
+ sig_printf("message too short to be valid, ignoring.\n");
+ return false;
+ }
+
+ unsigned char cmd, msg[MSG_MAX + 1];
struct child_data *sender = hash_lookup(child_map, &sender_pid);
if(!sender)
{
- printf("WARNING: got SIGUSR1 from unknown PID, ignoring.\n");
- return;
+ printf("WARNING: got data from unknown PID, ignoring.\n");
+ return false;
}
- read(sender->readpipe[0], &cmd, 1);
- req = hash_lookup(request_map, &cmd);
- if(!req)
+ 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;
+
+ unsigned char *msgptr = msg;
+ size_t have = 0;
+ while(have < msglen)
{
- sig_printf("Unknown request.\n");
- return;
+ ssize_t ret = read(sender->readpipe[0], msgptr, msglen - have);
+ if(ret < 0)
+ {
+ sig_printf("unexpected EOF\n");
+ return true;
+ }
+ msgptr += ret;
+ have += ret;
}
- sig_printf("Got command %d\n", cmd);
- if(req->havedata)
+ cmd = msg[0];
+ msg[MSG_MAX] = '\0';
+
+ unsigned char *data = msg + 1;
+
+ size_t datalen = msglen - 1;
+
+ const struct child_request *req = hash_lookup(request_map, &cmd);
+
+ if(!req)
{
- datalen = read(sender->readpipe[0], data, sizeof(data));
+ sig_printf("Unknown request.\n");
+ return true;
}
write(sender->outpipe[1], &req->cmd_to_send, 1);
@@ -380,12 +445,32 @@ finish:
if(req && req->finalize)
req->finalize(data, datalen, sender);
- if(sender)
- kill(sender->pid, SIGUSR2);
- else
- sig_printf("WARN: unknown child sent request.\n");
+ union sigval junk;
+ sigqueue(sender->pid, SIGRTMIN, junk);
+
+ while(num_acks_recvd < num_acks_wanted)
+ {
+ sigsuspend(&old);
+ sig_printf("Got %d total acks\n", num_acks_recvd);
+ }
+
+ inc_acks = 0;
+
+ sigprocmask(SIG_SETMASK, &old, NULL);
sig_printf("finished handling client request\n");
+
+ return true;
+}
+
+static void master_ack_handler(int s, siginfo_t *info, void *v)
+{
+ sig_printf("Parent gets ACK\n");
+ if(inc_acks && hash_lookup(child_map, &info->si_pid))
+ {
+ ++num_acks_recvd;
+ sig_printf("%d acks now\n", num_acks_recvd);
+ }
}
void init_signals(void)
@@ -399,6 +484,8 @@ void init_signals(void)
if (sigaction(SIGCHLD, &sa, NULL) < 0)
error("sigaction");
+ sigemptyset(&sa.sa_mask);
+ sigaddset(&sa.sa_mask, SIGINT);
sa.sa_handler = sigint_handler;
sa.sa_flags = SA_RESTART;
if(sigaction(SIGINT, &sa, NULL) < 0)
@@ -406,10 +493,25 @@ void init_signals(void)
if(sigaction(SIGTERM, &sa, NULL) < 0)
error("sigaction");
- sigaddset(&sa.sa_mask, SIGUSR1);
- sa.sa_sigaction = sigusr1_handler;
+ sigemptyset(&sa.sa_mask);
+ sigaddset(&sa.sa_mask, SIGRTMIN+1);
+ sa.sa_sigaction = master_ack_handler;
+ sa.sa_flags = SA_SIGINFO | SA_RESTART;
+ if(sigaction(SIGRTMIN+1, &sa, NULL) < 0)
+ error("sigaction");
+
+ sigemptyset(&sa.sa_mask);
+ sa.sa_handler = SIG_IGN;
+ sa.sa_flags = SA_RESTART;
+ if(sigaction(SIGPIPE, &sa, NULL) < 0)
+ error("sigaction");
+
+ /* set this now so there's no race condition later */
+ sigemptyset(&sa.sa_mask);
+ void sig_rt_0_handler(int s, siginfo_t *info, void *v);
+ sa.sa_sigaction = sig_rt_0_handler;
sa.sa_flags = SA_RESTART | SA_SIGINFO;
- if(sigaction(SIGUSR1, &sa, NULL) < 0)
+ if(sigaction(SIGRTMIN, &sa, NULL) < 0)
error("sigaction");
}
@@ -481,8 +583,8 @@ int main(int argc, char *argv[])
server_socket = server_bind();
- /* set up signal handlers for SIGCHLD, SIGUSR1, and SIGINT */
- /* SIGUSR1 is used for broadcast signaling */
+ /* set up signal handlers */
+ /* SIGRTMIN+0 is used for broadcast signaling */
init_signals();
check_userfile();
@@ -492,68 +594,100 @@ int main(int argc, char *argv[])
reqmap_init();
/* this is set very low to make iteration faster */
- child_map = hash_init(8, pid_hash, pid_equal);
+ child_map = hash_init(11, pid_hash, pid_equal);
+ hash_setfreedata_cb(child_map, free_child_data);
+ hash_setfreekey_cb(child_map, free);
printf("Listening on port %d\n", port);
+ fd_set read_fds, active_fds;
+ FD_ZERO(&active_fds);
+ FD_SET(server_socket, &active_fds);
+
while(1)
{
- struct sockaddr_in client;
- socklen_t client_len = sizeof(client);
- int new_sock = accept(server_socket, (struct sockaddr*) &client, &client_len);
- if(new_sock < 0)
- error("accept");
-
- ++num_clients;
-
- int readpipe[2]; /* child->parent */
- int outpipe [2]; /* parent->child */
-
- if(pipe(readpipe) < 0)
- error("pipe");
- if(pipe(outpipe) < 0)
- error("pipe");
+ read_fds = active_fds;
+ int num_events = select(FD_SETSIZE, &read_fds, NULL, NULL, NULL);
+ if(num_events < 0)
+ error("select");
- pid_t pid = fork();
- if(pid < 0)
- error("fork");
-
- if(!pid)
- {
- /* child */
- close(readpipe[0]);
- close(outpipe[1]);
- close(server_socket);
-
- /* only the master process controls the world */
- world_free();
-
- printf("Child with PID %d spawned\n", getpid());
-
- server_socket = new_sock;
-
- handle_client(new_sock, &client, num_clients, readpipe[1], outpipe[0]);
-
- exit(0);
- }
- else
+ for(int i = 0; i < FD_SETSIZE; ++i)
{
- close(readpipe[1]);
- close(outpipe[0]);
- close(new_sock);
-
- /* add the child to the child map */
- struct child_data *new = calloc(1, sizeof(struct child_data));
- memcpy(new->outpipe, outpipe, sizeof(outpipe));
- memcpy(new->readpipe, readpipe, sizeof(readpipe));
- new->addr = client.sin_addr;
- new->pid = pid;
- new->state = STATE_INIT;
- new->user = NULL;
-
- pid_t *pidbuf = malloc(sizeof(pid_t));
- *pidbuf = pid;
- hash_insert(child_map, pidbuf, new);
+ if(FD_ISSET(i, &read_fds))
+ {
+ if(server_socket == i)
+ {
+ struct sockaddr_in client;
+ socklen_t client_len = sizeof(client);
+ int new_sock = accept(server_socket, (struct sockaddr*) &client, &client_len);
+ if(new_sock < 0)
+ error("accept");
+
+ ++num_clients;
+
+ int readpipe[2]; /* child->parent */
+ int outpipe [2]; /* parent->child */
+
+ if(pipe(readpipe) < 0)
+ error("pipe");
+ if(pipe(outpipe) < 0)
+ error("pipe");
+
+ pid_t pid = fork();
+ if(pid < 0)
+ error("fork");
+
+ if(!pid)
+ {
+ /* child */
+ close(readpipe[0]);
+ close(outpipe[1]);
+ close(server_socket);
+
+ /* only the master process controls the world */
+ world_free();
+ hash_free(request_map);
+ request_map = NULL;
+ hash_free(child_map);
+ child_map = NULL;
+
+ printf("Child with PID %d spawned\n", getpid());
+
+ server_socket = new_sock;
+
+ handle_client(new_sock, &client, num_clients, readpipe[1], outpipe[0]);
+
+ exit(0);
+ }
+ else
+ {
+ /* parent */
+ close(readpipe[1]);
+ close(outpipe[0]);
+ close(new_sock);
+
+ /* add the child to the child map */
+ struct child_data *new = calloc(1, sizeof(struct child_data));
+ memcpy(new->outpipe, outpipe, sizeof(outpipe));
+ memcpy(new->readpipe, readpipe, sizeof(readpipe));
+ FD_SET(new->readpipe[0], &active_fds);
+ new->addr = client.sin_addr;
+ new->pid = pid;
+ new->state = STATE_INIT;
+ new->user = NULL;
+
+ pid_t *pidbuf = malloc(sizeof(pid_t));
+ *pidbuf = pid;
+ hash_insert(child_map, pidbuf, new);
+ }
+ }
+ else
+ {
+ /* must be data from a child */
+ if(!handle_child_req(i))
+ FD_CLR(i, &active_fds);
+ }
+ }
}
}
}
diff --git a/src/telnet.c b/src/telnet.c
index 377f5cf..f984528 100644
--- a/src/telnet.c
+++ b/src/telnet.c
@@ -1,3 +1,21 @@
+/*
+ * NetCosm - a MUD server
+ * Copyright (C) 2015 Franklin Wei
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
#include "netcosm.h"
void telnet_handle_command(const unsigned char *buf)
diff --git a/src/telnet.h b/src/telnet.h
index 11d1a42..91364dc 100644
--- a/src/telnet.h
+++ b/src/telnet.h
@@ -1,3 +1,21 @@
+/*
+ * NetCosm - a MUD server
+ * Copyright (C) 2015 Franklin Wei
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
/* commands */
#define IAC 255
#define DONT 254
diff --git a/src/util.c b/src/util.c
index 6efcb76..d69ed0a 100644
--- a/src/util.c
+++ b/src/util.c
@@ -1,3 +1,21 @@
+/*
+ * NetCosm - a MUD server
+ * Copyright (C) 2015 Franklin Wei
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
#include "netcosm.h"
void remove_cruft(char *str)