Example #1
0
int http_server_sendfile2(const char *filename, const char *additional_headers, const char *ext) {
    if (0 == *filename)
        filename = ".";
    struct http_server_context *ctx = http_server_get_context();
    int ffd = open(filename, O_RDONLY);
    if (ffd < 0)
        return HTTP_SERVER_NOT_FOUND;
    struct stat st;
    if (0 > fstat(ffd, &st)) {
        LOGGER_PERROR(filename);
        close(ffd);
        return HTTP_SERVER_NOT_FOUND;
    }
    if (S_ISDIR(st.st_mode)) {
        close(ffd);
        return 1;
    }

    vmbuf_reset(&ctx->header);

    if (NULL != ext)
        http_server_header_start(HTTP_STATUS_200, mime_types_by_ext(ext));
    else
        http_server_header_start(HTTP_STATUS_200, mime_types_by_filename(filename));
    vmbuf_sprintf(&ctx->header, "%s%lu", CONTENT_LENGTH, st.st_size);
    if (additional_headers)
        vmbuf_strcpy(&ctx->header, additional_headers);

    http_server_header_close();
    int res = http_server_sendfile_payload(ffd, st.st_size);
    close(ffd);
    if (0 > res)
        LOGGER_PERROR(filename);
    return res;
}
Example #2
0
int http_client_pool_init(struct http_client_pool *http_client_pool, size_t initial, size_t grow) {
    LOGGER_INFO("http client pool: initial=%zu, grow=%zu", initial, grow);
    if (0 > ctx_pool_init(&http_client_pool->ctx_pool, initial, grow, CLIENT_STACK_SIZE, sizeof(struct http_client_context)))
        return -1;

    /* Global to all clients */
    if (!client_chains) {
        struct rlimit rlim;
        if (0 > getrlimit(RLIMIT_NOFILE, &rlim))
            return LOGGER_PERROR("getrlimit(RLIMIT_NOFILE)"), -1;

        client_chains = calloc(rlim.rlim_cur, sizeof(struct list));
        if (!client_chains)
            return LOGGER_PERROR("calloc client_chains"), -1;

        /* storage for multiple client chains */
        client_heads = calloc(rlim.rlim_cur, sizeof(struct list));
        struct list *tmp = client_heads, *tmp_end = tmp + rlim.rlim_cur;
        if (!client_heads)
            return LOGGER_PERROR("calloc client_heads"), -1;
        for (; tmp != tmp_end; ++tmp)
            list_insert_tail(&free_list, tmp);

        idle_ctx = ribs_context_create(SMALL_STACK_SIZE, http_client_idle_handler);

        hashtable_init(&ht_persistent_clients, rlim.rlim_cur);
    }
    return timeout_handler_init(&http_client_pool->timeout_handler);
}
Example #3
0
int http_server_init(struct http_server *server) {
    /*
     * one time global initializers
     */
    if (0 > mime_types_init())
        return LOGGER_ERROR("failed to initialize mime types"), -1;
    if (0 > http_headers_init())
        return LOGGER_ERROR("failed to initialize http headers"), -1;
    /*
     * idle connection handler
     */
    server->idle_ctx = ribs_context_create(SMALL_STACK_SIZE, http_server_idle_handler);
    server->idle_ctx->data.ptr = server;
    /*
     * context pool
     */
    if (0 == server->stack_size || 0 == server->num_stacks) {
        struct rlimit rlim;
        if (0 > getrlimit(RLIMIT_STACK, &rlim))
            return LOGGER_PERROR("getrlimit(RLIMIT_STACK)"), -1;

        long total_mem = sysconf(_SC_PHYS_PAGES);
        if (total_mem < 0)
            return LOGGER_PERROR("sysconf"), -1;
        total_mem *= getpagesize();
        size_t num_ctx_in_one_map = total_mem / rlim.rlim_cur;
        /* half of total mem to start with so we don't need to enable overcommit */
        num_ctx_in_one_map >>= 1;
        LOGGER_INFO("http server pool: initial=%zu, grow=%zu", num_ctx_in_one_map, num_ctx_in_one_map);
        ctx_pool_init(&server->ctx_pool, num_ctx_in_one_map, num_ctx_in_one_map, rlim.rlim_cur, sizeof(struct http_server_context) + server->context_size);
    } else {
Example #4
0
File: timer.c Project: Pferd/ribs2
int ribs_timer(time_t msec, void (*handler)(int)) {
    int tfd = ribs_timer_create(handler);
    if (0 > tfd)
        return LOGGER_PERROR("timerfd"), -1;
    struct itimerspec timerspec = {{msec/1000,(msec % 1000)*1000000},{msec/1000,(msec % 1000)*1000000}};
    if (0 > timerfd_settime(tfd, 0, &timerspec, NULL))
        return LOGGER_PERROR("timerfd_settime: %d", tfd), close(tfd), -1;
    return 0;
}
Example #5
0
File: timer.c Project: Pferd/ribs2
int ribs_timer_create(void (*handler)(int)) {
    int tfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
    if (0 > tfd)
        return LOGGER_PERROR("timerfd_create"), -1;
    struct ribs_context *ctx = ribs_context_create(1024 * 1024, sizeof(void (*)(void)), _ribs_timer_wrapper);
    void (**ref)(int) = (void (**)(int))ctx->reserved;
    *ref = handler;
    if (0 > ribs_epoll_add(tfd, EPOLLIN, ctx))
        return LOGGER_PERROR("epoll_add"), close(tfd), -1;
    return tfd;
}
Example #6
0
int ribs_epoll_add(int fd, uint32_t events, struct ribs_context* ctx) {
    struct epoll_event ev = { .events = events, .data.fd = fd };
    if (0 > epoll_ctl(ribs_epoll_fd, EPOLL_CTL_ADD, fd, &ev))
        return LOGGER_PERROR("epoll_ctl"), -1;
    epoll_worker_set_fd_ctx(fd, ctx);
    return 0;
}

struct ribs_context* small_ctx_for_fd(int fd, void (*func)(void)) {
    void *ctx=ribs_context_create(SMALL_STACK_SIZE, func);
    if (NULL == ctx)
        return LOGGER_PERROR("ribs_context_create"), NULL;
    if (0 > ribs_epoll_add(fd, EPOLLIN, ctx))
        return NULL;
    return ctx;
}
Example #7
0
inline static int create_pidfile(const char *pidfile)
{
    int fd = open(pidfile, O_WRONLY | O_CREAT /*| O_EXCL*/, 0644);
    if (0 > fd)
        LOGGER_PERROR("pidfile: %s", pidfile);
    return fd;
}
Example #8
0
_RIBS_INLINE_ int read_data(struct timeout_handler *timeout_handler, int cfd, struct vmbuf *response, size_t ofs) {
    int res = vmbuf_read(response, cfd);
    if (0 > res) {
        close(cfd);
        return -1;
    }
    *vmbuf_wloc(response) = 0;
    char *data, *line;
    /* Keep reading if
     * 1.you don't see a CRLF
     * OR
     * 2.see a dash and CRLF in the current line
     */
    while ((NULL == strstr((data = vmbuf_data_ofs(response, ofs)), CRLF))
           || ((NULL != strchr((line = strrstr(data, CRLF)), '-')) && (NULL != strstr(line, CRLF)))) {
        if (1 == res)
            sendemail_yield(timeout_handler, cfd);
        if (0 >= (res = vmbuf_read(response, cfd))) {
            LOGGER_PERROR("read");
            close(cfd);
            return -1;
        }
    }
    *vmbuf_wloc(response) = 0;
    return 0;
}
Example #9
0
int daemonize(void) {
    if (0 > pipe(child_is_up_pipe))
        return LOGGER_ERROR("failed to create pipe"), -1;

    pid_t pid = fork();
    if (pid < 0) {
        LOGGER_PERROR("daemonize, fork");
        exit(EXIT_FAILURE);
    }

    if (pid > 0) {
        LOGGER_INFO("daemonize started (pid=%d)", pid);
        close(child_is_up_pipe[1]); /* close the write side */
        /* wait for child to come up */
        uint8_t t;
        int res;
        LOGGER_INFO("waiting for the child process to start...");
        if (0 >= (res = read(child_is_up_pipe[0], &t, sizeof(t)))) {
            if (0 > res)
                LOGGER_PERROR("pipe");
            LOGGER_ERROR("child process failed to start");
            exit(EXIT_FAILURE);
        }
        LOGGER_INFO("child process started successfully");
        exit(EXIT_SUCCESS);
    }

    close(child_is_up_pipe[0]); /* close the read side */

    umask(0);

    if (0 > setsid()) {
        LOGGER_PERROR("daemonize, setsid");
        exit(EXIT_FAILURE);
    }

    int fdnull = open("/dev/null", O_RDWR);
    dup2(fdnull, STDIN_FILENO);
    dup2(fdnull, STDOUT_FILENO);
    dup2(fdnull, STDERR_FILENO);
    close(fdnull);

    pid = getpid();

    LOGGER_INFO("child process started (pid=%d)", pid);
    return 0;
}
Example #10
0
struct http_client_context *http_client_pool_create_client(struct http_client_pool *http_client_pool, struct in_addr addr, uint16_t port, struct ribs_context *rctx) {
    int cfd;
    struct http_client_key key = { .addr = addr, .port = port };
    uint32_t ofs = hashtable_lookup(&ht_persistent_clients, &key, sizeof(struct http_client_key));
    struct list *head;
    if (ofs > 0 && !list_empty(head = client_heads + *(uint32_t *)hashtable_get_val(&ht_persistent_clients, ofs))) {
        struct list *client = list_pop_head(head);
        cfd = client - client_chains;
    } else {
        cfd = socket(PF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP);
        if (0 > cfd)
            return LOGGER_PERROR("socket"), NULL;

        const int option = 1;
        if (0 > setsockopt(cfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option)))
            return LOGGER_PERROR("setsockopt SO_REUSEADDR"), close(cfd), NULL;

        if (0 > setsockopt(cfd, IPPROTO_TCP, TCP_NODELAY, &option, sizeof(option)))
            return LOGGER_PERROR("setsockopt TCP_NODELAY"), close(cfd), NULL;

        struct sockaddr_in saddr = { .sin_family = AF_INET, .sin_port = htons(port), .sin_addr = addr };
        if (0 > connect(cfd, (struct sockaddr *)&saddr, sizeof(saddr)) && EINPROGRESS != errno)
            return LOGGER_PERROR("connect"), close(cfd), NULL;

        struct epoll_event ev;
        ev.events = EPOLLIN | EPOLLOUT | EPOLLET;
        ev.data.fd = cfd;
        if (0 > epoll_ctl(ribs_epoll_fd, EPOLL_CTL_ADD, cfd, &ev))
            return LOGGER_PERROR("epoll_ctl"), close(cfd), NULL;
    }
    struct ribs_context *new_ctx = ctx_pool_get(&http_client_pool->ctx_pool);
    new_ctx->fd = cfd;
    new_ctx->data.ptr = http_client_pool;
    struct epoll_worker_fd_data *fd_data = epoll_worker_fd_map + cfd;
    fd_data->ctx = new_ctx;
    ribs_makecontext(new_ctx, rctx ? rctx : current_ctx, http_client_fiber_main);
    struct http_client_context *cctx = (struct http_client_context *)new_ctx->reserved;
    cctx->key = (struct http_client_key) {
        .addr = addr, .port = port
    };
    vmbuf_init(&cctx->request, 4096);
    vmbuf_init(&cctx->response, 4096);
    timeout_handler_add_fd_data(&http_client_pool->timeout_handler, fd_data);
    return cctx;
}
Example #11
0
_RIBS_INLINE_ int write_data(int cfd, struct vmbuf *request) {
    int sres = vmbuf_write(request, cfd);
    if (0 > sres) {
        LOGGER_PERROR("write request");
        close(cfd);
        return -1;
    }
    return 0;
}
Example #12
0
void daemon_finalize(void) {
    if (0 > child_is_up_pipe[1])
        return;
    uint8_t t = 0;
    if (0 > write(child_is_up_pipe[1], &t, sizeof(t))) {
        LOGGER_PERROR("pipe");
        exit(EXIT_FAILURE);
    }
}
Example #13
0
struct ribs_context *ribs_context_create(size_t stack_size, size_t reserved_size, void (*func)(void)) {
    void *stack;
    stack = calloc(1, stack_size + sizeof(struct ribs_context) + reserved_size);
    if (!stack)
        return LOGGER_PERROR("calloc stack"), NULL;
    stack += stack_size;
    struct ribs_context *ctx = (struct ribs_context *)stack;
    ribs_makecontext(ctx, current_ctx, func);
    return ctx;
}
Example #14
0
static void _handle_sig_child(void) {
    siginfo_t info;
    memset(&info, 0, sizeof(info));
    if (0 > waitid(P_ALL, 0, &info, WEXITED | WNOWAIT | WNOHANG))
        return LOGGER_PERROR("waitid");
    if (info.si_pid > 0) {
        LOGGER_ERROR("child process [%d] terminated unexpectedly: %s, status=%d", info.si_pid, _get_exit_reason(&info), info.si_status);
        epoll_worker_exit();
    }
}
Example #15
0
void ribs_ssl_init(void) {
    if (NULL != ssl_map)
        return;
    struct rlimit rlim;
    if (0 > getrlimit(RLIMIT_NOFILE, &rlim)) {
        LOGGER_PERROR("getrlimit(RLIMIT_NOFILE)");
        return;
    }
    ssl_map = calloc(rlim.rlim_cur, sizeof(SSL *));
}
Example #16
0
static void pipe_to_context(void) {
    void *ctx;
    while (1) {
        if (sizeof(&ctx) != read(last_epollev.data.fd, &ctx, sizeof(&ctx))) {
            LOGGER_PERROR("read in pipe_to_context");
            yield();
        } else
            ribs_swapcurcontext(ctx);
    }
}
Example #17
0
int mime_types_init(void) {
    if (hashtable_is_initialized(&ht_mime_types))
        return 1;
    LOGGER_INFO("initializing mime types");
    int fd = open(MIME_TYPES, O_RDONLY);
    struct stat st;
    if (0 > fstat(fd, &st))
        return LOGGER_PERROR("mime_types fstat"), close(fd), -1;
    void *mem = mmap(NULL, st.st_size + 1, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
    if (MAP_FAILED == mem)
        return LOGGER_PERROR("mime_types mmap"), close(fd), -1;

    hashtable_init(&ht_mime_types, 4096);
    char *content = (char *)mem;
    char *content_end = content + st.st_size;

    while (content < content_end)
    {
        // find end of line
        char *line = content, *p = line;
        for (; p < content_end && *p != '\n'; ++p);
        *p = 0; // mmap'd one extra byte to avoid checks
        content = p + 1;

        p = strchrnul(line, '#');
        *p = 0; // truncate line after comment

        p = line;
        char *saveptr;
        char *mime = strtok_r(p, MIME_DELIMS, &saveptr);
        if (!mime)
            continue;
        char *ext;
        while (NULL != (ext = strtok_r(NULL, MIME_DELIMS, &saveptr))) {
            for (p = ext; *p; *p = tolower(*p), ++p);
            hashtable_insert(&ht_mime_types, ext, strlen(ext), mime, strlen(mime) + 1);
        }
    }
    munmap(mem, st.st_size + 1);
    close(fd);
    return 0;
}
Example #18
0
int _ribified_connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
    int flags=fcntl(sockfd, F_GETFL);
    if (0 > fcntl(sockfd, F_SETFL, flags | O_NONBLOCK))
        return LOGGER_PERROR("fcntl"), -1;

    int res = connect(sockfd, addr, addrlen);
    if (res < 0 && errno != EINPROGRESS) {
        return res;
    }
    return ribs_epoll_add(sockfd, EPOLLIN | EPOLLOUT | EPOLLET | EPOLLRDHUP, event_loop_ctx);
}
Example #19
0
int http_server_sendfile_payload(int ffd, off_t size) {
    struct http_server_context *ctx = http_server_get_context();
    int fd = ctx->fd;
    int option = 1;
    if (0 > setsockopt(fd, IPPROTO_TCP, TCP_CORK, &option, sizeof(option)))
        LOGGER_PERROR("TCP_CORK set");
    epoll_worker_resume_events(ctx->fd);
    http_server_write();
    vmbuf_reset(&ctx->header);
    off_t ofs = 0;
    for (;;http_server_yield()) {
        if (0 > sendfile(fd, ffd, &ofs, size - ofs) && EAGAIN != errno)
            return ctx->persistent = 0, -1;
        if (ofs >= size) break;
    }
    option = 0;
    if (0 > setsockopt(fd, IPPROTO_TCP, TCP_CORK, &option, sizeof(option)))
        LOGGER_PERROR("TCP_CORK release");
    return 0;
}
Example #20
0
static void sigrtmin_to_context(void) {
    struct signalfd_siginfo siginfo;
    while (1) {
       int res = read(last_epollev.data.fd, &siginfo, sizeof(struct signalfd_siginfo));
       if (sizeof(struct signalfd_siginfo) != res || NULL == (void *)siginfo.ssi_ptr) {
           LOGGER_PERROR("sigrtmin_to_ctx got NULL or < 128 bytes: %d", res);
           yield();
       } else
           ribs_swapcurcontext((void *)siginfo.ssi_ptr);
    }
}
Example #21
0
int mime_types_init(void) {
    if (hashtable_is_initialized(&ht_mime_types) > 0)
        return 1;
    LOGGER_INFO("initializing mime types");
    int fd = open(MIME_TYPES, O_RDONLY);
    /* if can't find mime file, use copy - needed for mac osx */
    if (0 > fd) {
        build_mime_table(mime_file, mime_file+sizeof(mime_file));
    } else {
        struct stat st;
        if (0 > fstat(fd, &st))
            return LOGGER_PERROR("mime_types fstat"), close(fd), -1;
        void *mem = mmap(NULL, st.st_size + 1, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
        if (MAP_FAILED == mem)
            return LOGGER_PERROR("mime_types mmap"), close(fd), -1;
        build_mime_table((char *)mem, mem + st.st_size);
        munmap(mem, st.st_size + 1);
        close(fd);
    }
    return 0;
}
Example #22
0
File: timer.c Project: Pferd/ribs2
static void _ribs_timer_wrapper(void) {
    void (**handler)(int) = (void (**)(int))current_ctx->reserved;
    for (;;yield()) {
        uint64_t num_exp;
        ssize_t res;
        if (sizeof(num_exp) == (res = read(last_epollev.data.fd, &num_exp, sizeof(num_exp))))
            (*handler)(last_epollev.data.fd);
        if (0 > res) {
            if (errno != EAGAIN)
                LOGGER_PERROR("timerfd (%d)", last_epollev.data.fd);
        } else
            LOGGER_ERROR("size mismatch when reading from timerfd: %d", last_epollev.data.fd);
    }
}
Example #23
0
int queue_current_ctx(void) {
    while (0 > write(queue_ctx_fd, &current_ctx, sizeof(void *))) {
        if (EAGAIN != errno)
            return LOGGER_PERROR("unable to queue context: write"), -1;
        /* pipe is full!!! wait for it to clear
           This is switching to LIFO mode, which can cause IO starvation
           if too many contexes are trying to use this facility, very unlikely
        */
        LOGGER_INFO("Warning: context queue is full");
        struct ribs_context *previous_context = epoll_worker_fd_map[queue_ctx_fd].ctx;
        epoll_worker_fd_map[queue_ctx_fd].ctx = current_ctx;
        yield(); // come back to me when can write
        epoll_worker_fd_map[queue_ctx_fd].ctx = previous_context;
    }
    return 0;
}
Example #24
0
int epoll_worker_init(void) {
    struct rlimit rlim;
    if (0 > getrlimit(RLIMIT_NOFILE, &rlim))
        return LOGGER_PERROR("getrlimit(RLIMIT_NOFILE)"), -1;
    epoll_worker_fd_map = calloc(rlim.rlim_cur, sizeof(struct epoll_worker_fd_data));

    ribs_epoll_fd = epoll_create1(EPOLL_CLOEXEC);
    if (ribs_epoll_fd < 0)
        return LOGGER_PERROR("epoll_create1"), -1;

    /* block some signals */
    sigset_t set;
    sigemptyset(&set);
    sigaddset(&set, SIGPIPE);
    if (-1 == sigprocmask(SIG_BLOCK, &set, NULL))
        return LOGGER_PERROR("sigprocmask"), -1;

#ifdef UGLY_GETADDRINFO_WORKAROUND
    sigemptyset(&set);
    sigaddset(&set, SIGRTMIN);
    if (-1 == sigprocmask(SIG_BLOCK, &set, NULL))
        return LOGGER_PERROR("sigprocmask"), -1;

    /* sigrtmin to context */
    int sfd = signalfd(-1, &set, SFD_NONBLOCK);
    if (0 > sfd)
        return LOGGER_PERROR("signalfd"), -1;
    if (NULL == small_ctx_for_fd(sfd, sigrtmin_to_context))
        return -1;
#endif

    event_loop_ctx = ribs_context_create(SMALL_STACK_SIZE, event_loop);

    /* pipe to conetxt */
    int pipefd[2];
    if (0 > pipe2(pipefd, O_NONBLOCK))
        return LOGGER_PERROR("pipe"), -1;
    if (NULL == small_ctx_for_fd(pipefd[0], pipe_to_context))
        return -1;
    queue_ctx_fd = pipefd[1];
    return ribs_epoll_add(queue_ctx_fd, EPOLLOUT | EPOLLET, event_loop_ctx);
}
Example #25
0
static int _logger_init(const char *filename) {
    int res = 0;
    if ('|' == *filename) {
        ++filename;
        int fds[2];
        if (0 > pipe2(fds, O_CLOEXEC))
            return LOGGER_PERROR("pipe"), -1;
        int pid = fork();
        if (0 > pid)
            return -1;
        if (0 == pid) {
            if (0 > dup2(fds[0], STDIN_FILENO))
                LOGGER_PERROR("dup2"), res = -1;
            close(fds[0]);
            close(fds[1]);
            if (0 > execl("/bin/sh", "/bin/sh", "-c", filename, NULL))
                LOGGER_PERROR("execl: %s", filename), res = -1;
        } else {
            logger_pid = pid;
            if (0 > dup2(fds[1], STDOUT_FILENO) ||
                0 > dup2(fds[1], STDERR_FILENO))
                LOGGER_PERROR("dup2"), res = -1;
            close(fds[0]);
            close(fds[1]);
        }
    } else {
        // open
        int fd = open(filename, O_CREAT | O_WRONLY | O_APPEND, 0644);
        if (fd < 0)
            return LOGGER_PERROR(filename), -1;
        if (0 > dup2(fd, STDOUT_FILENO) ||
            0 > dup2(fd, STDERR_FILENO))
            LOGGER_PERROR("dup2"), res = -1;
        close(fd);
    }
    return res;
}
Example #26
0
void http_client_fiber_main(void) {
    struct http_client_context *ctx = (struct http_client_context *)current_ctx->reserved;
    int fd = current_ctx->fd;
    struct epoll_worker_fd_data *fd_data = epoll_worker_fd_map + fd;
    TIMEOUT_HANDLER_REMOVE_FD_DATA(fd_data);

    int persistent = 0;
    epoll_worker_set_last_fd(fd); /* needed in the case where epoll_wait never occured */

    /*
     * write request
     */
    int res;
    for (; (res = vmbuf_write(&ctx->request, fd)) == 0; http_client_yield());
    if (0 > res) {
        LOGGER_PERROR("write");
        CLIENT_ERROR();
    }
    /*
     * HTTP header
     */
    uint32_t eoh_ofs;
    char *data;
    char *eoh;
    res = vmbuf_read(&ctx->response, fd);
    *vmbuf_wloc(&ctx->response) = 0;
    READ_MORE_DATA_STR(NULL == (eoh = strstr(data = vmbuf_data(&ctx->response), CRLFCRLF)));
    eoh_ofs = eoh - data + SSTRLEN(CRLFCRLF);
    *eoh = 0;
    char *p = strstr(data, CONNECTION);
    if (p != NULL) {
        p += SSTRLEN(CONNECTION);
        persistent = (0 == SSTRNCMPI(CONNECTION_CLOSE, p) ? 0 : 1);
    }
    SSTRL(HTTP, "HTTP/");
    if (0 != SSTRNCMP(HTTP, data))
        CLIENT_ERROR();

    p = strchrnul(data, ' ');
    int code = (*p ? atoi(p + 1) : 0);
    if (0 == code)
        CLIENT_ERROR();
    do {
        if (code == 204 || code == 304) /* No Content,  Not Modified */
            break;
        /*
         * content length
         */
        char *content_len_str = strstr(data, CONTENT_LENGTH);
        if (NULL != content_len_str) {
            content_len_str += SSTRLEN(CONTENT_LENGTH);
            size_t content_end = eoh_ofs + atoi(content_len_str);
            READ_MORE_DATA(vmbuf_wlocpos(&ctx->response) < content_end);
            break;
        }
        /*
         * chunked encoding
         */
        char *transfer_encoding_str = strstr(data, TRANSFER_ENCODING);
        if (NULL != transfer_encoding_str &&
                0 == SSTRNCMP(transfer_encoding_str + SSTRLEN(TRANSFER_ENCODING), "chunked")) {
            size_t chunk_start = eoh_ofs;
            size_t data_start = eoh_ofs;
            char *p;
            for (;;) {
                READ_MORE_DATA_STR(*(p = strchrnul((data = vmbuf_data(&ctx->response)) + chunk_start, '\r')) == 0);
                if (0 != SSTRNCMP(CRLF, p))
                    CLIENT_ERROR();
                uint32_t s = strtoul(data + chunk_start, NULL, 16);
                if (0 == s) {
                    vmbuf_wlocset(&ctx->response, data_start);
                    break;
                }
                chunk_start = p - data + SSTRLEN(CRLF);
                size_t chunk_end = chunk_start + s + SSTRLEN(CRLF);
                READ_MORE_DATA(vmbuf_wlocpos(&ctx->response) < chunk_end);
                memmove(vmbuf_data(&ctx->response) + data_start, vmbuf_data(&ctx->response) + chunk_start, s);
                data_start += s;
                chunk_start = chunk_end;
            }
            break;
        }
        /*
         * older versions of HTTP, terminated by disconnect
         */
        for (;; yield()) {
            if ((res = vmbuf_read(&ctx->response, fd)) < 0)
                CLIENT_ERROR();
            if (0 == res)
                break; /* remote side closed connection */
        }
    } while (0);
    ctx->content = vmbuf_data_ofs(&ctx->response, eoh_ofs);
    ctx->content_length = vmbuf_wlocpos(&ctx->response) - eoh_ofs;
    ctx->http_status_code = code;
    vmbuf_data_ofs(&ctx->response, eoh_ofs - SSTRLEN(CRLFCRLF))[0] = CR;
    *vmbuf_wloc(&ctx->response) = 0;
    ctx->persistent = persistent;
    if (!persistent)
        close(fd);
}
Example #27
0
File: timer.c Project: Pferd/ribs2
int ribs_timer_arm(int tfd, time_t msec) {
    struct itimerspec timerspec = {{0,0},{msec/1000,(msec % 1000)*1000000}};
    if (0 > timerfd_settime(tfd, 0, &timerspec, NULL))
        return LOGGER_PERROR("timerfd_settime: %d", tfd), close(tfd), -1;
    return 0;
}
Example #28
0
int http_server_init(struct http_server *server) {
    /*
     * one time global initializers
     */
    if (0 > mime_types_init())
        return LOGGER_ERROR("failed to initialize mime types"), -1;
    if (0 > http_headers_init())
        return LOGGER_ERROR("failed to initialize http headers"), -1;
    /*
     * idle connection handler
     */
    server->idle_ctx = ribs_context_create(SMALL_STACK_SIZE, sizeof(struct http_server *), http_server_idle_handler);
    struct http_server **server_ref = (struct http_server **)server->idle_ctx->reserved;
    *server_ref = server;
    /*
     * context pool
     */
    if (0 == server->num_stacks)
        server->num_stacks = DEFAULT_NUM_STACKS;
    if (0 == server->stack_size) {
        struct rlimit rlim;
        if (0 > getrlimit(RLIMIT_STACK, &rlim))
            return LOGGER_PERROR("getrlimit(RLIMIT_STACK)"), -1;
        server->stack_size = rlim.rlim_cur;
    }
    LOGGER_INFO("http server pool: initial=%zu, grow=%zu, stack_size=%zu", server->num_stacks, server->num_stacks, server->stack_size);
    ctx_pool_init(&server->ctx_pool, server->num_stacks, server->num_stacks, server->stack_size, sizeof(struct http_server_context) + server->context_size);
    /*
     * listen socket
     */
    const int LISTEN_BACKLOG = 32768;
    LOGGER_INFO("listening on port: %d, backlog: %d", server->port, LISTEN_BACKLOG);
    int lfd = socket(PF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP);
    if (0 > lfd)
        return -1;

    int rc;
    const int option = 1;
    rc = setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option));
    if (0 > rc)
        return LOGGER_PERROR("setsockopt, SO_REUSEADDR"), rc;

    rc = setsockopt(lfd, IPPROTO_TCP, TCP_NODELAY, &option, sizeof(option));
    if (0 > rc)
        return LOGGER_PERROR("setsockopt, TCP_NODELAY"), rc;

    struct linger ls;
    ls.l_onoff = 0;
    ls.l_linger = 0;
    rc = setsockopt(lfd, SOL_SOCKET, SO_LINGER, (void *)&ls, sizeof(ls));
    if (0 > rc)
        return LOGGER_PERROR("setsockopt, SO_LINGER"), rc;

    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(struct sockaddr_in));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(server->port);
    addr.sin_addr.s_addr = INADDR_ANY;
    if (0 > bind(lfd, (struct sockaddr *)&addr, sizeof(addr)))
        return LOGGER_PERROR("bind"), -1;

    if (0 > listen(lfd, LISTEN_BACKLOG))
        return LOGGER_PERROR("listen"), -1;

    server->accept_ctx = ribs_context_create(ACCEPTOR_STACK_SIZE, sizeof(struct http_server *), http_server_accept_connections);
    server->fd = lfd;
    server_ref = (struct http_server **)server->accept_ctx->reserved;
    *server_ref = server;

    if (server->max_req_size == 0)
        server->max_req_size = DEFAULT_MAX_REQ_SIZE;
    return 0;
}
Example #29
0
static void _handle_sig_child(void) {
    struct signalfd_siginfo sfd_info;
    for (;;yield()) {
        for (;;) {
            ssize_t res = read(sigfd, &sfd_info, sizeof(struct signalfd_siginfo));
            if (0 > res) {
                if (errno != EAGAIN)
                    LOGGER_PERROR("read from signal fd");
                break;
            }
            if (sizeof(struct signalfd_siginfo) != res) {
                LOGGER_ERROR("failed to read from signal fd, incorrect size");
                continue;
            }
            memset(&last_sig_info, 0, sizeof(last_sig_info));
            epoll_worker_ignore_events(sigfd);
            for (;;) {
                last_sig_info.si_pid = 0;
                if (0 > waitid(P_ALL, 0, &last_sig_info, WEXITED | WNOHANG) && ECHILD != errno) {
                    LOGGER_PERROR("waitid");
                    break;
                }
                pid_t pid = last_sig_info.si_pid;
                if (0 == pid)
                    break; /* no more events */
                if (logger_pid == pid) {
                    char fname[256];
                    snprintf(fname, sizeof(fname), "%s-%d.crash.log", program_invocation_short_name, getpid());
                    fname[sizeof(fname)-1] = 0;
                    int fd = creat(fname, 0644);
                    if (0 <= fd) {
                        dup2(fd, STDOUT_FILENO);
                        dup2(fd, STDERR_FILENO);
                        close(fd);
                        LOGGER_ERROR("logger [%d] terminated unexpectedly: %s, status=%d", pid, _get_exit_reason(&last_sig_info), last_sig_info.si_status);
                    }
                    epoll_worker_exit();
                }
                int i;
                for (i = 0; i < num_instances-1; ++i) {
                    /* check if it is our pid */
                    if (children_pids[i] == pid) {
                        children_pids[i] = 0; /* mark pid as handled */
                        LOGGER_ERROR("child process [%d] terminated unexpectedly: %s, status=%d", pid, _get_exit_reason(&last_sig_info), last_sig_info.si_status);
                        epoll_worker_exit();
                    }
                }
                uint32_t loc = hashtable_lookup(&ht_pid_to_ctx, &pid, sizeof(pid));
                if (0 == loc) {
                    LOGGER_ERROR("unhandled SIGCHLD detected, exiting...");
                    epoll_worker_exit();
                } else {
                    if (0 > queue_current_ctx()) {
                        LOGGER_ERROR("failed to queue current context, exiting...");
                        return epoll_worker_exit();
                    }
                    struct ribs_context *ctx = *(struct ribs_context **)hashtable_get_val(&ht_pid_to_ctx, loc);
                    hashtable_remove(&ht_pid_to_ctx, &pid, sizeof(pid));
                    ribs_swapcurcontext(ctx);
                }
            }
            epoll_worker_resume_events(sigfd);
        }
    }
}
Example #30
0
static int _set_signals(void) {
    struct sigaction sa = {
        .sa_handler = signal_handler,
        .sa_flags = 0
    };
    sigaction(SIGTERM, &sa, NULL);
    sigaction(SIGINT, &sa, NULL);

    sigset_t sigset;
    sigemptyset(&sigset);
    sigaddset(&sigset, SIGCHLD);
    if (0 > sigprocmask(SIG_BLOCK, &sigset, NULL))
        return LOGGER_PERROR("sigprocmask"), -1;
    sigfd = signalfd(-1, &sigset, SFD_NONBLOCK | SFD_CLOEXEC);
    if (0 > sigfd)
        return LOGGER_PERROR("signalfd"), -1;
    if (NULL == (sigfd_ctx = ribs_context_create(SIG_CHLD_STACK_SIZE, 0, _handle_sig_child)))
        return -1;
    return 0;
}

