Ejemplo n.º 1
0
void
lwan_main_loop(struct lwan *l)
{
    uint64_t cores = 0;

    assert(main_socket == -1);
    main_socket = l->main_socket;
    if (signal(SIGINT, sigint_handler) == SIG_ERR)
        lwan_status_critical("Could not set signal handler");

    lwan_status_info("Ready to serve");

    while (LIKELY(main_socket >= 0)) {
        if (UNLIKELY(accept_herd(l, &cores) == HERD_SHUTDOWN))
            break;

        if (LIKELY(cores)) {
            for (unsigned short t = 0; t < l->thread.count; t++) {
                if (cores & 1ULL<<t)
                    lwan_thread_nudge(&l->thread.threads[t]);
            }

            cores = 0;
        }
    }
}
Ejemplo n.º 2
0
void
lwan_main_loop(lwan_t *l)
{
    assert(main_socket == -1);
    main_socket = l->main_socket;
    if (signal(SIGINT, sigint_handler) == SIG_ERR)
        lwan_status_critical("Could not set signal handler");

    lwan_status_info("Ready to serve");

    for (;;) {
        int client_fd = accept4((int)main_socket, NULL, NULL, SOCK_NONBLOCK | SOCK_CLOEXEC);
        if (UNLIKELY(client_fd < 0)) {
            switch (errno) {
            case EBADF:
            case ECONNABORTED:
                if (main_socket < 0) {
                    lwan_status_info("Signal 2 (Interrupt) received");
                } else {
                    lwan_status_info("Main socket closed for unknown reasons");
                }
                return;
            }

            lwan_status_perror("accept");
        } else {
            schedule_client(l, client_fd);
        }
    }
}
Ejemplo n.º 3
0
void
lwan_main_loop(lwan_t *l)
{
    assert(main_socket == -1);
    main_socket = l->main_socket;
    if (signal(SIGINT, sigint_handler) == SIG_ERR)
        lwan_status_critical("Could not set signal handler");

    lwan_status_info("Ready to serve");

    for (;;) {
        int client_fd = accept4(main_socket, NULL, NULL, SOCK_NONBLOCK | SOCK_CLOEXEC);
        if (UNLIKELY(client_fd < 0)) {
            if (errno != EBADF) {
                lwan_status_perror("accept");
                continue;
            }

            if (main_socket < 0) {
                lwan_status_info("Signal 2 (Interrupt) received");
            } else {
                lwan_status_info("Main socket closed for unknown reasons");
            }

            break;
        }

        schedule_client(l, client_fd);
    }
}
Ejemplo n.º 4
0
static void lwan_module_register(lwan_t *l, const lwan_module_t *module)
{
    if (!module->name)
        lwan_status_critical("Module at %p has no name", module);

    lwan_status_debug("Registering module \"%s\"", module->name);
    hash_add(l->module_registry, module->name, module);
}
Ejemplo n.º 5
0
Archivo: lwan.c Proyecto: diviaki/lwan
static bool setup_from_config(lwan_t *lwan)
{
    config_t conf;
    config_line_t line;
    bool has_listener = false;
    char path_buf[PATH_MAX];
    const char *path;

    path = get_config_path(path_buf);
    lwan_status_info("Loading configuration file: %s", path);

    lwan->url_map_trie = lwan_trie_new(destroy_urlmap);

    if (!config_open(&conf, path))
        return false;

    while (config_read_line(&conf, &line)) {
        switch (line.type) {
        case CONFIG_LINE_TYPE_LINE:
            if (!strcmp(line.line.key, "keep_alive_timeout"))
                lwan->config.keep_alive_timeout = parse_int(line.line.value,
                            default_config.keep_alive_timeout);
            else if (!strcmp(line.line.key, "quiet"))
                lwan->config.quiet = parse_bool(line.line.value,
                            default_config.quiet);
            else if (!strcmp(line.line.key, "reuse_port"))
                lwan->config.reuse_port = parse_bool(line.line.value,
                            default_config.reuse_port);
            else
                config_error(&conf, "Unknown config key: %s", line.line.key);
            break;
        case CONFIG_LINE_TYPE_SECTION:
            if (!has_listener) {
                has_listener = true;
                if (!strcmp(line.section.name, "listener"))
                    parse_listener(&conf, &line, lwan);
                else
                    config_error(&conf, "Unknown section type: %s", line.section.name);
            } else {
                config_error(&conf, "Only one listener supported");
            }
            break;
        case CONFIG_LINE_TYPE_SECTION_END:
            config_error(&conf, "Unexpected section end");
        }
    }

    if (conf.error_message) {
        lwan_status_critical("Error on config file \"%s\", line %d: %s",
              path, conf.line, conf.error_message);
    }

    config_close(&conf);

    return true;
}
Ejemplo n.º 6
0
void
lwan_thread_init(lwan_t *l)
{
    lwan_status_debug("Initializing threads");

    l->thread.threads = calloc((size_t)l->thread.count, sizeof(lwan_thread_t));
    if (!l->thread.threads)
        lwan_status_critical("Could not allocate memory for threads");

    for (short i = 0; i < l->thread.count; i++)
        create_thread(l, &l->thread.threads[i]);
}
Ejemplo n.º 7
0
Archivo: lwan.c Proyecto: lpereira/lwan
static void try_setup_from_config(struct lwan *l, const struct lwan_config *config)
{
    if (!setup_from_config(l, config->config_file_path)) {
        if (config->config_file_path) {
            lwan_status_critical("Could not read config file: %s",
                                 config->config_file_path);
        }
    }

    /* `quiet` key might have changed value. */
    lwan_status_init(l);
}
Ejemplo n.º 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");
}
Ejemplo n.º 9
0
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;
}
Ejemplo n.º 10
0
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;
}
Ejemplo n.º 11
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)))
        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))
        return NULL;

    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);
    const lwan_t *lwan = t->lwan;
    lwan_connection_t *conns = 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, lwan);

    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_coro(conn, &switcher, &dq);
                } else {
                    conn = ep_event->data.ptr;
                    if (UNLIKELY(ep_event->events & (EPOLLRDHUP | EPOLLHUP))) {
                        destroy_coro(&dq, conn);
                        continue;
                    }

                    resume_coro_if_needed(&dq, conn, epoll_fd);
                }

                death_queue_move_to_last(&dq, conn);
            }
        }
    }

