/* Niotso Server - Niotso daemon based on PostgreSQL Server.cpp Copyright (c) 2012 Niotso Project Author(s): Fatbag This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include "Server.hpp" #define SHUTDOWN(X) do { syslog(LOG_INFO, " Could not recover from errors. Shutting down."); \ exitval = EXIT_FAILURE; goto X; } while(0) #define SHUTDOWN_M(X, Y) do { syslog(LOG_ERR, " %s (%s)", X, strerror(errno)); \ SHUTDOWN(Y); } while(0) //IPV6_V6ONLY static void term(int) { } int main(int, char **) { const pid_t pid = fork(); if(pid < 0){ const char *msg1 = " Failed to create child process (%s).", *msg2 = strerror(errno); openlog(SERVER_NAME, 0, LOG_DAEMON); syslog(LOG_ERR, msg1, msg2); closelog(); fprintf(stderr, msg1, msg2); fprintf(stderr, "\n"); return EXIT_FAILURE; }else if(pid > 0) return EXIT_SUCCESS; int exitval = EXIT_SUCCESS, sockfd, epollfd; { //New scope required for error handling int ret; FILE * fd; umask(0); openlog(SERVER_NAME, LOG_PID, LOG_DAEMON); if(setsid() < 0) SHUTDOWN_M("Failed to create session", close_msg); if(chdir(CONFIG_DIR) < 0) SHUTDOWN_M("Failed to change into \""CONFIG_DIR"\"", close_msg); close(STDIN_FILENO); close(STDOUT_FILENO); close(STDERR_FILENO); struct sigaction sigact; sigact.sa_handler = term; sigemptyset(&sigact.sa_mask); sigact.sa_flags = 0; sigaction(SIGTERM, &sigact, NULL); sigaction(SIGINT, &sigact, NULL); syslog(LOG_INFO, " "SERVER_NAME" (version "VERSIONSTR") is starting..."); fd = fopen("network.conf", "r"); if(!fd) SHUTDOWN_M("Failed to open \""CONFIG_DIR"/network.conf\"", close_msg); unsigned int port = 0; ret = fscanf(fd, "%u", &port); fclose(fd); if(ret < 0) SHUTDOWN_M("Failed to read \""CONFIG_DIR"/network.conf\"", close_msg); if(port > 65535){ syslog(LOG_ERR, " Invalid port '%u' specified in \""CONFIG_DIR"/network.conf\".", port); SHUTDOWN(close_msg); } sockfd = socket(AF_INET6, SOCK_DGRAM, 0); if(sockfd < 0) SHUTDOWN_M("Failed to open socket", close_msg); int flags = fcntl(sockfd, F_GETFL, 0); if(flags < 0) SHUTDOWN_M("Failed to read socket flags", close_socket); if(fcntl(sockfd, F_SETFL, flags | O_NONBLOCK) < 0) SHUTDOWN_M("Failed to set socket flags", close_socket); sockaddr_in6 server_addr, client_addr; memset(&server_addr, 0, sizeof(sockaddr_in6)); memset(&client_addr, 0, sizeof(sockaddr_in6)); server_addr.sin6_family = AF_INET6; server_addr.sin6_port = htons((uint16_t) port); server_addr.sin6_addr = in6addr_any; client_addr.sin6_family = AF_INET6; client_addr.sin6_addr = in6addr_any; if(bind(sockfd, (const sockaddr*) &server_addr, sizeof(sockaddr_in6)) < 0) SHUTDOWN_M("Failed to bind to socket", close_socket); epollfd = epoll_create(1); if(epollfd < 0) SHUTDOWN_M("Failed to create an epoll handle", close_socket); epoll_event epev[1]; epev[0].events = EPOLLIN | EPOLLPRI; epev[0].data.fd = sockfd; if(epoll_ctl(epollfd, EPOLL_CTL_ADD, sockfd, epev) < 0) SHUTDOWN_M("Failed to register the socket to the epoll handle", close_epoll); //Now that we're running, stop being terminate-on-failure-happy syslog(LOG_INFO, " Running."); /**** ** Listen loop */ int eventcount; while((eventcount = epoll_wait(epollfd, epev, 1, -1)) >= 0){ if(eventcount == 0) continue; else if(epev[0].events & EPOLLERR) SHUTDOWN_M("Socket closed unexpectedly with EPOLLERR", close_epoll); else if(epev[0].events & EPOLLHUP) SHUTDOWN_M("Socket closed unexpectedly with EPOLLHUP", close_epoll); else if(!(epev[0].events & EPOLLIN) && !(epev[0].events & EPOLLPRI)) continue; uint8_t packetdata[1500]; socklen_t addrlen = sizeof(sockaddr_in6); ssize_t packetsize = recvfrom(epev[0].data.fd, packetdata, 1500, 0, (sockaddr*) &client_addr, &addrlen); if(packetsize < 0){ if(errno == EINTR || errno == ECONNRESET || errno == ENOTCONN || errno == ETIMEDOUT || errno == EAGAIN || errno == EWOULDBLOCK) continue; SHUTDOWN_M("Socket closed unexpectedly on call to recvfrom", close_epoll); } //Pass the packet down (even zero-length packets might be meaningful in the protocol) //... } /**** ** Shutdown */ } close_epoll: close(epollfd); close_socket: close(sockfd); close_msg: syslog(LOG_INFO, " Shut down gracefully."); closelog(); return exitval; }