Browse Source

Initial commit

personal/stbuehler/wip
Stefan Bühler 14 years ago
commit
9372e41393
  1. 3
      .bzrignore
  2. 14
      src/base.h
  3. 436
      src/chunks.c
  4. 191
      src/chunks.h
  5. 31
      src/log.c
  6. 48
      src/log.h
  7. 31
      src/options.h
  8. 48
      src/plugin.h
  9. 7
      src/server.c
  10. 189
      src/settings.h
  11. 64
      src/sys-files.c
  12. 84
      src/sys-files.h
  13. 26
      src/sys-mmap.h
  14. 17
      src/sys-process.h
  15. 76
      src/sys-socket.c
  16. 80
      src/sys-socket.h
  17. 46
      src/sys-strings.h
  18. 120
      src/wscript
  19. 145
      waf
  20. 490
      wscript

3
.bzrignore

@ -0,0 +1,3 @@
build
.lock-wscript
.waf-*

14
src/base.h

@ -0,0 +1,14 @@
#ifndef _LIGHTTPD_BASE_H_
#define _LIGHTTPD_BASE_H_
#include "settings.h"
#define CONST_STR_LEN(x) x, x ? sizeof(x) - 1 : 0
struct server;
typedef struct server server;
struct connection;
typedef struct connection connection;
#endif

436
src/chunks.c