epoll_fd_closed:
    death_queue_kill_all(&dq);
    free(events);

    return NULL;
}
Ejemplo n.º 12
0
Archivo: lwan.c Proyecto: lpereira/lwan
struct lwan_fd_watch *lwan_watch_fd(struct lwan *l,
                                    int fd,
                                    uint32_t events,
                                    coro_function_t coro_fn,
                                    void *data)
{
    struct lwan_fd_watch *watch;

    watch = malloc(sizeof(*watch));
    if (!watch)
        return NULL;

    watch->coro = coro_new(&l->switcher, coro_fn, data);
    if (!watch->coro)
        goto out;

    struct epoll_event ev = {.events = events, .data.ptr = watch->coro};
    if (epoll_ctl(l->epfd, EPOLL_CTL_ADD, fd, &ev) < 0) {
        coro_free(watch->coro);
        goto out;
    }

    watch->fd = fd;
    return watch;

out:
    free(watch);
    return NULL;
}

void lwan_unwatch_fd(struct lwan *l, struct lwan_fd_watch *w)
{
    if (l->main_socket != w->fd) {
        if (epoll_ctl(l->epfd, EPOLL_CTL_DEL, w->fd, NULL) < 0)
            lwan_status_perror("Could not unwatch fd %d", w->fd);
    }

    coro_free(w->coro);
    free(w);
}