static int _init_subprocesses(const char *pidfilename, int num_forks) {
    if (0 >= num_forks) {
        num_forks = sysconf(_SC_NPROCESSORS_CONF);
        if (0 > num_forks)
            exit(EXIT_FAILURE);
    }
    LOGGER_INFO("num forks = %d", num_forks);
    num_instances = num_forks;
    daemon_instance = 0;
    children_pids = calloc(num_forks - 1, sizeof(pid_t));
    int i;
    for (i = 1; i < num_forks; ++i) {
        LOGGER_INFO("starting sub-process %d", i);
        pid_t pid = fork();
        if (0 > pid) return LOGGER_PERROR("fork"), -1;
        if (0 == pid) {
            daemon_instance = i;
            if (0 > prctl(PR_SET_PDEATHSIG, SIGTERM))
                return LOGGER_PERROR("prctl"), -1;
            if (0 > _set_signals())
                return LOGGER_ERROR("failed to set signals"), -1;
            LOGGER_INFO("sub-process %d started", i);
            return 0;
        } else
            children_pids[i-1] = pid;
    }
    if (pidfilename && 0 > _set_pidfile(pidfilename))
        return LOGGER_ERROR("failed to set pidfile"), -1;
    if (0 > _set_signals())
        return LOGGER_ERROR("failed to set signals"), -1;
    return 0;
}