@ -0,0 +1,436 @@
#include "base.h"
#include "chunks.h"
#include "log.h"
/******************
* chunkfile *
******************/
static chunkfile *chunkfile_new(GString *name, int fd, gboolean is_temp) {
chunkfile *cf = g_slice_new(chunkfile);
cf->refcount = 1;
cf->name = name;
cf->fd = fd;
cf->is_temp = is_temp;
return cf;
}
static void chunkfile_acquire(chunkfile *cf) {
// assert(cf->refcount > 0)
cf->refcount++;
}
static void chunkfile_release(chunkfile *cf) {
// assert(cf->refcount > 0)
if (!(--cf->refcount)) {
if (-1 != cf->fd) close(cf->fd);
cf->fd = -1;
if (cf->is_temp) unlink(cf->name->str);
cf->is_temp = FALSE;
g_string_free(cf->name, TRUE);
cf->name = NULL;
g_slice_free(chunkfile, cf);
}
}
/* open the file cf->name if it is not already opened for reading
* may return HANDLER_GO_ON, HANDLER_ERROR, HANDLER_WAIT_FOR_FD
*/
handler_t chunkfile_open(server *srv, connection *con, chunkfile *cf) {
if (!cf) return HANDLER_ERROR;
if (-1 != cf->fd) return HANDLER_GO_ON;
if (!cf->name) {
CON_ERROR(srv, con, "%s", "Missing filename for FILE_CHUNK");
return HANDLER_ERROR;
}
if (-1 == (cf->fd = open(cf->name->str, O_RDONLY))) {
if (EMFILE == errno) return HANDLER_WAIT_FOR_FD;
CON_ERROR(srv, con, "Couldn't open file '%s': %s (%i)", cf->name->str, strerror(errno), errno);
return HANDLER_ERROR;
}
#ifdef FD_CLOEXEC
fcntl(cf->fd, F_SETFD, FD_CLOEXEC);
#endif
return HANDLER_GO_ON;
}
/******************
* chunk iterator *
******************/
/* must be powers of 2 */
#define MAX_MMAP_CHUNK (2*1024*1024)
#define MMAP_CHUNK_ALIGN (4*1024)
/* get the data from a chunk; easy in case of a MEM_CHUNK,
* but needs to do io in case of FILE_CHUNK; it tries mmap and
* falls back to read(...)
* the data is _not_ marked as "done"
* may return HANDLER_GO_ON, HANDLER_ERROR, HANDLER_WAIT_FOR_FD
*/
handler_t chunkiter_read(server *srv, connection *con, chunkiter iter, off_t start, off_t length, char **data_start, off_t *data_len) {
chunk *c = chunkiter_chunk(iter);
off_t we_want, we_have, our_start, our_offset;
handler_t res = HANDLER_GO_ON;
int mmap_errno = 0;
if (!c) return HANDLER_ERROR;
if (!data_start || !data_len) return HANDLER_ERROR;
if (HANDLER_GO_ON != (res = chunkfile_open(srv, con, c->file.file))) return res;
we_have = chunk_length(c) - start;
if (length > we_have) length = we_have;
if (length <= 0) return HANDLER_ERROR;
switch (c->type) {
case UNUSED_CHUNK: return HANDLER_ERROR;
case MEM_CHUNK:
*data_start = c->mem->str + c->offset + start;
*data_len = length;
break;
case FILE_CHUNK:
if ( !(c->file.mmap.data != MAP_FAILED || c->mem) /* no data present */
|| !( /* or in the wrong range */
(start + c->offset >= c->file.mmap.offset)
&& (start + c->offset + length <= c->file.mmap.offset + c->file.mmap.length)) ) {
/* then find new range */
our_offset = start % MMAP_CHUNK_ALIGN;
our_start = start - our_offset;
if (length > MAX_MMAP_CHUNK) length = MAX_MMAP_CHUNK;
we_want = length + MAX_MMAP_CHUNK;
if (we_want > we_have) we_want = we_have;
we_want += our_offset;
if (MAP_FAILED != c->file.mmap.data) {
munmap(c->file.mmap.data, c->file.mmap.length);
c->file.mmap.data = MAP_FAILED;
}
c->file.mmap.offset = our_offset;
c->file.mmap.length = we_want;
if (!c->mem) { /* mmap did not fail till now */
c->file.mmap.data = mmap(0, we_want, PROT_READ, MAP_SHARED, c->file.file->fd, our_offset);
mmap_errno = errno;
}
if (MAP_FAILED == c->file.mmap.data) {
/* fallback to read(...) */
if (!c->mem) {
c->mem = g_string_sized_new(we_want);
} else {
g_string_set_size(c->mem, we_want);
}
if (-1 == lseek(c->file.file->fd, our_offset, SEEK_SET)) {
/* prefer the error of the first syscall */
if (0 != mmap_errno) {
CON_ERROR(srv, con, "mmap failed for '%s' (fd = %i): %s (%i)",
c->file.file->name->str, c->file.file->fd,
strerror(mmap_errno), mmap_errno);
} else {
CON_ERROR(srv, con, "lseek failed for '%s' (fd = %i): %s (%i)",
c->file.file->name->str, c->file.file->fd,
strerror(errno), errno);
}
g_string_free(c->mem, TRUE);
c->mem = NULL;
return HANDLER_ERROR;
}
read_chunk:
if (-1 == (we_have = read(c->file.file->fd, c->mem->str, we_want))) {
if (EINTR == errno) goto read_chunk;
/* prefer the error of the first syscall */
if (0 != mmap_errno) {
CON_ERROR(srv, con, "mmap failed for '%s' (fd = %i): %s (%i)",
c->file.file->name->str, c->file.file->fd,
strerror(mmap_errno), mmap_errno);
} else {
CON_ERROR(srv, con, "read failed for '%s' (fd = %i): %s (%i)",
c->file.file->name->str, c->file.file->fd,
strerror(errno), errno);
}
g_string_free(c->mem, TRUE);
c->mem = NULL;
return HANDLER_ERROR;
} else if (we_have != we_want) {
/* may return less than requested bytes due to signals */
/* CON_TRACE(srv, con, "read return unexpected number of bytes"); */
we_want = we_have;
if (length > we_have) length = we_have;
c->file.mmap.length = we_want;
g_string_set_size(c->mem, we_want);
}
} else {
#ifdef HAVE_MADVISE
/* don't advise files < 64Kb */
if (c->file.mmap.length > (64*1024) &&
0 != madvise(c->file.mmap.data, c->file.mmap.length, MADV_WILLNEED)) {
CON_ERROR(srv, con, "madvise failed for '%s' (fd = %i): %s (%i)",
c->file.file->name->str, c->file.file->fd,
strerror(errno), errno);
}
#endif
}
}
*data_start = (c->mem ? c->mem->str : c->file.mmap.data) + start + c->offset - c->file.mmap.offset;
*data_len = length;
break;
}
return HANDLER_GO_ON;
}
/******************
* chunk *
******************/
static chunk* chunk_new() {
chunk *c = g_slice_new0(chunk);
c->file.mmap.data = MAP_FAILED;
return c;
}
/*
static void chunk_reset(chunk *c) {
if (!c) return;
c->type = UNUSED_CHUNK;
c->offset = 0;
if (c->mem) g_string_free(c->mem, TRUE);
c->mem = NULL;
if (c->file.file) chunkfile_release(c->file.file);
c->file.file = NULL;
c->file.start = 0;
c->file.length = 0;
if (MAP_FAILED != c->file.mmap.data) munmap(c->file.mmap.data, c->file.mmap.length);
c->file.mmap.data = MAP_FAILED;
c->file.mmap.length = 0;
c->file.mmap.offset = 0;
}
*/
static void chunk_free(chunk *c) {
if (!c) return;
c->type = UNUSED_CHUNK;
if (c->mem) g_string_free(c->mem, TRUE);
c->mem = NULL;
if (c->file.file) chunkfile_release(c->file.file);
c->file.file = NULL;
if (c->file.mmap.data) munmap(c->file.mmap.data, c->file.mmap.length);
c->file.mmap.data = NULL;
g_slice_free(chunk, c);
}
/******************
* chunkqueue *
******************/
chunkqueue* chunkqueue_new() {
chunkqueue *cq = g_slice_new0(chunkqueue);
cq->queue = g_queue_new();
return cq;
}
static void __chunk_free(gpointer c, gpointer UNUSED_PARAM(userdata)) {
chunk_free((chunk*) c);
}
void chunkqueue_reset(chunkqueue *cq) {
if (!cq) return;
cq->is_closed = FALSE;
cq->bytes_in = cq->bytes_out = cq->length = 0;
g_queue_foreach(cq->queue, __chunk_free, NULL);
g_queue_clear(cq->queue);
}
void chunkqueue_free(chunkqueue *cq) {
if (!cq) return;
g_queue_foreach(cq->queue, __chunk_free, NULL);
g_queue_free(cq->queue);
cq->queue = NULL;
g_slice_free(chunkqueue, cq);
}
/* pass ownership of str to chunkqueue, do not free/modify it afterwards
* you may modify the data (not the length) if you are sure it isn't sent before.
*/
void chunkqueue_append_string(chunkqueue *cq, GString *str) {
chunk *c = chunk_new();
c->type = MEM_CHUNK;
c->mem = str;
g_queue_push_tail(cq->queue, c);
cq->length += str->len;
cq->bytes_in += str->len;
}
/* memory gets copied */
void chunkqueue_append_mem(chunkqueue *cq, void *mem, gssize len) {
chunk *c = chunk_new();
c->type = MEM_CHUNK;
c->mem = g_string_new_len(mem, len);
g_queue_push_tail(cq->queue, c);
cq->length += c->mem->len;
cq->bytes_in += c->mem->len;
}
static void __chunkqueue_append_file(chunkqueue *cq, GString *filename, off_t start, off_t length, int fd, gboolean is_temp) {
chunk *c = chunk_new();
c->file.file = chunkfile_new(filename, fd, is_temp);
c->file.start = start;
c->file.length = length;
g_queue_push_tail(cq->queue, c);
cq->length += length;
cq->bytes_in += length;
}
/* pass ownership of filename, do not free it */
void chunkqueue_append_file(chunkqueue *cq, GString *filename, off_t start, off_t length) {
__chunkqueue_append_file(cq, filename, start, length, -1, FALSE);
}
/* if you already opened the file, you can pass the fd here - do not close it */
void chunkqueue_append_file_fd(chunkqueue *cq, GString *filename, off_t start, off_t length, int fd) {
__chunkqueue_append_file(cq, filename, start, length, fd, FALSE);
}
/* temp files get deleted after usage */
/* pass ownership of filename, do not free it */
void chunkqueue_append_tempfile(chunkqueue *cq, GString *filename, off_t start, off_t length) {
__chunkqueue_append_file(cq, filename, start, length, -1, TRUE);
}
/* if you already opened the file, you can pass the fd here - do not close it */
void chunkqueue_append_tempfile_fd(chunkqueue *cq, GString *filename, off_t start, off_t length, int fd) {
__chunkqueue_append_file(cq, filename, start, length, fd, TRUE);
}
/* steal up to length bytes from in and put them into out, return number of bytes stolen */
goffset chunkqueue_steal_len(chunkqueue *out, chunkqueue *in, goffset length) {
chunk *c, *cnew;
GList* l;
goffset bytes = 0;
goffset we_have;
while ( (NULL != (c = chunkqueue_first_chunk(in))) && length > 0 ) {
we_have = chunk_length(c);
if (!we_have) { /* remove empty chunks */
chunk_free(c);
g_queue_pop_head(in->queue);
continue;
}
if (we_have <= length) { /* move complete chunk */
l = g_queue_pop_head_link(in->queue);
g_queue_push_tail_link(out->queue, l);
bytes += we_have;
length -= we_have;
} else { /* copy first part of a chunk */
cnew = chunk_new();
switch (c->type) {
case UNUSED_CHUNK: /* impossible, has length 0 */
/* remove "empty" chunks */
chunk_free(c);
chunk_free(cnew);
g_queue_pop_head(in->queue);
continue;
case MEM_CHUNK:
cnew->type = MEM_CHUNK;
cnew->mem = g_string_new_len(c->mem->str + c->offset, length);
break;
case FILE_CHUNK:
cnew->type = FILE_CHUNK;
chunkfile_acquire(c->file.file);
cnew->file.file = c->file.file;
cnew->file.start = c->file.start + c->offset;
cnew->file.length = length;
break;
}
c->offset += length;
bytes += length;
length = 0;
g_queue_push_tail(out->queue, cnew);
}
}
in->bytes_out += bytes;
in->length -= bytes;
out->bytes_in += bytes;
out->length += bytes;
return bytes;
}
/* steal all chunks from in and put them into out, return number of bytes stolen */
goffset chunkqueue_steal_all(chunkqueue *out, chunkqueue *in) {
/* if in->queue is empty, do nothing */
if (!in->length) return 0;
/* if out->queue is empty, just swap in->queue/out->queue */
if (g_queue_is_empty(out->queue)) {
GQueue *tmp = in->queue; in->queue = out->queue; out->queue = tmp;
} else {
/* link the two "lists", neither of them is empty */
out->queue->tail->next = in->queue->head;
in->queue->head->prev = out->queue->tail;
/* update the queue tail and length */
out->queue->tail = in->queue->tail;
out->queue->length += in->queue->length;
/* reset in->queue) */
g_queue_init(in->queue);
}
/* count bytes in chunkqueues */
goffset len = in->length;
in->bytes_out += len;
in->length = 0;
out->bytes_in += len;
out->length += len;
return len;
}
/* steal the first chunk from in and append it to out, return number of bytes stolen */
goffset chunkqueue_steal_chunk(chunkqueue *out, chunkqueue *in) {
goffset length;
GList *l = g_queue_pop_head_link(in->queue);
if (!l) return 0;
g_queue_push_tail_link(out->queue, l);
length = chunk_length((chunk*) l->data);
in->bytes_out += length;
in->length -= length;
out->bytes_in += length;
out->length += length;
return length;
}
/* skip up to length bytes in a chunkqueue, return number of bytes skipped */
goffset chunkqueue_skip(chunkqueue *cq, goffset length) {
chunk *c;
goffset bytes = 0;
goffset we_have;
while ( (NULL != (c = chunkqueue_first_chunk(cq))) && length > 0 ) {
we_have = chunk_length(c);
if (we_have <= length) {
/* skip (delete) complete chunk */
chunk_free(c);
g_queue_pop_head(cq->queue);
bytes += we_have;
length -= we_have;
} else { /* skip first part of a chunk */
c->offset += length;
bytes += length;
length = 0;
}
}
cq->bytes_out += bytes;
cq->length -= bytes;
return bytes;
}
goffset chunkqueue_skip_all(chunkqueue *cq) {
goffset bytes = cq->length;
g_queue_foreach(cq->queue, __chunk_free, NULL);
g_queue_clear(cq->queue);
cq->bytes_out += bytes;
cq->length = 0;
return bytes;
}

