#include <sys/sendfile.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
/* Default values */
#define MAX_FAILURE_COUNT 30
#define PORT 8080
#define THREAD_COUNT 10
#define BIND_ADDR "::"
/* Arguments for worker threads */
struct filed_worker_thread_args {
int fd;
};
static void filed_init(void) {
mlockall(MCL_CURRENT | MCL_FUTURE);
}
static int filed_listen(const char *address, unsigned int port) {
struct sockaddr_in6 addr;
int pton_ret, bind_ret, listen_ret;
int fd;
addr.sin6_family = AF_INET6;
addr.sin6_flowinfo = 0;
addr.sin6_scope_id = 0;
addr.sin6_port = htons(port);
pton_ret = inet_pton(AF_INET6, address, addr.sin6_addr.s6_addr);
if (pton_ret != 1) {
return(-1);
}
fd = socket(AF_INET6, SOCK_STREAM, 0);
if (fd < 0) {
return(fd);
}
bind_ret = bind(fd, (const struct sockaddr *) &addr, sizeof(addr));
if (bind_ret < 0) {
close(fd);
return(-1);
}
listen_ret = listen(fd, 128);
if (listen_ret != 0) {
close(fd);
return(-1);
}
return(fd);
}
static int filed_logging_thread_init(void) {
/* XXX:TODO: Unimplemented */
return(0);
}
struct filed_fileinfo {
int fd;
size_t len;
char *lastmod;
char *type;
};
static struct filed_fileinfo *filed_open_file(const char *path, struct filed_fileinfo *buffer) {
/* XXX:TODO: Cache file descriptors */
off_t len;
int fd;
fd = open(path, O_RDONLY);
if (fd < 0) {
return(NULL);
}
len = lseek(fd, 0, SEEK_END);
lseek(fd, 0, SEEK_SET);
buffer->fd = fd;
buffer->len = len;
/* XXX:TODO: Determine */
buffer->type = "text/plain";
buffer->lastmod = "Now";
return(buffer);
}
static char *filed_get_http_request(FILE *fp, char *buffer, size_t buffer_len) {
char *method, *path, *version;
char tmpbuf[1010];
int i;
setlinebuf(fp);
fgets(buffer, buffer_len, fp);
method = buffer;
buffer = strchr(buffer, ' ');
if (buffer == NULL) {
return(NULL);
}
*buffer = '\0';
buffer++;
path = buffer;
buffer = strchr(buffer, ' ');
if (buffer != NULL) {
*buffer = '\0';
buffer++;
version = buffer;
}
for (i = 0; i < 100; i++) {
fgets(tmpbuf, sizeof(tmpbuf), fp);
if (memcmp(tmpbuf, "\r\n", 2) == 0) {
break;
}
}
fflush(fp);
/* IGNORED */
version = version;
method = method;
return(path);
}
static void filed_handle_client(int fd) {
struct filed_fileinfo *fileinfo, fileinfo_b;
char *path, path_b[1010];
char *date_current;
FILE *fp;
/* XXX:TODO: Determine */
date_current = "Now";
/* Open socket as ANSI I/O for ease of use */
fp = fdopen(fd, "w+b");
if (fp == NULL) {
close(fd);
return;
}
path = filed_get_http_request(fp, path_b, sizeof(path_b));
if (path == NULL) {
/* XXX: TODO: Return error page */
fclose(fp);
return;
}
fileinfo = filed_open_file(path, &fileinfo_b);
if (fileinfo == NULL) {
/* XXX: TODO: Return error page */
} else {
fprintf(fp, "HTTP/1.1 200 OK\r\nDate: %s\r\nServer: filed\r\nLast-Modified: %s\r\nContent-Length: %llu\r\nContent-Type: %s\r\nConnection: close\r\n\r\n",
date_current,
fileinfo->lastmod,
(unsigned long long) fileinfo->len,
fileinfo->type
);
sendfile(fd, fileinfo->fd, NULL, fileinfo->len);
close(fileinfo->fd);
}
fclose(fp);
return;
}
static void *filed_worker_thread(void *arg_v) {
struct filed_worker_thread_args *arg;
struct sockaddr_in6 addr;
socklen_t addrlen;
int failure_count = 0, max_failure_count = MAX_FAILURE_COUNT;
int master_fd, fd;
/* Read arguments */
arg = arg_v;
master_fd = arg->fd;
while (1) {
/* Failure loop prevention */
if (failure_count > max_failure_count) {
break;
}
/* Accept a new client */
addrlen = sizeof(addr);
fd = accept(master_fd, (struct sockaddr *) &addr, &addrlen);
/*
* If we fail, make a note of it so we don't go into a loop of
* accept() failing
*/
if (fd < 0) {
failure_count++;
continue;
}
/* Reset failure count*/
failure_count = 0;
/* Handle socket */
filed_handle_client(fd);
}
/* XXX:TODO: Report error */
return(NULL);
}
static int filed_worker_threads_init(int fd, int thread_count) {
struct filed_worker_thread_args *arg;
pthread_t threadid;
int pthread_ret;
int i;
for (i = 0; i < thread_count; i++) {
arg = malloc(sizeof(*arg));
arg->fd = fd;
pthread_ret = pthread_create(&threadid, NULL, filed_worker_thread, arg);
if (pthread_ret != 0) {
return(-1);
}
}
return(0);
}
int main(int argc, char **argv) {
int port = PORT, thread_count = THREAD_COUNT;
const char *bind_addr = BIND_ADDR;
int fd;
/* Ignore */
argc = argc;
argv = argv;
/* Create listening socket */
fd = filed_listen(bind_addr, port);
if (fd < 0) {
perror("filed_listen");
return(1);
}
/* Become a daemon */
/* XXX:TODO: Become a daemon */
/* Initialize */
filed_init();
/* Create logging thread */
/* XXX:TODO: Check for errors */
filed_logging_thread_init();
/* Create worker threads */
/* XXX:TODO: Check for errors */
filed_worker_threads_init(fd, thread_count);
/* Wait for threads to exit */
/* XXX:TODO: Monitor thread usage */
while (1) {
sleep(60);
}
/* Return in failure */
return(2);
}