aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormutantturkey <crazycal00@gmail.com>2011-01-02 12:56:32 -0500
committermutantturkey <crazycal00@gmail.com>2011-01-02 12:56:32 -0500
commitb3c2fc344e8624f18b969d0839acf58879e3c06d (patch)
tree34acb9dc5ed6ee8d209a9f3773d6246da8364f14
parentabfa80bb6bea53e7a7bd092659bc8e773b11bd57 (diff)
no need for darkhttpd.c anymore
-rw-r--r--darkhttpd.c2703
1 files changed, 0 insertions, 2703 deletions
diff --git a/darkhttpd.c b/darkhttpd.c
deleted file mode 100644
index 902df8c..0000000
--- a/darkhttpd.c
+++ /dev/null
@@ -1,2703 +0,0 @@
-/* darkhttpd
- * copyright (c) 2003-2008 Emil Mikulic.
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the
- * above copyright notice and this permission notice appear in all
- * copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
- * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
- * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
- * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
- * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
- * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- * PERFORMANCE OF THIS SOFTWARE.
- */
-
-static const char
- pkgname[] = "darkhttpd/1.7",
- copyright[] = "copyright (c) 2003-2008 Emil Mikulic",
- rcsid[] = "$Id: darkhttpd.c 188 2008-11-04 08:53:22Z emil $";
-
-#ifndef DEBUG
-#define NDEBUG
-static const int debug = 0;
-#else
-static const int debug = 1;
-#endif
-
-#ifdef __linux
-#define _GNU_SOURCE /* for strsignal() and vasprintf() */
-#include <sys/sendfile.h>
-#endif
-
-#ifdef __sun__
-#include <sys/sendfile.h>
-#endif
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/resource.h>
-#include <sys/wait.h>
-#include <sys/param.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-#include <arpa/inet.h>
-#include <assert.h>
-#include <ctype.h>
-#include <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <grp.h>
-#include <pwd.h>
-#include <signal.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-
-#ifndef min
-#define min(a,b) ( ((a)<(b)) ? (a) : (b) )
-#endif
-
-#ifndef INADDR_NONE
-#define INADDR_NONE -1
-#endif
-
-#if defined(O_EXCL) && !defined(O_EXLOCK)
-#define O_EXLOCK O_EXCL
-#endif
-
-#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__linux)
-#include <err.h>
-#else
-/* err - prints "error: format: strerror(errno)" to stderr and exit()s with
- * the given code.
- */
-static void
-err(const int code, const char *format, ...)
-{
- va_list va;
-
- va_start(va, format);
- fprintf(stderr, "error: ");
- vfprintf(stderr, format, va);
- fprintf(stderr, ": %s\n", strerror(errno));
- va_end(va);
- exit(code);
-}
-
-/* errx - err without the strerror */
-static void
-errx(const int code, const char *format, ...)
-{
- va_list va;
-
- va_start(va, format);
- fprintf(stderr, "error: ");
- vfprintf(stderr, format, va);
- fprintf(stderr, "\n");
- va_end(va);
- exit(code);
-}
-
-/* warn - err without the exit */
-static void
-warn(const char *format, ...)
-{
- va_list va;
-
- va_start(va, format);
- fprintf(stderr, "warning: ");
- vfprintf(stderr, format, va);
- fprintf(stderr, ": %s\n", strerror(errno));
- va_end(va);
-}
-#endif
-
-/* ---------------------------------------------------------------------------
- * LIST_* macros taken from FreeBSD's src/sys/sys/queue.h,v 1.56
- * Copyright (c) 1991, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * Under a BSD license.
- */
-#define LIST_HEAD(name, type) \
-struct name { \
- struct type *lh_first; /* first element */ \
-}
-
-#define LIST_HEAD_INITIALIZER(head) \
- { NULL }
-
-#define LIST_ENTRY(type) \
-struct { \
- struct type *le_next; /* next element */ \
- struct type **le_prev; /* address of previous next element */ \
-}
-
-#define LIST_FIRST(head) ((head)->lh_first)
-
-#define LIST_FOREACH(var, head, field) \
- for ((var) = LIST_FIRST((head)); \
- (var); \
- (var) = LIST_NEXT((var), field))
-
-#define LIST_FOREACH_SAFE(var, head, field, tvar) \
- for ((var) = LIST_FIRST((head)); \
- (var) && ((tvar) = LIST_NEXT((var), field), 1); \
- (var) = (tvar))
-
-#define LIST_INIT(head) do { \
- LIST_FIRST((head)) = NULL; \
-} while (0)
-
-#define LIST_INSERT_HEAD(head, elm, field) do { \
- if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \
- LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\
- LIST_FIRST((head)) = (elm); \
- (elm)->field.le_prev = &LIST_FIRST((head)); \
-} while (0)
-
-#define LIST_NEXT(elm, field) ((elm)->field.le_next)
-
-#define LIST_REMOVE(elm, field) do { \
- if (LIST_NEXT((elm), field) != NULL) \
- LIST_NEXT((elm), field)->field.le_prev = \
- (elm)->field.le_prev; \
- *(elm)->field.le_prev = LIST_NEXT((elm), field); \
-} while (0)
-/* ------------------------------------------------------------------------ */
-
-
-
-LIST_HEAD(conn_list_head, connection) connlist =
- LIST_HEAD_INITIALIZER(conn_list_head);
-
-struct connection
-{
- LIST_ENTRY(connection) entries;
-
- int socket;
- in_addr_t client;
- time_t last_active;
- enum {
- RECV_REQUEST, /* receiving request */
- SEND_HEADER, /* sending generated header */
- SEND_REPLY, /* sending reply */
- DONE /* connection closed, need to remove from queue */
- } state;
-
- /* char request[request_length+1] is null-terminated */
- char *request;
- size_t request_length;
-
- /* request fields */
- char *method, *uri, *referer, *user_agent;
- size_t range_begin, range_end;
- int range_begin_given, range_end_given;
-
- char *header;
- size_t header_length, header_sent;
- int header_dont_free, header_only, http_code, conn_close;
-
- enum { REPLY_GENERATED, REPLY_FROMFILE } reply_type;
- char *reply;
- int reply_dont_free;
- int reply_fd;
- size_t reply_start, reply_length, reply_sent;
-
- unsigned int total_sent; /* header + body = total, for logging */
-};
-
-
-
-struct mime_mapping
-{
- char *extension, *mimetype;
-};
-
-struct mime_mapping *mime_map = NULL;
-size_t mime_map_size = 0;
-size_t longest_ext = 0;
-
-
-
-/* If a connection is idle for idletime seconds or more, it gets closed and
- * removed from the connlist. Set to 0 to remove the timeout
- * functionality.
- */
-static int idletime = 60;
-static char *keep_alive_field = NULL;
-
-/* Time is cached in the event loop to avoid making an excessive number of
- * gettimeofday() calls.
- */
-static time_t now;
-
-/* To prevent a malformed request from eating up too much memory, die once the
- * request exceeds this many bytes:
- */
-#define MAX_REQUEST_LENGTH 4000
-
-
-
-/* Defaults can be overridden on the command-line */
-static in_addr_t bindaddr = INADDR_ANY;
-static unsigned short bindport = 80;
-static int max_connections = -1; /* kern.ipc.somaxconn */
-static const char *index_name = "index.html";
-
-static int sockin = -1; /* socket to accept connections from */
-static char *wwwroot = NULL; /* a path name */
-static char *logfile_name = NULL; /* NULL = no logging */
-static FILE *logfile = NULL;
-static char *pidfile_name = NULL; /* NULL = no pidfile */
-static int want_chroot = 0, want_daemon = 0, want_accf = 0;
-static uint32_t num_requests = 0;
-static uint64_t total_in = 0, total_out = 0;
-
-static int running = 1; /* signal handler sets this to false */
-
-#define INVALID_UID ((uid_t) -1)
-#define INVALID_GID ((gid_t) -1)
-
-static uid_t drop_uid = INVALID_UID;
-static gid_t drop_gid = INVALID_GID;
-
-/* Default mimetype mappings - make sure this array is NULL terminated. */
-static const char *default_extension_map[] = {
- "application/ogg" " ogg",
- "application/pdf" " pdf",
- "application/xml" " xsl xml",
- "application/xml-dtd" " dtd",
- "application/xslt+xml" " xslt",
- "application/zip" " zip",
- "audio/mpeg" " mp2 mp3 mpga",
- "image/gif" " gif",
- "image/jpeg" " jpeg jpe jpg",
- "image/png" " png",
- "text/css" " css",
- "text/html" " html htm",
- "text/javascript" " js",
- "text/plain" " txt asc",
- "video/mpeg" " mpeg mpe mpg",
- "video/quicktime" " qt mov",
- "video/x-msvideo" " avi",
- NULL
-};
-
-static const char default_mimetype[] = "application/octet-stream";
-
-
-
-/* Connection or Keep-Alive field, depending on conn_close. */
-#define keep_alive(conn) ((conn)->conn_close ? \
- "Connection: close\r\n" : keep_alive_field)
-
-/* Prototypes. */
-static void poll_recv_request(struct connection *conn);
-static void poll_send_header(struct connection *conn);
-static void poll_send_reply(struct connection *conn);
-
-
-
-/* ---------------------------------------------------------------------------
- * close that dies on error.
- */
-static void xclose(const int fd)
-{
- if (close(fd) == -1) err(1, "close()");
-}
-
-
-
-/* ---------------------------------------------------------------------------
- * malloc that errx()s if it can't allocate.
- */
-static void *xmalloc(const size_t size)
-{
- void *ptr = malloc(size);
- if (ptr == NULL) errx(1, "can't allocate %u bytes", size);
- return ptr;
-}
-
-
-
-/* ---------------------------------------------------------------------------
- * realloc() that errx()s if it can't allocate.
- */
-static void *xrealloc(void *original, const size_t size)
-{
- void *ptr = realloc(original, size);
- if (ptr == NULL) errx(1, "can't reallocate %u bytes", size);
- return ptr;
-}
-
-
-
-/* ---------------------------------------------------------------------------
- * strdup() that errx()s if it can't allocate. Do this by hand since strdup()
- * isn't C89.
- */
-static char *xstrdup(const char *src)
-{
- size_t len = strlen(src) + 1;
- char *dest = xmalloc(len);
- memcpy(dest, src, len);
- return dest;
-}
-
-
-
-#ifdef __sun /* unimpressed by Solaris */
-static int vasprintf(char **strp, const char *fmt, va_list ap)
-{
- char tmp;
- int result = vsnprintf(&tmp, 1, fmt, ap);
- *strp = xmalloc(result+1);
- result = vsnprintf(*strp, result+1, fmt, ap);
- return result;
-}
-#endif
-
-
-
-/* ---------------------------------------------------------------------------
- * vasprintf() that errx()s if it fails.
- */
-static unsigned int xvasprintf(char **ret, const char *format, va_list ap)
-{
- int len = vasprintf(ret, format, ap);
- if (ret == NULL || len == -1) errx(1, "out of memory in vasprintf()");
- return (unsigned int)len;
-}
-
-
-
-/* ---------------------------------------------------------------------------
- * asprintf() that errx()s if it fails.
- */
-static unsigned int xasprintf(char **ret, const char *format, ...)
-{
- va_list va;
- unsigned int len;
-
- va_start(va, format);
- len = xvasprintf(ret, format, va);
- va_end(va);
- return len;
-}
-
-
-
-/* ---------------------------------------------------------------------------
- * Append buffer code. A somewhat efficient string buffer with pool-based
- * reallocation.
- */
-#define APBUF_INIT 4096
-#define APBUF_GROW APBUF_INIT
-struct apbuf
-{
- size_t length, pool;
- char *str;
-};
-
-
-
-static struct apbuf *make_apbuf(void)
-{
- struct apbuf *buf = xmalloc(sizeof(struct apbuf));
- buf->length = 0;
- buf->pool = APBUF_INIT;
- buf->str = xmalloc(buf->pool);
- return buf;
-}
-
-
-
-static void appendl(struct apbuf *buf, const char *s, const size_t len)
-{
- if (buf->pool < buf->length + len)
- {
- /* pool has dried up */
- while (buf->pool < buf->length + len) buf->pool += APBUF_GROW;
- buf->str = xrealloc(buf->str, buf->pool);
- }
-
- memcpy(buf->str + buf->length, s, len);
- buf->length += len;
-}
-
-
-
-#ifdef __GNUC__
-#define append(buf, s) appendl(buf, s, \
- (__builtin_constant_p(s) ? sizeof(s)-1 : strlen(s)) )
-#else
-static void append(struct apbuf *buf, const char *s)
-{
- appendl(buf, s, strlen(s));
-}
-#endif
-
-
-
-static void appendf(struct apbuf *buf, const char *format, ...)
-{
- char *tmp;
- va_list va;
- size_t len;
-
- va_start(va, format);
- len = xvasprintf(&tmp, format, va);
- va_end(va);
-
- appendl(buf, tmp, len);
- free(tmp);
-}
-
-
-
-/* ---------------------------------------------------------------------------
- * Make the specified socket non-blocking.
- */
-static void
-nonblock_socket(const int sock)
-{
- int flags = fcntl(sock, F_GETFL, NULL);
-
- if (flags == -1)
- err(1, "fcntl(F_GETFL)");
- flags |= O_NONBLOCK;
- if (fcntl(sock, F_SETFL, flags) == -1)
- err(1, "fcntl() to set O_NONBLOCK");
-}
-
-
-
-/* ---------------------------------------------------------------------------
- * Split string out of src with range [left:right-1]
- */
-static char *split_string(const char *src,
- const size_t left, const size_t right)
-{
- char *dest;
- assert(left <= right);
- assert(left < strlen(src)); /* [left means must be smaller */
- assert(right <= strlen(src)); /* right) means can be equal or smaller */
-
- dest = xmalloc(right - left + 1);
- memcpy(dest, src+left, right-left);
- dest[right-left] = '\0';
- return dest;
-}
-
-
-
-/* ---------------------------------------------------------------------------
- * Consolidate slashes in-place by shifting parts of the string over repeated
- * slashes.
- */
-static void consolidate_slashes(char *s)
-{
- size_t left = 0, right = 0;
- int saw_slash = 0;
-
- assert(s != NULL);
-
- while (s[right] != '\0')
- {
- if (saw_slash)
- {
- if (s[right] == '/') right++;
- else
- {
- saw_slash = 0;
- s[left++] = s[right++];
- }
- }
- else
- {
- if (s[right] == '/') saw_slash++;
- s[left++] = s[right++];
- }
- }
- s[left] = '\0';
-}
-
-
-
-/* ---------------------------------------------------------------------------
- * Resolve /./ and /../ in a URI, in-place. Returns NULL if the URI is
- * invalid/unsafe, or the original buffer if successful.
- */
-static char *make_safe_uri(char *uri)
-{
- struct {
- char *start;
- size_t len;
- } *chunks;
- unsigned int num_slashes, num_chunks;
- size_t urilen, i, j, pos;
- int ends_in_slash;
-
- assert(uri != NULL);
- if (uri[0] != '/') return NULL;
- consolidate_slashes(uri);
- urilen = strlen(uri);
- if (urilen > 0)
- ends_in_slash = (uri[urilen-1] == '/');
- else
- ends_in_slash = 1;
-
- /* count the slashes */
- for (i=0, num_slashes=0; i<urilen; i++)
- if (uri[i] == '/') num_slashes++;
-
- /* make an array for the URI elements */
- chunks = xmalloc(sizeof(*chunks) * num_slashes);
-
- /* split by slashes and build chunks array */
- num_chunks = 0;
- for (i=1; i<urilen;) {
- /* look for the next slash */
- for (j=i; j<urilen && uri[j] != '/'; j++)
- ;
-
- /* process uri[i,j) */
- if ((j == i+1) && (uri[i] == '.'))
- /* "." */;
- else if ((j == i+2) && (uri[i] == '.') && (uri[i+1] == '.')) {
- /* ".." */
- if (num_chunks == 0) {
- /* unsafe string so free chunks */
- free(chunks);
- return (NULL);
- } else
- num_chunks--;
- } else {
- chunks[num_chunks].start = uri+i;
- chunks[num_chunks].len = j-i;
- num_chunks++;
- }
-
- i = j + 1; /* uri[j] is a slash - move along one */
- }
-
- /* reassemble in-place */
- pos = 0;
- for (i=0; i<num_chunks; i++) {
- assert(pos <= urilen);
- uri[pos++] = '/';
-
- assert(pos + chunks[i].len <= urilen);
- assert(uri + pos <= chunks[i].start);
-
- if (uri+pos < chunks[i].start)
- memmove(uri+pos, chunks[i].start, chunks[i].len);
- pos += chunks[i].len;
- }
- free(chunks);
-
- if ((num_chunks == 0) || ends_in_slash) uri[pos++] = '/';
- assert(pos <= urilen);
- uri[pos] = '\0';
- return uri;
-}
-
-
-
-/* ---------------------------------------------------------------------------
- * Associates an extension with a mimetype in the mime_map. Entries are in
- * unsorted order. Makes copies of extension and mimetype strings.
- */
-static void add_mime_mapping(const char *extension, const char *mimetype)
-{
- size_t i;
- assert(strlen(extension) > 0);
- assert(strlen(mimetype) > 0);
-
- /* update longest_ext */
- i = strlen(extension);
- if (i > longest_ext) longest_ext = i;
-
- /* look through list and replace an existing entry if possible */
- for (i=0; i<mime_map_size; i++)
- if (strcmp(mime_map[i].extension, extension) == 0)
- {
- free(mime_map[i].mimetype);
- mime_map[i].mimetype = xstrdup(mimetype);
- return;
- }
-
- /* no replacement - add a new entry */
- mime_map_size++;
- mime_map = xrealloc(mime_map,
- sizeof(struct mime_mapping) * mime_map_size);
- mime_map[mime_map_size-1].extension = xstrdup(extension);
- mime_map[mime_map_size-1].mimetype = xstrdup(mimetype);
-}
-
-
-
-/* ---------------------------------------------------------------------------
- * qsort() the mime_map. The map must be sorted before it can be searched
- * through.
- */
-static int mime_mapping_cmp(const void *a, const void *b)
-{
- return strcmp( ((const struct mime_mapping *)a)->extension,
- ((const struct mime_mapping *)b)->extension );
-}
-
-static void sort_mime_map(void)
-{
- qsort(mime_map, mime_map_size, sizeof(struct mime_mapping),
- mime_mapping_cmp);
-}
-
-
-
-/* ---------------------------------------------------------------------------
- * Parses a mime.types line and adds the parsed data to the mime_map.
- */
-static void parse_mimetype_line(const char *line)
-{
- unsigned int pad, bound1, lbound, rbound;
-
- /* parse mimetype */
- for (pad=0; line[pad] == ' ' || line[pad] == '\t'; pad++);
- if (line[pad] == '\0' || /* empty line */
- line[pad] == '#') /* comment */
- return;
-
- for (bound1=pad+1;
- line[bound1] != ' ' &&
- line[bound1] != '\t';
- bound1++)
- {
- if (line[bound1] == '\0') return; /* malformed line */
- }
-
- lbound = bound1;
- for (;;)
- {
- char *mimetype, *extension;
-
- /* find beginning of extension */
- for (; line[lbound] == ' ' || line[lbound] == '\t'; lbound++);
- if (line[lbound] == '\0') return; /* end of line */
-
- /* find end of extension */
- for (rbound = lbound;
- line[rbound] != ' ' &&
- line[rbound] != '\t' &&
- line[rbound] != '\0';
- rbound++);
-
- mimetype = split_string(line, pad, bound1);
- extension = split_string(line, lbound, rbound);
- add_mime_mapping(extension, mimetype);
- free(mimetype);
- free(extension);
-
- if (line[rbound] == '\0') return; /* end of line */
- else lbound = rbound + 1;
- }
-}
-
-
-
-/* ---------------------------------------------------------------------------
- * Adds contents of default_extension_map[] to mime_map list. The array must
- * be NULL terminated.
- */
-static void parse_default_extension_map(void)
-{
- int i;
-
- for (i=0; default_extension_map[i] != NULL; i++)
- parse_mimetype_line(default_extension_map[i]);
-}
-
-
-
-/* ---------------------------------------------------------------------------
- * read_line - read a line from [fp], return its contents in a
- * dynamically allocated buffer, not including the line ending.
- *
- * Handles CR, CRLF and LF line endings, as well as NOEOL correctly. If
- * already at EOF, returns NULL. Will err() or errx() in case of
- * unexpected file error or running out of memory.
- */
-static char *read_line(FILE *fp)
-{
- char *buf;
- long startpos, endpos;
- size_t linelen, numread;
- int c;
-
- startpos = ftell(fp);
- if (startpos == -1) err(1, "ftell()");
-
- /* find end of line (or file) */
- linelen = 0;
- for (;;)
- {
- c = fgetc(fp);
- if (c == EOF || c == (int)'\n' || c == (int)'\r') break;
- linelen++;
- }
-
- /* return NULL on EOF (and empty line) */
- if (linelen == 0 && c == EOF) return NULL;
-
- endpos = ftell(fp);
- if (endpos == -1) err(1, "ftell()");
-
- /* skip CRLF */
- if (c == (int)'\r' && fgetc(fp) == (int)'\n') endpos++;
-
- buf = (char*)xmalloc(linelen + 1);
-
- /* rewind file to where the line stared and load the line */
- if (fseek(fp, startpos, SEEK_SET) == -1) err(1, "fseek()");
- numread = fread(buf, 1, linelen, fp);
- if (numread != linelen)
- errx(1, "fread() %u bytes, expecting %u bytes", numread, linelen);
-
- /* terminate buffer */
- buf[linelen] = 0;
-
- /* advance file pointer over the endline */
- if (fseek(fp, endpos, SEEK_SET) == -1) err(1, "fseek()");
-
- return buf;
-}
-
-
-
-/* ---------------------------------------------------------------------------
- * Removes the ending newline in a string, if there is one.
- */
-static void chomp(char *str)
-{
- size_t len = strlen(str);
- if (len == 0) return;
- if (str[len-1] == '\n') str[len-1] = '\0';
-}
-
-
-
-/* ---------------------------------------------------------------------------
- * Adds contents of specified file to mime_map list.
- */
-static void parse_extension_map_file(const char *filename)
-{
- char *buf;
- FILE *fp = fopen(filename, "rb");
- if (fp == NULL) err(1, "fopen(\"%s\")", filename);
-
- while ( (buf = read_line(fp)) != NULL )
- {
- chomp(buf);
- parse_mimetype_line(buf);
- free(buf);
- }
-
- fclose(fp);
-}
-
-
-
-/* ---------------------------------------------------------------------------
- * Uses the mime_map to determine a Content-Type: for a requested URI. This
- * bsearch()es mime_map, so make sure it's sorted first.
- */
-static int mime_mapping_cmp_str(const void *a, const void *b)
-{
- return strcmp(
- (const char *)a,
- ((const struct mime_mapping *)b)->extension
- );
-}
-
-static const char *uri_content_type(const char *uri)
-{
- size_t period, urilen = strlen(uri);
-
- for (period=urilen-1;
- period > 0 &&
- uri[period] != '.' &&
- (urilen-period-1) <= longest_ext;
- period--)
- ;
-
- if (uri[period] == '.')
- {
- struct mime_mapping *result =
- bsearch((uri+period+1), mime_map, mime_map_size,
- sizeof(struct mime_mapping), mime_mapping_cmp_str);
-
- if (result != NULL)
- {
- assert(strcmp(uri+period+1, result->extension) == 0);
- return result->mimetype;
- }
- }
- /* else no period found in the string */
- return default_mimetype;
-}
-
-
-
-/* ---------------------------------------------------------------------------
- * Initialize the sockin global. This is the socket that we accept
- * connections from.
- */
-static void init_sockin(void)
-{
- struct sockaddr_in addrin;
- int sockopt;
-
- /* create incoming socket */
- sockin = socket(PF_INET, SOCK_STREAM, 0);
- if (sockin == -1) err(1, "socket()");
-
- /* reuse address */
- sockopt = 1;
- if (setsockopt(sockin, SOL_SOCKET, SO_REUSEADDR,
- &sockopt, sizeof(sockopt)) == -1)
- err(1, "setsockopt(SO_REUSEADDR)");
-
-#if 0
- /* disable Nagle since we buffer everything ourselves */
- sockopt = 1;
- if (setsockopt(sockin, IPPROTO_TCP, TCP_NODELAY,
- &sockopt, sizeof(sockopt)) == -1)
- err(1, "setsockopt(TCP_NODELAY)");
-#endif
-
-#ifdef TORTURE
- /* torture: cripple the kernel-side send buffer so we can only squeeze out
- * one byte at a time (this is for debugging)
- */
- sockopt = 1;
- if (setsockopt(sockin, SOL_SOCKET, SO_SNDBUF,
- &sockopt, sizeof(sockopt)) == -1)
- err(1, "setsockopt(SO_SNDBUF)");
-#endif
-
- /* bind socket */
- addrin.sin_family = (u_char)PF_INET;
- addrin.sin_port = htons(bindport);
- addrin.sin_addr.s_addr = bindaddr;
- memset(&(addrin.sin_zero), 0, 8);
- if (bind(sockin, (struct sockaddr *)&addrin,
- sizeof(struct sockaddr)) == -1)
- err(1, "bind(port %u)", bindport);
-
- printf("listening on %s:%u\n", inet_ntoa(addrin.sin_addr), bindport);
-
- /* listen on socket */
- if (listen(sockin, max_connections) == -1)
- err(1, "listen()");
-
- /* enable acceptfilter (this is only available on FreeBSD) */
- if (want_accf)
- {
-#if defined(__FreeBSD__)
- struct accept_filter_arg filt = {"httpready", ""};
- if (setsockopt(sockin, SOL_SOCKET, SO_ACCEPTFILTER,
- &filt, sizeof(filt)) == -1)
- fprintf(stderr, "cannot enable acceptfilter: %s\n",
- strerror(errno));
- else
- printf("enabled acceptfilter\n");
-#else
- printf("this platform doesn't support acceptfilter\n");
-#endif
- }
-}
-
-
-
-/* ---------------------------------------------------------------------------
- * Prints a usage statement.
- */
-static void usage(void)
-{
- printf("\n"
- "usage: darkhttpd /path/to/wwwroot [options]\n\n"
- "options:\n\n");
- printf(
- "\t--port number (default: %u)\n" /* bindport */
- "\t\tSpecifies which port to listen on for connections.\n"
- "\n", bindport);
- printf(
- "\t--addr ip (default: all)\n"
- "\t\tIf multiple interfaces are present, specifies\n"
- "\t\twhich one to bind the listening port to.\n"
- "\n");
- printf(
- "\t--maxconn number (default: system maximum)\n"
- "\t\tSpecifies how many concurrent connections to accept.\n"
- "\n");
- printf(
- "\t--log filename (default: no logging)\n"
- "\t\tSpecifies which file to append the request log to.\n"
- "\n");
- printf(
- "\t--chroot (default: don't chroot)\n"
- "\t\tLocks server into wwwroot directory for added security.\n"
- "\n");
- printf(
- "\t--daemon (default: don't daemonize)\n"
- "\t\tDetach from the controlling terminal and run in the background.\n"
- "\n");
- printf(
- "\t--index filename (default: %s)\n" /* index_name */
- "\t\tDefault file to serve when a directory is requested.\n"
- "\n", index_name);
- printf(
- "\t--mimetypes filename (optional)\n"
- "\t\tParses specified file for extension-MIME associations.\n"
- "\n");
- printf(
- "\t--uid uid/uname, --gid gid/gname (default: don't privdrop)\n"
- "\t\tDrops privileges to given uid:gid after initialization.\n"
- "\n");
- printf(
- "\t--pidfile filename (default: no pidfile)\n"
- "\t\tWrite PID to the specified file. Note that if you are\n"
- "\t\tusing --chroot, then the pidfile must be relative to,\n"
- "\t\tand inside the wwwroot."
- "\n");
- printf(
- "\t--help \n"
- "\t\tprints this dialogue.\n"
- "\n");
-#ifdef __FreeBSD__
- printf(
- "\t--accf (default: don't use acceptfilter)\n"
- "\t\tUse acceptfilter. Needs the accf_http module loaded.\n"
- "\n");
-#endif
-}
-
-
-
-/* ---------------------------------------------------------------------------
- * Returns 1 if string is a number, 0 otherwise. Set num to NULL if
- * disinterested in value.
- */
-static int str_to_num(const char *str, int *num)
-{
- char *endptr;
- long l = strtol(str, &endptr, 10);
- if (*endptr != '\0') return 0;
-
- if (num != NULL) *num = (int)l;
- return 1;
-}
-
-
-
-/* ---------------------------------------------------------------------------
- * Parses commandline options.
- */
-static void parse_commandline(const int argc, char *argv[])
-{
- int i;
-
-
- if(argc == 2 && strcmp(argv[1], "--help") == 0) {
- usage();
- exit(EXIT_FAILURE);
- }
- if (argc == 2 && strcmp(argv[1], "--version") == 0) {
- printf("%s \n", rcsid);
- exit(EXIT_FAILURE);
- }
-
- if (argc < 2) {
- wwwroot = getcwd(NULL, PATH_MAX);
- }
- else {
- wwwroot = xstrdup(argv[1]);
- }
-
- puts(wwwroot);
-
- /* Strip ending slash. */
- if (wwwroot[strlen(wwwroot)-1] == '/') wwwroot[strlen(wwwroot)-1] = '\0';
-
- /* walk through the remainder of the arguments (if any) */
- for (i=2; i<argc; i++)
- {
- if (strcmp(argv[i], "--port") == 0)
- {
- if (++i >= argc) errx(1, "missing number after --port");
- bindport = (unsigned short)atoi(argv[i]);
- }
- else if (strcmp(argv[i], "--addr") == 0)
- {
- if (++i >= argc) errx(1, "missing ip after --addr");
- bindaddr = inet_addr(argv[i]);
- if (bindaddr == (in_addr_t)INADDR_NONE)
- errx(1, "malformed --addr argument");
- }
- else if (strcmp(argv[i], "--maxconn") == 0)
- {
- if (++i >= argc) errx(1, "missing number after --maxconn");
- max_connections = atoi(argv[i]);
- }
- else if (strcmp(argv[i], "--log") == 0)
- {
- if (++i >= argc) errx(1, "missing filename after --log");
- logfile_name = argv[i];
- }
- else if (strcmp(argv[i], "--chroot") == 0)
- {
- want_chroot = 1;
- }
- else if (strcmp(argv[i], "--daemon") == 0)
- {
- want_daemon = 1;
- }
- else if (strcmp(argv[i], "--index") == 0)
- {
- if (++i >= argc) errx(1, "missing filename after --index");
- index_name = argv[i];
- }
- else if (strcmp(argv[i], "--mimetypes") == 0)
- {
- if (++i >= argc) errx(1, "missing filename after --mimetypes");
- parse_extension_map_file(argv[i]);
- }
- else if (strcmp(argv[i], "--uid") == 0)
- {
- struct passwd *p;
- int num;
- if (++i >= argc) errx(1, "missing uid after --uid");
- p = getpwnam(argv[i]);
- if ((p == NULL) && (str_to_num(argv[i], &num)))
- p = getpwuid( (uid_t)num );
-
- if (p == NULL) errx(1, "no such uid: `%s'", argv[i]);
- drop_uid = p->pw_uid;
- }
- else if (strcmp(argv[i], "--gid") == 0)
- {
- struct group *g;
- int num;
- if (++i >= argc) errx(1, "missing gid after --gid");
- g = getgrnam(argv[i]);
- if ((g == NULL) && (str_to_num(argv[i], &num)))
- g = getgrgid( (gid_t)num );
-
- if (g == NULL) errx(1, "no such gid: `%s'", argv[i]);
- drop_gid = g->gr_gid;
- }
- else if (strcmp(argv[i], "--pidfile") == 0)
- {
- if (++i >= argc)
- errx(1, "missing filename after --pidfile");
- pidfile_name = argv[i];
- }
- else if (strcmp(argv[i], "--accf") == 0)
- {
- want_accf = 1;
- }
- else
- errx(1, "unknown argument `%s'", argv[i]);
- }
-}
-
-
-
-/* ---------------------------------------------------------------------------
- * Allocate and initialize an empty connection.
- */
-static struct connection *new_connection(void)
-{
- struct connection *conn = xmalloc(sizeof(struct connection));
-
- conn->socket = -1;
- conn->client = INADDR_ANY;
- conn->last_active = now;
- conn->request = NULL;
- conn->request_length = 0;
- conn->method = NULL;
- conn->uri = NULL;
- conn->referer = NULL;
- conn->user_agent = NULL;
- conn->range_begin = 0;
- conn->range_end = 0;
- conn->range_begin_given = 0;
- conn->range_end_given = 0;
- conn->header = NULL;
- conn->header_length = 0;
- conn->header_sent = 0;
- conn->header_dont_free = 0;
- conn->header_only = 0;
- conn->http_code = 0;
- conn->conn_close = 1;
- conn->reply = NULL;
- conn->reply_dont_free = 0;
- conn->reply_fd = -1;
- conn->reply_start = 0;
- conn->reply_length = 0;
- conn->reply_sent = 0;
- conn->total_sent = 0;
-
- /* Make it harmless so it gets garbage-collected if it should, for some
- * reason, fail to be correctly filled out.
- */
- conn->state = DONE;
-
- return conn;
-}
-
-
-
-/* ---------------------------------------------------------------------------
- * Accept a connection from sockin and add it to the connection queue.
- */
-static void accept_connection(void)
-{
- struct sockaddr_in addrin;
- socklen_t sin_size;
- struct connection *conn;
-
- /* allocate and initialise struct connection */
- conn = new_connection();
-
- sin_size = sizeof(addrin);
- memset(&addrin, 0, sin_size);
- conn->socket = accept(sockin, (struct sockaddr *)&addrin,
- &sin_size);
- if (conn->socket == -1) err(1, "accept()");
-
- nonblock_socket(conn->socket);
-
- conn->state = RECV_REQUEST;
- conn->client = addrin.sin_addr.s_addr;
- LIST_INSERT_HEAD(&connlist, conn, entries);
-
- if (debug) printf("accepted connection from %s:%u\n",
- inet_ntoa(addrin.sin_addr),
- ntohs(addrin.sin_port) );
-
- /* try to read straight away rather than going through another iteration
- * of the select() loop.
- */
- poll_recv_request(conn);
-}
-
-
-
-static void log_connection(const struct connection *conn);
-
-/* ---------------------------------------------------------------------------
- * Log a connection, then cleanly deallocate its internals.
- */
-static void free_connection(struct connection *conn)
-{
- if (debug) printf("free_connection(%d)\n", conn->socket);
- log_connection(conn);
- if (conn->socket != -1) xclose(conn->socket);
- if (conn->request != NULL) free(conn->request);
- if (conn->method != NULL) free(conn->method);
- if (conn->uri != NULL) free(conn->uri);
- if (conn->referer != NULL) free(conn->referer);
- if (conn->user_agent != NULL) free(conn->user_agent);
- if (conn->header != NULL && !conn->header_dont_free)
- free(conn->header);
- if (conn->reply != NULL && !conn->reply_dont_free) free(conn->reply);
- if (conn->reply_fd != -1) xclose(conn->reply_fd);
-}
-
-
-
-/* ---------------------------------------------------------------------------
- * Recycle a finished connection for HTTP/1.1 Keep-Alive.
- */
-static void recycle_connection(struct connection *conn)
-{
- int socket_tmp = conn->socket;
- if (debug) printf("recycle_connection(%d)\n", socket_tmp);
- conn->socket = -1; /* so free_connection() doesn't close it */
- free_connection(conn);
- conn->socket = socket_tmp;
-
- /* don't reset conn->client */
- conn->request = NULL;
- conn->request_length = 0;
- conn->method = NULL;
- conn->uri = NULL;
- conn->referer = NULL;
- conn->user_agent = NULL;
- conn->range_begin = 0;
- conn->range_end = 0;
- conn->range_begin_given = 0;
- conn->range_end_given = 0;
- conn->header = NULL;
- conn->header_length = 0;
- conn->header_sent = 0;
- conn->header_dont_free = 0;
- conn->header_only = 0;
- conn->http_code = 0;
- conn->conn_close = 1;
- conn->reply = NULL;
- conn->reply_dont_free = 0;
- conn->reply_fd = -1;
- conn->reply_start = 0;
- conn->reply_length = 0;
- conn->reply_sent = 0;
- conn->total_sent = 0;
-
- conn->state = RECV_REQUEST; /* ready for another */
-}
-
-
-
-/* ---------------------------------------------------------------------------
- * Uppercasify all characters in a string of given length.
- */
-static void strntoupper(char *str, const size_t length)
-{
- size_t i;
- for (i=0; i<length; i++)
- str[i] = toupper(str[i]);
-}
-
-
-
-/* ---------------------------------------------------------------------------
- * If a connection has been idle for more than idletime seconds, it will be
- * marked as DONE and killed off in httpd_poll()
- */
-static void poll_check_timeout(struct connection *conn)
-{
- if (idletime > 0) /* optimised away by compiler */
- {
- if (now - conn->last_active >= idletime)
- {
- if (debug) printf("poll_check_timeout(%d) caused closure\n",
- conn->socket);
- conn->conn_close = 1;
- conn->state = DONE;
- }
- }
-}
-
-
-
-/* ---------------------------------------------------------------------------
- * Format [when] as an RFC1123 date, stored in the specified buffer. The same
- * buffer is returned for convenience.
- */
-#define DATE_LEN 30 /* strlen("Fri, 28 Feb 2003 00:02:08 GMT")+1 */
-static char *rfc1123_date(char *dest, const time_t when)
-{
- time_t when_copy = when;
- if (strftime(dest, DATE_LEN,
- "%a, %d %b %Y %H:%M:%S GMT", gmtime(&when_copy) ) == 0)
- errx(1, "strftime() failed [%s]", dest);
- return dest;
-}
-
-
-
-/* ---------------------------------------------------------------------------
- * Decode URL by converting %XX (where XX are hexadecimal digits) to the
- * character it represents. Don't forget to free the return value.
- */
-static char *urldecode(const char *url)
-{
- size_t i, len = strlen(url);
- char *out = xmalloc(len+1);
- int pos;
-
- for (i=0, pos=0; i<len; i++)
- {
- if (url[i] == '%' && i+2 < len &&
- isxdigit(url[i+1]) && isxdigit(url[i+2]))
- {
- /* decode %XX */
- #define HEX_TO_DIGIT(hex) ( \
- ((hex) >= 'A' && (hex) <= 'F') ? ((hex)-'A'+10): \
- ((hex) >= 'a' && (hex) <= 'f') ? ((hex)-'a'+10): \
- ((hex)-'0') )
-
- out[pos++] = HEX_TO_DIGIT(url[i+1]) * 16 +
- HEX_TO_DIGIT(url[i+2]);
- i += 2;
-
- #undef HEX_TO_DIGIT
- }
- else
- {
- /* straight copy */
- out[pos++] = url[i];
- }
- }
- out[pos] = '\0';
- return (out);
-}
-
-
-
-/* ---------------------------------------------------------------------------
- * A default reply for any (erroneous) occasion.
- */
-static void default_reply(struct connection *conn,
- const int errcode, const char *errname, const char *format, ...)
-{
- char *reason, date[DATE_LEN];
- va_list va;
-
- va_start(va, format);
- xvasprintf(&reason, format, va);
- va_end(va);
-
- /* Only really need to calculate the date once. */
- rfc1123_date(date, now);
-
- conn->reply_length = xasprintf(&(conn->reply),
- "<html><head><title>%d %s</title></head><body>\n"
- "<h1>%s</h1>\n" /* errname */
- "%s\n" /* reason */
- "<hr>\n"
- "Generated by %s on %s\n"
- "</body></html>\n",
- errcode, errname, errname, reason, pkgname, date);
- free(reason);
-
- conn->header_length = xasprintf(&(conn->header),
- "HTTP/1.1 %d %s\r\n"
- "Date: %s\r\n"
- "Server: %s\r\n"
- "%s" /* keep-alive */
- "Content-Length: %d\r\n"
- "Content-Type: text/html\r\n"
- "\r\n",
- errcode, errname, date, pkgname, keep_alive(conn),
- conn->reply_length);
-
- conn->reply_type = REPLY_GENERATED;
- conn->http_code = errcode;
-}
-
-
-
-/* ---------------------------------------------------------------------------
- * Redirection.
- */
-static void redirect(struct connection *conn, const char *format, ...)
-{
- char *where, date[DATE_LEN];
- va_list va;
-
- va_start(va, format);
- xvasprintf(&where, format, va);
- va_end(va);
-
- /* Only really need to calculate the date once. */
- rfc1123_date(date, now);
-
- conn->reply_length = xasprintf(&(conn->reply),
- "<html><head><title>301 Moved Permanently</title></head><body>\n"
- "<h1>Moved Permanently</h1>\n"
- "Moved to: <a href=\"%s\">%s</a>\n" /* where x 2 */
- "<hr>\n"
- "Generated by %s on %s\n"
- "</body></html>\n",
- where, where, pkgname, date);
-
- conn->header_length = xasprintf(&(conn->header),
- "HTTP/1.1 301 Moved Permanently\r\n"
- "Date: %s\r\n"
- "Server: %s\r\n"
- "Location: %s\r\n"
- "%s" /* keep-alive */
- "Content-Length: %d\r\n"
- "Content-Type: text/html\r\n"
- "\r\n",
- date, pkgname, where, keep_alive(conn), conn->reply_length);
-
- free(where);
- conn->reply_type = REPLY_GENERATED;
- conn->http_code = 301;
-}
-
-
-
-/* ---------------------------------------------------------------------------
- * Parses a single HTTP request field. Returns string from end of [field] to
- * first \r, \n or end of request string. Returns NULL if [field] can't be
- * matched.
- *
- * You need to remember to deallocate the result.
- * example: parse_field(conn, "Referer: ");
- */
-static char *parse_field(const struct connection *conn, const char *field)
-{
- size_t bound1, bound2;
- char *pos;
-
- /* find start */
- pos = strstr(conn->request, field);
- if (pos == NULL) return NULL;
- bound1 = pos - conn->request + strlen(field);
-
- /* find end */
- for (bound2 = bound1;
- conn->request[bound2] != '\r' &&
- bound2 < conn->request_length; bound2++)
- ;
-
- /* copy to buffer */
- return split_string(conn->request, bound1, bound2);
-}
-
-
-
-/* ---------------------------------------------------------------------------
- * Parse a Range: field into range_begin and range_end. Only handles the
- * first range if a list is given. Sets range_{begin,end}_given to 1 if
- * either part of the range is given.
- */
-static void parse_range_field(struct connection *conn)
-{
- size_t bound1, bound2, len;
- char *range;
-
- range = parse_field(conn, "Range: bytes=");
- if (range == NULL) return;
- len = strlen(range);
-
- do /* break handling */
- {
- /* parse number up to hyphen */
- bound1 = 0;
- for (bound2=0;
- isdigit( (int)range[bound2] ) && bound2 < len;
- bound2++)
- ;
-
- if (bound2 == len || range[bound2] != '-')
- break; /* there must be a hyphen here */
-
- if (bound1 != bound2)
- {
- conn->range_begin_given = 1;
- conn->range_begin = (size_t)strtol(range+bound1, NULL, 10);
-
- }
-
- /* parse number after hyphen */
- bound2++;
- for (bound1=bound2;
- isdigit( (int)range[bound2] ) && bound2 < len;
- bound2++)
- ;
-
- if (bound2 != len && range[bound2] != ',')
- break; /* must be end of string or a list to be valid */
-
- if (bound1 != bound2)
- {
- conn->range_end_given = 1;
- conn->range_end = (size_t)strtol(range+bound1, NULL, 10);
- }
- }
- while(0); /* break handling */
- free(range);
-
- /* sanity check: begin <= end */
- if (conn->range_begin_given && conn->range_end_given &&
- (conn->range_begin > conn->range_end))
- {
- conn->range_begin_given = conn->range_end_given = 0;
- }
-}
-
-
-
-/* ---------------------------------------------------------------------------
- * Parse an HTTP request like "GET / HTTP/1.1" to get the method (GET), the
- * url (/), the referer (if given) and the user-agent (if given). Remember to
- * deallocate all these buffers. The method will be returned in uppercase.
- */
-static int parse_request(struct connection *conn)
-{
- size_t bound1, bound2;
- char *tmp;
- assert(conn->request_length == strlen(conn->request));
-
- /* parse method */
- for (bound1 = 0; bound1 < conn->request_length &&
- conn->request[bound1] != ' '; bound1++)
- ;
-
- conn->method = split_string(conn->request, 0, bound1);
- strntoupper(conn->method, bound1);
-
- /* parse uri */
- for (; bound1 < conn->request_length &&
- conn->request[bound1] == ' '; bound1++)
- ;
-
- if (bound1 == conn->request_length) return 0; /* fail */
-
- for (bound2=bound1+1; bound2 < conn->request_length &&
- conn->request[bound2] != ' ' &&
- conn->request[bound2] != '\r'; bound2++)
- ;
-
- conn->uri = split_string(conn->request, bound1, bound2);
-
- /* parse protocol to determine conn_close */
- if (conn->request[bound2] == ' ')
- {
- char *proto;
- for (bound1 = bound2; bound1 < conn->request_length &&
- conn->request[bound1] == ' '; bound1++)
- ;
-
- for (bound2=bound1+1; bound2 < conn->request_length &&
- conn->request[bound2] != ' ' &&
- conn->request[bound2] != '\r'; bound2++)
- ;
-
- proto = split_string(conn->request, bound1, bound2);
- if (strcasecmp(proto, "HTTP/1.1") == 0) conn->conn_close = 0;
- free(proto);
- }
-
- /* parse connection field */
- tmp = parse_field(conn, "Connection: ");
- if (tmp != NULL)
- {
- if (strcasecmp(tmp, "close") == 0) conn->conn_close = 1;
- else if (strcasecmp(tmp, "keep-alive") == 0) conn->conn_close = 0;
- free(tmp);
- }
-
- /* parse important fields */
- conn->referer = parse_field(conn, "Referer: ");
- conn->user_agent = parse_field(conn, "User-Agent: ");
- parse_range_field(conn);
- return 1;
-}
-
-
-
-/* ---------------------------------------------------------------------------
- * Check if a file exists.
- */
-static int file_exists(const char *path)
-{
- struct stat filestat;
- if ((stat(path, &filestat) == -1) && (errno = ENOENT))
- return 0;
- else
- return 1;
-}
-
-
-
-/* ---------------------------------------------------------------------------
- * Make sorted list of files in a directory. Returns number of entries, or -1
- * if error occurs.
- */
-struct dlent
-{
- char *name;
- int is_dir;
- off_t size;
-};
-
-static int dlent_cmp(const void *a, const void *b)
-{
- return strcmp( (*((const struct dlent * const *)a))->name,
- (*((const struct dlent * const *)b))->name );
-}
-
-static ssize_t make_sorted_dirlist(const char *path, struct dlent ***output)
-{
- DIR *dir;
- struct dirent *ent;
- size_t entries = 0, pool = 0;
- #define POOL_INCR 100
- char *currname;
- struct dlent **list = NULL;
-
- dir = opendir(path);
- if (dir == NULL) return -1;
-
- currname = xmalloc(strlen(path) + MAXNAMLEN + 1);
-
- /* construct list */
- while ((ent = readdir(dir)) != NULL)
- {
- struct stat s;
-
- if (ent->d_name[0] == '.' && ent->d_name[1] == '\0')
- continue; /* skip "." */
- assert(strlen(ent->d_name) <= MAXNAMLEN);
- sprintf(currname, "%s%s", path, ent->d_name);
- if (stat(currname, &s) == -1)
- continue; /* skip un-stat-able files */
-
- if (entries == pool)
- {
- pool += POOL_INCR;
- list = xrealloc(list, sizeof(struct dlent*) * pool);
- }
-
- list[entries] = xmalloc(sizeof(struct dlent));
- list[entries]->name = xstrdup(ent->d_name);
- list[entries]->is_dir = S_ISDIR(s.st_mode);
- list[entries]->size = s.st_size;
- entries++;
- }
-
- (void)closedir(dir); /* can't error out if opendir() succeeded */
-
- free(currname);
- qsort(list, entries, sizeof(struct dlent*), dlent_cmp);
- *output = xrealloc(list, sizeof(struct dlent*) * entries);
- return entries;
- #undef POOL_INCR
-}
-
-
-
-/* ---------------------------------------------------------------------------
- * Cleanly deallocate a sorted list of directory files.
- */
-static void cleanup_sorted_dirlist(struct dlent **list, const ssize_t size)
-{
- ssize_t i;
- for (i=0; i<size; i++)
- {
- free(list[i]->name);
- free(list[i]);
- }
-}
-
-/* ---------------------------------------------------------------------------
- * Should this character be urlencoded (according to rfc1738)
- */
-static int needs_urlencoding(unsigned char c)
-{
- int i;
- const static char bad[] = "<>\"%{}|^~[]`\\;:/?@#=&";
-
- for (i=0; i<sizeof(bad)-1; i++)
- if (c == bad[i])
- return 1;
-
- /* Non-US-ASCII characters */
- if ((c <= 0x1F) || (c >= 0x80) || (c == 0x7F))
- return 1;
-
- return 0;
-}
-
-/* ---------------------------------------------------------------------------
- * Encode filename to be an rfc1738-compliant URL part
- */
-static void urlencode_filename(unsigned char *name, unsigned char *safe_url)
-{
- const static char hex[] = "0123456789ABCDEF";
- int i, j;
-
- for (i = j = 0; name[i] != '\0'; i++)
- {
- if (needs_urlencoding(name[i]))
- {
- safe_url[j++] = '%';
- safe_url[j++] = hex[(name[i] >> 4) & 0xF];
- safe_url[j++] = hex[ name[i] & 0xF];
- }
- else
- safe_url[j++] = name[i];
- }
-
- safe_url[j] = '\0';
-}
-
-/* ---------------------------------------------------------------------------
- * Generate directory listing.
- */
-static void generate_dir_listing(struct connection *conn, const char *path)
-{
- char date[DATE_LEN], *spaces;
- struct dlent **list;
- ssize_t listsize;
- size_t maxlen = 0;
- int i;
- struct apbuf *listing = make_apbuf();
-
- listsize = make_sorted_dirlist(path, &list);
- if (listsize == -1)
- {
- default_reply(conn, 500, "Internal Server Error",
- "Couldn't list directory: %s", strerror(errno));
- return;
- }
-
- for (i=0; i<listsize; i++)
- {
- size_t tmp = strlen(list[i]->name);
- if (maxlen < tmp) maxlen = tmp;
- }
-
- append(listing, "<html>\n<head>\n <title>");
- append(listing, conn->uri);
- append(listing, "</title>\n</head>\n<body>\n<h1>");
- append(listing, conn->uri);
- append(listing, "</h1>\n<tt><pre>\n");
-
- spaces = xmalloc(maxlen);
- memset(spaces, ' ', maxlen);
-
- for (i=0; i<listsize; i++)
- {
- /* If a filename is made up of entirely unsafe chars,
- * the url would be three times its original length.
- */
- char safe_url[MAXNAMLEN*3 + 1];
-
- urlencode_filename(list[i]->name, safe_url);
-
- append(listing, "<a href=\"");
- append(listing, safe_url);
- append(listing, "\">");
- append(listing, list[i]->name);
- append(listing, "</a>");
-
- if (list[i]->is_dir)
- append(listing, "/\n");
- else
- {
- appendl(listing, spaces, maxlen-strlen(list[i]->name));
- appendf(listing, "%10d\n", list[i]->size);
- }
- }
-
- cleanup_sorted_dirlist(list, listsize);
- free(list);
- free(spaces);
-
- rfc1123_date(date, now);
- append(listing,
- "</pre></tt>\n"
- "<hr>\n"
- "Generated by ");
- append(listing, pkgname);
- append(listing, " on ");
- append(listing, date);
- append(listing, "\n</body>\n</html>\n");
-
- conn->reply = listing->str;
- conn->reply_length = listing->length;
- free(listing); /* don't free inside of listing */
-
- conn->header_length = xasprintf(&(conn->header),
- "HTTP/1.1 200 OK\r\n"
- "Date: %s\r\n"
- "Server: %s\r\n"
- "%s" /* keep-alive */
- "Content-Length: %u\r\n"
- "Content-Type: text/html\r\n"
- "\r\n",
- date, pkgname, keep_alive(conn), conn->reply_length);
-
- conn->reply_type = REPLY_GENERATED;
- conn->http_code = 200;
-}
-
-
-
-/* ---------------------------------------------------------------------------
- * Process a GET/HEAD request
- */
-static void process_get(struct connection *conn)
-{
- char *decoded_url, *target, *if_mod_since;
- char date[DATE_LEN], lastmod[DATE_LEN];
- const char *mimetype = NULL;
- struct stat filestat;
-
- /* work out path of file being requested */
- decoded_url = urldecode(conn->uri);
-
- /* make sure it's safe */
- if (make_safe_uri(decoded_url) == NULL) {
- default_reply(conn, 400, "Bad Request",
- "You requested an invalid URI: %s", conn->uri);
- free(decoded_url);
- return;
- }
-
- /* does it end in a slash? serve up url/index_name */
- if (decoded_url[strlen(decoded_url)-1] == '/')
- {
- xasprintf(&target, "%s%s%s", wwwroot, decoded_url, index_name);
- if (!file_exists(target))
- {
- free(target);
- xasprintf(&target, "%s%s", wwwroot, decoded_url);
- generate_dir_listing(conn, target);
- free(target);
- free(decoded_url);
- return;
- }
- mimetype = uri_content_type(index_name);
- }
- else /* points to a file */
- {
- xasprintf(&target, "%s%s", wwwroot, decoded_url);
- mimetype = uri_content_type(decoded_url);
- }
- free(decoded_url);
- if (debug) printf("uri=%s, target=%s, content-type=%s\n",
- conn->uri, target, mimetype);
-
- /* open file */
- conn->reply_fd = open(target, O_RDONLY | O_NONBLOCK);
- free(target);
-
- if (conn->reply_fd == -1)
- {
- /* open() failed */
- if (errno == EACCES)
- default_reply(conn, 403, "Forbidden",
- "You don't have permission to access (%s).", conn->uri);
- else if (errno == ENOENT)
- default_reply(conn, 404, "Not Found",
- "The URI you requested (%s) was not found.", conn->uri);
- else
- default_reply(conn, 500, "Internal Server Error",
- "The URI you requested (%s) cannot be returned: %s.",
- conn->uri, strerror(errno));
-
- return;
- }
-
- /* stat the file */
- if (fstat(conn->reply_fd, &filestat) == -1)
- {
- default_reply(conn, 500, "Internal Server Error",
- "fstat() failed: %s.", strerror(errno));
- return;
- }
-
- /* make sure it's a regular file */
- if (S_ISDIR(filestat.st_mode))
- {
- redirect(conn, "%s/", conn->uri);
- return;
- }
- else if (!S_ISREG(filestat.st_mode))
- {
- default_reply(conn, 403, "Forbidden", "Not a regular file.");
- return;
- }
-
- conn->reply_type = REPLY_FROMFILE;
- (void) rfc1123_date(lastmod, filestat.st_mtime);
-
- /* check for If-Modified-Since, may not have to send */
- if_mod_since = parse_field(conn, "If-Modified-Since: ");
- if (if_mod_since != NULL &&
- strcmp(if_mod_since, lastmod) == 0)
- {
- if (debug) printf("not modified since %s\n", if_mod_since);
- default_reply(conn, 304, "Not Modified", "");
- conn->header_only = 1;
- free(if_mod_since);
- return;
- }
- free(if_mod_since);
-
- if (conn->range_begin_given || conn->range_end_given)
- {
- size_t from, to;
-
- if (conn->range_begin_given && conn->range_end_given)
- {
- /* 100-200 */
- from = conn->range_begin;
- to = conn->range_end;
-
- /* clamp [to] to filestat.st_size-1 */
- if (to > (size_t)(filestat.st_size-1))
- to = filestat.st_size-1;
- }
- else if (conn->range_begin_given && !conn->range_end_given)
- {
- /* 100- :: yields 100 to end */
- from = conn->range_begin;
- to = filestat.st_size-1;
- }
- else if (!conn->range_begin_given && conn->range_end_given)
- {
- /* -200 :: yields last 200 */
- to = filestat.st_size-1;
- from = to - conn->range_end + 1;
-
- /* check for wrapping */
- if (from > to) from = 0;
- }
- else errx(1, "internal error - from/to mismatch");
-
- conn->reply_start = from;
- conn->reply_length = to - from + 1;
-
- conn->header_length = xasprintf(&(conn->header),
- "HTTP/1.1 206 Partial Content\r\n"
- "Date: %s\r\n"
- "Server: %s\r\n"
- "%s" /* keep-alive */
- "Content-Length: %d\r\n"
- "Content-Range: bytes %d-%d/%d\r\n"
- "Content-Type: %s\r\n"
- "Last-Modified: %s\r\n"
- "\r\n"
- ,
- rfc1123_date(date, now), pkgname, keep_alive(conn),
- conn->reply_length, from, to, filestat.st_size,
- mimetype, lastmod
- );
- conn->http_code = 206;
- if (debug) printf("sending %u-%u/%u\n",
- (unsigned int)from, (unsigned int)to,
- (unsigned int)filestat.st_size);
- }
- else /* no range stuff */
- {
- conn->reply_length = filestat.st_size;
-
- conn->header_length = xasprintf(&(conn->header),
- "HTTP/1.1 200 OK\r\n"
- "Date: %s\r\n"
- "Server: %s\r\n"
- "%s" /* keep-alive */
- "Content-Length: %d\r\n"
- "Content-Type: %s\r\n"
- "Last-Modified: %s\r\n"
- "\r\n"
- ,
- rfc1123_date(date, now), pkgname, keep_alive(conn),
- conn->reply_length, mimetype, lastmod
- );
- conn->http_code = 200;
- }
-}
-
-
-
-/* ---------------------------------------------------------------------------
- * Process a request: build the header and reply, advance state.
- */
-static void process_request(struct connection *conn)
-{
- num_requests++;
- if (!parse_request(conn))
- {
- default_reply(conn, 400, "Bad Request",
- "You sent a request that the server couldn't understand.");
- }
- else if (strcmp(conn->method, "GET") == 0)
- {
- process_get(conn);
- }
- else if (strcmp(conn->method, "HEAD") == 0)
- {
- process_get(conn);
- conn->header_only = 1;
- }
- else if (strcmp(conn->method, "OPTIONS") == 0 ||
- strcmp(conn->method, "POST") == 0 ||
- strcmp(conn->method, "PUT") == 0 ||
- strcmp(conn->method, "DELETE") == 0 ||
- strcmp(conn->method, "TRACE") == 0 ||
- strcmp(conn->method, "CONNECT") == 0)
- {
- default_reply(conn, 501, "Not Implemented",
- "The method you specified (%s) is not implemented.",
- conn->method);
- }
- else
- {
- default_reply(conn, 400, "Bad Request",
- "%s is not a valid HTTP/1.1 method.", conn->method);
- }
-
- /* advance state */
- conn->state = SEND_HEADER;
-
- /* request not needed anymore */
- free(conn->request);
- conn->request = NULL; /* important: don't free it again later */
-}
-
-
-
-/* ---------------------------------------------------------------------------
- * Receiving request.
- */
-static void poll_recv_request(struct connection *conn)
-{
- #define BUFSIZE 65536
- char buf[BUFSIZE];
- ssize_t recvd;
-
- assert(conn->state == RECV_REQUEST);
- recvd = recv(conn->socket, buf, BUFSIZE, 0);
- if (debug) printf("poll_recv_request(%d) got %d bytes\n",
- conn->socket, (int)recvd);
- if (recvd <= 0)
- {
- if (recvd == -1) {
- if (errno == EAGAIN) {
- if (debug) printf("poll_recv_request would have blocked\n");
- return;
- }
- if (debug) printf("recv(%d) error: %s\n",
- conn->socket, strerror(errno));
- }
- conn->conn_close = 1;
- conn->state = DONE;
- return;
- }
- conn->last_active = now;
- #undef BUFSIZE
-
- /* append to conn->request */
- conn->request = xrealloc(conn->request, conn->request_length+recvd+1);
- memcpy(conn->request+conn->request_length, buf, (size_t)recvd);
- conn->request_length += recvd;
- conn->request[conn->request_length] = 0;
- total_in += recvd;
-
- /* process request if we have all of it */
- if ((conn->request_length > 2) &&
- (memcmp(conn->request+conn->request_length-2, "\n\n", 2) == 0))
- process_request(conn);
- else if ((conn->request_length > 4) &&
- (memcmp(conn->request+conn->request_length-4, "\r\n\r\n", 4) == 0))
- process_request(conn);
-
- /* die if it's too long */
- if (conn->request_length > MAX_REQUEST_LENGTH)
- {
- default_reply(conn, 413, "Request Entity Too Large",
- "Your request was dropped because it was too long.");
- conn->state = SEND_HEADER;
- }
-
- /* if we've moved on to the next state, try to send right away, instead of
- * going through another iteration of the select() loop.
- */
- if (conn->state == SEND_HEADER)
- poll_send_header(conn);
-}
-
-
-
-/* ---------------------------------------------------------------------------
- * Sending header. Assumes conn->header is not NULL.
- */
-static void poll_send_header(struct connection *conn)
-{
- ssize_t sent;
-
- assert(conn->state == SEND_HEADER);
- assert(conn->header_length == strlen(conn->header));
-
- sent = send(conn->socket, conn->header + conn->header_sent,
- conn->header_length - conn->header_sent, 0);
- conn->last_active = now;
- if (debug) printf("poll_send_header(%d) sent %d bytes\n",
- conn->socket, (int)sent);
-
- /* handle any errors (-1) or closure (0) in send() */
- if (sent < 1)
- {
- if ((sent == -1) && (errno == EAGAIN)) {
- if (debug) printf("poll_send_header would have blocked\n");
- return;
- }
- if (debug && (sent == -1))
- printf("send(%d) error: %s\n", conn->socket, strerror(errno));
- conn->conn_close = 1;
- conn->state = DONE;
- return;
- }
- conn->header_sent += sent;
- conn->total_sent += sent;
- total_out += sent;
-
- /* check if we're done sending header */
- if (conn->header_sent == conn->header_length)
- {
- if (conn->header_only)
- conn->state = DONE;
- else {
- conn->state = SEND_REPLY;
- /* go straight on to body, don't go through another iteration of
- * the select() loop.
- */
- poll_send_reply(conn);
- }
- }
-}
-
-
-
-/* ---------------------------------------------------------------------------
- * Send chunk on socket <s> from FILE *fp, starting at <ofs> and of size
- * <size>. Use sendfile() if possible since it's zero-copy on some platforms.
- * Returns the number of bytes sent, 0 on closure, -1 if send() failed, -2 if
- * read error.
- */
-static ssize_t send_from_file(const int s, const int fd,
- off_t ofs, const size_t size)
-{
-#ifdef __FreeBSD__
- off_t sent;
- int ret = sendfile(fd, s, ofs, size, NULL, &sent, 0);
-
- /* It is possible for sendfile to send zero bytes due to a blocking
- * condition. Handle this correctly.
- */
- if (ret == -1)
- if (errno == EAGAIN)
- if (sent == 0)
- return -1;
- else
- return sent;
- else
- return -1;
- else
- return size;
-#else
-#if defined(__linux) || defined(__sun__)
- return sendfile(s, fd, &ofs, size);
-#else
- #define BUFSIZE 20000
- char buf[BUFSIZE];
- size_t amount = min((size_t)BUFSIZE, size);
- ssize_t numread;
- #undef BUFSIZE
-
- if (lseek(fd, ofs, SEEK_SET) == -1) err(1, "fseek(%d)", (int)ofs);
- numread = read(fd, buf, amount);
- if (numread == 0)
- {
- fprintf(stderr, "premature eof on fd %d\n", fd);
- return -1;
- }
- else if (numread == -1)
- {
- fprintf(stderr, "error reading on fd %d: %s", fd, strerror(errno));
- return -1;
- }
- else if ((size_t)numread != amount)
- {
- fprintf(stderr, "read %d bytes, expecting %u bytes on fd %d\n",
- numread, amount, fd);
- return -1;
- }
- else
- return send(s, buf, amount, 0);
-#endif
-#endif
-}
-
-
-
-/* ---------------------------------------------------------------------------
- * Sending reply.
- */
-static void poll_send_reply(struct connection *conn)
-{
- ssize_t sent;
-
- assert(conn->state == SEND_REPLY);
- assert(!conn->header_only);
- if (conn->reply_type == REPLY_GENERATED)
- {
- sent = send(conn->socket,
- conn->reply + conn->reply_start + conn->reply_sent,
- conn->reply_length - conn->reply_sent, 0);
- }
- else
- {
- sent = send_from_file(conn->socket, conn->reply_fd,
- (off_t)(conn->reply_start + conn->reply_sent),
- conn->reply_length - conn->reply_sent);
- }
- conn->last_active = now;
- if (debug) printf("poll_send_reply(%d) sent %d: %d+[%d-%d] of %d\n",
- conn->socket, (int)sent, (int)conn->reply_start,
- (int)conn->reply_sent,
- (int)(conn->reply_sent + sent - 1),
- (int)conn->reply_length);
-
- /* handle any errors (-1) or closure (0) in send() */
- if (sent < 1)
- {
- if (sent == -1)
- {
- if (errno == EAGAIN) {
- if (debug) printf("poll_send_reply would have blocked\n");
- return;
- }
- if (debug) printf("send(%d) error: %s\n",
- conn->socket, strerror(errno));
- }
- else if (sent == 0)
- {
- if (debug) printf("send(%d) closure\n", conn->socket);
- }
- conn->conn_close = 1;
- conn->state = DONE;
- return;
- }
- conn->reply_sent += (unsigned int)sent;
- conn->total_sent += (unsigned int)sent;
- total_out += sent;
-
- /* check if we're done sending */
- if (conn->reply_sent == conn->reply_length) conn->state = DONE;
-}
-
-
-
-/* ---------------------------------------------------------------------------
- * Add a connection's details to the logfile.
- */
-static void log_connection(const struct connection *conn)
-{
- struct in_addr inaddr;
-
- if (logfile == NULL)
- return;
- if (conn->http_code == 0)
- return; /* invalid - died in request */
- if (conn->method == NULL)
- return; /* invalid - didn't parse - maybe too long */
-
- /* Separated by tabs:
- * time client_ip method uri http_code bytes_sent "referer" "user-agent"
- */
-
- inaddr.s_addr = conn->client;
-
- fprintf(logfile, "%lu\t%s\t%s\t%s\t%d\t%u\t\"%s\"\t\"%s\"\n",
- (unsigned long int)now, inet_ntoa(inaddr),
- conn->method, conn->uri,
- conn->http_code, conn->total_sent,
- (conn->referer == NULL)?"":conn->referer,
- (conn->user_agent == NULL)?"":conn->user_agent
- );
- fflush(logfile);
-}
-
-
-
-/* ---------------------------------------------------------------------------
- * Main loop of the httpd - a select() and then delegation to accept
- * connections, handle receiving of requests, and sending of replies.
- */
-static void httpd_poll(void)
-{
- fd_set recv_set, send_set;
- int max_fd, select_ret;
- struct connection *conn, *next;
- int bother_with_timeout = 0;
- struct timeval timeout;
-
- timeout.tv_sec = idletime;
- timeout.tv_usec = 0;
-
- FD_ZERO(&recv_set);
- FD_ZERO(&send_set);
- max_fd = 0;
-
- /* set recv/send fd_sets */
- #define MAX_FD_SET(sock, fdset) { FD_SET(sock,fdset); \
- max_fd = (max_fd<sock) ? sock : max_fd; }
-
- MAX_FD_SET(sockin, &recv_set);
-
- LIST_FOREACH_SAFE(conn, &connlist, entries, next)
- {
- poll_check_timeout(conn);
- switch (conn->state)
- {
- case DONE:
- /* do nothing */
- break;
-
- case RECV_REQUEST:
- MAX_FD_SET(conn->socket, &recv_set);
- bother_with_timeout = 1;
- break;
-
- case SEND_HEADER:
- case SEND_REPLY:
- MAX_FD_SET(conn->socket, &send_set);
- bother_with_timeout = 1;
- break;
-
- default: errx(1, "invalid state");
- }
- }
- #undef MAX_FD_SET
-
- /* -select- */
- select_ret = select(max_fd + 1, &recv_set, &send_set, NULL,
- (bother_with_timeout) ? &timeout : NULL);
- if (select_ret == 0)
- {
- if (!bother_with_timeout)
- errx(1, "select() timed out");
- else
- return;
- }
- if (select_ret == -1) {
- if (errno == EINTR)
- return; /* interrupted by signal */
- else
- err(1, "select() failed");
- }
-
- /* update time */
- now = time(NULL);
-
- /* poll connections that select() says need attention */
- if (FD_ISSET(sockin, &recv_set)) accept_connection();
-
- LIST_FOREACH_SAFE(conn, &connlist, entries, next)
- {
- switch (conn->state)
- {
- case RECV_REQUEST:
- if (FD_ISSET(conn->socket, &recv_set)) poll_recv_request(conn);
- break;
-
- case SEND_HEADER:
- if (FD_ISSET(conn->socket, &send_set)) poll_send_header(conn);
- break;
-
- case SEND_REPLY:
- if (FD_ISSET(conn->socket, &send_set)) poll_send_reply(conn);
- break;
-
- case DONE:
- /* (handled later; ignore for now as it's a valid state) */
- break;
-
- default: errx(1, "invalid state");
- }
-
- if (conn->state == DONE) {
- /* clean out finished connection */
- if (conn->conn_close) {
- LIST_REMOVE(conn, entries);
- free_connection(conn);
- free(conn);
- } else {
- recycle_connection(conn);
- /* and go right back to recv_request without going through
- * select() again.
- */
- poll_recv_request(conn);
- }
- }
- }
-}
-
-
-
-/* ---------------------------------------------------------------------------
- * Daemonize helpers.
- */
-#define PATH_DEVNULL "/dev/null"
-static int lifeline[2] = { -1, -1 };
-static int fd_null = -1;
-
-static void
-daemonize_start(void)
-{
- pid_t f, w;
-
- if (pipe(lifeline) == -1)
- err(1, "pipe(lifeline)");
-
- fd_null = open(PATH_DEVNULL, O_RDWR, 0);
- if (fd_null == -1)
- err(1, "open(" PATH_DEVNULL ")");
-
- f = fork();
- if (f == -1)
- err(1, "fork");
- else if (f != 0) {
- /* parent: wait for child */
- char tmp[1];
- int status;
-
- if (close(lifeline[1]) == -1)
- warn("close lifeline in parent");
- read(lifeline[0], tmp, sizeof(tmp));
- w = waitpid(f, &status, WNOHANG);
- if (w == -1)
- err(1, "waitpid");
- else if (w == 0)
- /* child is running happily */
- exit(EXIT_SUCCESS);
- else
- /* child init failed, pass on its exit status */
- exit(WEXITSTATUS(status));
- }
- /* else we are the child: continue initializing */
-}
-
-static void
-daemonize_finish(void)
-{
- if (fd_null == -1)
- return; /* didn't daemonize_start() so we're not daemonizing */
-
- if (setsid() == -1)
- err(1, "setsid");
- if (close(lifeline[0]) == -1)
- warn("close read end of lifeline in child");
- if (close(lifeline[1]) == -1)
- warn("couldn't cut the lifeline");
-
- /* close all our std fds */
- if (dup2(fd_null, STDIN_FILENO) == -1)
- warn("dup2(stdin)");
- if (dup2(fd_null, STDOUT_FILENO) == -1)
- warn("dup2(stdout)");
- if (dup2(fd_null, STDERR_FILENO) == -1)
- warn("dup2(stderr)");
- if (fd_null > 2)
- close(fd_null);
-}
-
-/* ---------------------------------------------------------------------------
- * Pidfile helpers, based on FreeBSD src/lib/libutil/pidfile.c,v 1.3
- * Original was copyright (c) 2005 Pawel Jakub Dawidek <pjd@FreeBSD.org>
- */
-static int pidfile_fd = -1;
-#define PIDFILE_MODE 0600
-
-static void
-pidfile_remove(void)
-{
- if (unlink(pidfile_name) == -1)
- err(1, "unlink(pidfile) failed");
- /* if (flock(pidfile_fd, LOCK_UN) == -1)
- err(1, "unlock(pidfile) failed"); */
- xclose(pidfile_fd);
- pidfile_fd = -1;
-}
-
-static int
-pidfile_read(void)
-{
- char buf[16], *endptr;
- int fd, i, pid;
-
- fd = open(pidfile_name, O_RDONLY);
- if (fd == -1)
- err(1, " after create failed");
-
- i = read(fd, buf, sizeof(buf) - 1);
- if (i == -1)
- err(1, "read from pidfile failed");
- xclose(fd);
- buf[i] = '\0';
-
- pid = (int)strtoul(buf, &endptr, 10);
- if (endptr != &buf[i])
- err(1, "invalid pidfile contents: \"%s\"", buf);
- return (pid);
-}
-
-static void
-pidfile_create(void)
-{
- int error, fd;
- char pidstr[16];
-
- /* Open the PID file and obtain exclusive lock. */
- fd = open(pidfile_name,
- O_WRONLY | O_CREAT | O_EXLOCK | O_TRUNC | O_NONBLOCK, PIDFILE_MODE);
- if (fd == -1) {
- if ((errno == EWOULDBLOCK) || (errno == EEXIST))
- errx(1, "daemon already running with PID %d", pidfile_read());
- else
- err(1, "can't create pidfile %s", pidfile_name);
- }
- pidfile_fd = fd;
-
- if (ftruncate(fd, 0) == -1) {
- error = errno;
- pidfile_remove();
- errno = error;
- err(1, "ftruncate() failed");
- }
-
- snprintf(pidstr, sizeof(pidstr), "%u", getpid());
- if (pwrite(fd, pidstr, strlen(pidstr), 0) != (ssize_t)strlen(pidstr)) {
- error = errno;
- pidfile_remove();
- errno = error;
- err(1, "pwrite() failed");
- }
-}
-
-/* end of pidfile helpers.
- * ---------------------------------------------------------------------------
- * Close all sockets and FILEs and exit.
- */
-static void
-stop_running(int sig)
-{
- running = 0;
- fprintf(stderr, "\ncaught %s, stopping\n", strsignal(sig));
-}
-
-/* ---------------------------------------------------------------------------
- * Execution starts here.
- */
-int
-main(int argc, char **argv)
-{
- printf("%s, %s.\n", pkgname, copyright);
- parse_default_extension_map();
- parse_commandline(argc, argv);
- /* parse_commandline() might override parts of the extension map by
- * parsing a user-specified file.
- */
- sort_mime_map();
- xasprintf(&keep_alive_field, "Keep-Alive: timeout=%d\r\n", idletime);
- init_sockin();
-
- /* open logfile */
- if (logfile_name != NULL)
- {
- logfile = fopen(logfile_name, "ab");
- if (logfile == NULL)
- err(1, "opening logfile: fopen(\"%s\")", logfile_name);
- }
-
- if (want_daemon) daemonize_start();
-
- /* signals */
- if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
- err(1, "signal(ignore SIGPIPE)");
- if (signal(SIGINT, stop_running) == SIG_ERR)
- err(1, "signal(SIGINT)");
- if (signal(SIGQUIT, stop_running) == SIG_ERR)
- err(1, "signal(SIGQUIT)");
- if (signal(SIGTERM, stop_running) == SIG_ERR)
- err(1, "signal(SIGTERM)");
-
- /* security */
- if (want_chroot)
- {
- tzset(); /* read /etc/localtime before we chroot */
- if (chdir(wwwroot) == -1)
- err(1, "chdir(%s)", wwwroot);
- if (chroot(wwwroot) == -1)
- err(1, "chroot(%s)", wwwroot);
- printf("chrooted to `%s'\n", wwwroot);
- wwwroot[0] = '\0'; /* empty string */
- }
- if (drop_gid != INVALID_GID)
- {
- if (setgid(drop_gid) == -1) err(1, "setgid(%d)", drop_gid);
- printf("set gid to %d\n", drop_gid);
- }
- if (drop_uid != INVALID_UID)
- {
- if (setuid(drop_uid) == -1) err(1, "setuid(%d)", drop_uid);
- printf("set uid to %d\n", drop_uid);
- }
-
- /* create pidfile */
- if (pidfile_name) pidfile_create();
-
- if (want_daemon) daemonize_finish();
-
- /* main loop */
- while (running) httpd_poll();
-
- /* clean exit */
- xclose(sockin);
- if (logfile != NULL) fclose(logfile);
- if (pidfile_name) pidfile_remove();
-
- /* close and free connections */
- {
- struct connection *conn, *next;
-
- LIST_FOREACH_SAFE(conn, &connlist, entries, next)
- {
- LIST_REMOVE(conn, entries);
- free_connection(conn);
- free(conn);
- }
- }
-
- /* free the mallocs */
- {
- size_t i;
- for (i=0; i<mime_map_size; i++)
- {
- free(mime_map[i].extension);
- free(mime_map[i].mimetype);
- }
- free(mime_map);
- free(keep_alive_field);
- free(wwwroot);
- }
-
- /* usage stats */
- {
- struct rusage r;
-
- getrusage(RUSAGE_SELF, &r);
- printf("CPU time used: %u.%02u user, %u.%02u system\n",
- (unsigned int)r.ru_utime.tv_sec,
- (unsigned int)(r.ru_utime.tv_usec/10000),
- (unsigned int)r.ru_stime.tv_sec,
- (unsigned int)(r.ru_stime.tv_usec/10000)
- );
- printf("Requests: %u\n", num_requests);
- printf("Bytes: %llu in, %llu out\n", total_in, total_out);
- }
-
- return (0);
-}
-
-/* vim:set tabstop=4 shiftwidth=4 expandtab tw=78: */