191
src/chunks.h

@ -0,0 +1,191 @@
#ifndef _LIGHTTPD_CHUNK_H_
#define _LIGHTTPD_CHUNK_H_
#include <glib.h>
struct chunkfile;
typedef struct chunkfile chunkfile;
struct chunk;
typedef struct chunk chunk;
struct chunkqueue;
typedef struct chunkqueue chunkqueue;
struct chunkiter;
typedef struct chunkiter chunkiter;
#include "base.h"
/* Open a file only once, so it shouldn't get lost;
* as a file may get split into many chunks, we
* use this struct to keep track of the usage
*/
struct chunkfile {
gint refcount;
GString *name; /* name of the file */
int fd;
gboolean is_temp; /* file is temporary and will be deleted on cleanup */
};
struct chunk {
enum { UNUSED_CHUNK, MEM_CHUNK, FILE_CHUNK } type;
goffset offset;
/* if type == FILE_CHUNK and mem != NULL,
* mem contains the data [file.mmap.offset .. file.mmap.offset + file.mmap.length)
* from the file, and file.mmap.start is NULL as mmap failed and read(...) was used.
*/
GString *mem;
struct {
chunkfile *file;
off_t start; /* starting offset in the file */
off_t length; /* octets to send from the starting offset */
struct {
char *data; /* the pointer of the mmap'ed area */
size_t length; /* size of the mmap'ed area */
off_t offset; /* start is <n> octets away from the start of the file */
} mmap;
} file;
};
struct chunkqueue {
/* public */
gboolean is_closed;
/* read only */
goffset bytes_in, bytes_out, length;
/* private */
GQueue *queue;
};
struct chunkiter {
/* private */
GList *element;
};
/******************
* chunkfile *
******************/
/* open the file cf->name if it is not already opened for reading
* may return HANDLER_GO_ON, HANDLER_ERROR, HANDLER_WAIT_FOR_FD
*/
handler_t chunkfile_open(server *srv, connection *con, chunkfile *cf);
/******************
* chunk iterator *
******************/
INLINE chunk* chunkiter_chunk(chunkiter iter);
INLINE gboolean chunkiter_next(chunkiter *iter);
INLINE goffset chunkiter_length(chunkiter iter);
/* get the data from a chunk; easy in case of a MEM_CHUNK,
* but needs to do io in case of FILE_CHUNK; it tries mmap and
* falls back to read(...)
* the data is _not_ marked as "done"
* may return HANDLER_GO_ON, HANDLER_ERROR, HANDLER_WAIT_FOR_FD
*/
handler_t chunkiter_read(server *srv, connection *con, chunkiter iter, off_t start, off_t length, char **data_start, off_t *data_len);
/******************
* chunk *
******************/
INLINE goffset chunk_length(chunk *c);
/******************
* chunkqueue *
******************/
chunkqueue* chunkqueue_new();
void chunkqueue_reset(chunkqueue *cq);
void chunkqueue_free(chunkqueue *cq);
/* pass ownership of str to chunkqueue, do not free/modify it afterwards
* you may modify the data (not the length) if you are sure it isn't sent before.
*/
void chunkqueue_append_string(chunkqueue *cq, GString *str);
/* memory gets copied */
void chunkqueue_append_mem(chunkqueue *cq, void *mem, gssize len);
/* pass ownership of filename, do not free it */
void chunkqueue_append_file(chunkqueue *cq, GString *filename, off_t start, off_t length);
/* if you already opened the file, you can pass the fd here - do not close it */
void chunkqueue_append_file_fd(chunkqueue *cq, GString *filename, off_t start, off_t length, int fd);
/* temp files get deleted after usage */
/* pass ownership of filename, do not free it */
void chunkqueue_append_tempfile(chunkqueue *cq, GString *filename, off_t start, off_t length);
/* if you already opened the file, you can pass the fd here - do not close it */
void chunkqueue_append_tempfile_fd(chunkqueue *cq, GString *filename, off_t start, off_t length, int fd);
/* steal up to length bytes from in and put them into out, return number of bytes stolen */
goffset chunkqueue_steal_len(chunkqueue *out, chunkqueue *in, goffset length);
/* steal all chunks from in and put them into out, return number of bytes stolen */
goffset chunkqueue_steal_all(chunkqueue *out, chunkqueue *in);
/* steal the first chunk from in and append it to out, return number of bytes stolen */
goffset chunkqueue_steal_chunk(chunkqueue *out, chunkqueue *in);
/* skip up to length bytes in a chunkqueue, return number of bytes skipped */
goffset chunkqueue_skip(chunkqueue *cq, goffset length);
/* skip all chunks in a queue (similar to reset, but keeps stats) */
goffset chunkqueue_skip_all(chunkqueue *cq);
/* if the chunk an iterator refers gets stolen/skipped/...,
* the iterator isn't valid anymore
*/
INLINE chunkiter chunkqueue_iter(chunkqueue *cq);
INLINE chunk* chunkqueue_first_chunk(chunkqueue *cq);
/********************
* Inline functions *
********************/
INLINE chunk* chunkiter_chunk(chunkiter iter) {
if (!iter.element) return NULL;
return (chunk*) iter.element->data;
}
INLINE gboolean chunkiter_next(chunkiter *iter) {
if (!iter || !iter->element) return FALSE;
return NULL != (iter->element = g_list_next(iter->element));
}
INLINE goffset chunkiter_length(chunkiter iter) {
return chunk_length(chunkiter_chunk(iter));
}
INLINE goffset chunk_length(chunk *c) {
if (!c) return 0;
switch (c->type) {
case UNUSED_CHUNK:
return 0;
case MEM_CHUNK:
return c->mem->len - c->offset;
case FILE_CHUNK:
return c->file.length - c->offset;
}
return 0;
}
INLINE chunkiter chunkqueue_iter(chunkqueue *cq) {
chunkiter i;
i.element = g_queue_peek_head_link(cq->queue);
return i;
}
INLINE chunk* chunkqueue_first_chunk(chunkqueue *cq) {
return (chunk*) g_queue_peek_head(cq->queue);
}
#endif

