#include #include #include #include #include #include #include "server.h" #include "socket.h" #include "log.h" typedef struct client_thread_args { pthread_t self; int client_fd; struct sockaddr_in clientaddr; const server_ctx *server_ctx; } client_thread_args; static int server_accept_client(const server_ctx ctx[], size_t siz, struct epoll_event *event); static void * client_mainloop_epoll(void *arg); server_ctx * server_init_ctx(server_ctx *ctx, init_cb init_fn) { if (!ctx) ctx = (server_ctx *) malloc(sizeof(*ctx)); assert(ctx); memset(ctx, 0, sizeof(*ctx)); if (!init_fn(ctx)) return NULL; return ctx; } int server_validate_ctx(const server_ctx *ctx) { assert(ctx); assert(ctx->server_cbs.on_connect && ctx->server_cbs.on_disconnect && ctx->server_cbs.mainloop); assert(ctx->server_cbs.on_free && ctx->server_cbs.on_listen && ctx->server_cbs.on_shutdown); return 0; } int server_setup_epoll(const server_ctx ctx[], size_t siz) { int s, fd = epoll_create1(0); /* flags == 0 -> obsolete size arg is dropped */ struct epoll_event ev; assert(ctx); assert(siz > 0 && siz < POTD_MAXFD); if (fd < 0) return -1; for (size_t i = 0; i < siz; ++i) { memset(&ev, 0, sizeof(ev)); ev.data.fd = ctx[i].sock.fd; ev.events = EPOLLIN | EPOLLET; s = epoll_ctl(fd, EPOLL_CTL_ADD, ctx[i].sock.fd, &ev); if (s) { close(fd); return -2; } } return fd; } int server_mainloop_epoll(int epoll_fd, const server_ctx ctx[], size_t siz) { static struct epoll_event *events = NULL; if (!events) events = (struct epoll_event *) calloc(POTD_MAXEVENTS, sizeof(*events)); assert(events); assert(ctx); assert(siz > 0 && siz < POTD_MAXFD); while (1) { int n, i; n = epoll_wait(epoll_fd, events, POTD_MAXEVENTS, -1); if (n < 0) return 1; for (i = 0; i < n; ++i) { if ((events[i].events & EPOLLERR) || (events[i].events & EPOLLHUP) || (!(events[i].events & EPOLLIN))) { E("Epoll for descriptor %d failed", events[i].data.fd); E_STRERR("epoll_wait"); close(events[i].data.fd); continue; } else { if (server_accept_client(ctx, siz, &events[i])) { /* new client connection, accept succeeded */ continue; } D2("FD: %d/%lu , %d", events[i].data.fd, siz, n); } } } free(events); return 0; } static int server_accept_client(const server_ctx ctx[], size_t siz, struct epoll_event *event) { size_t i; int client_fd; struct sockaddr_in clientaddr; for (i = 0; i < siz; ++i) { if (ctx[i].sock.fd == event->data.fd) { client_fd = socket_accept_in(&ctx[i].sock, &clientaddr); if (client_fd < 0) { E_STRERR("Could not accept client connection"); return 0; } client_thread_args *args = (client_thread_args *) malloc(sizeof(client_thread_args)); args->client_fd = client_fd; args->clientaddr = clientaddr; args->server_ctx = &ctx[i]; if (pthread_create(&args->self, NULL, client_mainloop_epoll, args)) { E_STRERR("Thread creation"); free(args); return 0; } return 1; } } return 0; } static void * client_mainloop_epoll(void *arg) { client_thread_args *args; int s, epoll_fd; struct epoll_event event; struct epoll_event *events; assert(arg); args = (client_thread_args *) arg; events = (struct epoll_event *) calloc(POTD_MAXEVENTS, sizeof(*events)); assert(events); epoll_fd = epoll_create1(0); if (epoll_fd < 0) goto finish; event.data.fd = args->client_fd; event.events = EPOLLIN | EPOLLOUT | EPOLLET; memset(&event, 0, sizeof(event)); s = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, args->client_fd, &event); if (s) goto finish; finish: close(epoll_fd); close(args->client_fd); free(events); free(args); return NULL; }