static int ribs_server_init_daemon(const char *pidfilename, const char *logfilename, int num_forks) {
    if (0 > pipe2(child_is_up_pipe, O_CLOEXEC))
        return LOGGER_ERROR("failed to create pipe"), -1;

    pid_t pid = fork();
    if (pid < 0)
        return LOGGER_PERROR("ribs_server_init_daemon, fork"), -1;

    if (pid > 0) {
        close(child_is_up_pipe[1]); /* close the write side */
        /* wait for child to come up */
        uint8_t t;
        int res;
        LOGGER_INFO("waiting for the child process [%d] to start...", pid);
        if (0 >= (res = read(child_is_up_pipe[0], &t, sizeof(t)))) {
            if (0 > res)
                LOGGER_PERROR("pipe");
            LOGGER_ERROR("child process failed to start");
            return -1;
        }
        LOGGER_INFO("child process started successfully");
        exit(EXIT_SUCCESS);
    }
    close(child_is_up_pipe[0]); /* close the read side */

    umask(0);

    if (0 > setsid())
        return LOGGER_PERROR("daemonize, setsid"), -1;

    int fdnull = open("/dev/null", O_RDWR);
    if (0 > fdnull)
        return LOGGER_PERROR("/dev/null"), -1;

    dup2(fdnull, STDIN_FILENO);
    if (logfilename) {
        if (0 > _logger_init(logfilename))
            return -1;
    } else {
        dup2(fdnull, STDOUT_FILENO);
        dup2(fdnull, STDERR_FILENO);
    }
    close(fdnull);
    LOGGER_INFO("child process started");
    return _init_subprocesses(pidfilename, num_forks);
}