31
src/log.c

@ -0,0 +1,31 @@
#include "log.h"
#include <stdarg.h>
#if REMOVE_PATH_FROM_FILE
const char *remove_path(const char *path) {
char *p = strrchr(path, DIR_SEPERATOR);
if (NULL != p && *(p) != '\0') {
return (p + 1);
}
return path;
}
#endif
int log_write(server* UNUSED_PARAM(srv), connection* UNUSED_PARAM(con), const char *fmt, ...) {
va_list ap;
GString *logline;
logline = g_string_sized_new(0);
va_start(ap, fmt);
g_string_vprintf(logline, fmt, ap);
va_end(ap);
g_string_append_len(logline, CONST_STR_LEN("\r\n"));
write(STDERR_FILENO, logline->str, logline->len);
g_string_free(logline, TRUE);
return 0;
}

48
src/log.h

@ -0,0 +1,48 @@
#ifndef _LIGHTTPD_LOG_H_
#define _LIGHTTPD_LOG_H_
/* #include "valgrind/valgrind.h" */
#include "base.h"
#define REMOVE_PATH_FROM_FILE 1
#if REMOVE_PATH_FROM_FILE
LI_API const char *remove_path(const char *path);
#define REMOVE_PATH(file) remove_path(file)
#else
#define REMOVE_PATH(file) file
#endif
#define ERROR(fmt, ...) \
log_write(NULL, NULL, "%s.%d: (error) "fmt, REMOVE_PATH(__FILE__), __LINE__, __VA_ARGS__)
#define TRACE(fmt, ...) \
log_write(NULL, NULL, "%s.%d: (trace) "fmt, REMOVE_PATH(__FILE__), __LINE__, __VA_ARGS__)
#define SEGFAULT(fmt, ...) \
do { \
log_write(NULL, NULL, "%s.%d: (crashing) "fmt, REMOVE_PATH(__FILE__), __LINE__, __VA_ARGS__); \
/* VALGRIND_PRINTF_BACKTRACE(fmt, __VA_ARGS__); */\
abort();\
} while(0)
#define CON_ERROR(srv, con, fmt, ...) \
log_write(srv, con, "%s.%d: (error) "fmt, REMOVE_PATH(__FILE__), __LINE__, __VA_ARGS__)
#define CON_TRACE(srv, con, fmt, ...) \
log_write(srv, con, "%s.%d: (trace) "fmt, REMOVE_PATH(__FILE__), __LINE__, __VA_ARGS__)
#define CON_SEGFAULT(srv, con, fmt, ...) \
do { \
log_write(srv, con, "%s.%d: (crashing) "fmt, REMOVE_PATH(__FILE__), __LINE__, __VA_ARGS__); \
/* VALGRIND_PRINTF_BACKTRACE(fmt, __VA_ARGS__); */ \
abort();\
} while(0)
/* TODO: perhaps make portable (detect if cc supports) */
#define __ATTRIBUTE_PRINTF_FORMAT(fmt, arg) __attribute__ ((__format__ (__printf__, fmt, arg)))
LI_API int log_write(server *srv, connection *con, const char *fmt, ...) __ATTRIBUTE_PRINTF_FORMAT(3, 4);
#endif

