diff options
| author | Franklin Wei <git@fwei.tk> | 2015-12-26 21:02:54 -0500 |
|---|---|---|
| committer | Franklin Wei <git@fwei.tk> | 2015-12-26 21:03:15 -0500 |
| commit | 0a2f9197058cc5248ec8e4bed7c361397c8d1c79 (patch) | |
| tree | b4ac31dcaa64bdfded9cdb2450bf936904802d08 /src | |
| parent | f7041112f179aa79b6e315e7d57afbf76d3cd8bb (diff) | |
| download | netcosm-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.c | 24 | ||||
| -rw-r--r-- | src/client.c | 79 | ||||
| -rw-r--r-- | src/hash.c | 65 | ||||
| -rw-r--r-- | src/hash.h | 23 | ||||
| -rw-r--r-- | src/netcosm.h | 25 | ||||
| -rw-r--r-- | src/room.c | 66 | ||||
| -rw-r--r-- | src/server.c | 326 | ||||
| -rw-r--r-- | src/telnet.c | 18 | ||||
| -rw-r--r-- | src/telnet.h | 18 | ||||
| -rw-r--r-- | src/util.c | 18 |
10 files changed, 506 insertions, 156 deletions
@@ -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) @@ -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; } @@ -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]; @@ -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 @@ -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) |