Esempio n. 1
0
void
lwan_thread_shutdown(lwan_t *l)
{
    lwan_status_debug("Shutting down threads");

    for (int i = l->thread.count - 1; i >= 0; i--) {
        lwan_thread_t *t = &l->thread.threads[i];
        char less_than_int = 0;

        lwan_status_debug("Closing epoll for thread %d (fd=%d)", i,
            t->epoll_fd);

        /* Close the epoll_fd and write less than an int to signal the
         * thread to gracefully finish.  */
        close(t->epoll_fd);
        write(t->pipe_fd[1], &less_than_int, sizeof(less_than_int));
    }

    for (int i = l->thread.count - 1; i >= 0; i--) {
        lwan_thread_t *t = &l->thread.threads[i];

        lwan_status_debug("Waiting for thread %d to finish", i);
        pthread_join(l->thread.threads[i].self, NULL);

        lwan_status_debug("Closing pipe (%d, %d)", t->pipe_fd[0],
            t->pipe_fd[1]);
        close(t->pipe_fd[0]);
        close(t->pipe_fd[1]);
    }

    free(l->thread.threads);
}
Esempio n. 2
0
File: lwan.c Progetto: lpereira/lwan
void lwan_init_with_config(struct lwan *l, const struct lwan_config *config)
{
    /* Load defaults */
    memset(l, 0, sizeof(*l));
    memcpy(&l->config, config, sizeof(*config));
    l->config.listener = dup_or_null(l->config.listener);
    l->config.config_file_path = dup_or_null(l->config.config_file_path);

    /* 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_tables_init();

    try_setup_from_config(l, config);

    lwan_response_init(l);

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

    l->n_cpus = get_number_of_cpus();
    if (!l->config.n_threads) {
        l->thread.count = l->n_cpus;
        if (l->thread.count == 1)
            l->thread.count = 2;
    } else if (l->config.n_threads > 3 * l->n_cpus) {
        l->thread.count = (short unsigned int)(l->n_cpus * 3);

        lwan_status_warning("%d threads requested, but only %d online CPUs; "
                            "capping to %d threads",
                            l->config.n_threads, l->n_cpus, 3 * l->n_cpus);
    } else if (l->config.n_threads > 63) {
        l->thread.count = 64;

        lwan_status_warning("%d threads requested, but max 64 supported",
            l->config.n_threads);
    } else {
        l->thread.count = l->config.n_threads;
    }

    rlim_t max_open_files = setup_open_file_count_limits();
    allocate_connections(l, (size_t)max_open_files);

    l->thread.max_fd = (unsigned)max_open_files / (unsigned)l->thread.count;
    lwan_status_info("Using %d threads, maximum %d sockets per thread",
                     l->thread.count, l->thread.max_fd);

    signal(SIGPIPE, SIG_IGN);

    lwan_readahead_init();
    lwan_thread_init(l);
    lwan_socket_init(l);
    lwan_http_authorize_init();
    lwan_fd_watch_init(l);
}
Esempio n. 3
0
static void lwan_module_init(lwan_t *l)
{
    if (!l->module_registry) {
        lwan_status_debug("Initializing module registry");
        l->module_registry = hash_str_new(NULL, NULL);
    }
}
Esempio 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);
}
Esempio n. 5
0
void
lwan_thread_shutdown(lwan_t *l)
{
    lwan_status_debug("Shutting down threads");

    for (int i = l->thread.count - 1; i >= 0; i--) {
        lwan_thread_t *t = &l->thread.threads[i];
        char less_than_int = 0;
        ssize_t r;

        lwan_status_debug("Closing epoll for thread %d (fd=%d)", i,
            t->epoll_fd);

        /* Close the epoll_fd and write less than an int to signal the
         * thread to gracefully finish.  */
        close(t->epoll_fd);

        while (true) {
            r = write(t->pipe_fd[1], &less_than_int, sizeof(less_than_int));

            if (r >= 0)
                break;

            if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
                continue;

            lwan_status_error("Could not write to I/O thread (%d) pipe to shutdown", i);
            break;
        }
    }

    for (int i = l->thread.count - 1; i >= 0; i--) {
        lwan_thread_t *t = &l->thread.threads[i];

        lwan_status_debug("Waiting for thread %d to finish", i);
        pthread_join(l->thread.threads[i].self, NULL);

        lwan_status_debug("Closing pipe (%d, %d)", t->pipe_fd[0],
            t->pipe_fd[1]);
        close(t->pipe_fd[0]);
        close(t->pipe_fd[1]);
    }

    free(l->thread.threads);
}
Esempio n. 6
0
File: lwan.c Progetto: diviaki/lwan
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();
}
Esempio n. 7
0
void
lwan_init_with_config(lwan_t *l, const lwan_config_t *config)
{
    /* Load defaults */
    memset(l, 0, sizeof(*l));
    memcpy(&l->config, config, sizeof(*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_tables_init();

    lwan_module_init(l);

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

        /* `quiet` key might have changed value. */
        lwan_status_init(l);
    }

    lwan_response_init(l);

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

    if (!l->config.n_threads) {
        l->thread.count = get_number_of_cpus();
        if (l->thread.count == 1)
            l->thread.count = 2;
    } else {
        l->thread.count = l->config.n_threads;
    }

    rlim_t max_open_files = setup_open_file_count_limits();
    allocate_connections(l, (size_t)max_open_files);

    l->thread.max_fd = (unsigned)max_open_files / (unsigned)l->thread.count;
    lwan_status_info("Using %d threads, maximum %d sockets per thread",
        l->thread.count, l->thread.max_fd);

    signal(SIGPIPE, SIG_IGN);

    lwan_thread_init(l);
    lwan_socket_init(l);
    lwan_http_authorize_init();
}
Esempio n. 8
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]);
}
Esempio n. 9
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");
}
Esempio n. 10
0
void
lwan_tables_init(void)
{
    lwan_status_debug("Initializing tables");
    if (mime_types)
        return;

    mime_types = hash_str_new(NULL, NULL);
    if (!mime_types)
        return;

    size_t i = 0;
    for (i = 0; i < N_ELEMENTS(mime_type_array); i++)
        hash_add(mime_types, mime_type_array[i].extension,
                    mime_type_array[i].mime_type);
}
Esempio n. 11
0
void
lwan_thread_shutdown(lwan_t *l)
{
    lwan_status_debug("Shutting down threads");

    for (int i = l->thread.count - 1; i >= 0; i--) {
        lwan_thread_t *t = &l->thread.threads[i];

        /* Closing epoll_fd makes the thread gracefully finish. */
        close(t->epoll_fd);

        close(t->pipe_fd[0]);
        close(t->pipe_fd[1]);

        pthread_tryjoin_np(l->thread.threads[i].self, NULL);
    }

    free(l->thread.threads);
}
Esempio n. 12
0
static const lwan_module_t *lwan_module_find(lwan_t *l, const char *name)
{
    lwan_module_t *module = hash_find(l->module_registry, name);
    if (!module) {
        lwan_module_t *(*module_fn)(void);
        char module_symbol[128];
        int r;

        for (const char *p = name; *p; p++) {
            if (isalnum(*p) || *p == '_')
                continue;

            lwan_status_error("Module name (%s) contains invalid character: %c",
                name, *p);
            return NULL;
        }

        r = snprintf(module_symbol, sizeof(module_symbol),
            "lwan_module_%s", name);
        if (r < 0 || r >= (int)sizeof(module_symbol)) {
            lwan_status_error("Module name too long: %s", name);
            return NULL;
        }

        module_fn = find_handler_symbol(module_symbol);
        if (!module_fn) {
            lwan_status_error("Module \"%s\" does not exist", name);
            return NULL;
        }

        module = module_fn();
        if (!module) {
            lwan_status_error("Function \"%s()\" didn't return a module",
                module_symbol);
            return NULL;
        }

        lwan_status_debug("Module \"%s\" registered", name);
        hash_add(l->module_registry, module->name, module);
    }

    return module;
}
Esempio n. 13
0
void lwan_shutdown(struct lwan *l)
{
    lwan_status_info("Shutting down");

    free(l->config.listener);
    free(l->config.error_template);
    free(l->config.config_file_path);

    lwan_job_thread_shutdown();
    lwan_thread_shutdown(l);

    lwan_status_debug("Shutting down URL handlers");
    lwan_trie_destroy(&l->url_map_trie);

    free(l->conns);

    lwan_response_shutdown(l);
    lwan_tables_shutdown();
    lwan_status_shutdown(l);
    lwan_http_authorize_shutdown();
}
Esempio n. 14
0
void
lwan_shutdown(lwan_t *l)
{
    lwan_status_info("Shutting down");

    if (l->config.listener != default_config.listener)
        free(l->config.listener);

    lwan_job_thread_shutdown();
    lwan_thread_shutdown(l);

    lwan_status_debug("Shutting down URL handlers");
    lwan_trie_destroy(&l->url_map_trie);

    free(l->conns);

    lwan_response_shutdown();
    lwan_tables_shutdown();
    lwan_status_shutdown(l);
    lwan_http_authorize_shutdown();
    lwan_module_shutdown(l);
}
Esempio n. 15
0
File: lwan.c Progetto: diviaki/lwan
void
lwan_shutdown(lwan_t *l)
{
    lwan_status_info("Shutting down");

    lwan_job_thread_shutdown();
    lwan_thread_shutdown(l);
    lwan_socket_shutdown(l);

    lwan_status_debug("Shutting down URL handlers");
    lwan_trie_destroy(l->url_map_trie);

    int i;
    for (i = l->thread.max_fd * l->thread.count - 1; i >= 0; --i)
        strbuf_free(l->conns[i].response_buffer);

    free(l->conns);

    lwan_response_shutdown();
    lwan_tables_shutdown();
    lwan_status_shutdown(l);
    lwan_http_authorize_shutdown();
}
Esempio n. 16
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;
}
Esempio n. 17
0
void
lwan_tables_shutdown(void)
{
    lwan_status_debug("Shutting down tables");
    hash_free(mime_types);
}