31
src/options.h

@ -0,0 +1,31 @@
#ifndef _LIGHTTPD_OPTIONS_H_
#define _LIGHTTPD_OPTIONS_H_
typedef enum { OPTION_NONE, OPTION_BOOLEAN, OPTION_INT, OPTION_STRING, OPTION_LIST, OPTION_HASH } option_type;
struct option;
typedef struct option option;
struct option_mark;
typedef struct option_mark option_mark;
struct option {
option_type type;
union {
gboolean opt_bool;
gint opt_int;
GString *opt_string;
/* array of option */
GArray *opt_list;
/* hash GString => option */
GHash *opt_hash;
} value;
};
struct option_mark {
size_t ndx;
gpointer value;
};
#endif

48
src/plugin.h

@ -0,0 +1,48 @@
#ifndef _PLUGIN_H_
#define _PLUGIN_H_
#include "base.h"
#include "option.h"
struct plugin;
typedef struct plugin plugin;
struct module_option;
typedef struct module_option module_option;
#define INIT_FUNC(x) \
LI_EXPORT void * x(server *srv, plugin *)
#define PLUGIN_DATA \
size_t id; \
ssize_t option_base_ndx
struct plugin {
size_t version;
GString *name; /* name of the plugin */
void *(* init) (server *srv, plugin *p);
/* the plugin must free the _content_ of the option */
option_mark *(* parse_option) (server *src, void *p_d, size_t option_ndx, option *option);
gpointer data;
/* dlopen handle */
void *lib;
module_option *options;
};
struct module_option {
const char *key;
option_type type;
};
struct server_option {
plugin *p;
size_t index, module_index;
option_type type;
};
#endif

7
src/server.c

@ -0,0 +1,7 @@
#include "log.h"
int main() {
TRACE("%s", "Test!");
return 0;
}

189
src/settings.h

