# #
#############################################################################*/
+#include <errno.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <unistd.h>
+
#include "logging.h"
#include "main.h"
#include "server.h"
+// Set the size of the read buffer to 1 MiB
+#define BUFFER_SIZE 1048576
+
+#define SOCKET_BACKLOG 1024
+#define EPOLL_MAX_EVENTS 1024
+
+static int create_socket(struct fireperf_config* conf) {
+ // Open a new socket
+ int fd = socket(AF_INET6, SOCK_STREAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0);
+ if (fd < 0) {
+ ERROR(conf, "Could not open socket: %s\n", strerror(errno));
+ goto ERROR;
+ }
+
+ struct sockaddr_in6 addr = {
+ .sin6_family = AF_INET6,
+ .sin6_port = htons(conf->port),
+ };
+
+ // Bind it to the selected port
+ int r = bind(fd, &addr, sizeof(addr));
+ if (r) {
+ ERROR(conf, "Could not bind socket: %s\n", strerror(errno));
+ goto ERROR;
+ }
+
+ // Listen
+ r = listen(fd, SOCKET_BACKLOG);
+ if (r) {
+ ERROR(conf, "Could not listen on socket: %s\n", strerror(errno));
+ goto ERROR;
+ }
+
+ DEBUG(conf, "Created listening socket %d\n", fd);
+
+ return fd;
+
+ERROR:
+ close(fd);
+
+ return -1;
+}
+
+static int accept_connection(struct fireperf_config* conf, int sockfd) {
+ struct sockaddr_in6 addr;
+ socklen_t l = sizeof(addr);
+
+ int fd = -1;
+
+ // The listening socket is ready, there is a new connection waiting to be accepted
+ do {
+ fd = accept(sockfd, &addr, &l);
+ } while (fd < 0 && (errno == EAGAIN || errno == EWOULDBLOCK));
+
+ if (fd < 0) {
+ ERROR(conf, "Could not accept a new connection: %s\n", strerror(errno));
+ return -1;
+ }
+
+ DEBUG(conf, "New connection accepted on socket %d\n", fd);
+
+ return fd;
+}
+
+static int handle_io_on_connection(struct fireperf_config* conf, int fd) {
+ char buffer[BUFFER_SIZE];
+ ssize_t bytes_read;
+
+ // Try reading into buffer
+ do {
+ bytes_read = recv(fd, buffer, sizeof(buffer), 0);
+ } while (bytes_read < 0 && (errno == EAGAIN || errno == EWOULDBLOCK));
+
+ // Error?
+ if (bytes_read < 0) {
+ ERROR(conf, "Could not read from socket %d: %s\n", fd, strerror(errno));
+ return -1;
+
+ // Connection closed
+ } else if (bytes_read == 0) {
+ DEBUG(conf, "Connection %d has closed\n", fd);
+ return 1;
+ }
+
+ DEBUG(conf, "Read %zu bytes from socket %d\n", bytes_read, fd);
+
+ return 0;
+}
+
int fireperf_server(struct fireperf_config* conf) {
DEBUG(conf, "Launching " PACKAGE_NAME " in server mode\n");
- return 0;
+ int sockfd = -1;
+
+ int epollfd = -1;
+ struct epoll_event events[EPOLL_MAX_EVENTS];
+
+ int r = 1;
+
+ // Create listening socket
+ sockfd = create_socket(conf);
+ if (sockfd < 0)
+ return 1;
+
+ // Initialize epoll()
+ epollfd = epoll_create1(0);
+ if (epollfd < 0) {
+ ERROR(conf, "Could not initialize epoll(): %s\n", strerror(errno));
+ return 1;
+ }
+
+ // Add listening socket
+ struct epoll_event ev = {
+ .events = EPOLLIN,
+ .data.fd = sockfd,
+ };
+
+ if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sockfd, &ev)) {
+ ERROR(conf, "Could not add socket file descriptor to epoll(): %s\n",
+ strerror(errno));
+ goto ERROR;
+ }
+
+ DEBUG(conf, "Entering main loop...\n");
+
+ while (!conf->terminated) {
+ int fds = epoll_wait(epollfd, events, EPOLL_MAX_EVENTS, -1);
+ if (fds < 1) {
+ ERROR(conf, "epoll_wait() failed: %s\n", strerror(errno));
+ goto ERROR;
+ }
+
+ DEBUG(conf, "%d event(s) are ready\n", fds);
+
+ for (int i = 0; i < fds; i++) {
+ int fd = events[i].data.fd;
+
+ // The listening socket
+ if (fd == sockfd) {
+ int connfd = accept_connection(conf, sockfd);
+ if (connfd < 0)
+ goto ERROR;
+
+ // Add the new socket to epoll()
+ ev.events = EPOLLIN;
+ ev.data.fd = connfd;
+
+ if (epoll_ctl(epollfd, EPOLL_CTL_ADD, connfd, &ev)) {
+ ERROR(conf, "Could not add socket file descriptor to epoll(): %s\n",
+ strerror(errno));
+ goto ERROR;
+ }
+
+ // One of the connections had IO
+ } else {
+ r = handle_io_on_connection(conf, fd);
+ if (r < 0)
+ goto ERROR;
+
+ // Connection closed?
+ else if (r == 1) {
+ if (epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, NULL)) {
+ ERROR(conf, "Could not remove socket file descriptfor from epoll(): %s\n",
+ strerror(errno));
+ }
+ }
+ }
+ }
+ }
+
+ERROR:
+ if (sockfd > 0)
+ close(sockfd);
+
+ if (epollfd > 0)
+ close(epollfd);
+
+ return r;
}