int ribs_server_init(int daemonize, const char *pidfilename, const char *logfilename, int num_forks) {
    if (0 > hashtable_init(&ht_pid_to_ctx, 0))
        return -1;
    if (daemonize)
        return ribs_server_init_daemon(pidfilename, logfilename, num_forks);
    /* if (logfilename && 0 > _logger_init(logfilename)) */
    /*     exit(EXIT_FAILURE); */
    return _init_subprocesses(pidfilename, num_forks);
}

int ribs_server_signal_children(int sig) {
    if (0 >= num_instances || 0 != daemon_instance)
        return 0;
    int i, res = 0;
    for (i = 0; i < num_instances-1; ++i) {
        if (0 < children_pids[i] && 0 > kill(children_pids[i], sig)) {
            LOGGER_PERROR("kill [%d] %d", sig, children_pids[i]);
            res = -1;
        }
    }
    return res;
}

void ribs_server_start(void) {
    daemon_finalize();
    if (0 <= sigfd && 0 > ribs_epoll_add(sigfd, EPOLLIN, sigfd_ctx))
        return LOGGER_ERROR("ribs_epoll_add: sigfd");
    epoll_worker_loop();
    if (0 >= num_instances || 0 != daemon_instance)
        return;
    LOGGER_INFO("sending SIGTERM to sub-processes");
    ribs_server_signal_children(SIGTERM);
    LOGGER_INFO("waiting for sub-processes to exit");
    int i, status;
    for (i = 0; i < num_instances-1; ++i) {
        if (0 < children_pids[i] && 0 > waitpid(children_pids[i], &status, 0))
            LOGGER_PERROR("waitpid %d", children_pids[i]);
    }
    LOGGER_INFO("sub-processes terminated");
}