diff options
-rw-r--r-- | config.c | 730 | ||||
-rw-r--r-- | config.h | 12 | ||||
-rw-r--r-- | ctest.c | 74 | ||||
-rw-r--r-- | inotify.c | 730 | ||||
-rw-r--r-- | inotify.h | 12 | ||||
-rw-r--r-- | main.c | 284 | ||||
-rw-r--r-- | main.h | 55 | ||||
-rw-r--r-- | meson.build | 9 | ||||
-rw-r--r-- | server.c | 423 | ||||
-rw-r--r-- | server.h | 51 | ||||
-rw-r--r-- | stest.c | 102 | ||||
-rw-r--r-- | uring.c | 294 | ||||
-rw-r--r-- | uring.h | 41 | ||||
-rw-r--r-- | utils.c | 37 | ||||
-rw-r--r-- | utils.h | 138 |
15 files changed, 2992 insertions, 0 deletions
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 <stdio.h> +#include <ctype.h> +#include <string.h> +#include <stdbool.h> +#include <sys/inotify.h> +#include <dirent.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> +#include <unistd.h> +#include <arpa/inet.h> +#include <inttypes.h> + +#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 @@ -0,0 +1,74 @@ +#define _GNU_SOURCE +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <string.h> +#include <netinet/ip.h> +#include <arpa/inet.h> +#include <unistd.h> +#include <fcntl.h> +#include <stdbool.h> + +#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 <addr> <port> <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 <stdio.h> +#include <ctype.h> +#include <string.h> +#include <stdbool.h> +#include <sys/inotify.h> +#include <dirent.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> +#include <unistd.h> +#include <arpa/inet.h> +#include <inttypes.h> + +#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 @@ -0,0 +1,284 @@ +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdbool.h> +#include <getopt.h> +#include <sys/signalfd.h> +#include <sys/eventfd.h> +#include <signal.h> + +#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); +} @@ -0,0 +1,55 @@ +#ifndef foomainhfoo +#define foomainhfoo + +#include <sys/socket.h> +#include <netinet/ip.h> +#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 <stdlib.h> +#include <string.h> +#include <arpa/inet.h> +#include <unistd.h> +#include <inttypes.h> + +#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 : "<undefined>"); + 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 + @@ -0,0 +1,102 @@ +#define _GNU_SOURCE +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <string.h> +#include <netinet/ip.h> +#include <arpa/inet.h> +#include <unistd.h> +#include <fcntl.h> +#include <stdbool.h> + +#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 <addr> <port>\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); +} + @@ -0,0 +1,294 @@ +#include <liburing.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> + +#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; +} + @@ -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 @@ -0,0 +1,37 @@ +#include <stdlib.h> +#include <errno.h> +#include <stdint.h> +#include <limits.h> + +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; +} + @@ -0,0 +1,138 @@ +#ifndef fooutilshfoo +#define fooutilshfoo + +#include <stdio.h> +#include <stdbool.h> +#include <stdlib.h> + +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 + |