void lwan_main_loop(struct lwan *l)
{
    struct epoll_event evs[16];
    struct lwan_fd_watch *watch;

    assert(main_socket == -1);
    main_socket = l->main_socket;

    if (signal(SIGINT, sigint_handler) == SIG_ERR)
        lwan_status_critical("Could not set signal handler");

    watch = lwan_watch_fd(l, l->main_socket, EPOLLIN | EPOLLHUP | EPOLLRDHUP,
                          accept_connection_coro, l);
    if (!watch)
        lwan_status_critical("Could not watch main socket");

    lwan_status_info("Ready to serve");

    while (true) {
        int n_evs = epoll_wait(l->epfd, evs, N_ELEMENTS(evs), -1);

        if (UNLIKELY(n_evs < 0)) {
            if (main_socket < 0)
                break;
            if (errno == EINTR || errno == EAGAIN)
                continue;
            break;
        }

        for (int i = 0; i < n_evs; i++) {
            if (!coro_resume_value(evs[i].data.ptr, (int)evs[i].events))
                break;
        }
    }

    lwan_unwatch_fd(l, watch);
}

#ifdef CLOCK_MONOTONIC_COARSE
__attribute__((constructor)) static void detect_fastest_monotonic_clock(void)
{
    struct timespec ts;

    if (!clock_gettime(CLOCK_MONOTONIC_COARSE, &ts))
        monotonic_clock_id = CLOCK_MONOTONIC_COARSE;
}
#endif

