2012-02-27 02:55:53 -06:00
|
|
|
/*
|
2012-05-21 23:27:44 -05:00
|
|
|
Niotso Server - Niotso daemon based on PostgreSQL
|
|
|
|
Server.cpp
|
|
|
|
Copyright (c) 2012 Niotso Project <http://niotso.org/>
|
|
|
|
Author(s): Fatbag <X-Fi6@phppoll.org>
|
2012-02-27 02:55:53 -06:00
|
|
|
|
|
|
|
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 <http://www.gnu.org/licenses/>.
|
2012-11-11 17:10:53 -06:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <syslog.h>
|
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <sys/epoll.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include "Server.hpp"
|
|
|
|
|
|
|
|
#define SHUTDOWN(X) do { syslog(LOG_INFO, "<info> Could not recover from errors. Shutting down."); \
|
|
|
|
exitval = EXIT_FAILURE; goto X; } while(0)
|
|
|
|
#define SHUTDOWN_M(X, Y) do { syslog(LOG_ERR, "<error> %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 = "<error> 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, "<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, "<error> 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, "<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)
|
|
|
|
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, "<info> Shut down gracefully.");
|
|
|
|
closelog();
|
|
|
|
|
|
|
|
return exitval;
|
|
|
|
}
|