@ -0,0 +1,189 @@
#ifndef _LIGHTTPD_SETTINGS_H_
#define _LIGHTTPD_SETTINGS_H_
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <glib.h>
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#ifdef HAVE_STDINT_H
# include <stdint.h>
#endif
#ifdef HAVE_STDDEF_H
# include <stddef.h>
#endif
#ifdef HAVE_INTTYPES_H
# include <inttypes.h>
#endif
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#include <errno.h>
#include <string.h>
/**
* if glib supports threads we will use it for async file-io
*/
#ifdef G_THREADS_ENABLED
# ifndef USE_GTHREAD
# define USE_GTHREAD
# endif
#endif
/* on linux 2.4.x you get either sendfile or LFS */
#if defined HAVE_SYS_SENDFILE_H && defined HAVE_SENDFILE && (!defined _LARGEFILE_SOURCE || defined HAVE_SENDFILE64) && defined HAVE_WRITEV && defined(__linux__) && !defined HAVE_SENDFILE_BROKEN
# define USE_LINUX_SENDFILE
# include <sys/sendfile.h>
# include <sys/uio.h>
#endif
/* all the Async IO backends need GTHREAD support */
#if defined(USE_GTHREAD)
# if defined(USE_LINUX_SENDFILE)
# if 0 && defined(HAVE_LIBAIO_H)
/** disabled for now as not all FSs are async-io capable */
# define USE_LINUX_AIO_SENDFILE
# endif
# define USE_GTHREAD_SENDFILE
# endif
# if defined(HAVE_AIO_H) && (!defined(__FreeBSD__))
/* FreeBSD has no SIGEV_THREAD for us */
# define USE_POSIX_AIO
# include <sys/types.h> /* macosx wants it */
# include <aio.h>
# endif
# ifdef HAVE_MMAP
# define USE_GTHREAD_AIO
# endif
#endif
#if defined HAVE_SYS_UIO_H && defined HAVE_SENDFILE && defined HAVE_WRITEV && (defined(__FreeBSD__) || defined(__DragonFly__))
# define USE_FREEBSD_SENDFILE
# include <sys/uio.h>
#endif
#if defined HAVE_SYS_SENDFILE_H && defined HAVE_SENDFILEV && defined HAVE_WRITEV && defined(__sun)
# define USE_SOLARIS_SENDFILEV
# include <sys/sendfile.h>
# include <sys/uio.h>
#endif
#if defined HAVE_SYS_UIO_H && defined HAVE_WRITEV
# define USE_WRITEV
# include <sys/uio.h>
#endif
#if defined HAVE_SYS_MMAN_H && defined HAVE_MMAP
# define USE_MMAP
# include <sys/mman.h>
/* NetBSD 1.3.x needs it */
# ifndef MAP_FAILED
# define MAP_FAILED -1
# endif
#if defined(MAP_ANON)
#define HAVE_MEM_MMAP_ANON
#else
/* let's try /dev/zero */
#define HAVE_MEM_MMAP_ZERO
#endif
#endif
#if defined HAVE_SYS_UIO_H && defined HAVE_WRITEV && defined HAVE_SEND_FILE && defined(__aix)
# define USE_AIX_SENDFILE
#endif
/**
* unix can use read/write or recv/send on sockets
* win32 only recv/send
*/
#ifdef _WIN32
# define WIN32_LEAN_AND_MEAN
# define NOGDI
# define USE_WIN32_SEND
/* wait for async-io support
# define USE_WIN32_TRANSMITFILE
*/
#else
# define USE_WRITE
#endif
typedef enum { HANDLER_UNSET,
HANDLER_GO_ON,
HANDLER_FINISHED,
HANDLER_COMEBACK,
HANDLER_WAIT_FOR_EVENT,
HANDLER_ERROR,
HANDLER_WAIT_FOR_FD
} handler_t;
/* Shared library support */
#ifdef _WIN32
#define LI_IMPORT __declspec(dllimport)
#define LI_EXPORT __declspec(dllexport)
#define LI_DLLLOCAL
#define LI_DLLPUBLIC
#else
#define LI_IMPORT
#ifdef GCC_HASCLASSVISIBILITY
#define LI_EXPORT __attribute__ ((visibility("default")))
#define LI_DLLLOCAL __attribute__ ((visibility("hidden")))
#define LI_DLLPUBLIC __attribute__ ((visibility("default")))
#else
#define LI_EXPORT
#define LI_DLLLOCAL
#define LI_DLLPUBLIC
#endif
#endif
#ifdef LI_DECLARE_EXPORTS
#define LI_API LI_EXPORT
#else
#define LI_API LI_IMPORT
#endif
/* Throwable classes must always be visible on GCC in all binaries */
#ifdef _WIN32
#define LI_EXCEPTIONAPI(api) api
#elif defined(GCC_HASCLASSVISIBILITY)
#define LI_EXCEPTIONAPI(api) LI_EXPORT
#else
#define LI_EXCEPTIONAPI(api)
#endif
#ifdef UNUSED_PARAM
#elif defined(__GNUC__)
# define UNUSED_PARAM(x) UNUSED_ ## x __attribute__((unused))
#elif defined(__LCLINT__)
# define UNUSED_PARAM(x) /*@unused@*/ x
#else
# define UNUSED_PARAM(x) x
#endif
#if __GNUC__
#define INLINE static inline
// # define INLINE extern inline
#else
# define INLINE static
#endif
#include "sys-files.h"
#include "sys-mmap.h"
#include "sys-process.h"
#include "sys-socket.h"
#include "sys-strings.h"
#endif

64
src/sys-files.c

