Beispiel #1
0
void
lwan_init(lwan_t *l)
{
    int max_threads = sysconf(_SC_NPROCESSORS_ONLN);
    struct rlimit r;

    /* Load defaults */
    memcpy(&l->config, &default_config, sizeof(default_config));

    /* Initialize status first, as it is used by other things during
     * their initialization. */
    lwan_status_init(l);

    /* These will only print debugging messages. Debug messages are always
     * printed if we're on a debug build, so the quiet setting will be
     * respected. */
    lwan_job_thread_init();
    lwan_response_init();
    lwan_tables_init();

    /* Load the configuration file. */
    if (!setup_from_config(l))
        lwan_status_warning("Could not read config file, using defaults");

    /* Continue initialization as normal. */
    lwan_status_debug("Initializing lwan web server");

    l->thread.count = max_threads > 0 ? max_threads : 2;

    if (getrlimit(RLIMIT_NOFILE, &r) < 0)
        lwan_status_critical_perror("getrlimit");

    if (r.rlim_max == RLIM_INFINITY)
        r.rlim_cur *= 8;
    else if (r.rlim_cur < r.rlim_max)
        r.rlim_cur = r.rlim_max;
    if (setrlimit(RLIMIT_NOFILE, &r) < 0)
        lwan_status_critical_perror("setrlimit");

    l->conns = calloc(r.rlim_cur, sizeof(lwan_connection_t));
    l->thread.max_fd = r.rlim_cur / l->thread.count;
    lwan_status_info("Using %d threads, maximum %d sockets per thread",
        l->thread.count, l->thread.max_fd);

    for (--r.rlim_cur; r.rlim_cur; --r.rlim_cur)
        l->conns[r.rlim_cur].response_buffer = strbuf_new();

    srand(time(NULL));
    signal(SIGPIPE, SIG_IGN);
    close(STDIN_FILENO);

    lwan_thread_init(l);
    lwan_socket_init(l);
    lwan_http_authorize_init();
}
Beispiel #2
0
static void
allocate_connections(lwan_t *l, size_t max_open_files)
{
    l->conns = calloc(max_open_files, sizeof(lwan_connection_t));
    if (!l->conns)
        lwan_status_critical_perror("calloc");
}
static int
setup_socket_from_systemd(void)
{
    int fd = SD_LISTEN_FDS_START;

    if (!sd_is_socket_inet(fd, AF_UNSPEC, SOCK_STREAM, 1, 0))
        lwan_status_critical("Passed file descriptor is not a "
            "listening TCP socket");

    int flags = fcntl(fd, F_GETFD);
    if (flags < 0)
        lwan_status_critical_perror("Could not obtain socket flags");
    if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0)
        lwan_status_critical_perror("Could not set socket flags");

    return fd;
}
Beispiel #4
0
static void allocate_connections(struct lwan *l, size_t max_open_files)
{
    const size_t sz = max_open_files * sizeof(struct lwan_connection);

    if (posix_memalign((void **)&l->conns, 64, align_to_size(sz, 64)))
        lwan_status_critical_perror("aligned_alloc");

    memset(l->conns, 0, sz);
}
Beispiel #5
0
static rlim_t setup_open_file_count_limits(void)
{
    struct rlimit r;

    if (getrlimit(RLIMIT_NOFILE, &r) < 0)
        lwan_status_critical_perror("getrlimit");

    if (r.rlim_max != r.rlim_cur) {
        if (r.rlim_max == RLIM_INFINITY)
            r.rlim_cur = OPEN_MAX;
        else if (r.rlim_cur < r.rlim_max)
            r.rlim_cur = r.rlim_max;
        if (setrlimit(RLIMIT_NOFILE, &r) < 0)
            lwan_status_critical_perror("setrlimit");
    }

    return r.rlim_cur;
}
Beispiel #6
0
static void
allocate_connections(lwan_t *l, size_t max_open_files)
{
#if defined(_ISOC11_SOURCE)
    const size_t sz = max_open_files * sizeof(lwan_connection_t);

    l->conns = aligned_alloc(64, sz);
    if (!l->conns)
        lwan_status_critical_perror("aligned_alloc");

    memset(l->conns, 0, sz);
#else
    l->conns = calloc(max_open_files, sizeof(lwan_connection_t));

    if (!l->conns)
        lwan_status_critical_perror("calloc");
#endif
}
Beispiel #7
0
static struct lwan_url_map *add_url_map(struct lwan_trie *t, const char *prefix,
                                        const struct lwan_url_map *map)
{
    struct lwan_url_map *copy = malloc(sizeof(*copy));

    if (!copy)
        lwan_status_critical_perror("Could not copy URL map");

    memcpy(copy, map, sizeof(*copy));

    copy->prefix = strdup(prefix ? prefix : copy->prefix);
    if (!copy->prefix)
        lwan_status_critical_perror("Could not copy URL prefix");

    copy->prefix_len = strlen(copy->prefix);
    lwan_trie_add(t, copy->prefix, copy);

    return copy;
}
Beispiel #8
0
void lwan_straitjacket_enforce(config_t *c, config_line_t *l)
{
    char *user_name = NULL;
    char *chroot_path = NULL;
    uid_t uid;
    gid_t gid;

    if (geteuid() != 0) {
        config_error(c, "Straitjacket requires root privileges");
        return;
    }

    while (config_read_line(c, l)) {
        switch (l->type) {
        case CONFIG_LINE_TYPE_LINE:
            /* TODO: limit_syscalls */
            if (!strcmp(l->line.key, "user")) {
                user_name = strdupa(l->line.value);
            } else if (!strcmp(l->line.key, "chroot")) {
                chroot_path = strdupa(l->line.value);
            } else {
                config_error(c, "Invalid key: %s", l->line.key);
                return;
            }
            break;
        case CONFIG_LINE_TYPE_SECTION:
            config_error(c, "Straitjacket accepts no sections");
            return;
        case CONFIG_LINE_TYPE_SECTION_END:
            if (!get_user_uid_gid(user_name, &uid, &gid)) {
                config_error(c, "Unknown user: %s", user_name);
                return;
            }

            if (chroot_path) {
                if (chroot(chroot_path) < 0) {
                    lwan_status_critical_perror("Could not chroot() to %s",
                        chroot_path);
                }
                lwan_status_debug("Jailed to %s", chroot_path);
            }

            if (!switch_to_user(uid, gid, user_name)) {
                lwan_status_critical("Could not drop privileges to %s, aborting",
                    user_name);
            }
            return;
        }
    }

    config_error(c, "Expecting section end while parsing straitjacket");
}
Beispiel #9
0
void
lwan_trie_add(struct lwan_trie *trie, const char *key, void *data)
{
    if (UNLIKELY(!trie || !key || !data))
        return;

    struct lwan_trie_node **knode, *node;
    const char *orig_key = key;

    /* Traverse the trie, allocating nodes if necessary */
    for (knode = &trie->root; *key; knode = &node->next[(int)(*key++ & 7)])
        GET_NODE();

    /* Get the leaf node (allocate it if necessary) */
    GET_NODE();

    struct lwan_trie_leaf *leaf = find_leaf_with_key(node, orig_key, (size_t)(key - orig_key));
    bool had_key = leaf;
    if (!leaf) {
        leaf = malloc(sizeof(*leaf));
        if (!leaf)
            lwan_status_critical_perror("malloc");
    } else if (trie->free_node) {
        trie->free_node(leaf->data);
    }

    leaf->data = data;
    if (!had_key) {
        leaf->key = strdup(orig_key);
        leaf->next = node->leaf;
        node->leaf = leaf;
    }
    return;

oom:
    lwan_status_critical_perror("calloc");
}
Beispiel #10
0
static void
create_thread(lwan_t *l, lwan_thread_t *thread)
{
    pthread_attr_t attr;

    memset(thread, 0, sizeof(*thread));
    thread->lwan = l;

    if ((thread->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0)
        lwan_status_critical_perror("epoll_create");

    if (pthread_attr_init(&attr))
        lwan_status_critical_perror("pthread_attr_init");

    if (pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM))
        lwan_status_critical_perror("pthread_attr_setscope");

    if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE))
        lwan_status_critical_perror("pthread_attr_setdetachstate");

    if (pipe2(thread->pipe_fd, O_NONBLOCK | O_CLOEXEC) < 0)
        lwan_status_critical_perror("pipe");

    struct epoll_event event = { .events = EPOLLIN, .data.ptr = NULL };
    if (epoll_ctl(thread->epoll_fd, EPOLL_CTL_ADD, thread->pipe_fd[0], &event) < 0)
        lwan_status_critical_perror("epoll_ctl");

    if (pthread_create(&thread->self, &attr, thread_io_loop, thread))
        lwan_status_critical_perror("pthread_create");

    if (pthread_attr_destroy(&attr))
        lwan_status_critical_perror("pthread_attr_destroy");
}

