You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
230 lines
6.0 KiB
C
230 lines
6.0 KiB
C
|
|
#include "base.h"
|
|
#include "utils.h"
|
|
|
|
static void server_option_free(gpointer _so) {
|
|
g_slice_free(server_option, _so);
|
|
}
|
|
|
|
static void server_action_free(gpointer _sa) {
|
|
g_slice_free(server_action, _sa);
|
|
}
|
|
|
|
static void server_setup_free(gpointer _ss) {
|
|
g_slice_free(server_setup, _ss);
|
|
}
|
|
|
|
server* server_new() {
|
|
server* srv = g_slice_new0(server);
|
|
|
|
srv->magic = LIGHTTPD_SERVER_MAGIC;
|
|
srv->state = SERVER_STARTING;
|
|
|
|
srv->loop = ev_default_loop (0);
|
|
if (!srv->loop) {
|
|
fatal ("could not initialise libev, bad $LIBEV_FLAGS in environment?");
|
|
}
|
|
|
|
srv->connections_active = 0;
|
|
srv->connections = g_array_new(FALSE, TRUE, sizeof(connection*));
|
|
srv->sockets = g_array_new(FALSE, TRUE, sizeof(server_socket*));
|
|
|
|
srv->plugins = g_hash_table_new(g_str_hash, g_str_equal);
|
|
srv->options = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, server_option_free);
|
|
srv->actions = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, server_action_free);
|
|
srv->setups = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, server_setup_free);
|
|
|
|
srv->mainaction = NULL;
|
|
|
|
srv->exiting = FALSE;
|
|
srv->tmp_str = g_string_sized_new(255);
|
|
|
|
srv->cur_ts = time(0);
|
|
srv->last_generated_date_ts = 0;
|
|
srv->ts_date_str = g_string_sized_new(255);
|
|
|
|
return srv;
|
|
}
|
|
|
|
void server_free(server* srv) {
|
|
if (!srv) return;
|
|
/* TODO */
|
|
|
|
g_hash_table_destroy(srv->options);
|
|
g_hash_table_destroy(srv->actions);
|
|
g_hash_table_destroy(srv->setups);
|
|
g_hash_table_destroy(srv->plugins);
|
|
|
|
action_release(srv, srv->mainaction);
|
|
|
|
g_string_free(srv->tmp_str, TRUE);
|
|
g_string_free(srv->ts_date_str, TRUE);
|
|
|
|
/* free logs */
|
|
GHashTableIter iter;
|
|
gpointer k, v;
|
|
g_hash_table_iter_init(&iter, srv->logs);
|
|
while (g_hash_table_iter_next(&iter, &k, &v)) {
|
|
log_free(srv, v);
|
|
}
|
|
g_hash_table_destroy(srv->logs);
|
|
|
|
g_mutex_free(srv->log_mutex);
|
|
g_async_queue_unref(srv->log_queue);
|
|
|
|
g_slice_free(server, srv);
|
|
}
|
|
|
|
static connection* con_get(server *srv) {
|
|
connection *con;
|
|
if (srv->connections_active >= srv->connections->len) {
|
|
con = connection_new(srv);
|
|
con->idx = srv->connections_active++;
|
|
g_array_append_val(srv->connections, con);
|
|
} else {
|
|
con = g_array_index(srv->connections, connection*, srv->connections_active++);
|
|
}
|
|
return con;
|
|
}
|
|
|
|
void con_put(server *srv, connection *con) {
|
|
connection_reset(srv, con);
|
|
srv->connections_active--;
|
|
if (con->idx != srv->connections_active) {
|
|
/* Swap [con->idx] and [srv->connections_active] */
|
|
connection *tmp;
|
|
assert(con->idx < srv->connections_active); /* con must be an active connection) */
|
|
tmp = g_array_index(srv->connections, connection*, srv->connections_active);
|
|
tmp->idx = con->idx;
|
|
con->idx = srv->connections_active;
|
|
g_array_index(srv->connections, connection*, con->idx) = con;
|
|
g_array_index(srv->connections, connection*, tmp->idx) = tmp;
|
|
}
|
|
}
|
|
|
|
static void server_listen_cb(struct ev_loop *loop, ev_io *w, int revents) {
|
|
server_socket *sock = (server_socket*) w->data;
|
|
server *srv = sock->srv;
|
|
int s;
|
|
sock_addr remote_addr;
|
|
socklen_t l = sizeof(remote_addr);
|
|
UNUSED(loop);
|
|
UNUSED(revents);
|
|
|
|
while (-1 != (s = accept(w->fd, (struct sockaddr*) &remote_addr, &l))) {
|
|
connection *con = con_get(srv);
|
|
con->remote_addr = remote_addr;
|
|
ev_io_set(&con->sock.watcher, s, EV_READ);
|
|
ev_io_start(srv->loop, &con->sock.watcher);
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
errno = WSAGetLastError();
|
|
#endif
|
|
|
|
switch (errno) {
|
|
case EAGAIN:
|
|
#if EWOULDBLOCK != EAGAIN
|
|
case EWOULDBLOCK:
|
|
#endif
|
|
case EINTR:
|
|
/* we were stopped _before_ we had a connection */
|
|
case ECONNABORTED: /* this is a FreeBSD thingy */
|
|
/* we were stopped _after_ we had a connection */
|
|
break;
|
|
|
|
case EMFILE: /* we are out of FDs */
|
|
/* TODO: server_out_of_fds(srv, NULL); */
|
|
break;
|
|
default:
|
|
ERROR(srv, "accept failed on fd=%d with error: (%d) %s", w->fd, errno, strerror(errno));
|
|
break;
|
|
}
|
|
}
|
|
|
|
void server_listen(server *srv, int fd) {
|
|
server_socket *sock;
|
|
|
|
sock = g_slice_new0(server_socket);
|
|
sock->srv = srv;
|
|
sock->watcher.data = sock;
|
|
fd_init(fd);
|
|
ev_io_init(&sock->watcher, server_listen_cb, fd, EV_READ);
|
|
if (srv->state == SERVER_RUNNING) ev_io_start(srv->loop, &sock->watcher);
|
|
|
|
g_array_append_val(srv->sockets, sock);
|
|
}
|
|
|
|
|
|
static void sigint_cb(struct ev_loop *loop, struct ev_signal *w, int revents) {
|
|
UNUSED(w); UNUSED(revents);
|
|
ev_unloop (loop, EVUNLOOP_ALL);
|
|
}
|
|
|
|
static void sigpipe_cb(struct ev_loop *loop, struct ev_signal *w, int revents) {
|
|
/* ignore */
|
|
UNUSED(loop); UNUSED(w); UNUSED(revents);
|
|
}
|
|
|
|
static struct ev_signal
|
|
sig_w_INT,
|
|
sig_w_TERM,
|
|
sig_w_PIPE;
|
|
|
|
#define CATCH_SIGNAL(loop, cb, n) do {\
|
|
ev_signal_init(&sig_w_##n, cb, SIG##n); \
|
|
ev_signal_start(loop, &sig_w_##n); \
|
|
} while (0)
|
|
|
|
void server_start(server *srv) {
|
|
guint i;
|
|
if (srv->state == SERVER_STOPPING || srv->state == SERVER_RUNNING) return; /* no restart after stop */
|
|
srv->state = SERVER_RUNNING;
|
|
|
|
if (!srv->mainaction) {
|
|
ERROR(srv, "%s", "No action handlers defined");
|
|
server_stop(srv);
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < srv->sockets->len; i++) {
|
|
server_socket *sock = g_array_index(srv->sockets, server_socket*, i);
|
|
ev_io_start(srv->loop, &sock->watcher);
|
|
}
|
|
|
|
CATCH_SIGNAL(srv->loop, sigint_cb, INT);
|
|
CATCH_SIGNAL(srv->loop, sigint_cb, TERM);
|
|
CATCH_SIGNAL(srv->loop, sigpipe_cb, PIPE);
|
|
|
|
ev_loop(srv->loop, 0);
|
|
}
|
|
|
|
void server_stop(server *srv) {
|
|
guint i;
|
|
if (srv->state == SERVER_STOPPING) return;
|
|
srv->state = SERVER_STOPPING;
|
|
|
|
for (i = 0; i < srv->sockets->len; i++) {
|
|
server_socket *sock = g_array_index(srv->sockets, server_socket*, i);
|
|
ev_io_stop(srv->loop, &sock->watcher);
|
|
}
|
|
}
|
|
|
|
void joblist_append(server *srv, connection *con) {
|
|
connection_state_machine(srv, con);
|
|
}
|
|
|
|
GString *server_current_timestamp(server *srv) {
|
|
srv->cur_ts = time(0); /* TODO: update cur_ts somewhere else */
|
|
if (srv->cur_ts != srv->last_generated_date_ts) {
|
|
g_string_set_size(srv->ts_date_str, 255);
|
|
strftime(srv->ts_date_str->str, srv->ts_date_str->allocated_len,
|
|
"%a, %d %b %Y %H:%M:%S GMT", gmtime(&(srv->cur_ts)));
|
|
|
|
g_string_set_size(srv->ts_date_str, strlen(srv->ts_date_str->str));
|
|
|
|
srv->last_generated_date_ts = srv->cur_ts;
|
|
}
|
|
return srv->ts_date_str;
|
|
}
|