@ -0,0 +1,64 @@
#include "sys-files.h"
#ifdef _WIN32
DIR *opendir(const char *dn) {
DIR *d = g_slice_new0(DIR);
if (INVALID_HANDLE_VALUE == (d->h = FindFirstFile(dn, &(d->finddata)))) {
return NULL;
}
return d;
}
struct dirent *readdir(DIR *d) {
if (!d->dent.d_name) {
/* opendir has set a finddata already, push it out */
d->dent.d_name = d->finddata.cFileName;
return &(d->dent);
}
if (FindNextFile(d->h, &(d->finddata))) {
d->dent.d_name = d->finddata.cFileName;
return &(d->dent);
} else {
return NULL;
}
}
void closedir(DIR *d) {
FindClose(d);
g_slice_free(DIR, d);
}
GString *pathname_unix2local(GString *fn) {
size_t i;
for (i = 0; i < fn->len; i++) {
if (fn->str[i] == '/') {
fn->str[i] = '\\';
}
}
return fn;
}
GString *filename_unix2local(GString *fn) {
size_t i;
for (i = 0; i < fn->len; i++) {
if (fn->str[i] == '/') {
fn->str[i] = '\\';
}
}
#if 0
buffer_prepare_append(fn, 4);
memmove(fn->ptr + 4, fn->ptr, fn->used);
memcpy(fn->ptr, "\\\\?\\", 4);
fn->used += 4;
#endif
return fn;
}
#endif

84
src/sys-files.h

@ -0,0 +1,84 @@
#ifndef _SYS_FILES_H_
#define _SYS_FILES_H_
#define DIR_SEPERATOR_UNIX '/'
#define DIR_SEPERATOR_UNIX_STR "/"
#define DIR_SEPERATOR_WIN '\\'
#define DIR_SEPERATOR_WIN_STR "\\"
#include "settings.h"
#ifdef _WIN32
#include <windows.h>
#include <io.h> /* open */
#include <direct.h> /* chdir */
#define DIR_SEPERATOR DIR_SEPERATOR_WIN
#define DIR_SEPERATOR_STR DIR_SEPERATOR_WIN_STR
#define __S_ISTYPE(mode, mask) (((mode) & _S_IFMT) == (mask))
#undef S_ISDIR
#undef S_ISCHR
#undef S_ISBLK
#undef S_ISREG
#define S_ISDIR(mode) __S_ISTYPE((mode), _S_IFDIR)
#define S_ISCHR(mode) __S_ISTYPE((mode), _S_IFCHR)
#define S_ISBLK(mode) __S_ISTYPE((mode), _S_IFBLK)
#define S_ISREG(mode) __S_ISTYPE((mode), _S_IFREG)
/* we don't support symlinks */
#define S_ISLNK(mode) 0
#define lstat stat
#define mkstemp(x) open(mktemp(x), O_RDWR)
#define mkdir(x, y) mkdir(x)
/* retrieve the most recent network, or general libc error */
#define light_sock_errno() (WSAGetLastError())
struct dirent {
const char *d_name;
};
typedef struct {
HANDLE h;
WIN32_FIND_DATA finddata;
struct dirent dent;
} DIR;
LI_EXPORT DIR * opendir(const char *dn);
LI_EXPORT struct dirent * readdir(DIR *d);
LI_EXPORT void closedir(DIR *d);
LI_EXPORT GString * filename_unix2local(GString *b);
LI_EXPORT GString * pathname_unix2local(GString *b);
#else /* _WIN32 */
#include <unistd.h>
#include <dirent.h>
#define DIR_SEPERATOR DIR_SEPERATOR_UNIX
#define DIR_SEPERATOR_STR DIR_SEPERATOR_UNIX_STR
#define light_sock_errno() (errno)
#define filename_unix2local(x) /* (x) */
#define pathname_unix2local(x) /* (x) */
#endif /* _WIN32 */
#define PATHNAME_APPEND_SLASH(x) \
if (x->len > 1 && x->ptr[x->len - 1] != DIR_SEPERATOR) { \
g_string_append_c(DIR_SEPEARATOR); \
}
#ifndef O_LARGEFILE
# define O_LARGEFILE 0
#endif
#ifndef O_NOATIME
# define O_NOATIME 0
#endif
#endif

26
src/sys-mmap.h

@ -0,0 +1,26 @@
#ifndef WIN32_MMAP_H
#define WIN32_MMAP_H
#include "settings.h"
#ifdef _WIN32
#define MAP_FAILED -1
#define PROT_SHARED 0
#define MAP_SHARED 0
#define PROT_READ 0
#define mmap(a, b, c, d, e, f) (-1)
#define munmap(a, b) (-1)
#include <windows.h>
#else
#include <sys/mman.h>
#ifndef MAP_FAILED
#define MAP_FAILED -1
#endif
#endif
#endif

17
src/sys-process.h

@ -0,0 +1,17 @@
#ifndef _SYS_PROCESS_H_
#define _SYS_PROCESS_H_
#ifdef _WIN32
#include <process.h>
#define pid_t int
/* win32 has no fork() */
#define kill(x, y) do { } while (0)
#define getpid() 0
#else
#include <sys/wait.h>
#include <unistd.h>
#endif
#endif

76
src/sys-socket.c

@ -0,0 +1,76 @@
#include "base.h"
#include "sys-socket.h"
#ifndef HAVE_INET_ATON
/* win32 has inet_addr instead if inet_aton */
# ifdef HAVE_INET_ADDR
int inet_aton(const char *cp, struct in_addr *inp) {
struct in_addr a;
a.s_addr = inet_addr(cp);
if (INADDR_NONE == a.s_addr) {
return 0;
}
inp->s_addr = a.s_addr;
return 1;
}
# else
# error no inet_aton emulation found
# endif
#endif
#ifdef _WIN32
#include <winsock2.h>
/* windows doesn't have inet_ntop */
/*
I have to look into this more. WSAAddressToString takes a sockaddr structure, which includes
the port number, so I must first test this stuff more carefully. For now, no IPV6 on windows.
You will notice that HAVE_IPV6 is never true for win32.
*/
const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt)
{
/* WSAAddressToString takes a full sockaddr, while inet_ntop only takes the address */
struct sockaddr_in sock4;
struct sockaddr_in6 sock6;
DWORD addrLen = cnt;
int err = 0;
/* src is either an in_addr or an in6_addr. */
const struct in_addr *src4 = (const struct in_addr*) src;
const struct in6_addr *src6 = (const struct in6_addr*) src;
int ipv6 = af == AF_INET6;
// DebugBreak();
if ( ipv6 )
{
sock6.sin6_family = AF_INET6;
sock6.sin6_port = 0;
sock6.sin6_addr = *src6;
}
else
{
sock4.sin_family = AF_INET;
sock4.sin_port = 0;
sock4.sin_addr = *src4;
}
err = WSAAddressToStringA(
ipv6 ? (LPSOCKADDR) &sock6 : (LPSOCKADDR) &sock4,
ipv6 ? sizeof(sock6) : sizeof(sock4),
NULL,
dst, &addrLen );
return err == 0 ? dst : NULL;
}
#endif

