aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile4
-rw-r--r--src/auth.c3
-rw-r--r--src/auth.h5
-rw-r--r--src/child_reqs.h12
-rw-r--r--src/client.c18
-rw-r--r--src/hash.c16
-rw-r--r--src/hash.h16
-rw-r--r--src/netcosm.h51
-rw-r--r--src/room.c2
-rw-r--r--src/server.c264
-rw-r--r--src/server_reqs.c21
-rw-r--r--src/telnet.c6
-rw-r--r--src/telnet.h10
-rw-r--r--src/userdb.c58
-rw-r--r--src/userdb.h16
-rw-r--r--src/util.c28
16 files changed, 302 insertions, 228 deletions
diff --git a/Makefile b/Makefile
index 01dae17..211a81f 100644
--- a/Makefile
+++ b/Makefile
@@ -5,8 +5,8 @@ PLATFORM = unix
NETCOSM_SRC = $(shell cat SOURCES)
NETCOSM_OBJ := $(NETCOSM_SRC:.c=.o)
-CFLAGS = -Og -g -I src/ -I target/$(PLATFORM) -Wall -Wextra -Wshadow
-LDFLAGS = -lgcrypt
+CFLAGS = -O3 -g -I src/ -I target/$(PLATFORM) -Wall -Wextra -Wshadow -std=gnu99
+LDFLAGS = -lgcrypt -lev
HEADERS = src/netcosm.h src/hash.h src/telnet.h src/userdb.h
diff --git a/src/auth.c b/src/auth.c
index 400f5cd..5e716ac 100644
--- a/src/auth.c
+++ b/src/auth.c
@@ -183,6 +183,9 @@ struct userdata_t *auth_check(const char *name2, const char *pass2)
/* find it in the user list */
struct userdata_t *data = userdb_request_lookup(name);
+
+ free(name);
+
char *salt = data->salt, *hash = data->passhash;
if(data)
diff --git a/src/auth.h b/src/auth.h
index 2717d30..b771152 100644
--- a/src/auth.h
+++ b/src/auth.h
@@ -16,8 +16,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef _AUTH_H_
-#define _AUTH_H_
+#pragma once
#define SALT_LEN 12
#define ALGO GCRY_MD_SHA512
@@ -42,5 +41,3 @@ bool auth_user_del(const char *user);
/* lists users through out() */
void auth_user_list(void);
-
-#endif
diff --git a/src/child_reqs.h b/src/child_reqs.h
deleted file mode 100644
index db3257a..0000000
--- a/src/child_reqs.h
+++ /dev/null
@@ -1,12 +0,0 @@
-/* functions allowing a child process to send its master requests */
-
-extern struct userdata_t sent_userdata;
-extern bool child_req_success;
-
-void client_change_room(room_id id);
-void client_change_state(int state);
-void client_change_user(const char *user);
-void client_move(const char *dir);
-void client_look(void);
-
-void send_master(unsigned char cmd, const void *data, size_t sz);
diff --git a/src/client.c b/src/client.c
index 330187d..77072e2 100644
--- a/src/client.c
+++ b/src/client.c
@@ -102,6 +102,8 @@ void send_master(unsigned char cmd, const void *data, size_t sz)
sigprocmask(SIG_SETMASK, &old, NULL);
while(!request_complete) usleep(1);
+
+ free(req);
}
#define BUFSZ 128
@@ -119,9 +121,11 @@ tryagain:
buf[BUFSZ - 1] = '\0';
if(buf[0] & 0x80)
{
- telnet_handle_command((unsigned char*)buf);
+ int ret = telnet_handle_command((unsigned char*)buf);
free(buf);
+ if(ret == TELNET_EXIT)
+ exit(0);
goto tryagain;
}
@@ -140,15 +144,6 @@ char *client_read_password(void)
return ret;
}
-void all_upper(char *s)
-{
- while(*s)
- {
- *s = toupper(*s);
- s++;
- }
-}
-
static void print_all(int fd)
{
unsigned char buf[MSG_MAX + 1];
@@ -161,9 +156,6 @@ static void print_all(int fd)
} while(1);
}
-struct userdata_t sent_userdata;
-bool child_req_success;
-
enum reqdata_typespec reqdata_type = TYPE_NONE;
union reqdata_t returned_reqdata;
diff --git a/src/hash.c b/src/hash.c
index d5eeba0..9e27fee 100644
--- a/src/hash.c
+++ b/src/hash.c
@@ -16,7 +16,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include "hash.h"
+#include "netcosm.h"
#include <stdlib.h>
#include <string.h>
@@ -34,6 +34,7 @@ struct hash_map {
size_t table_sz;
void (*free_key)(void *key);
void (*free_data)(void *data);
+ size_t n_entries;
};
unsigned hash_djb(const void *ptr)
@@ -63,6 +64,7 @@ void *hash_init(size_t sz, unsigned (*hash_fn)(const void*),
ret->table_sz = sz;
ret->hash = hash_fn;
ret->compare = compare_keys;
+ ret->n_entries = 0;
return ret;
}
@@ -81,6 +83,7 @@ void hash_setfreedata_cb(void *ptr, void (*cb)(void *data))
void hash_free(void *ptr)
{
+ sig_debugf("freeing map\n");
if(ptr)
{
struct hash_map *map = ptr;
@@ -92,7 +95,10 @@ void hash_free(void *ptr)
struct hash_node *next = node->next;
if(map->free_data)
+ {
+ debugf("freeing data\n");
map->free_data((void*)node->data);
+ }
if(map->free_key)
map->free_key((void*)node->key);
free(node);
@@ -172,6 +178,7 @@ void *hash_insert(void *ptr, const void *key, const void *data)
map->table[hash] = new;
else
last->next = new;
+ ++map->n_entries;
return NULL;
}
@@ -223,6 +230,7 @@ bool hash_remove(void *ptr, const void *key)
map->free_key((void*)iter->key);
if(map->free_data)
map->free_data((void*)iter->data);
+ --map->n_entries;
free(iter);
return true;
}
@@ -248,3 +256,9 @@ void *hash_getkeyptr(void *ptr, const void *key)
}
return NULL;
}
+
+size_t hash_size(void *ptr)
+{
+ struct hash_map *map = ptr;
+ return map->n_entries;
+}
diff --git a/src/hash.h b/src/hash.h
index b154bd0..e7fbe00 100644
--- a/src/hash.h
+++ b/src/hash.h
@@ -19,6 +19,8 @@
#include <stdbool.h>
#include <stddef.h>
+#pragma once
+
/* simple, generic chained hash map implementation */
unsigned hash_djb(const void*);
@@ -63,7 +65,7 @@ void *hash_iterate(void *map, void **saved, void **keyptr);
struct hash_pair {
void *key;
unsigned char value[0];
-};
+} __attribute__((packed));
/* insert n key->pair members of size pairsize */
void hash_insert_pairs(void*, const struct hash_pair*, size_t pairsize, size_t n);
@@ -71,14 +73,16 @@ void hash_insert_pairs(void*, const struct hash_pair*, size_t pairsize, size_t n
/* gets the original pointer used to store the tuple */
void *hash_getkeyptr(void*, const void *key);
-#define SIMP_HASH(TYPE, NAME) \
- unsigned NAME (const void *key) \
- { \
- return *((TYPE*)key); \
+#define SIMP_HASH(TYPE, NAME) \
+ unsigned NAME (const void *key) \
+ { \
+ return (unsigned)*((const TYPE*)key); \
}
#define SIMP_EQUAL(TYPE, NAME) \
int NAME (const void *a, const void *b) \
{ \
- return !(*((TYPE*)a) == *((TYPE*)b)); \
+ return !(*((const TYPE*)a) == *((const TYPE*)b)); \
}
+
+size_t hash_size(void*);
diff --git a/src/netcosm.h b/src/netcosm.h
index c5801a6..34e2f3d 100644
--- a/src/netcosm.h
+++ b/src/netcosm.h
@@ -16,16 +16,18 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef _NC_H_
-#define _NC_H_
+/* You should use #pragma once everywhere. */
+#pragma once
-#define _GNU_SOURCE /* for pipe2() */
+#define _GNU_SOURCE
+
+#include <ev.h>
+#include <gcrypt.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
-#include <gcrypt.h>
#include <netdb.h>
#include <netinet/in.h>
#include <poll.h>
@@ -48,9 +50,9 @@
#define WORLDFILE "world.dat"
#define WORLD_MAGIC 0xff467777
#define MAX_FAILURES 3
-#define NETCOSM_VERSION "v0.1"
+#define NETCOSM_VERSION "0.2"
-#define MAX_NAME_LEN 20
+#define MAX_NAME_LEN 32
#define PRIV_NONE -1
#define PRIV_USER 0
@@ -70,6 +72,7 @@
#define NONE_IN NULL
#define NONE_OT NULL
+/* needs to be less than PIPE_BUF, which is 4096 */
#define MSG_MAX 2048
#define ARRAYLEN(x) (sizeof(x)/sizeof(x[0]))
@@ -77,7 +80,7 @@
#define MIN(a,b) ((a<b)?(a):(b))
#ifndef NDEBUG
-#define debugf(fmt,...) debugf_real(fmt, ##__VA_ARGS__)
+#define debugf(fmt,...) debugf_real(__func__, __LINE__, __FILE__, fmt, ##__VA_ARGS__)
#define sig_debugf debugf
#else
#define debugf(fmt,...)
@@ -152,22 +155,35 @@ struct child_data {
room_id room;
char *user;
+ ev_io *io_watcher;
+ ev_child *sigchld_watcher;
+ struct ev_loop *loop;
+
struct in_addr addr;
};
-extern const struct roomdata_t netcosm_world[];
-extern const size_t netcosm_world_sz;
-extern const char *netcosm_world_name;
-
#include "auth.h"
#include "hash.h"
#include "server_reqs.h"
#include "telnet.h"
#include "userdb.h"
+/* globals */
+extern const struct roomdata_t netcosm_world[];
+extern const size_t netcosm_world_sz;
+extern const char *netcosm_world_name;
+
extern volatile int num_clients;
extern void *child_map;
+extern enum reqdata_typespec { TYPE_NONE = 0, TYPE_USERDATA, TYPE_BOOLEAN } reqdata_type;
+extern union reqdata_t {
+ struct userdata_t userdata;
+ bool boolean;
+} returned_reqdata;
+
+extern bool are_child;
+
/* called for every client */
void client_main(int sock, struct sockaddr_in *addr, int, int to_parent, int from_parent);
@@ -187,17 +203,12 @@ void world_free(void);
/* utility functions */
void __attribute__((noreturn,format(printf,1,2))) error(const char *fmt, ...);
-void debugf_real(const char *fmt, ...);
+void debugf_real(const char*, int, const char*, const char *fmt, ...);
void remove_cruft(char*);
-
-extern enum reqdata_typespec { TYPE_NONE = 0, TYPE_USERDATA, TYPE_BOOLEAN } reqdata_type;
-extern union reqdata_t {
- struct userdata_t userdata;
- bool boolean;
-} returned_reqdata;
-
+void all_upper(char*);
/* call from child process ONLY */
void send_master(unsigned char cmd, const void *data, size_t sz);
-#endif
+/* the master sends the child SIGRTMIN+0 */
+void sig_rt_0_handler(int s, siginfo_t *info, void *v);
diff --git a/src/room.c b/src/room.c
index f79c422..b387a23 100644
--- a/src/room.c
+++ b/src/room.c
@@ -153,6 +153,8 @@ void world_free(void)
{
if(world)
{
+ if(world_name)
+ free(world_name);
for(unsigned i = 0; i < world_sz; ++i)
{
room_free(world + i);
diff --git a/src/server.c b/src/server.c
index 02e71b2..53d627e 100644
--- a/src/server.c
+++ b/src/server.c
@@ -21,6 +21,18 @@
#define PORT 1234
#define BACKLOG 16
+/* global data */
+bool are_child = false;
+void *child_map = NULL;
+
+/* assume int is atomic */
+volatile int num_clients = 0;
+
+/* local data */
+static uint16_t port;
+
+static int server_socket;
+
void __attribute__((noreturn)) error(const char *fmt, ...)
{
char buf[128];
@@ -33,11 +45,6 @@ void __attribute__((noreturn)) error(const char *fmt, ...)
exit(EXIT_FAILURE);
}
-/* assume int is atomic */
-volatile int num_clients = 0;
-void *child_map = NULL;
-static fd_set read_fds, active_fds;
-
static void free_child_data(void *ptr)
{
struct child_data *child = ptr;
@@ -46,12 +53,8 @@ static void free_child_data(void *ptr)
free(ptr);
}
-static void sigchld_handler(int s, siginfo_t *info, void *vp)
+static void handle_disconnects(void)
{
- (void) s;
- (void) info;
- (void) vp;
- // waitpid() might overwrite errno, so we save and restore it:
int saved_errno = errno;
pid_t pid;
@@ -59,7 +62,8 @@ static void sigchld_handler(int s, siginfo_t *info, void *vp)
{
sig_debugf("Client disconnect.\n");
struct child_data *child = hash_lookup(child_map, &pid);
- FD_CLR(child->readpipe[0], &active_fds);
+ ev_io_stop(child->loop, child->io_watcher);
+ free(child->io_watcher);
--num_clients;
@@ -69,7 +73,11 @@ static void sigchld_handler(int s, siginfo_t *info, void *vp)
errno = saved_errno;
}
-int port;
+static void sigchld_handler(int s)
+{
+ (void) s;
+ handle_disconnects();
+}
static void handle_client(int fd, struct sockaddr_in *addr,
int nclients, int to, int from)
@@ -77,9 +85,7 @@ static void handle_client(int fd, struct sockaddr_in *addr,
client_main(fd, addr, nclients, to, from);
}
-int server_socket;
-
-static void serv_cleanup(void)
+static void __attribute__((noreturn)) serv_cleanup(void)
{
sig_debugf("Shutdown server.\n");
@@ -93,26 +99,27 @@ static void serv_cleanup(void)
hash_free(child_map);
child_map = NULL;
+ ev_default_destroy();
userdb_shutdown();
_exit(0);
}
-static void sigint_handler(int s)
+static void __attribute__((noreturn)) sigint_handler(int s)
{
(void) s;
serv_cleanup();
}
-void init_signals(void)
+static void init_signals(void)
{
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sigaddset(&sa.sa_mask, SIGCHLD);
- sa.sa_sigaction = sigchld_handler; // reap all dead processes
- sa.sa_flags = SA_RESTART | SA_SIGINFO;
+ sa.sa_handler = sigchld_handler;
+ sa.sa_flags = SA_RESTART;
if(sigaction(SIGCHLD, &sa, NULL) < 0)
error("sigaction");
@@ -139,8 +146,6 @@ void init_signals(void)
if(sigaction(SIGPIPE, &sa, NULL) < 0)
error("sigaction");
- void sig_rt_0_handler(int s, siginfo_t *info, void *v);
-
/* we set this now so there's no race condition after a fork() */
sigemptyset(&sa.sa_mask);
sigaddset(&sa.sa_mask, SIGRTMIN);
@@ -205,6 +210,105 @@ static int server_bind(void)
static SIMP_HASH(pid_t, pid_hash);
static SIMP_EQUAL(pid_t, pid_equal);
+static void childreq_cb(EV_P_ ev_io *w, int revents)
+{
+ (void) EV_A;
+ (void) w;
+ (void) revents;
+ /* data from a child's pipe */
+ if(!handle_child_req(w->fd))
+ {
+ handle_disconnects();
+ }
+}
+
+static void new_connection_cb(EV_P_ ev_io *w, int revents)
+{
+ (void) EV_A;
+ (void) w;
+ (void) revents;
+ 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");
+
+ int flags = fcntl(outpipe[0], F_GETFL, 0);
+ if(fcntl(outpipe[0], F_SETFL, flags | O_NONBLOCK) < 0)
+ error("fcntl");
+
+ pid_t pid = fork();
+ if(pid < 0)
+ error("fork");
+
+ if(!pid)
+ {
+ /* child */
+ are_child = true;
+ close(readpipe[0]);
+ close(outpipe[1]);
+ close(server_socket);
+
+ /* only the master process controls the world */
+ world_free();
+ reqmap_free();
+ hash_free(child_map);
+ child_map = NULL;
+
+ /* we don't need libev anymore */
+ ev_default_destroy();
+
+ /* user DB requests go through the master */
+ userdb_shutdown();
+
+ debugf("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));
+ new->addr = client.sin_addr;
+ new->pid = pid;
+ new->state = STATE_INIT;
+ new->user = NULL;
+
+ ev_io *new_io_watcher = calloc(1, sizeof(ev_io));
+ ev_io_init(new_io_watcher, childreq_cb, new->readpipe[0], EV_READ);
+ ev_io_start(EV_A_ new_io_watcher);
+ new->io_watcher = new_io_watcher;
+
+ new->loop = loop;
+
+ pid_t *pidbuf = malloc(sizeof(pid_t));
+ *pidbuf = pid;
+
+ hash_insert(child_map, pidbuf, new);
+ }
+}
+
int main(int argc, char *argv[])
{
if(argc != 2)
@@ -215,11 +319,6 @@ int main(int argc, char *argv[])
srand(time(0));
server_socket = server_bind();
-
- /* set up signal handlers */
- /* SIGRTMIN+0 is used for broadcast signaling */
- init_signals();
-
userdb_init(USERFILE);
check_userfile();
@@ -229,110 +328,23 @@ int main(int argc, char *argv[])
reqmap_init();
/* this initial size very low to make iteration faster */
- child_map = hash_init(7, pid_hash, pid_equal);
+ child_map = hash_init(16, pid_hash, pid_equal);
hash_setfreedata_cb(child_map, free_child_data);
hash_setfreekey_cb(child_map, free);
debugf("Listening on port %d\n", port);
- FD_ZERO(&active_fds);
- FD_SET(server_socket, &active_fds);
+ struct ev_loop *loop = ev_default_loop(0);
- while(1)
- {
- read_fds = active_fds;
- int num_events;
- sigset_t all, old;
- sigemptyset(&all);
- sigaddset(&all, SIGPIPE);
- sigprocmask(SIG_SETMASK, &all, &old);
- do {
- num_events = select(FD_SETSIZE, &read_fds, NULL, NULL, NULL);
- } while (num_events < 0);
-
- sigprocmask(SIG_SETMASK, &old, NULL);
- //if(num_events < 0)
- // error("select");
-
- for(int i = 0; i < FD_SETSIZE; ++i)
- {
- 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(pipe2(outpipe, O_NONBLOCK) < 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();
- reqmap_free();
- hash_free(child_map);
- child_map = NULL;
-
- /* user DB requests go through the master */
- userdb_shutdown();
-
- debugf("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
- {
- /* data from a child's pipe */
- if(!handle_child_req(i))
- FD_CLR(i, &active_fds);
- }
- }
- }
- }
+ /* set up signal handlers AFTER creating the default loop, because it will grab SIGCHLD */
+ init_signals();
+
+ ev_io server_watcher;
+ ev_io_init(&server_watcher, new_connection_cb, server_socket, EV_READ);
+ ev_io_start(EV_A_ &server_watcher);
+
+ ev_loop(loop, 0);
+
+ /* should never get here */
+ error("unexpected termination");
}
diff --git a/src/server_reqs.c b/src/server_reqs.c
index ac53396..10f6ad3 100644
--- a/src/server_reqs.c
+++ b/src/server_reqs.c
@@ -46,9 +46,9 @@ static void req_send_clientinfo(unsigned char *data, size_t datalen,
inet_ntoa(child->addr), child->pid, state[child->state]);
if(sender->pid == child->pid)
- strncat(buf, " [YOU]\n", sizeof(buf));
+ strncat(buf, " [YOU]\n", sizeof(buf) - 1);
else
- strncat(buf, "\n", sizeof(buf));
+ strncat(buf, "\n", sizeof(buf) - 1);
write(sender->outpipe[1], buf, strlen(buf));
}
@@ -167,13 +167,6 @@ static void req_move_room(unsigned char *data, size_t datalen, struct child_data
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)
@@ -417,9 +410,17 @@ finish:
else
sig_debugf("Unknown PID sent request.\n");
+ /* 5 ms */
+#define ACK_TIMEOUT 5000
+
+ struct timespec timeout;
+ timeout.tv_sec = 0;
+ timeout.tv_nsec = ACK_TIMEOUT;
+
while(num_acks_recvd < MIN(num_clients,num_acks_wanted))
{
- sigsuspend(&old);
+ if(sigtimedwait(&old, NULL, &timeout) < 0 && errno == EAGAIN)
+ break;
}
inc_acks = 0;
diff --git a/src/telnet.c b/src/telnet.c
index 173574b..038c54c 100644
--- a/src/telnet.c
+++ b/src/telnet.c
@@ -18,7 +18,7 @@
#include "netcosm.h"
-void telnet_handle_command(const unsigned char *buf)
+int telnet_handle_command(const unsigned char *buf)
{
bool cmd = false;
@@ -58,7 +58,7 @@ void telnet_handle_command(const unsigned char *buf)
switch(c)
{
case IP:
- exit(0);
+ return TELNET_EXIT;
default:
break;
}
@@ -70,6 +70,8 @@ void telnet_handle_command(const unsigned char *buf)
if(cmd)
debugf("\n");
+
+ return TELNET_OK;
}
void telnet_echo_off(void)
diff --git a/src/telnet.h b/src/telnet.h
index c0725c4..673ad1c 100644
--- a/src/telnet.h
+++ b/src/telnet.h
@@ -16,6 +16,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#pragma once
+
/* commands */
#define IAC 255
#define DONT 254
@@ -39,6 +41,12 @@
#define LINEMODE 34
void telnet_init(void);
-void telnet_handle_command(const unsigned char*);
+
+#define TELNET_OK 0
+#define TELNET_EXIT 1
+
+/* returns either 0 or TELNET_EXIT */
+int telnet_handle_command(const unsigned char*);
+
void telnet_echo_on(void);
void telnet_echo_off(void);
diff --git a/src/userdb.c b/src/userdb.c
index c58771f..749115c 100644
--- a/src/userdb.c
+++ b/src/userdb.c
@@ -2,18 +2,16 @@
static void *map = NULL;
static char *db_file = NULL;
-static size_t entries = 0;
/*
* the user DB is stored on disk as an ASCII database
*
- * this is then loaded into variable-sized hash map at init
+ * this is then loaded into fixed-sized hash map at init
* TODO: implement with B-tree
*/
void userdb_init(const char *file)
{
db_file = strdup(file);
- entries = 0;
/* FILE* for getline() */
FILE *f = fopen(file, "r");
@@ -21,7 +19,7 @@ void userdb_init(const char *file)
hash_setfreedata_cb(map, free);
char *format;
- asprintf(&format, "%%%d[a-z0-9]:%%%d[A-Z]:%%%ds:%%d\n",
+ asprintf(&format, "%%%d[a-z0-9 ]:%%%d[A-Z]:%%%ds:%%d\n",
MAX_NAME_LEN, SALT_LEN, AUTH_HASHLEN * 2);
if(f)
@@ -31,16 +29,18 @@ void userdb_init(const char *file)
struct userdata_t *data = calloc(1, sizeof(*data));
int ret = fscanf(f, format,
- &data->username,
+ data->username,
data->salt,
data->passhash,
&data->priv);
if(ret != 4)
+ {
+ free(data);
break;
+ }
hash_insert(map, data->username, data);
- ++entries;
}
fclose(f);
@@ -59,8 +59,8 @@ void userdb_write(const char *file)
ptr = NULL;
if(!user)
break;
- dprintf(fd, "%*s:%*s:%*s:%d\n",
- MAX_NAME_LEN, user->username,
+ dprintf(fd, "%s:%*s:%*s:%d\n",
+ user->username,
SALT_LEN, user->salt,
AUTH_HASHLEN*2, user->passhash,
user->priv);
@@ -77,13 +77,13 @@ bool userdb_remove(const char *key)
{
if(hash_remove(map, key))
{
- --entries;
userdb_write(db_file);
return true;
}
return false;
}
+/* should never fail, but returns NULL if something weird happens */
struct userdata_t *userdb_add(struct userdata_t *data)
{
struct userdata_t *new = calloc(1, sizeof(*new)); /* only in C! */
@@ -99,17 +99,16 @@ struct userdata_t *userdb_add(struct userdata_t *data)
}
userdb_write(db_file);
- if(!ret)
- ++entries;
return ret;
}
void userdb_shutdown(void)
{
- debugf("shutting down userdb with %d entries\n", entries);
+ debugf("shutting down userdb with %d entries\n", hash_size(map));
if(map)
{
+ debugf("shutting down userdb\n");
hash_free(map);
map = NULL;
}
@@ -122,28 +121,43 @@ void userdb_shutdown(void)
size_t userdb_size(void)
{
- return entries;
+ return hash_size(map);
}
/* child request wrappers */
+/* NOTE: also works from the master, but it's better to use the userdb_* funcs instead */
-/* loop until we can return a user data structure */
struct userdata_t *userdb_request_lookup(const char *name)
{
- send_master(REQ_GETUSERDATA, name, strlen(name) + 1);
- if(reqdata_type == TYPE_USERDATA)
- return &returned_reqdata.userdata;
- return NULL;
+ if(are_child)
+ {
+ send_master(REQ_GETUSERDATA, name, strlen(name) + 1);
+ if(reqdata_type == TYPE_USERDATA)
+ return &returned_reqdata.userdata;
+ return NULL;
+ }
+ else
+ return userdb_lookup(name);
}
bool userdb_request_add(struct userdata_t *data)
{
- send_master(REQ_ADDUSERDATA, data, sizeof(*data));
- return returned_reqdata.boolean;
+ if(are_child)
+ {
+ send_master(REQ_ADDUSERDATA, data, sizeof(*data));
+ return returned_reqdata.boolean;
+ }
+ else
+ return userdb_add(data);
}
bool userdb_request_remove(const char *name)
{
- send_master(REQ_DELUSERDATA, name, strlen(name) + 1);
- return returned_reqdata.boolean;
+ if(are_child)
+ {
+ send_master(REQ_DELUSERDATA, name, strlen(name) + 1);
+ return returned_reqdata.boolean;
+ }
+ else
+ return userdb_remove(name);
}
diff --git a/src/userdb.h b/src/userdb.h
index ac169b2..a941e9a 100644
--- a/src/userdb.h
+++ b/src/userdb.h
@@ -1,12 +1,8 @@
+#pragma once
+
#include "netcosm.h"
#include "auth.h"
-/*
- * on-disk database for storing user data
- *
- * child processes MUST go through the master to use this
- */
-
struct userdata_t {
char username[MAX_NAME_LEN + 1];
@@ -16,8 +12,11 @@ struct userdata_t {
char passhash[AUTH_HASHLEN * 2 + 1];
int priv;
+ room_id room;
};
+/*** functions for the master process ONLY ***/
+
/* call before using anything else */
void userdb_init(const char *dbfile);
@@ -42,3 +41,8 @@ void userdb_shutdown(void);
/* save the DB to disk */
void userdb_write(const char*);
+
+/*** child-only functions ***/
+struct userdata_t *userdb_request_lookup(const char *name);
+bool userdb_request_add(struct userdata_t *data);
+bool userdb_request_remove(const char *name);
diff --git a/src/util.c b/src/util.c
index b084261..103b4f9 100644
--- a/src/util.c
+++ b/src/util.c
@@ -28,15 +28,37 @@ void remove_cruft(char *str)
* WARNING: not signal-safe
* TODO: rewrite to avoid calling *printf()
*/
-void debugf_real(const char *fmt, ...)
+void debugf_real(const char *func, int line, const char *file, const char *fmt, ...)
{
+ (void) func;
+ (void) line;
+ (void) file;
+ int len;
+#if 0
+ char *prefix;
+ len = asprintf(&prefix, "%s:%s:%d: ", func, file, line);
+ write(STDOUT_FILENO, prefix, len);
+ free(prefix);
+#endif
+
va_list ap;
va_start(ap, fmt);
- char buf[128];
- int len = vsnprintf(buf, sizeof(buf), fmt, ap);
+ char *buf;
+ len = vasprintf(&buf, fmt, ap);
write(STDOUT_FILENO, buf, len);
+ free(buf);
+
va_end(ap);
}
+
+void all_upper(char *s)
+{
+ while(*s)
+ {
+ *s = toupper(*s);
+ s++;
+ }
+}