void lwan_set_thread_name(const char *name)
{
    char thread_name[16];
    char process_name[PATH_MAX];
    char *tmp;
    int ret;

    if (proc_pidpath(getpid(), process_name, sizeof(process_name)) < 0)
        return;

    tmp = strrchr(process_name, '/');
    if (!tmp)
        return;

    ret = snprintf(thread_name, sizeof(thread_name), "%s %s", tmp + 1, name);
    if (ret < 0)
        return;

    pthread_set_name_np(pthread_self(), thread_name);
}
Ejemplo n.º 13
0
Archivo: lwan.c Proyecto: lpereira/lwan
static bool setup_from_config(struct lwan *lwan, const char *path)
{
    struct config *conf;
    struct config_line line;
    bool has_listener = false;
    char path_buf[PATH_MAX];

    if (!path)
        path = lwan_get_config_path(path_buf, sizeof(path_buf));
    lwan_status_info("Loading configuration file: %s", path);

    conf = config_open(path);
    if (!conf)
        return false;

    if (!lwan_trie_init(&lwan->url_map_trie, destroy_urlmap))
        return false;

    while (config_read_line(conf, &line)) {
        switch (line.type) {
        case CONFIG_LINE_TYPE_LINE:
            if (streq(line.key, "keep_alive_timeout")) {
                lwan->config.keep_alive_timeout = (unsigned short)parse_long(
                    line.value, default_config.keep_alive_timeout);
            } else if (streq(line.key, "quiet")) {
                lwan->config.quiet =
                    parse_bool(line.value, default_config.quiet);
            } else if (streq(line.key, "reuse_port")) {
                lwan->config.reuse_port =
                    parse_bool(line.value, default_config.reuse_port);
            } else if (streq(line.key, "proxy_protocol")) {
                lwan->config.proxy_protocol =
                    parse_bool(line.value, default_config.proxy_protocol);
            } else if (streq(line.key, "allow_cors")) {
                lwan->config.allow_cors =
                    parse_bool(line.value, default_config.allow_cors);
            } else if (streq(line.key, "expires")) {
                lwan->config.expires =
                    parse_time_period(line.value, default_config.expires);
            } else if (streq(line.key, "error_template")) {
                free(lwan->config.error_template);
                lwan->config.error_template = strdup(line.value);
            } else if (streq(line.key, "threads")) {
                long n_threads =
                    parse_long(line.value, default_config.n_threads);
                if (n_threads < 0)
                    config_error(conf, "Invalid number of threads: %ld",
                                 n_threads);
                lwan->config.n_threads = (unsigned short int)n_threads;
            } else if (streq(line.key, "max_post_data_size")) {
                long max_post_data_size = parse_long(
                    line.value, (long)default_config.max_post_data_size);
                if (max_post_data_size < 0)
                    config_error(conf, "Negative maximum post data size");
                else if (max_post_data_size > 128 * (1 << 20))
                    config_error(conf,
                                 "Maximum post data can't be over 128MiB");
                lwan->config.max_post_data_size = (size_t)max_post_data_size;
            } else if (streq(line.key, "allow_temp_files")) {
                lwan->config.allow_post_temp_file =
                    !!strstr(line.value, "post");
            } else {
                config_error(conf, "Unknown config key: %s", line.key);
            }
            break;
        case CONFIG_LINE_TYPE_SECTION:
            if (streq(line.key, "listener")) {
                if (!has_listener) {
                    parse_listener(conf, &line, lwan);
                    has_listener = true;
                } else {
                    config_error(conf, "Only one listener supported");
                }
            } else if (streq(line.key, "straitjacket")) {
                lwan_straitjacket_enforce_from_config(conf);
            } else {
                config_error(conf, "Unknown section type: %s", line.key);
            }
            break;
        case CONFIG_LINE_TYPE_SECTION_END:
            config_error(conf, "Unexpected section end");
        }
    }

    if (config_last_error(conf)) {
        lwan_status_critical("Error on config file \"%s\", line %d: %s", path,
                             config_cur_line(conf), config_last_error(conf));
    }

    config_close(conf);

    return true;
}
Ejemplo n.º 14
0
static bool setup_from_config(lwan_t *lwan)
{
    config_t conf;
    config_line_t line;
    bool has_listener = false;
    char path_buf[PATH_MAX];
    const char *path;

    path = get_config_path(path_buf);
    lwan_status_info("Loading configuration file: %s", path);

    if (!lwan_trie_init(&lwan->url_map_trie, destroy_urlmap))
        return false;

    if (!config_open(&conf, path))
        return false;

    while (config_read_line(&conf, &line)) {
        switch (line.type) {
        case CONFIG_LINE_TYPE_LINE:
            if (!strcmp(line.line.key, "keep_alive_timeout"))
                lwan->config.keep_alive_timeout = (unsigned short)parse_long(line.line.value,
                            default_config.keep_alive_timeout);
            else if (!strcmp(line.line.key, "quiet"))
                lwan->config.quiet = parse_bool(line.line.value,
                            default_config.quiet);
            else if (!strcmp(line.line.key, "reuse_port"))
                lwan->config.reuse_port = parse_bool(line.line.value,
                            default_config.reuse_port);
            else if (!strcmp(line.line.key, "proxy_protocol"))
                lwan->config.proxy_protocol = parse_bool(line.line.value,
                            default_config.proxy_protocol);
            else if (!strcmp(line.line.key, "expires"))
                lwan->config.expires = parse_time_period(line.line.value,
                            default_config.expires);
            else if (!strcmp(line.line.key, "error_template")) {
                free(lwan->config.error_template);
                lwan->config.error_template = strdup(line.line.value);
            } else if (!strcmp(line.line.key, "threads")) {
                long n_threads = parse_long(line.line.value, default_config.n_threads);
                if (n_threads < 0)
                    config_error(&conf, "Invalid number of threads: %d", n_threads);
                lwan->config.n_threads = (unsigned short int)n_threads;
            }
            else
                config_error(&conf, "Unknown config key: %s", line.line.key);
            break;
        case CONFIG_LINE_TYPE_SECTION:
            if (!strcmp(line.section.name, "listener")) {
                if (!has_listener) {
                    parse_listener(&conf, &line, lwan);
                    has_listener = true;
                } else {
                    config_error(&conf, "Only one listener supported");
                }
            } else if (!strcmp(line.section.name, "straitjacket")) {
                lwan_straitjacket_enforce(&conf, &line);
            } else {
                config_error(&conf, "Unknown section type: %s", line.section.name);
            }
            break;
        case CONFIG_LINE_TYPE_SECTION_END:
            config_error(&conf, "Unexpected section end");
        }
    }

    if (conf.error_message) {
        lwan_status_critical("Error on config file \"%s\", line %d: %s",
              path, conf.line, conf.error_message);
    }

    config_close(&conf);

    return true;
}