void
lwan_thread_add_client(lwan_thread_t *t, int fd)
{
    t->lwan->conns[fd].flags = 0;
    t->lwan->conns[fd].thread = t;

    if (UNLIKELY(write(t->pipe_fd[1], &fd, sizeof(int)) < 0))
        lwan_status_perror("write");
}
Beispiel #11
0
void lwan_set_url_map(struct lwan *l, const struct lwan_url_map *map)
{
    lwan_trie_destroy(&l->url_map_trie);
    if (UNLIKELY(!lwan_trie_init(&l->url_map_trie, destroy_urlmap)))
        lwan_status_critical_perror("Could not initialize trie");

    for (; map->prefix; map++) {
        struct lwan_url_map *copy = add_url_map(&l->url_map_trie, NULL, map);

        if (copy->module && copy->module->create) {
            copy->data = copy->module->create (map->prefix, copy->args);
            copy->flags = copy->module->flags;
            copy->handler = copy->module->handle_request;
        } else {
            copy->flags = HANDLER_PARSE_MASK;
        }
    }
}
static int
listen_addrinfo(int fd, const struct addrinfo *addr)
{
    if (listen(fd, get_backlog_size()) < 0)
        lwan_status_critical_perror("listen");

    char host_buf[NI_MAXHOST], serv_buf[NI_MAXSERV];
    int ret = getnameinfo(addr->ai_addr, addr->ai_addrlen, host_buf, sizeof(host_buf),
                      serv_buf, sizeof(serv_buf), NI_NUMERICHOST | NI_NUMERICSERV);
    if (ret)
        lwan_status_critical("getnameinfo: %s", gai_strerror(ret));

    if (addr->ai_family == AF_INET6)
        lwan_status_info("Listening on http://[%s]:%s", host_buf, serv_buf);
    else
        lwan_status_info("Listening on http://%s:%s", host_buf, serv_buf);

    return fd;
}
Beispiel #13
0
void lwan_set_url_map(lwan_t *l, const lwan_url_map_t *map)
{
    lwan_trie_destroy(&l->url_map_trie);
    if (UNLIKELY(!lwan_trie_init(&l->url_map_trie, destroy_urlmap)))
        lwan_status_critical_perror("Could not initialize trie");

    for (; map->prefix; map++) {
        lwan_url_map_t *copy = add_url_map(&l->url_map_trie, NULL, map);

        if (UNLIKELY(!copy))
            continue;

        if (copy->module && copy->module->init) {
            copy->data = copy->module->init(copy->args);
            copy->flags = copy->module->flags;
            copy->handler = copy->module->handle;
        } else {
            copy->flags = HANDLER_PARSE_MASK;
        }
    }
}
Beispiel #14
0
static ALWAYS_INLINE void
_push_request_fd(lwan_t *l, int fd)
{
    unsigned thread;
#ifdef __x86_64__
    assert(sizeof(lwan_connection_t) == 32);
    /* Since lwan_connection_t is guaranteed to be 32-byte long, two of them
     * can fill up a cache line.  This formula will group two connections
     * per thread in a way that false-sharing is avoided.  This gives wrong
     * results when fd=0, but this shouldn't happen (as 0 is either the
     * standard input or the main socket, but even if that changes,
     * scheduling will still work).  */
    thread = ((fd - 1) / 2) % l->thread.count;
#else
    static int counter = 0;
    thread = counter++ % l->thread.count;
#endif
    int epoll_fd = l->thread.threads[thread].epoll_fd;

    struct epoll_event event = {
        .events = EPOLLIN | EPOLLRDHUP | EPOLLERR | EPOLLET,
        .data.ptr = &l->conns[fd]
    };

    l->conns[fd].flags = 0;
    l->conns[fd].thread = &l->thread.threads[thread];

    if (UNLIKELY(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &event) < 0))
        lwan_status_critical_perror("epoll_ctl");
}

