From db66484c4300f5f0e857eff01d15fd3593002a79 Mon Sep 17 00:00:00 2001 From: David Härdeman Date: Fri, 5 Jun 2020 14:09:18 +0200 Subject: Initial commit --- config.c | 730 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ config.h | 12 + ctest.c | 74 ++++++ inotify.c | 730 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ inotify.h | 12 + main.c | 284 +++++++++++++++++++++++ main.h | 55 +++++ meson.build | 9 + server.c | 423 +++++++++++++++++++++++++++++++++++ server.h | 51 +++++ stest.c | 102 +++++++++ uring.c | 294 ++++++++++++++++++++++++ uring.h | 41 ++++ utils.c | 37 +++ utils.h | 138 ++++++++++++ 15 files changed, 2992 insertions(+) create mode 100644 config.c create mode 100644 config.h create mode 100644 ctest.c create mode 100644 inotify.c create mode 100644 inotify.h create mode 100644 main.c create mode 100644 main.h create mode 100644 meson.build create mode 100644 server.c create mode 100644 server.h create mode 100644 stest.c create mode 100644 uring.c create mode 100644 uring.h create mode 100644 utils.c create mode 100644 utils.h diff --git a/config.c b/config.c new file mode 100644 index 0000000..f362920 --- /dev/null +++ b/config.c @@ -0,0 +1,730 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "main.h" +#include "uring.h" +#include "config.h" +#include "server.h" + +static void +eat_whitespace_and_comments(char **pos) +{ + while (true) { + while (isspace(**pos)) + (*pos)++; + + if (**pos == '#') { + while (**pos != '\r' && **pos != '\n' && **pos != '\0') + (*pos)++; + continue; + } + + return; + } +} + +static char * +get_line(char **pos) +{ + char *begin = *pos; + char *end; + + while (isspace(*begin)) + begin++; + + if (*begin == '\0') + return NULL; + + end = begin; + while (*end != '\n' && *end != '\0') + end++; + + if (*end == '\0') + *pos = end; + else + *pos = end + 1; + + while (isspace(*end)) { + *end = '\0'; + end--; + } + + return begin; +} + +static bool +strtosockaddrs(const char *str, struct list_head *list) +{ + char *tmp; + uint16_t port; + int r; + struct sockaddr_in46 *addr; + + if (!str || *str == '\0' || !list) + return false; + list_init(list); + + if (*str == '[') { + /* IPv6, [a:b:c...h]:p or [*]:p */ + str++; + tmp = strchr(str, ']'); + if (!tmp) + goto out; + *tmp = '\0'; + + addr = zmalloc(sizeof(*addr)); + if (!addr) + goto out; + list_add(&addr->list, list); + + if (!strcmp(str, "*")) + addr->in6.sin6_addr = in6addr_any; + else if (inet_pton(AF_INET6, str, &addr->in6.sin6_addr) <= 0) + goto out; + + tmp++; + if (*tmp != ':') + goto out; + + tmp++; + if (strtou16_strict(tmp, &port) < 0) + goto out; + + addr->in6.sin6_family = AF_INET6; + addr->in6.sin6_port = htons(port); + addr->addrlen = sizeof(addr->in6); + + } else if (*str == '*') { + /* IPv4, *:p */ + str++; + if (*str != ':') + goto out; + + str++; + if (strtou16_strict(str, &port) < 0) + goto out; + + addr = zmalloc(sizeof(*addr)); + if (!addr) + goto out; + list_add(&addr->list, list); + + addr->in4.sin_family = AF_INET; + addr->in4.sin_addr.s_addr = INADDR_ANY; + addr->in4.sin_port = htons(port); + addr->addrlen = sizeof(addr->in4); + + } else if ((tmp = strchr(str, ':'))) { + /* IPv4, a.b.c.d:p or IPv4/6 hostname:p */ + fprintf(stderr, "Got an IPv4:port or hostname:port\n"); + *tmp = '\0'; + tmp++; + if (strtou16_strict(tmp, &port) < 0) + goto out; + + addr = zmalloc(sizeof(*addr)); + if (!addr) + goto out; + + if (inet_pton(AF_INET, str, &addr->in4.sin_addr) > 0) { + fprintf(stderr, "...Got an IPv4:port (0x%p, list 0x%p)\n", addr, &addr->list); + addr->in4.sin_family = AF_INET; + addr->in4.sin_port = htons(port); + addr->addrlen = sizeof(addr->in4); + list_add(&addr->list, list); + goto success; + } else { + free(addr); + } + + struct addrinfo hints = { + .ai_family = AF_UNSPEC, + .ai_socktype = SOCK_STREAM, + .ai_protocol = 0, + .ai_flags = 0, + }; + struct addrinfo *results, *ai; + + /* FIXME: This is completely synchronous but getaddrinfo_a is not very ergonomic */ + r = getaddrinfo(str, tmp, &hints, &results); + if (r != 0) { + fprintf(stderr, "gettaddrinfo(%s): %s\n", str, gai_strerror(r)); + goto out; + } + + fprintf(stderr, "...Got an hostname:port\n"); + for (ai = results; ai; ai = ai->ai_next) { + fprintf(stderr, "Got a result from getaddrinfo\n"); + + addr = zmalloc(sizeof(*addr)); + if (!addr) { + freeaddrinfo(results); + goto out; + } + + switch (ai->ai_family) { + case AF_INET: { + struct sockaddr_in *naddr = (struct sockaddr_in *)ai->ai_addr; + + addr->in4.sin_family = AF_INET; + addr->in4.sin_addr = naddr->sin_addr; + addr->in4.sin_port = naddr->sin_port; + addr->addrlen = sizeof(addr->in4); + list_add(&addr->list, list); + break; + } + case AF_INET6: { + struct sockaddr_in6 *naddr = (struct sockaddr_in6 *)ai->ai_addr; + + addr->in6.sin6_family = AF_INET6; + addr->in6.sin6_addr = naddr->sin6_addr; + addr->in6.sin6_port = naddr->sin6_port; + addr->addrlen = sizeof(addr->in6); + list_add(&addr->list, list); + break; + } + default: + fprintf(stderr, " Fam: Unknown (%i)\n", ai->ai_family); + free(addr); + continue; + } + } + + freeaddrinfo(results); + + } else if (strtou16_strict(tmp, &port) == 0) { + /* Port */ + + addr = zmalloc(sizeof(*addr)); + if (!addr) + goto out; + addr->in6.sin6_family = AF_INET6; + addr->in6.sin6_addr = in6addr_any; + addr->in6.sin6_port = htons(port); + addr->addrlen = sizeof(addr->in6); + list_add(&addr->list, list); + + addr = zmalloc(sizeof(*addr)); + if (!addr) + goto out; + addr->in4.sin_family = AF_INET; + addr->in4.sin_addr.s_addr = INADDR_ANY; + addr->in4.sin_port = htons(port); + addr->addrlen = sizeof(addr->in4); + list_add(&addr->list, list); + } + +success: + if (list_empty(list)) { + fprintf(stderr, "Success but empty list!?\n"); + return false; + } else { + int i = 0; + struct list_head *pos; + + list_for_each(pos, list) + i++; + fprintf(stderr, "Success, %i entries\n", i); + } + + return true; + +out: + if (!list_empty(list)) { + struct sockaddr_in46 *tmpaddr; + + list_for_each_entry_safe(addr, tmpaddr, list, list) { + list_del(&addr->list); + free(addr); + } + } + return false; +} + +enum scfg_keys { + SCFG_KEY_TYPE, + SCFG_KEY_NAME, + SCFG_KEY_PORT, + SCFG_KEY_LOCAL, + SCFG_KEY_REMOTE, + SCFG_KEY_INVALID +}; + +enum value_type { + VAL_TYPE_STRING, + VAL_TYPE_UINT16, + VAL_TYPE_ADDR, + VAL_TYPE_INVALID +}; + +struct scfg_key_map { + const char *key_name; + enum scfg_keys key_value; + enum value_type value_type; +} scfg_key_map[] = { + { + .key_name = "type", + .key_value = SCFG_KEY_TYPE, + .value_type = VAL_TYPE_STRING, + }, { + .key_name = "name", + .key_value = SCFG_KEY_NAME, + .value_type = VAL_TYPE_STRING, + }, { + .key_name = "port", + .key_value = SCFG_KEY_PORT, + .value_type = VAL_TYPE_UINT16, + }, { + .key_name = "local", + .key_value = SCFG_KEY_LOCAL, + .value_type = VAL_TYPE_ADDR, + }, { + .key_name = "remote", + .key_value = SCFG_KEY_REMOTE, + .value_type = VAL_TYPE_ADDR, + } +}; + +union cfg_value { + const char *str; + uint16_t uint16; + struct list_head addr_list; +}; + + +static void +line_get_key_value(char *line, enum scfg_keys *rkey, union cfg_value *rvalue) +{ + char *tmp, *key; + int i; + + *rkey = SCFG_KEY_INVALID; + if (!line) + return; + + tmp = line; + while (isspace(*tmp)) + tmp++; + + if (*tmp == '\0') + return; + + key = tmp; + while (*tmp != '\0' && !isspace(*tmp)) + tmp++; + + if (*tmp == '\0') + return; + + *tmp = '\0'; + tmp++; + + while (isspace(*tmp)) + tmp++; + + if (*tmp != '=') + return; + + tmp++; + while (isspace(*tmp)) + tmp++; + + if (*tmp == '\0') + return; + + for (i = 0; i < ARRAY_SIZE(scfg_key_map); i++) { + if (strcmp(scfg_key_map[i].key_name, key)) + continue; + + switch (scfg_key_map[i].value_type) { + + case VAL_TYPE_STRING: + rvalue->str = tmp; + break; + + case VAL_TYPE_UINT16: { + uint16_t v; + + if (strtou16_strict(tmp, &v) < 0) + return; + rvalue->uint16 = v; + break; + } + + case VAL_TYPE_ADDR: { + if (!strtosockaddrs(tmp, &rvalue->addr_list)) + return; + if (list_empty(&rvalue->addr_list)) { + fprintf(stderr, "VAL_TYPE_ADDR with zero list!?\n"); + return; + } else { + int i = 0; + struct list_head *pos; + + list_for_each(pos, &rvalue->addr_list) + i++; + fprintf(stderr, "VAL_TYPE_ADDR with list %i entries\n", i); + } + break; + } + + case VAL_TYPE_INVALID: + /* fall through */ + default: + return; + } + *rkey = scfg_key_map[i].key_value; + break; + } +} + +static void +scfg_parse(struct cfg *cfg, struct server *scfg) +{ + char *pos = &scfg->buf[0]; + char *line; + + eat_whitespace_and_comments(&pos); + + line = get_line(&pos); + if (!line) { + printf("Cfg: premature EOF\n"); + return; + } + + if (strcmp(line, "[server]")) { + printf("Invalid line: %s\n", line); + return; + } + + while (true) { + enum scfg_keys key; + union cfg_value value; + + eat_whitespace_and_comments(&pos); + line = get_line(&pos); + printf("Examining line: %s\n", line); + line_get_key_value(line, &key, &value); + if (key == SCFG_KEY_INVALID) + break; + printf("Got a key-value pair: %i = something\n", key); + + switch (key) { + + case SCFG_KEY_TYPE: + if (!strcmp(value.str, "proxy")) { + if (!server_set_type(cfg, scfg, SERVER_TYPE_PROXY)) + return; + } else if (!strcmp(value.str, "announce")) { + if (!server_set_type(cfg, scfg, SERVER_TYPE_ANNOUNCE)) + return; + } + break; + + case SCFG_KEY_NAME: + if (!server_set_pretty_name(cfg, scfg, value.str)) + return; + break; + + case SCFG_KEY_PORT: + if (!server_set_port(cfg, scfg, value.uint16)) + return; + break; + + case SCFG_KEY_LOCAL: { + struct sockaddr_in46 *addr, *tmp; + + list_for_each_entry_safe(addr, tmp, &value.addr_list, list) { + list_del(&addr->list); + server_add_local(cfg, scfg, addr); + } + break; + } + + case SCFG_KEY_REMOTE: { + struct sockaddr_in46 *addr, *tmp; + + list_for_each_entry_safe(addr, tmp, &value.addr_list, list) { + list_del(&addr->list); + server_add_remote(cfg, scfg, addr); + } + break; + } + + case SCFG_KEY_INVALID: + default: + break; + } + } + + //printf("Cfg:\n%s\n\n", pos); +} + +static void +scfg_read_cb(struct cfg *cfg, struct uring_task *task, int res) +{ + struct server *scfg = container_of(task, struct server, task); + + printf("Asked to parse server cfg %s (bytes %i)\n", scfg->name, res); + + if (res < 0) { + perrordie("read"); + } else if (res > 0) { + scfg->len += res; + if (scfg->len + 1 >= sizeof(scfg->buf)) { + fprintf(stderr, "Server config too large\n"); + server_delete(cfg, scfg); + return; + } + + uring_read(cfg, &scfg->task, scfg->buf + scfg->len, sizeof(scfg->buf) - scfg->len, scfg->len, scfg_read_cb); + return; + } else { + /* EOF */ + scfg->buf[scfg->len] = '\0'; + uring_task_close_fd(cfg, &scfg->task); + scfg_parse(cfg, scfg); + server_commit(cfg, scfg); + } +} + +static void +scfg_open_cb(struct cfg *cfg, struct uring_task *task, int res) +{ + struct server *scfg = container_of(task, struct server, task); + + if (res < 0) { + fprintf(stderr, "Open failed\n"); + server_delete(cfg, scfg); + return; + } + + printf("Asked to read server cfg %s (fd %i)\n", scfg->name, res); + uring_task_set_fd(&scfg->task, res); + scfg->len = 0; + uring_read(cfg, &scfg->task, scfg->buf, sizeof(scfg->buf), 0, scfg_read_cb); +} + +static bool +scfg_valid_filename(const char *name) +{ + const char *suffix; + + if (!name) + return false; + if (name[0] == '\0') + return false; + if (name[0] == '.') + return false; + if ((suffix = strrchr(name, '.')) == NULL) + return false; + if (strcmp(suffix, ".server")) + return false; + + return true; +} + +struct inotify_ev { + struct uring_task task; + char buf[4096] __attribute__((aligned(__alignof__(struct inotify_event)))); +}; + +static void +inotify_free(struct uring_task *task) +{ + struct inotify_ev *iev = container_of(task, struct inotify_ev, task); + struct cfg *cfg = container_of(task->parent, struct cfg, task); + + fprintf(stderr, "%s called\n", __func__); + if (!iev || !cfg) + die("%s: iev or cfg is NULL!?\n", __func__); + + free(iev); + cfg->iev = NULL; + uring_task_put(cfg, &cfg->task); +} + +static void +inotify_event_dump(const struct inotify_event *event) +{ + printf("Event:\n"); + printf(" * WD : %i\n", event->wd); + printf(" * Cookie : %" PRIu32 "\n", event->cookie); + printf(" * Length : %" PRIu32 "\n", event->len); + printf(" * Name : %s\n", event->name); + printf(" * Mask : %" PRIu32 "\n", event->mask); + if (event->mask & IN_ACCESS) + printf("\tIN_ACCESS\n"); + else if(event->mask & IN_MODIFY) + printf("\tIN_MODIFY\n"); + else if(event->mask & IN_ATTRIB) + printf("\tIN_ATTRIB\n"); + else if(event->mask & IN_CLOSE_WRITE) + printf("\tIN_CLOSE_WRITE\n"); + else if(event->mask & IN_CLOSE_NOWRITE) + printf("\tIN_CLOSE_NOWRITE\n"); + else if(event->mask & IN_OPEN) + printf("\tIN_OPEN\n"); + else if(event->mask & IN_MOVED_FROM) + printf("\tIN_MOVED_FROM\n"); + else if(event->mask & IN_MOVED_TO) + printf("\tIN_MOVED_TO\n"); + else if(event->mask & IN_CREATE) + printf("\tIN_CREATE\n"); + else if(event->mask & IN_DELETE) + printf("\tIN_DELETE\n"); + else if(event->mask & IN_DELETE_SELF) + printf("\tIN_DELETE_SELF\n"); + else if(event->mask & IN_MOVE_SELF) + printf("\tIN_MOVE_SELF\n"); + else if(event->mask & IN_UNMOUNT) + printf("\tIN_UNMOUNT\n"); + else if(event->mask & IN_Q_OVERFLOW) + printf("\tIN_Q_OVERFLOW\n"); + else if(event->mask & IN_IGNORED) + printf("\tIN_IGNORED\n"); + printf("\n"); +} + +static void +inotify_cb(struct cfg *cfg, struct uring_task *task, int res) +{ + struct inotify_ev *iev = container_of(task, struct inotify_ev, task); + const struct inotify_event *event; + char *ptr; + struct server *scfg; + + fprintf(stderr, "%s: ret is %i (ref %u)\n", __func__, res, task->refcount); + + if (task->dead) { + fprintf(stderr, "%s: task is dead\n", __func__); + uring_task_put(cfg, task); + return; + } + + if (res <= 0) + perrordie("inotify_read"); + + for (ptr = iev->buf; ptr < iev->buf + res; ptr += sizeof(struct inotify_event) + event->len) { + event = (const struct inotify_event *)ptr; + + if (debuglvl > 0) + inotify_event_dump(event); + + if (event->mask & (IN_IGNORED | IN_MOVE_SELF | IN_DELETE_SELF | IN_UNMOUNT)) + die("Configuration directory gone, exiting\n"); + + if (event->mask & IN_Q_OVERFLOW) { + error("inotify queue overflow!\n"); + continue; + } + + if (!scfg_valid_filename(event->name)) + continue; + + if (event->mask & (IN_MOVED_FROM | IN_DELETE)) + server_delete_by_name(cfg, event->name); + else if (event->mask & (IN_MOVED_TO | IN_CREATE | IN_CLOSE_WRITE)) { + scfg = server_new(cfg, event->name); + uring_openat(cfg, &scfg->task, event->name, scfg_open_cb); + } else + error("inotify: weird, unknown event: 0x%08x\n", event->mask); + } + + uring_read(cfg, &iev->task, iev->buf, sizeof(iev->buf), 0, inotify_cb); +} + +void +inotify_refdump(struct inotify_ev *iev) +{ + uring_task_refdump(&iev->task); +} + +void +scfg_stop_monitor_dir(struct cfg *cfg) +{ + if (!cfg->iev) { + fprintf(stderr, "%s called with no iev!\n", __func__); + return; + } + + fprintf(stderr, "%s called, closing fd %i\n", __func__, cfg->iev->task.fd); + uring_cancel(cfg, &cfg->iev->task); + cfg->iev = NULL; +} + +void +scfg_monitor_dir(struct cfg *cfg) +{ + int ifd; + int iwd; + struct inotify_ev *iev; + + iev = malloc(sizeof(*iev)); + if (!iev) + perrordie("malloc"); + + ifd = inotify_init1(IN_CLOEXEC); + if (ifd < 0) + perrordie("inotify_init1"); + + /* ln = IN_CREATE, cp/vi/mv = IN_CREATE, IN_OPEN, IN_CLOSE_WRITE */ + iwd = inotify_add_watch(ifd, ".", + IN_CLOSE_WRITE | IN_DELETE | IN_CREATE | + IN_DELETE_SELF | IN_MOVE_SELF | IN_MOVED_TO | + IN_MOVED_FROM | IN_DONT_FOLLOW | + IN_EXCL_UNLINK | IN_ONLYDIR ); + if (iwd < 0) + perrordie("inotify_add_watch"); + + uring_task_init(&iev->task, "iev", &cfg->task, inotify_free); + uring_task_set_fd(&iev->task, ifd); + cfg->iev = iev; + uring_read(cfg, &iev->task, iev->buf, sizeof(iev->buf), 0, inotify_cb); +} + +void +scfg_read_all(struct cfg *cfg) +{ + + DIR *cfgdir; + struct dirent *dent; + struct server *scfg; + + cfgdir = opendir("."); + if (!cfgdir) { + perror("opendir"); + free(cfg); + return; + } + + while ((dent = readdir(cfgdir)) != NULL) { + char *suffix; + + if (dent->d_name[0] == '.') + continue; + if (dent->d_type != DT_REG && dent->d_type != DT_UNKNOWN) + continue; + if ((suffix = strrchr(dent->d_name, '.')) == NULL) + continue; + if (strcmp(suffix, ".server")) + continue; + + scfg = server_new(cfg, dent->d_name); + uring_openat(cfg, &scfg->task, dent->d_name, scfg_open_cb); + } + + closedir(cfgdir); +} + diff --git a/config.h b/config.h new file mode 100644 index 0000000..c70c6ef --- /dev/null +++ b/config.h @@ -0,0 +1,12 @@ +#ifndef fooconfighfoo +#define fooconfighfoo + +void scfg_monitor_dir(struct cfg *cfg); + +void inotify_refdump(struct inotify_ev *iev); + +void scfg_stop_monitor_dir(struct cfg *cfg); + +void scfg_read_all(struct cfg *cfg); + +#endif diff --git a/ctest.c b/ctest.c new file mode 100644 index 0000000..2e871cb --- /dev/null +++ b/ctest.c @@ -0,0 +1,74 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PIPE_RD 0 +#define PIPE_WR 1 + +int +main(int argc, char **argv) { + int sfd; + struct sockaddr_in addr; + int zfd; + int pfd[2]; + size_t total = 0; + + if (argc != 4) { + fprintf(stderr, "Usage: %s <64k count>\n", argv[0]); + exit(EXIT_FAILURE); + } + + sfd = socket(AF_INET, SOCK_STREAM, 0); + if (sfd < 0) { + perror("socket"); + exit(EXIT_FAILURE); + } + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(atoi(argv[2])); + addr.sin_addr.s_addr = inet_addr(argv[1]); + + if (connect(sfd, &addr, sizeof(addr)) < 0) { + perror("connect"); + exit(EXIT_FAILURE); + } + + zfd = open("/dev/zero", O_RDONLY | O_CLOEXEC); + if (zfd < 0) { + perror("open"); + exit(EXIT_FAILURE); + } + + if (pipe2(pfd, O_CLOEXEC) < 0) { + perror("pipe2"); + exit(EXIT_FAILURE); + } + + for (int i = 0; i < atoi(argv[3]); i++) { + ssize_t r, w; + + r = splice(zfd, NULL, pfd[PIPE_WR], NULL, 64 * 1024, SPLICE_F_MOVE); + //fprintf(stderr, "Read %zi bytes from /dev/zero\n", r); + w = splice(pfd[PIPE_RD], NULL, sfd, NULL, r, SPLICE_F_MOVE); + //fprintf(stderr, "Wrote %zi bytes to socket\n", w); + if (r != w) { + fprintf(stderr, "Read/write mismatch\n"); + exit(EXIT_FAILURE); + } + total += w; + } + + printf("Client: sent %zu bytes\n", total); + exit(EXIT_SUCCESS); +} + diff --git a/inotify.c b/inotify.c new file mode 100644 index 0000000..f362920 --- /dev/null +++ b/inotify.c @@ -0,0 +1,730 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "main.h" +#include "uring.h" +#include "config.h" +#include "server.h" + +static void +eat_whitespace_and_comments(char **pos) +{ + while (true) { + while (isspace(**pos)) + (*pos)++; + + if (**pos == '#') { + while (**pos != '\r' && **pos != '\n' && **pos != '\0') + (*pos)++; + continue; + } + + return; + } +} + +static char * +get_line(char **pos) +{ + char *begin = *pos; + char *end; + + while (isspace(*begin)) + begin++; + + if (*begin == '\0') + return NULL; + + end = begin; + while (*end != '\n' && *end != '\0') + end++; + + if (*end == '\0') + *pos = end; + else + *pos = end + 1; + + while (isspace(*end)) { + *end = '\0'; + end--; + } + + return begin; +} + +static bool +strtosockaddrs(const char *str, struct list_head *list) +{ + char *tmp; + uint16_t port; + int r; + struct sockaddr_in46 *addr; + + if (!str || *str == '\0' || !list) + return false; + list_init(list); + + if (*str == '[') { + /* IPv6, [a:b:c...h]:p or [*]:p */ + str++; + tmp = strchr(str, ']'); + if (!tmp) + goto out; + *tmp = '\0'; + + addr = zmalloc(sizeof(*addr)); + if (!addr) + goto out; + list_add(&addr->list, list); + + if (!strcmp(str, "*")) + addr->in6.sin6_addr = in6addr_any; + else if (inet_pton(AF_INET6, str, &addr->in6.sin6_addr) <= 0) + goto out; + + tmp++; + if (*tmp != ':') + goto out; + + tmp++; + if (strtou16_strict(tmp, &port) < 0) + goto out; + + addr->in6.sin6_family = AF_INET6; + addr->in6.sin6_port = htons(port); + addr->addrlen = sizeof(addr->in6); + + } else if (*str == '*') { + /* IPv4, *:p */ + str++; + if (*str != ':') + goto out; + + str++; + if (strtou16_strict(str, &port) < 0) + goto out; + + addr = zmalloc(sizeof(*addr)); + if (!addr) + goto out; + list_add(&addr->list, list); + + addr->in4.sin_family = AF_INET; + addr->in4.sin_addr.s_addr = INADDR_ANY; + addr->in4.sin_port = htons(port); + addr->addrlen = sizeof(addr->in4); + + } else if ((tmp = strchr(str, ':'))) { + /* IPv4, a.b.c.d:p or IPv4/6 hostname:p */ + fprintf(stderr, "Got an IPv4:port or hostname:port\n"); + *tmp = '\0'; + tmp++; + if (strtou16_strict(tmp, &port) < 0) + goto out; + + addr = zmalloc(sizeof(*addr)); + if (!addr) + goto out; + + if (inet_pton(AF_INET, str, &addr->in4.sin_addr) > 0) { + fprintf(stderr, "...Got an IPv4:port (0x%p, list 0x%p)\n", addr, &addr->list); + addr->in4.sin_family = AF_INET; + addr->in4.sin_port = htons(port); + addr->addrlen = sizeof(addr->in4); + list_add(&addr->list, list); + goto success; + } else { + free(addr); + } + + struct addrinfo hints = { + .ai_family = AF_UNSPEC, + .ai_socktype = SOCK_STREAM, + .ai_protocol = 0, + .ai_flags = 0, + }; + struct addrinfo *results, *ai; + + /* FIXME: This is completely synchronous but getaddrinfo_a is not very ergonomic */ + r = getaddrinfo(str, tmp, &hints, &results); + if (r != 0) { + fprintf(stderr, "gettaddrinfo(%s): %s\n", str, gai_strerror(r)); + goto out; + } + + fprintf(stderr, "...Got an hostname:port\n"); + for (ai = results; ai; ai = ai->ai_next) { + fprintf(stderr, "Got a result from getaddrinfo\n"); + + addr = zmalloc(sizeof(*addr)); + if (!addr) { + freeaddrinfo(results); + goto out; + } + + switch (ai->ai_family) { + case AF_INET: { + struct sockaddr_in *naddr = (struct sockaddr_in *)ai->ai_addr; + + addr->in4.sin_family = AF_INET; + addr->in4.sin_addr = naddr->sin_addr; + addr->in4.sin_port = naddr->sin_port; + addr->addrlen = sizeof(addr->in4); + list_add(&addr->list, list); + break; + } + case AF_INET6: { + struct sockaddr_in6 *naddr = (struct sockaddr_in6 *)ai->ai_addr; + + addr->in6.sin6_family = AF_INET6; + addr->in6.sin6_addr = naddr->sin6_addr; + addr->in6.sin6_port = naddr->sin6_port; + addr->addrlen = sizeof(addr->in6); + list_add(&addr->list, list); + break; + } + default: + fprintf(stderr, " Fam: Unknown (%i)\n", ai->ai_family); + free(addr); + continue; + } + } + + freeaddrinfo(results); + + } else if (strtou16_strict(tmp, &port) == 0) { + /* Port */ + + addr = zmalloc(sizeof(*addr)); + if (!addr) + goto out; + addr->in6.sin6_family = AF_INET6; + addr->in6.sin6_addr = in6addr_any; + addr->in6.sin6_port = htons(port); + addr->addrlen = sizeof(addr->in6); + list_add(&addr->list, list); + + addr = zmalloc(sizeof(*addr)); + if (!addr) + goto out; + addr->in4.sin_family = AF_INET; + addr->in4.sin_addr.s_addr = INADDR_ANY; + addr->in4.sin_port = htons(port); + addr->addrlen = sizeof(addr->in4); + list_add(&addr->list, list); + } + +success: + if (list_empty(list)) { + fprintf(stderr, "Success but empty list!?\n"); + return false; + } else { + int i = 0; + struct list_head *pos; + + list_for_each(pos, list) + i++; + fprintf(stderr, "Success, %i entries\n", i); + } + + return true; + +out: + if (!list_empty(list)) { + struct sockaddr_in46 *tmpaddr; + + list_for_each_entry_safe(addr, tmpaddr, list, list) { + list_del(&addr->list); + free(addr); + } + } + return false; +} + +enum scfg_keys { + SCFG_KEY_TYPE, + SCFG_KEY_NAME, + SCFG_KEY_PORT, + SCFG_KEY_LOCAL, + SCFG_KEY_REMOTE, + SCFG_KEY_INVALID +}; + +enum value_type { + VAL_TYPE_STRING, + VAL_TYPE_UINT16, + VAL_TYPE_ADDR, + VAL_TYPE_INVALID +}; + +struct scfg_key_map { + const char *key_name; + enum scfg_keys key_value; + enum value_type value_type; +} scfg_key_map[] = { + { + .key_name = "type", + .key_value = SCFG_KEY_TYPE, + .value_type = VAL_TYPE_STRING, + }, { + .key_name = "name", + .key_value = SCFG_KEY_NAME, + .value_type = VAL_TYPE_STRING, + }, { + .key_name = "port", + .key_value = SCFG_KEY_PORT, + .value_type = VAL_TYPE_UINT16, + }, { + .key_name = "local", + .key_value = SCFG_KEY_LOCAL, + .value_type = VAL_TYPE_ADDR, + }, { + .key_name = "remote", + .key_value = SCFG_KEY_REMOTE, + .value_type = VAL_TYPE_ADDR, + } +}; + +union cfg_value { + const char *str; + uint16_t uint16; + struct list_head addr_list; +}; + + +static void +line_get_key_value(char *line, enum scfg_keys *rkey, union cfg_value *rvalue) +{ + char *tmp, *key; + int i; + + *rkey = SCFG_KEY_INVALID; + if (!line) + return; + + tmp = line; + while (isspace(*tmp)) + tmp++; + + if (*tmp == '\0') + return; + + key = tmp; + while (*tmp != '\0' && !isspace(*tmp)) + tmp++; + + if (*tmp == '\0') + return; + + *tmp = '\0'; + tmp++; + + while (isspace(*tmp)) + tmp++; + + if (*tmp != '=') + return; + + tmp++; + while (isspace(*tmp)) + tmp++; + + if (*tmp == '\0') + return; + + for (i = 0; i < ARRAY_SIZE(scfg_key_map); i++) { + if (strcmp(scfg_key_map[i].key_name, key)) + continue; + + switch (scfg_key_map[i].value_type) { + + case VAL_TYPE_STRING: + rvalue->str = tmp; + break; + + case VAL_TYPE_UINT16: { + uint16_t v; + + if (strtou16_strict(tmp, &v) < 0) + return; + rvalue->uint16 = v; + break; + } + + case VAL_TYPE_ADDR: { + if (!strtosockaddrs(tmp, &rvalue->addr_list)) + return; + if (list_empty(&rvalue->addr_list)) { + fprintf(stderr, "VAL_TYPE_ADDR with zero list!?\n"); + return; + } else { + int i = 0; + struct list_head *pos; + + list_for_each(pos, &rvalue->addr_list) + i++; + fprintf(stderr, "VAL_TYPE_ADDR with list %i entries\n", i); + } + break; + } + + case VAL_TYPE_INVALID: + /* fall through */ + default: + return; + } + *rkey = scfg_key_map[i].key_value; + break; + } +} + +static void +scfg_parse(struct cfg *cfg, struct server *scfg) +{ + char *pos = &scfg->buf[0]; + char *line; + + eat_whitespace_and_comments(&pos); + + line = get_line(&pos); + if (!line) { + printf("Cfg: premature EOF\n"); + return; + } + + if (strcmp(line, "[server]")) { + printf("Invalid line: %s\n", line); + return; + } + + while (true) { + enum scfg_keys key; + union cfg_value value; + + eat_whitespace_and_comments(&pos); + line = get_line(&pos); + printf("Examining line: %s\n", line); + line_get_key_value(line, &key, &value); + if (key == SCFG_KEY_INVALID) + break; + printf("Got a key-value pair: %i = something\n", key); + + switch (key) { + + case SCFG_KEY_TYPE: + if (!strcmp(value.str, "proxy")) { + if (!server_set_type(cfg, scfg, SERVER_TYPE_PROXY)) + return; + } else if (!strcmp(value.str, "announce")) { + if (!server_set_type(cfg, scfg, SERVER_TYPE_ANNOUNCE)) + return; + } + break; + + case SCFG_KEY_NAME: + if (!server_set_pretty_name(cfg, scfg, value.str)) + return; + break; + + case SCFG_KEY_PORT: + if (!server_set_port(cfg, scfg, value.uint16)) + return; + break; + + case SCFG_KEY_LOCAL: { + struct sockaddr_in46 *addr, *tmp; + + list_for_each_entry_safe(addr, tmp, &value.addr_list, list) { + list_del(&addr->list); + server_add_local(cfg, scfg, addr); + } + break; + } + + case SCFG_KEY_REMOTE: { + struct sockaddr_in46 *addr, *tmp; + + list_for_each_entry_safe(addr, tmp, &value.addr_list, list) { + list_del(&addr->list); + server_add_remote(cfg, scfg, addr); + } + break; + } + + case SCFG_KEY_INVALID: + default: + break; + } + } + + //printf("Cfg:\n%s\n\n", pos); +} + +static void +scfg_read_cb(struct cfg *cfg, struct uring_task *task, int res) +{ + struct server *scfg = container_of(task, struct server, task); + + printf("Asked to parse server cfg %s (bytes %i)\n", scfg->name, res); + + if (res < 0) { + perrordie("read"); + } else if (res > 0) { + scfg->len += res; + if (scfg->len + 1 >= sizeof(scfg->buf)) { + fprintf(stderr, "Server config too large\n"); + server_delete(cfg, scfg); + return; + } + + uring_read(cfg, &scfg->task, scfg->buf + scfg->len, sizeof(scfg->buf) - scfg->len, scfg->len, scfg_read_cb); + return; + } else { + /* EOF */ + scfg->buf[scfg->len] = '\0'; + uring_task_close_fd(cfg, &scfg->task); + scfg_parse(cfg, scfg); + server_commit(cfg, scfg); + } +} + +static void +scfg_open_cb(struct cfg *cfg, struct uring_task *task, int res) +{ + struct server *scfg = container_of(task, struct server, task); + + if (res < 0) { + fprintf(stderr, "Open failed\n"); + server_delete(cfg, scfg); + return; + } + + printf("Asked to read server cfg %s (fd %i)\n", scfg->name, res); + uring_task_set_fd(&scfg->task, res); + scfg->len = 0; + uring_read(cfg, &scfg->task, scfg->buf, sizeof(scfg->buf), 0, scfg_read_cb); +} + +static bool +scfg_valid_filename(const char *name) +{ + const char *suffix; + + if (!name) + return false; + if (name[0] == '\0') + return false; + if (name[0] == '.') + return false; + if ((suffix = strrchr(name, '.')) == NULL) + return false; + if (strcmp(suffix, ".server")) + return false; + + return true; +} + +struct inotify_ev { + struct uring_task task; + char buf[4096] __attribute__((aligned(__alignof__(struct inotify_event)))); +}; + +static void +inotify_free(struct uring_task *task) +{ + struct inotify_ev *iev = container_of(task, struct inotify_ev, task); + struct cfg *cfg = container_of(task->parent, struct cfg, task); + + fprintf(stderr, "%s called\n", __func__); + if (!iev || !cfg) + die("%s: iev or cfg is NULL!?\n", __func__); + + free(iev); + cfg->iev = NULL; + uring_task_put(cfg, &cfg->task); +} + +static void +inotify_event_dump(const struct inotify_event *event) +{ + printf("Event:\n"); + printf(" * WD : %i\n", event->wd); + printf(" * Cookie : %" PRIu32 "\n", event->cookie); + printf(" * Length : %" PRIu32 "\n", event->len); + printf(" * Name : %s\n", event->name); + printf(" * Mask : %" PRIu32 "\n", event->mask); + if (event->mask & IN_ACCESS) + printf("\tIN_ACCESS\n"); + else if(event->mask & IN_MODIFY) + printf("\tIN_MODIFY\n"); + else if(event->mask & IN_ATTRIB) + printf("\tIN_ATTRIB\n"); + else if(event->mask & IN_CLOSE_WRITE) + printf("\tIN_CLOSE_WRITE\n"); + else if(event->mask & IN_CLOSE_NOWRITE) + printf("\tIN_CLOSE_NOWRITE\n"); + else if(event->mask & IN_OPEN) + printf("\tIN_OPEN\n"); + else if(event->mask & IN_MOVED_FROM) + printf("\tIN_MOVED_FROM\n"); + else if(event->mask & IN_MOVED_TO) + printf("\tIN_MOVED_TO\n"); + else if(event->mask & IN_CREATE) + printf("\tIN_CREATE\n"); + else if(event->mask & IN_DELETE) + printf("\tIN_DELETE\n"); + else if(event->mask & IN_DELETE_SELF) + printf("\tIN_DELETE_SELF\n"); + else if(event->mask & IN_MOVE_SELF) + printf("\tIN_MOVE_SELF\n"); + else if(event->mask & IN_UNMOUNT) + printf("\tIN_UNMOUNT\n"); + else if(event->mask & IN_Q_OVERFLOW) + printf("\tIN_Q_OVERFLOW\n"); + else if(event->mask & IN_IGNORED) + printf("\tIN_IGNORED\n"); + printf("\n"); +} + +static void +inotify_cb(struct cfg *cfg, struct uring_task *task, int res) +{ + struct inotify_ev *iev = container_of(task, struct inotify_ev, task); + const struct inotify_event *event; + char *ptr; + struct server *scfg; + + fprintf(stderr, "%s: ret is %i (ref %u)\n", __func__, res, task->refcount); + + if (task->dead) { + fprintf(stderr, "%s: task is dead\n", __func__); + uring_task_put(cfg, task); + return; + } + + if (res <= 0) + perrordie("inotify_read"); + + for (ptr = iev->buf; ptr < iev->buf + res; ptr += sizeof(struct inotify_event) + event->len) { + event = (const struct inotify_event *)ptr; + + if (debuglvl > 0) + inotify_event_dump(event); + + if (event->mask & (IN_IGNORED | IN_MOVE_SELF | IN_DELETE_SELF | IN_UNMOUNT)) + die("Configuration directory gone, exiting\n"); + + if (event->mask & IN_Q_OVERFLOW) { + error("inotify queue overflow!\n"); + continue; + } + + if (!scfg_valid_filename(event->name)) + continue; + + if (event->mask & (IN_MOVED_FROM | IN_DELETE)) + server_delete_by_name(cfg, event->name); + else if (event->mask & (IN_MOVED_TO | IN_CREATE | IN_CLOSE_WRITE)) { + scfg = server_new(cfg, event->name); + uring_openat(cfg, &scfg->task, event->name, scfg_open_cb); + } else + error("inotify: weird, unknown event: 0x%08x\n", event->mask); + } + + uring_read(cfg, &iev->task, iev->buf, sizeof(iev->buf), 0, inotify_cb); +} + +void +inotify_refdump(struct inotify_ev *iev) +{ + uring_task_refdump(&iev->task); +} + +void +scfg_stop_monitor_dir(struct cfg *cfg) +{ + if (!cfg->iev) { + fprintf(stderr, "%s called with no iev!\n", __func__); + return; + } + + fprintf(stderr, "%s called, closing fd %i\n", __func__, cfg->iev->task.fd); + uring_cancel(cfg, &cfg->iev->task); + cfg->iev = NULL; +} + +void +scfg_monitor_dir(struct cfg *cfg) +{ + int ifd; + int iwd; + struct inotify_ev *iev; + + iev = malloc(sizeof(*iev)); + if (!iev) + perrordie("malloc"); + + ifd = inotify_init1(IN_CLOEXEC); + if (ifd < 0) + perrordie("inotify_init1"); + + /* ln = IN_CREATE, cp/vi/mv = IN_CREATE, IN_OPEN, IN_CLOSE_WRITE */ + iwd = inotify_add_watch(ifd, ".", + IN_CLOSE_WRITE | IN_DELETE | IN_CREATE | + IN_DELETE_SELF | IN_MOVE_SELF | IN_MOVED_TO | + IN_MOVED_FROM | IN_DONT_FOLLOW | + IN_EXCL_UNLINK | IN_ONLYDIR ); + if (iwd < 0) + perrordie("inotify_add_watch"); + + uring_task_init(&iev->task, "iev", &cfg->task, inotify_free); + uring_task_set_fd(&iev->task, ifd); + cfg->iev = iev; + uring_read(cfg, &iev->task, iev->buf, sizeof(iev->buf), 0, inotify_cb); +} + +void +scfg_read_all(struct cfg *cfg) +{ + + DIR *cfgdir; + struct dirent *dent; + struct server *scfg; + + cfgdir = opendir("."); + if (!cfgdir) { + perror("opendir"); + free(cfg); + return; + } + + while ((dent = readdir(cfgdir)) != NULL) { + char *suffix; + + if (dent->d_name[0] == '.') + continue; + if (dent->d_type != DT_REG && dent->d_type != DT_UNKNOWN) + continue; + if ((suffix = strrchr(dent->d_name, '.')) == NULL) + continue; + if (strcmp(suffix, ".server")) + continue; + + scfg = server_new(cfg, dent->d_name); + uring_openat(cfg, &scfg->task, dent->d_name, scfg_open_cb); + } + + closedir(cfgdir); +} + diff --git a/inotify.h b/inotify.h new file mode 100644 index 0000000..c70c6ef --- /dev/null +++ b/inotify.h @@ -0,0 +1,12 @@ +#ifndef fooconfighfoo +#define fooconfighfoo + +void scfg_monitor_dir(struct cfg *cfg); + +void inotify_refdump(struct inotify_ev *iev); + +void scfg_stop_monitor_dir(struct cfg *cfg); + +void scfg_read_all(struct cfg *cfg); + +#endif diff --git a/main.c b/main.c new file mode 100644 index 0000000..70d1b1e --- /dev/null +++ b/main.c @@ -0,0 +1,284 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "main.h" +#include "uring.h" +#include "config.h" +#include "server.h" + +int debuglvl = 0; + +void +debug(unsigned lvl, const char *fmt, ...) +{ + va_list ap; + + if (lvl > debuglvl) + return; + + va_start(ap, fmt); + vfprintf(stdout, fmt, ap); + va_end(ap); +} + +#define info(...) fprintf(stderr, __VA_ARGS__) +#define error(...) fprintf(stderr, __VA_ARGS__) + +__attribute__((noreturn)) +void +die(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + exit(EXIT_FAILURE); +}; + +#define perrordie(msg) die("%s: %m\n", msg) + +static void +cfg_free(struct uring_task *task) +{ + struct cfg *cfg = container_of(task, struct cfg, task); + + free(cfg); + exit(EXIT_SUCCESS); +} + +static void +cfg_read(struct cfg *cfg) +{ + FILE *cfgfile; + + cfgfile = fopen("./mcproxy.conf", "r"); + if (!cfgfile) { + if (errno == ENOENT) + return; + else + perrordie("fopen"); + } + + printf("Opened config file\n"); + fclose(cfgfile); +} + +static struct cfg * +cfg_init(int argc, char **argv) +{ + struct cfg *cfg; + int c; + + cfg = zmalloc(sizeof(*cfg)); + if (!cfg) { + perror("malloc"); + return NULL; + } + uring_task_init(&cfg->task, "cfg", NULL, cfg_free); + list_init(&cfg->servers); + + while (true) { + int option_index = 0; + static struct option long_options[] = { + { "homedir", required_argument, 0, 'h' }, + { "debug", no_argument, 0, 'd' }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, ":h:d", + long_options, &option_index); + if (c == -1) + break; + + switch (c) { + case 'h': + cfg->homedir = optarg; + break; + case 'd': + debuglvl++; + break; + default: + die("Invalid arguments\n"); + } + + } + + if (optind < argc) + die("Invalid arguments\n"); + + if (!cfg->homedir) + cfg->homedir = "/home/david/intest"; + + printf("Homedir is %s\n", cfg->homedir); + if (chdir(cfg->homedir)) + perrordie("chdir"); + + return cfg; +} + +struct signalfd_ev { + struct uring_task task; + //struct signalfd_siginfo buf; + uint64_t buf; +}; + +static void +signalfd_free(struct uring_task *task) +{ + struct signalfd_ev *sev = container_of(task, struct signalfd_ev, task); + + debug(2, "Freeing signalfd\n"); + free(sev); +} + +static void +dump_tree(struct cfg *cfg) +{ + struct server *server; + + fprintf(stderr, "\n\n\n\n"); + fprintf(stderr, "Dumping Tree\n"); + fprintf(stderr, "============\n"); + uring_task_refdump(&cfg->task); + uring_task_refdump(&cfg->sev->task); + uring_refdump(cfg->uev); + if (cfg->iev) + inotify_refdump(cfg->iev); + list_for_each_entry(server, &cfg->servers, list) + server_refdump(server); + fprintf(stderr, "============\n"); + fprintf(stderr, "\n\n\n\n"); +} + +static void +signalfd_read(struct cfg *cfg, struct uring_task *task, int res) +{ + struct signalfd_ev *sev = container_of(task, struct signalfd_ev, task); + struct server *server, *stmp; + static int count = 0; + + count++; + if (count > 5) + exit(EXIT_FAILURE); + + if (res != sizeof(sev->buf)) + die("Error in signalfd (%i)\n", res); + + if (sev->buf < 1000) { + fprintf(stderr, "Got a signal to quit\n"); + exit(EXIT_SUCCESS); + } else { + fprintf(stderr, "Got a signal to dump tree\n"); + dump_tree(cfg); + scfg_stop_monitor_dir(cfg); + list_for_each_entry_safe(server, stmp, &cfg->servers, list) + server_delete(cfg, server); + uring_read(cfg, &sev->task, &sev->buf, sizeof(sev->buf), 0, signalfd_read); + } +} + +static int hack_efd = -1; + +static void +hack_handler(int signum) +{ + uint64_t val; + + switch (signum) { + case SIGINT: + fprintf(stderr, "Got a SIGINT\n"); + val = 1000; + break; + case SIGHUP: + fprintf(stderr, "Got a SIGHUP\n"); + val = 1000; + break; + case SIGTERM: + fprintf(stderr, "Got a SIGTERM\n"); + val = 1; + break; + default: + fprintf(stderr, "Got an unknown sig (%i)\n", signum); + val = 1; + break; + } + + write(hack_efd, &val, sizeof(val)); +} + +static void +signalfd_init(struct cfg *cfg) +{ + int sfd; + //sigset_t mask; + struct signalfd_ev *sev; + + sev = zmalloc(sizeof(*sev)); + if (!sev) + perrordie("malloc"); + + /* + sigfillset(&mask); + if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) + perrordie("sigprocmask"); + + sfd = signalfd(-1, &mask, SFD_CLOEXEC); + if (sfd < 0) + perrordie("signalfd"); + */ + + struct sigaction action; + sigfillset(&action.sa_mask); + action.sa_handler = hack_handler; + action.sa_flags = 0; + + sigaction(SIGINT, &action, NULL); + sigaction(SIGHUP, &action, NULL); + sigaction(SIGTERM, &action, NULL); + + sfd = eventfd(0, EFD_CLOEXEC); + if (sfd < 0) + perrordie("eventfd"); + + fprintf(stderr, "signalfd init: %i\n", sfd); + uring_task_init(&sev->task, "sev", &cfg->task, signalfd_free); + uring_task_set_fd(&sev->task, sfd); + cfg->sev = sev; + hack_efd = sfd; + uring_read(cfg, &sev->task, &sev->buf, sizeof(sev->buf), 0, signalfd_read); +} + +int +main(int argc, char **argv) +{ + struct cfg *cfg; + + cfg = cfg_init(argc, argv); + + cfg_read(cfg); + + uring_init(cfg); + + signalfd_init(cfg); + + scfg_monitor_dir(cfg); + + scfg_read_all(cfg); + + uring_event_loop(cfg); + + exit(EXIT_SUCCESS); +} diff --git a/main.h b/main.h new file mode 100644 index 0000000..9ab769c --- /dev/null +++ b/main.h @@ -0,0 +1,55 @@ +#ifndef foomainhfoo +#define foomainhfoo + +#include +#include +#include "utils.h" + +extern int debuglvl; + +void debug(unsigned lvl, const char *fmt, ...); + +#define info(...) fprintf(stderr, __VA_ARGS__) +#define error(...) fprintf(stderr, __VA_ARGS__) + +void die(const char *fmt, ...); + +#define perrordie(msg) die("%s: %m\n", msg) + +struct cfg; + +struct uring_task; + +/* To save typing in all the function definitions below */ +typedef void (*callback_t)(struct cfg *, struct uring_task *, int res); + +struct uring_task { + const char *name; + unsigned refcount; + int fd; + void *parent; + void (*free)(struct uring_task *); + bool dead; + callback_t callback; +}; + +struct sockaddr_in46 { + union { + struct sockaddr_storage storage; + struct sockaddr_in in4; + struct sockaddr_in6 in6; + }; + socklen_t addrlen; + struct list_head list; +}; + +struct cfg { + const char *homedir; + struct uring_ev *uev; + struct inotify_ev *iev; + struct signalfd_ev *sev; + struct uring_task task; + struct list_head servers; +}; + +#endif diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..afc5f0d --- /dev/null +++ b/meson.build @@ -0,0 +1,9 @@ +project('mcproxy', 'c', default_options : ['c_std=gnu18']) +uring = dependency('liburing') + +executable('ctest', 'ctest.c') +executable('stest', 'stest.c') +executable('mcproxy', + ['main.c', 'uring.c', 'server.c', 'config.c', 'utils.c'], + dependencies: uring) + diff --git a/server.c b/server.c new file mode 100644 index 0000000..1045e43 --- /dev/null +++ b/server.c @@ -0,0 +1,423 @@ +#include +#include +#include +#include +#include + +#include "main.h" +#include "uring.h" +#include "config.h" +#include "server.h" + +#define ADDRSTRLEN (9 /*strlen("AF_INETX ")*/ + INET6_ADDRSTRLEN + 6 /*strlen(" 65535")*/ + 1) + +struct server_local { + struct sockaddr_in46 addr; + char addrstr[ADDRSTRLEN]; + struct sockaddr_in46 peer; + char peerstr[ADDRSTRLEN]; + struct uring_task task; + struct list_head list; +}; + +/* FIXME: This can be a plain sockaddr_in46 */ +struct server_remote { + struct sockaddr_in46 addr; + char addrstr[ADDRSTRLEN]; + struct list_head list; +}; + +struct server_proxy { + struct sockaddr_in46 client; + char clientstr[ADDRSTRLEN]; + struct sockaddr_in46 server; + char serverstr[ADDRSTRLEN]; + struct uring_task task; + char buf[4096]; + size_t len; + struct list_head list; +}; + +void +server_refdump(struct server *server) +{ + struct server_local *local; + struct server_proxy *proxy; + + uring_task_refdump(&server->task); + list_for_each_entry(local, &server->locals, list) + uring_task_refdump(&local->task); + list_for_each_entry(proxy, &server->proxys, list) + uring_task_refdump(&proxy->task); +} + +static void +server_free(struct uring_task *task) +{ + struct server *scfg = container_of(task, struct server, task); + + fprintf(stderr, "Freeing scfg %s\n", scfg->name); + list_del(&scfg->list); + free(scfg->pretty_name); + free(scfg->name); + free(scfg); +} + +void +server_delete(struct cfg *cfg, struct server *scfg) +{ + struct server_local *local, *ltmp; + struct server_remote *remote, *rtmp; + + fprintf(stderr, "Removing server cfg: %s\n", scfg->name); + + list_for_each_entry_safe(remote, rtmp, &scfg->remotes, list) { + list_del(&remote->list); + free(remote); + } + + list_for_each_entry_safe(local, ltmp, &scfg->locals, list) { + uring_cancel(cfg, &local->task); + } + + uring_task_put(cfg, &scfg->task); +} + +void +server_delete_by_name(struct cfg *cfg, const char *name) +{ + struct server *scfg; + + if (!cfg || !name || name[0] == '\0') + return; + + list_for_each_entry(scfg, &cfg->servers, list) { + if (!strcmp(scfg->name, name)) { + server_delete(cfg, scfg); + return; + } + } +} + +static char * +server_print_addr(struct sockaddr_in46 *addr, char *buf, size_t buflen) +{ + char abuf[ADDRSTRLEN]; + + switch (addr->storage.ss_family) { + case AF_INET: + snprintf(buf, buflen, "AF_INET4 %s %u", + inet_ntop(addr->in4.sin_family, &addr->in4.sin_addr, abuf, sizeof(abuf)), + (unsigned)ntohs(addr->in4.sin_port)); + break; + case AF_INET6: + snprintf(buf, buflen, "AF_INET6 %s %u", + inet_ntop(addr->in6.sin6_family, &addr->in6.sin6_addr, abuf, sizeof(abuf)), + (unsigned)ntohs(addr->in6.sin6_port)); + break; + default: + snprintf(buf, buflen, "AF_UNKNOWN"); + break; + } + + return buf; +} + +static void +server_dump(struct server *scfg) +{ + struct server_local *local; + struct server_remote *remote; + + fprintf(stderr, "Dumping server %s\n", scfg->name); + switch (scfg->type) { + case SERVER_TYPE_ANNOUNCE: + fprintf(stderr, " * Type: announce\n"); + break; + case SERVER_TYPE_PROXY: + fprintf(stderr, " * Type: proxy\n"); + break; + default: + fprintf(stderr, " * Type: unknown\n"); + break; + } + fprintf(stderr, " * Pretty name: %s\n", scfg->pretty_name ? scfg->pretty_name : ""); + fprintf(stderr, " * Announce port: %" PRIu16 "\n", scfg->announce_port); + fprintf(stderr, " * Listening ports:\n"); + list_for_each_entry(local, &scfg->locals, list) + fprintf(stderr, " * %s\n", local->addrstr); + fprintf(stderr, " * Remote ports:\n"); + list_for_each_entry(remote, &scfg->remotes, list) + fprintf(stderr, " * %s\n", remote->addrstr); +} + +/* +struct server_active_proxy { + struct sockaddr_in46 client; + char clientstr[ADDRSTRLEN]; + struct sockaddr_in46 server; + char serverstr[ADDRSTRLEN] + struct uring_task task; + char buf[4096]; + size_t len; + struct list_head list; +}; +*/ + +static void +server_proxy_free(struct uring_task *task) +{ + struct server_proxy *proxy = container_of(task, struct server_proxy, task); + + list_del(&proxy->list); + free(proxy); +} + +static void +server_proxy_connected(struct cfg *cfg, struct uring_task *task, int res) +{ + //struct server_proxy *proxy = container_of(task, struct server_proxy, task); + + fprintf(stderr, "%s: connected %i\n", __func__, res); + return; +} + +static void +server_local_free(struct uring_task *task) +{ + struct server_local *local = container_of(task, struct server_local, task); + + fprintf(stderr, "%s called: task 0x%p\n", __func__, task); + + list_del(&local->list); + free(local); +} + +static void +server_local_accept(struct cfg *cfg, struct uring_task *task, int res) +{ + struct server_local *local = container_of(task, struct server_local, task); + struct server *scfg = container_of(task->parent, struct server, task); + struct server_proxy *proxy; + struct server_remote *remote; + int sfd; + + fprintf(stderr, "%s called: task 0x%p and res %i\n", __func__, task, res); + fprintf(stderr, "%s called: scfg name is %s\n", __func__, scfg->name); + + if (task->dead) { + fprintf(stderr, "Task dead!\n"); + uring_task_put(cfg, task); + return; + } + + if (res < 0) { + fprintf(stderr, "%s: result was %i\n", __func__, res); + goto out; + } + + server_print_addr(&local->peer, local->peerstr, sizeof(local->peerstr)); + fprintf(stderr, "%s: incoming proxy connection: %s -> %s\n", scfg->name, local->peerstr, local->addrstr); + + if (list_empty(&scfg->remotes)) { + error("scfg->remotes empty!\n"); + goto out; + } + + proxy = zmalloc(sizeof(*proxy)); + if (!proxy) { + perror("malloc"); + uring_close(cfg, NULL, res, NULL); + goto out; + } + + remote = list_first_entry(&scfg->remotes, struct server_remote, list); + fprintf(stderr, "%s: attempting proxy connection to %s (len %u)\n", scfg->name, remote->addrstr, remote->addr.addrlen); + + sfd = socket(remote->addr.storage.ss_family, SOCK_STREAM, 0); + if (sfd < 0) { + perror("socket"); + uring_close(cfg, NULL, res, NULL); + goto out; + } + + proxy->client = local->peer; + memcpy(proxy->clientstr, local->peerstr, sizeof(proxy->clientstr)); + uring_task_init(&proxy->task, "proxy", &scfg->task, server_proxy_free); + uring_task_set_fd(&proxy->task, sfd); + list_add(&proxy->list, &scfg->proxys); + uring_connect(cfg, &proxy->task, &remote->addr, server_proxy_connected); + +out: + uring_accept(cfg, &local->task, &local->peer, server_local_accept); +} + +static bool +server_local_open(struct cfg *cfg, struct server *scfg, struct server_local *local) +{ + int sfd; + int enable = 1; + int r; + + sfd = socket(local->addr.storage.ss_family, SOCK_STREAM, 0); + if (sfd < 0) { + perror("socket"); + goto out; + } + + if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)) < 0) { + perror("setsockopt"); + goto out; + } + + r = bind(sfd, (struct sockaddr *)&local->addr.storage, local->addr.addrlen); + if (r < 0) { + perror("bind"); + goto out; + } + + r = listen(sfd, 100); + if (r < 0) { + perror("listen"); + goto out; + } + + uring_task_set_fd(&local->task, sfd); + uring_accept(cfg, &local->task, &local->peer, server_local_accept); + fprintf(stderr, "** Opened listening socket: %s\n", local->addrstr); + return true; + +out: + if (sfd >= 0) + close(sfd); + return false; +} + +bool +server_commit(struct cfg *cfg, struct server *scfg) +{ + struct server_local *local; + + /* FIXME: config, dont reread config if server running, make sure fd is available before this is called */ + /* FIXME: verify correct cfg */ + server_dump(scfg); + + list_for_each_entry(local, &scfg->locals, list) { + server_local_open(cfg, scfg, local); + } + return true; +} + +bool +server_add_remote(struct cfg *cfg, struct server *scfg, struct sockaddr_in46 *addr) +{ + struct server_remote *remote; + + if (!scfg || !addr) + return false; + + remote = zmalloc(sizeof(*remote)); + if (!remote) + return false; + + remote->addr.storage = addr->storage; + remote->addr.addrlen = addr->addrlen; + server_print_addr(&remote->addr, remote->addrstr, sizeof(remote->addrstr)); + list_add(&remote->list, &scfg->remotes); + return true; +} + +bool +server_add_local(struct cfg *cfg, struct server *scfg, struct sockaddr_in46 *addr) +{ + struct server_local *local; + + if (!scfg || !addr) + return false; + + local = zmalloc(sizeof(*local)); + if (!local) + return false; + + local->addr.storage = addr->storage; + local->addr.addrlen = addr->addrlen; + uring_task_init(&local->task, "local", &scfg->task, server_local_free); + server_print_addr(&local->addr, local->addrstr, sizeof(local->addrstr)); + fprintf(stderr, "Adding local: %s\n", local->addrstr); + list_add(&local->list, &scfg->locals); + return true; +} + +bool +server_set_port(struct cfg *cfg, struct server *scfg, uint16_t port) +{ + if (!scfg || scfg->announce_port != 0) + return false; + + scfg->announce_port = htons(port); + return true; +} + +bool +server_set_type(struct cfg *cfg, struct server *scfg, enum server_type type) +{ + if (!scfg || scfg->type != SERVER_TYPE_UNDEFINED) + return false; + + switch (type) { + case SERVER_TYPE_ANNOUNCE: + scfg->type = SERVER_TYPE_ANNOUNCE; + break; + case SERVER_TYPE_PROXY: + scfg->type = SERVER_TYPE_PROXY; + break; + default: + return false; + } + + return true; +} + +bool +server_set_pretty_name(struct cfg *cfg, struct server *scfg, const char *pretty_name) +{ + if (!pretty_name || pretty_name[0] == '\0' || !scfg || scfg->pretty_name) + return false; + + scfg->pretty_name = strdup(pretty_name); + if (!scfg->pretty_name) + return false; + + return true; +} + +struct server * +server_new(struct cfg *cfg, const char *name) +{ + struct server *scfg; + + list_for_each_entry(scfg, &cfg->servers, list) { + if (strcmp(name, scfg->name)) + continue; + debug(2, "Server already exists: %s\n", name); + return scfg; + } + + debug(2, "Would add server cfg: %s\n", name); + scfg = zmalloc(sizeof(*scfg)); + if (!scfg) { + error("malloc"); + return NULL; + } + + scfg->type = SERVER_TYPE_UNDEFINED; + scfg->name = strdup(name); + scfg->running = false; + uring_task_init(&scfg->task, "scfg", &cfg->task, server_free); + list_init(&scfg->remotes); + list_init(&scfg->locals); + list_init(&scfg->proxys); + list_add(&scfg->list, &cfg->servers); + + return scfg; +} diff --git a/server.h b/server.h new file mode 100644 index 0000000..d066588 --- /dev/null +++ b/server.h @@ -0,0 +1,51 @@ +#ifndef fooserverhfoo +#define fooserverhfoo + +enum server_type { + SERVER_TYPE_UNDEFINED, + SERVER_TYPE_ANNOUNCE, + SERVER_TYPE_PROXY +}; + +struct server { + enum server_type type; + char *name; + char *pretty_name; + uint16_t announce_port; + struct list_head locals; + struct list_head remotes; + struct list_head proxys; + bool running; + + struct uring_task task; + char buf[4096]; + size_t len; + struct list_head list; +}; + +void server_refdump(struct server *server); + +void server_delete(struct cfg *cfg, struct server *scfg); + +void server_delete_by_name(struct cfg *cfg, const char *name); + +bool server_commit(struct cfg *cfg, struct server *scfg); + +bool server_add_remote(struct cfg *cfg, struct server *scfg, + struct sockaddr_in46 *remote); + +bool server_add_local(struct cfg *cfg, struct server *scfg, + struct sockaddr_in46 *local); + +bool server_set_port(struct cfg *cfg, struct server *scfg, uint16_t port); + +bool server_set_type(struct cfg *cfg, struct server *scfg, + enum server_type type); + +bool server_set_pretty_name(struct cfg *cfg, struct server *scfg, + const char *pretty_name); + +struct server *server_new(struct cfg *cfg, const char *name); + +#endif + diff --git a/stest.c b/stest.c new file mode 100644 index 0000000..d60ae74 --- /dev/null +++ b/stest.c @@ -0,0 +1,102 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PIPE_RD 0 +#define PIPE_WR 1 + +int +main(int argc, char **argv) { + int sfd; + struct sockaddr_in addr; + socklen_t addrsz = sizeof(addr); + int pfd[2]; + int r; + int cfd; + int zfd; + size_t total = 0; + + if (argc != 3) { + fprintf(stderr, "Usage: %s \n", argv[0]); + exit(EXIT_FAILURE); + } + + sfd = socket(AF_INET, SOCK_STREAM, 0); + if (sfd < 0) { + perror("socket"); + exit(EXIT_FAILURE); + } + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(atoi(argv[2])); + addr.sin_addr.s_addr = inet_addr(argv[1]); + + int enable = 1; + if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)) < 0) { + perror("setsockopt"); + exit(EXIT_FAILURE); + } + + r = bind(sfd, (struct sockaddr *)&addr, sizeof(addr)); + if (r < 0) { + perror("bind"); + exit(EXIT_FAILURE); + } + + r = listen(sfd, 100); + if (r < 0) { + perror("listen"); + exit(EXIT_FAILURE); + } + + cfd = accept4(sfd, (struct sockaddr *)&addr, &addrsz, SOCK_CLOEXEC); + if (cfd < 0) { + perror("accept"); + exit(EXIT_FAILURE); + } + + zfd = open("/dev/null", O_WRONLY | O_CLOEXEC); + if (zfd < 0) { + perror("open"); + exit(EXIT_FAILURE); + } + + if (pipe2(pfd, O_CLOEXEC) < 0) { + perror("pipe2"); + exit(EXIT_FAILURE); + } + + while (true) { + ssize_t r, w; + + r = splice(cfd, NULL, pfd[PIPE_WR], NULL, 64 * 1024, SPLICE_F_MOVE); + if (r < 0) + perror("splice"); + //fprintf(stderr, "Read %zi bytes from socket\n", r); + if (r == 0) + break; + w = splice(pfd[PIPE_RD], NULL, zfd, NULL, r, SPLICE_F_MOVE); + if (w < 0) + perror("splice"); + if (w != r) { + fprintf(stderr, "Losing bytes\n"); + exit(EXIT_FAILURE); + } + //fprintf(stderr, "Wrote %zi bytes to /dev/null\n", r); + + total += w; + } + + printf("Server: received %zu bytes\n", total); +} + diff --git a/uring.c b/uring.c new file mode 100644 index 0000000..c43a8fb --- /dev/null +++ b/uring.c @@ -0,0 +1,294 @@ +#include +#include +#include +#include +#include +#include + +#include "main.h" +#include "uring.h" +#include "config.h" + +struct uring_ev { + struct io_uring uring; + struct io_uring_params uring_params; + struct uring_task task; +}; + +void +uring_task_refdump(struct uring_task *task) +{ + char buf[4096]; + struct uring_task *tmp; + + buf[0] = '\0'; + for (tmp = task; tmp; tmp = tmp->parent) { + size_t prefix; + char *dst; + + if (tmp->parent) + prefix = strlen("->") + strlen(tmp->name); + else + prefix = strlen(tmp->name); + + memmove(&buf[prefix], &buf[0], strlen(buf) + 1); + + dst = &buf[0]; + if (tmp->parent) { + *dst++ = '-'; + *dst++ = '>'; + } + + memcpy(dst, tmp->name, strlen(tmp->name)); + } + + fprintf(stderr, "%s (0x%p parent 0x%p free 0x%p fd %i ref %u)\n", + buf, task, task->parent, task->free, task->fd, + task->refcount); +} + +void +uring_task_put(struct cfg *cfg, struct uring_task *task) +{ + struct uring_task *parent = task->parent; + + fprintf(stderr, "%s: called with task 0x%p and refcount %u\n", __func__, task, task->refcount); + task->refcount--; + + if (task->refcount > 0) + return; + + if (task->refcount < 0) + error("Negative refcount!\n"); + + if (task->fd >= 0) { + uring_task_close_fd(cfg, task); + /* We'll be called again once the fd is closed */ + return; + } + + if (task->free) + task->free(task); + + if (parent) + uring_task_put(NULL, parent); +} + +void +uring_task_get(struct cfg *cfg, struct uring_task *task) +{ + fprintf(stderr, "%s: called with task 0x%p and refcount %u\n", __func__, task, task->refcount); + + if (task->refcount < 0) + error("Negative refcount!\n"); + + task->refcount++; +} + +void +uring_task_set_fd(struct uring_task *task, int fd) +{ + if (task->fd >= 0) + error("Leaking fd %i\n", task->fd); + + task->fd = fd; +} + +void +uring_task_close_fd(struct cfg *cfg, struct uring_task *task) +{ + fprintf(stderr, "%s called with task 0x%p\n", __func__, task); + if (task->fd < 0) + return; + + uring_close(cfg, task, task->fd, NULL); + task->fd = -1; +} + +void +uring_task_init(struct uring_task *task, const char *name, struct uring_task *parent, void (*free)(struct uring_task *)) +{ + if (!free) + die("uring_task_init called without destructor\n"); + + task->refcount = 1; + task->fd = -1; + task->parent = parent; + task->free = free; + task->dead = false; + task->name = name; + + if (task->parent) + uring_task_get(NULL, task->parent); +} + +void +uring_close(struct cfg *cfg, struct uring_task *task, int fd, callback_t callback) +{ + struct io_uring_sqe *sqe = io_uring_get_sqe(&cfg->uev->uring); + + fprintf(stderr, "%s: called with task 0x%p and cb 0x%p\n", __func__, task, callback); + if (!sqe) + perrordie("io_uring_sqe"); + + if (task) { + uring_task_get(cfg, task); + task->callback = callback; + } + io_uring_prep_close(sqe, fd); + io_uring_sqe_set_data(sqe, task); + + fprintf(stderr, "%s: done\n", __func__); +} + +void +uring_read(struct cfg *cfg, struct uring_task *task, void *buf, size_t len, off_t offset, callback_t callback) +{ + struct io_uring_sqe *sqe = io_uring_get_sqe(&cfg->uev->uring); + + if (!sqe) + perrordie("io_uring_sqe"); + + if (task->fd < 0) { + error("uring_read called with no fd set\n"); + return; + } + + uring_task_get(cfg, task); + task->callback = callback; + io_uring_prep_read(sqe, task->fd, buf, len, offset); + io_uring_sqe_set_data(sqe, task); +} + +void +uring_openat(struct cfg *cfg, struct uring_task *task, const char *path, callback_t callback) +{ + struct io_uring_sqe *sqe = io_uring_get_sqe(&cfg->uev->uring); + + if (!sqe) + perrordie("io_uring_sqe"); + + uring_task_get(cfg, task); + task->callback = callback; + io_uring_prep_openat(sqe, AT_FDCWD, path, O_RDONLY | O_CLOEXEC, 0); + io_uring_sqe_set_data(sqe, task); +} + +void +uring_connect(struct cfg *cfg, struct uring_task *task, struct sockaddr_in46 *addr, callback_t callback) +{ + struct io_uring_sqe *sqe = io_uring_get_sqe(&cfg->uev->uring); + + if (!sqe) + perrordie("io_uring_sqe"); + + if (task->fd < 0) { + error("uring_connect called with no fd set\n"); + return; + } + + uring_task_get(cfg, task); + task->callback = callback; + io_uring_prep_connect(sqe, task->fd, (struct sockaddr *)&addr->storage, addr->addrlen); + io_uring_sqe_set_data(sqe, task); +} + +void +uring_accept(struct cfg *cfg, struct uring_task *task, struct sockaddr_in46 *addr, callback_t callback) +{ + struct io_uring_sqe *sqe = io_uring_get_sqe(&cfg->uev->uring); + addr->addrlen = sizeof(addr->storage); + + if (!sqe) + perrordie("io_uring_sqe"); + + if (task->fd < 0) { + error("uring_accept called with no fd set\n"); + return; + } + + uring_task_get(cfg, task); + task->callback = callback; + io_uring_prep_accept(sqe, task->fd, (struct sockaddr *)&addr->storage, &addr->addrlen, 0); + io_uring_sqe_set_data(sqe, task); +} + +void +uring_cancel(struct cfg *cfg, struct uring_task *task) +{ + struct io_uring_sqe *sqe = io_uring_get_sqe(&cfg->uev->uring); + + if (!sqe) + perrordie("io_uring_sqe"); + + task->dead = true; + io_uring_prep_cancel(sqe, task, 0); + io_uring_sqe_set_data(sqe, NULL); +} + +static void +uring_free(struct uring_task *task) +{ + fprintf(stderr, "%s called\n", __func__); +} + +void +uring_refdump(struct uring_ev *uev) +{ + uring_task_refdump(&uev->task); +} + +void +uring_init(struct cfg *cfg) +{ + struct uring_ev *uev; + + uev = zmalloc(sizeof(*uev)); + if (!uev) + perrordie("malloc"); + + if (io_uring_queue_init_params(4096, &uev->uring, &uev->uring_params) < 0) + perrordie("io_uring_queue_init_params"); + + fprintf(stderr, "uring initialized, features: 0x%08x\n", uev->uring_params.features); + + uring_task_init(&uev->task, "uev", &cfg->task, uring_free); + cfg->uev = uev; +} + +int +uring_event_loop(struct cfg *cfg) +{ + while (true) { + struct io_uring_cqe *cqe; + unsigned nr, head; + int r; + + io_uring_submit(&cfg->uev->uring); + r = io_uring_wait_cqe(&cfg->uev->uring, &cqe); + if (r < 0) { + if (errno == EINTR) + continue; + else + perrordie("io_uring_wait_cqe"); + } + + nr = 0; + io_uring_for_each_cqe(&cfg->uev->uring, head, cqe) { + struct uring_task *task = io_uring_cqe_get_data(cqe); + + fprintf(stderr, "%s: got CEQ (res: %i, task: 0x%p, cb: 0x%p)\n", __func__, cqe->res, task, task ? task->callback : NULL); + if (task && task->callback) + task->callback(cfg, task, cqe->res); + nr++; + if (task) + uring_task_put(cfg, task); + } + + printf("%s: %u CQEs treated\n", __func__, nr); + io_uring_cq_advance(&cfg->uev->uring, nr); + } + + return 0; +} + diff --git a/uring.h b/uring.h new file mode 100644 index 0000000..4c5ee17 --- /dev/null +++ b/uring.h @@ -0,0 +1,41 @@ +#ifndef foouringhfoo +#define foouringhfoo + +void uring_task_refdump(struct uring_task *task); + +void uring_task_put(struct cfg *cfg, struct uring_task *task); + +void uring_task_get(struct cfg *cfg, struct uring_task *task); + +void uring_task_set_fd(struct uring_task *task, int fd); + +void uring_task_close_fd(struct cfg *cfg, struct uring_task *task); + +void uring_task_init(struct uring_task *task, const char *name, + struct uring_task *parent, + void (*free)(struct uring_task *)); + +void uring_close(struct cfg *cfg, struct uring_task *task, int fd, + callback_t callback); + +void uring_read(struct cfg *cfg, struct uring_task *task, void *buf, + size_t len, off_t offset, callback_t callback); + +void uring_openat(struct cfg *cfg, struct uring_task *task, const char *path, + callback_t callback); + +void uring_connect(struct cfg *cfg, struct uring_task *task, + struct sockaddr_in46 *addr, callback_t callback); + +void uring_accept(struct cfg *cfg, struct uring_task *task, + struct sockaddr_in46 *addr, callback_t callback); + +void uring_cancel(struct cfg *cfg, struct uring_task *task); + +void uring_refdump(struct uring_ev *uev); + +void uring_init(struct cfg *cfg); + +int uring_event_loop(struct cfg *cfg); + +#endif diff --git a/utils.c b/utils.c new file mode 100644 index 0000000..a84f63b --- /dev/null +++ b/utils.c @@ -0,0 +1,37 @@ +#include +#include +#include +#include + +int +strtou16_strict(const char *str, uint16_t *result) +{ + char *end; + long val; + + if (!str) + return -EINVAL; + + errno = 0; + val = strtol(str, &end, 10); + + if (errno == ERANGE && (val == LONG_MAX || val == LONG_MIN)) + return -EINVAL; + + if (errno != 0 && val == 0) + return -EINVAL; + + if (end == str) + return -EINVAL; + + if (*end != '\0') + return -EINVAL; + + if (val < 1 || val > UINT16_MAX) + return -EINVAL; + + if (result) + *result = val; + return 0; +} + diff --git a/utils.h b/utils.h new file mode 100644 index 0000000..ccebb30 --- /dev/null +++ b/utils.h @@ -0,0 +1,138 @@ +#ifndef fooutilshfoo +#define fooutilshfoo + +#include +#include +#include + +int strtou16_strict(const char *str, uint16_t *result); + +struct list_head { + struct list_head *next; + struct list_head *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) struct list_head name = LIST_HEAD_INIT(name) + +static inline void list_init(struct list_head *list) +{ + list->next = list; + list->prev = list; +} + +static inline void list_del(struct list_head *list) +{ + list->next->prev = list->prev; + list->prev->next = list->next; +} + +static inline void list_add(struct list_head *new, struct list_head *list) +{ + list->next->prev = new; + new->next = list->next; + new->prev = list; + list->next = new; +} + +static inline void list_replace(struct list_head *old, struct list_head *new) +{ + old->prev->next = new; + old->next->prev = new; + new->next = old->next; + new->prev = old->prev; + old->next = old->prev = NULL; +} + +static inline bool list_empty(struct list_head *list) +{ + return list->next == list; +} + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) + +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + +#define list_entry(ptr, type, member) \ + container_of(ptr, type, member) + +#define list_first_entry(ptr, type, member) \ + list_entry((ptr)->next, type, member) + +#define list_next_entry(pos, member) \ + list_entry((pos)->member.next, typeof(*(pos)), member) + +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +#define list_for_each_entry(pos, head, member) \ + for (pos = list_first_entry(head, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_next_entry(pos, member)) + +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +static inline void* zmalloc(size_t size) +{ + return calloc(1, size); +} + +/* +#define _cleanup_(x) __attribute__((cleanup(x))) + +#define DEFINE_TRIVIAL_CLEANUP_FUNC(type, func) \ + static inline void func##p(type *p) { \ + if (*p) \ + func(*p); \ + } \ + struct __useless_struct_to_allow_trailing_semicolon__ + +static inline unsigned +strv_length(char **strv) +{ + unsigned len; + + for (len = 0; strv && *strv; strv++) + len++; + + return len; +} + +static inline void strv_free(char **l) { + char **k; + if (l) { + for (k = l; *k; k++) + free(k); + free(l); + } +} +DEFINE_TRIVIAL_CLEANUP_FUNC(char **, strv_free); +#define _cleanup_strv_free_ _cleanup_(strv_freep) + +static inline void freep(void *p) { + free(*(void**) p); +} +#define _cleanup_free_ _cleanup_(freep) + +DEFINE_TRIVIAL_CLEANUP_FUNC(int, close); +#define _cleanup_close_ _cleanup_(closep) + +DEFINE_TRIVIAL_CLEANUP_FUNC(FILE *, fclose); +#define _cleanup_fclose_ _cleanup_(fclosep) + +DEFINE_TRIVIAL_CLEANUP_FUNC(DIR *, closedir); +#define _cleanup_closedir_ _cleanup_(closedirp) + +*/ + +#endif + -- cgit v1.2.3