80
src/sys-socket.h

@ -0,0 +1,80 @@
#ifndef SYS_SOCKET_H
#define SYS_SOCKET_H
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef _WIN32
#ifndef FD_SETSIZE
/* By default this is 64 */
#define FD_SETSIZE 4096
#endif
#include <winsock2.h>
#include <ws2tcpip.h>
//#include <wspiapi.h>
//#define HAVE_IPV6 -- not until we've resolved the inet_ntop issue.
#define ECONNRESET WSAECONNRESET
#define EINPROGRESS WSAEINPROGRESS
#define EALREADY WSAEALREADY
#define ENOTCONN WSAENOTCONN
#define EWOULDBLOCK WSAEWOULDBLOCK
#define ECONNABORTED WSAECONNABORTED
#define ECONNREFUSED WSAECONNREFUSED
#define EHOSTUNREACH WSAEHOSTUNREACH
#define ioctl ioctlsocket
#define hstrerror(x) ""
#define STDIN_FILENO 0
#define STDOUT_FILENO 1
#define STDERR_FILENO 2
#ifndef __MINGW32__
#define ssize_t int
#endif
#define sockread( fd, buf, bytes ) recv( fd, buf, bytes, 0 )
LI_EXPORT const char * inet_ntop(int af, const void *src, char *dst, socklen_t cnt);
int inet_aton(const char *cp, struct in_addr *inp);
#define HAVE_INET_ADDR
#undef HAVE_INET_ATON
#else
#include <sys/types.h> /* required by netinet/tcp.h on FreeBSD */
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/un.h>
#include <arpa/inet.h>
#ifndef SUN_LEN
#define SUN_LEN(su) \
(sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
#endif
#define sockread( fd, buf, bytes ) read( fd, buf, bytes )
#define closesocket(x) close(x)
#include <netdb.h>
#endif /* !_WIN32 */
#ifdef HAVE_INET_NTOP
/* only define it if it isn't defined yet */
#ifndef HAVE_IPV6
#define HAVE_IPV6
#endif
#endif
typedef union {
#ifdef HAVE_IPV6
struct sockaddr_in6 ipv6;
#endif
struct sockaddr_in ipv4;
#ifdef HAVE_SYS_UN_H
struct sockaddr_un un;
#endif
struct sockaddr plain;
} sock_addr;
#endif

46
src/sys-strings.h

@ -0,0 +1,46 @@
#ifndef _SYS_STRINGS_H_
#define _SYS_STRINGS_H_
#ifdef _WIN32
#define strcasecmp stricmp
#define strncasecmp strnicmp
#include <stdlib.h>
#define str_to_off_t(p, e, b) _strtoi64(p, e, b)
#define STR_OFF_T_MAX LLONG_MAX
#define STR_OFF_T_MIN LLONG_MIN
#define strtoull _strtoui64
#ifdef __MINGW32__
/* missing prototype */
unsigned __int64 _strtoui64(
const char *nptr,
char **endptr,
int base
);
__int64 _strtoi64(
const char *nptr,
char **endptr,
int base
);
#endif
#else /** we are a unix */
/**
* we use strtoll() for parsing the ranges into a off_t
*
* if off_t is 32bit, we can use strtol() instead
*/
#if SIZEOF_OFF_T == SIZEOF_LONG
#define str_to_off_t(p, e, b) strtol(p, e, b)
#define STR_OFF_T_MAX LONG_MAX
#define STR_OFF_T_MIN LONG_MIN
#elif defined(HAVE_STRTOLL)
#define str_to_off_t(p, e, b) strtoll(p, e, b)
#define STR_OFF_T_MAX LLONG_MAX
#define STR_OFF_T_MIN LLONG_MIN
#else
#error off_t is more than 4 bytes but we can not parse it with strtol() (run autogen.sh again if you build from svn)
#endif
#endif
#endif

120
src/wscript

@ -0,0 +1,120 @@
#! /usr/bin/env python
# encoding: utf-8
#import Action, Object, Params, os, sys
import Params
common_uselib = 'glib '
common_source='''
chunks.c
log.c
sys-files.c
'''
# sys-socket.c
main_source = '''
server.c
'''
#def node_in_same_dir(node, name):
#p = node.m_parent
#n = p.m_files_lookup.get(name, None)
#if not n: n = p.m_build_lookup.get(name, None)
#if n: return n
#newnode = Node(name, p)
#p.m_build_lookup[newnode.m_name]=newnode
#return newnode
#def lemon_file(self, node):
#lemon_task = self.create_task('lemon', nice=40)
#lemon_task.set_inputs([node, node_in_same_dir(node, 'lempar.c')])
#newnodes = [node.change_ext('.c'), node.change_ext('.h')]