static void
_signal_handler(int signal_number)
{
    lwan_status_info("Signal %d (%s) received",
                                signal_number, strsignal(signal_number));
    longjmp(cleanup_jmp_buf, 1);
}
Beispiel #15
0
static lwan_connection_t *
grab_and_watch_client(int epoll_fd, int pipe_fd, lwan_connection_t *conns)
{
    int fd;
    if (UNLIKELY(read(pipe_fd, &fd, sizeof(int)) != sizeof(int))) {
        lwan_status_perror("read");
        return NULL;
    }

    struct epoll_event event = {
        .events = events_by_write_flag[1],
        .data.ptr = &conns[fd]
    };
    if (UNLIKELY(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &event) < 0))
        lwan_status_critical_perror("epoll_ctl");

    return &conns[fd];
}

static void *
thread_io_loop(void *data)
{
    lwan_thread_t *t = data;
    const int epoll_fd = t->epoll_fd;
    const int read_pipe_fd = t->pipe_fd[0];
    const int max_events = min((int)t->lwan->thread.max_fd, 1024);
    lwan_connection_t *conns = t->lwan->conns;
    struct epoll_event *events;
    coro_switcher_t switcher;
    struct death_queue_t dq;
    int n_fds;

    lwan_status_debug("Starting IO loop on thread #%d",
        (unsigned short)(ptrdiff_t)(t - t->lwan->thread.threads) + 1);

    events = calloc((size_t)max_events, sizeof(*events));
    if (UNLIKELY(!events))
        lwan_status_critical("Could not allocate memory for events");

    death_queue_init(&dq, conns, t->lwan->config.keep_alive_timeout);

    for (;;) {
        switch (n_fds = epoll_wait(epoll_fd, events, max_events,
                                   death_queue_epoll_timeout(&dq))) {
        case -1:
            switch (errno) {
            case EBADF:
            case EINVAL:
                goto epoll_fd_closed;
            }
            continue;
        case 0: /* timeout: shutdown waiting sockets */
            death_queue_kill_waiting(&dq);
            break;
        default: /* activity in some of this poller's file descriptor */
            update_date_cache(t);

            for (struct epoll_event *ep_event = events; n_fds--; ep_event++) {
                lwan_connection_t *conn;

                if (!ep_event->data.ptr) {
                    conn = grab_and_watch_client(epoll_fd, read_pipe_fd, conns);
                    if (UNLIKELY(!conn))
                        continue;
                    spawn_or_reset_coro_if_needed(conn, &switcher, &dq);
                } else {
                    conn = ep_event->data.ptr;
                    if (UNLIKELY(ep_event->events & (EPOLLRDHUP | EPOLLHUP))) {
                        destroy_coro(&dq, conn);
                        continue;
                    }

                    spawn_or_reset_coro_if_needed(conn, &switcher, &dq);
                    resume_coro_if_needed(&dq, conn, epoll_fd);
                }

                death_queue_move_to_last(&dq, conn);
            }
        }
    }

epoll_fd_closed:
    free(events);

    return NULL;
}
Beispiel #16
0
static void lwan_fd_watch_init(struct lwan *l)
{
    l->epfd = epoll_create1(EPOLL_CLOEXEC);
    if (l->epfd < 0)
        lwan_status_critical_perror("epoll_create1");
}