src/server/daemon/main.c

Sat, 26 Nov 2022 17:07:08 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Sat, 26 Nov 2022 17:07:08 +0100
changeset 438
22eca559aded
parent 434
ff576305ae6e
child 443
ef3c8a0e1fee
permissions
-rw-r--r--

refactore http listener creation

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 2013 Olaf Wintermann. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   1. Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *
 *   2. Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions and the following disclaimer in the
 *      documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <pthread.h>
#include <fcntl.h>
#include <poll.h>

#include "../util/pool.h"
#include "../public/nsapi.h"
#include "../util/plist.h"
#include "../util/date.h"

#include <cx/string.h>

#include "webserver.h"
#include "log.h"
#include "httprequest.h"
#include "httplistener.h"
#include "srvctrl.h"

#include "configmanager.h"

#define LOG_THREAD_STACK_SIZE     32768
#define LOG_THREAD_MAX_POLL_FAILS 10
#define LOG_THREAD_READ_BUF       2048

static int std_out[2];
static int std_err[2];
static WSBool is_daemon;

void test() {
    time_t t = time(NULL);
    pool_handle_t *pool = pool_create();
    cxmutstr date = date_format_http(t, pool);
    printf("%s\n", date.ptr);
}


WSBool main_is_daemon(void) {
    return is_daemon;
}

/*
 * SIGUSR1: reload the configuration files
 */
void sig_usr1_reload(int sig) {
    log_ereport(LOG_INFORM, "sig reload");
    
    CfgManager mgr;
    if(cfgmgr_load_config(&mgr) != 0) {
        log_ereport(LOG_FAILURE, "cannot reload server.conf");
    } else {
        if(cfgmgr_apply_config(&mgr)) {
            log_ereport(LOG_FAILURE, "cannot reload config");
        }
    }
    
    // start newly created listeners
    start_all_listener();

    signal(SIGUSR1, sig_usr1_reload);
}

/*
 * SIGTERM: stop the server
 */
void sig_term(int sig) {
    webserver_shutdown();
    //exit(EXIT_SUCCESS);
}

static void set_pipe_nonblocking(int fd) {
    int flags = 0;
    flags = fcntl(fd, F_GETFL, 0);
    fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
}

static char log_pipe_readbuf[LOG_THREAD_READ_BUF];
static char log_pipe_stdout_buf[LOG_THREAD_READ_BUF];
static char log_pipe_stderr_buf[LOG_THREAD_READ_BUF];
static size_t log_pipe_stdout_tmp_pos = 0;
static size_t log_pipe_stderr_tmp_pos = 0;

static int log_pipe(const char *name, int fd, char *buf, size_t *pos) {
    ssize_t r = read(fd, log_pipe_readbuf, 2);
    if(r <= 0) {
        return 1;
    }
    
    char *tmp = buf;
    int tmplen = *pos;
    
    int s = 0;
    for(int i=0;i<r;i++) {
        if(log_pipe_readbuf[i] == '\n') {
            if(tmplen + i-s > 0) {
                log_message(name, "%.*s%.*s", tmplen, tmp, i-s, log_pipe_readbuf + s);
            }
            tmplen = 0;
            *pos = 0;
            s = i+1;
        }
    }
    
    int remaining = r - s;
    if(tmplen + remaining >= LOG_THREAD_READ_BUF) {
        log_message(name, "%.*s%.*s", tmplen, tmp, remaining, log_pipe_readbuf + s);
        *pos = 0;
    } else if(remaining > 0) {
        memcpy(buf + *pos, log_pipe_readbuf + s, remaining);
        *pos += remaining;
    }
    
    return 0;
}

void* log_pipe_thread(void *data) {
    set_pipe_nonblocking(std_out[0]);
    set_pipe_nonblocking(std_err[0]);
    
    struct pollfd fds[2];
    fds[0].fd = std_out[0];
    fds[0].events = POLLIN;
    fds[1].fd = std_err[0];
    fds[1].events = POLLIN;
    
    int poll_fails = 0;
    for(;;) {
        if(poll(fds, 1, 1000000) < 0) {
            if(errno == EINTR) {
                continue;
            }
            log_ereport(LOG_FAILURE, "log thread poll failed: %s", strerror(errno));
            if(poll_fails++ > LOG_THREAD_MAX_POLL_FAILS) {
                break;
            }
        }
        
        // check stdout
        if(fds[0].revents & POLLIN) {
            if(log_pipe("stdout", fds[0].fd, log_pipe_stdout_buf, &log_pipe_stdout_tmp_pos)) {
                break;
            }
        }
        
        // check stderr
        if(fds[1].revents & POLLIN) {
            if(log_pipe("stderr", fds[0].fd, log_pipe_stderr_buf, &log_pipe_stderr_tmp_pos)) {
                break;
            }
        }
    }
    
    log_ereport(LOG_INFORM, "log thread end");

    return NULL;
}

int main(int argc, char **argv) {
    //test();
    
    // if the -c parameter is specified, we don't create a daemon
    is_daemon = 1;
    for(int i=0;i<argc;i++) {
        char *p = argv[i];
        if(p[0] == '-' && p[1] == 'c') {
            is_daemon = 0;
            break;
        }
    }
    if(is_daemon) {
        // create daemon
        pid_t pid = fork();
        if(pid < 0) {
            return EXIT_FAILURE;
        } else if(pid > 0) {
            return EXIT_SUCCESS;
        }

        if(setsid() < 0) {
            fprintf(stderr, "setsid failed\n");
            return EXIT_FAILURE;
        }
        printf("start daemon\n");

        for(int i=0;i<3;i++) {
            close(i);
        }

        // stdio redirection
        // create pipes
        if(pipe(std_out) != 0) {
            perror("pipe");
            return EXIT_FAILURE;
        }
        if(pipe(std_err) != 0) {
            perror("pipe");
            return EXIT_FAILURE;
        }
        
        dup2(std_out[1], 1);
        dup2(std_err[1], 2);
        close(std_err[1]);
        
        // set log thread stack size
        pthread_attr_t tattr;
        pthread_attr_init(&tattr);
        pthread_attr_setstacksize(&tattr, LOG_THREAD_STACK_SIZE);

        pthread_t tid;
        pthread_create(&tid, &tattr, log_pipe_thread, NULL);
    }

    pool_init(NULL, NULL, NULL);
    
    // add signal handler
    signal(SIGUSR1, sig_usr1_reload);
    signal(SIGTERM, sig_term);
    signal(SIGINT, sig_term);
    
    struct sigaction act;
    ZERO(&act, sizeof(struct sigaction));
    act.sa_handler = SIG_IGN;
    sigaction(SIGPIPE, &act, NULL);

    // start webserver
    log_ereport(LOG_INFORM, "startup");
    
    int status;
    status = webserver_init();
    if(status != 0) {
        log_ereport(LOG_FAILURE, "cannot initialize server.");
        return EXIT_FAILURE;
    }

    status = webserver_run();
    if(status != 0) {
        log_ereport(LOG_FAILURE, "cannot run server.");
        return EXIT_FAILURE;
    }
    
    if(srvctrl_wait()) {
        return EXIT_FAILURE;
    }
    
    /* TODO: join threads (or not?) */
/*
    while(1) {
        if(is_daemon) {
            fflush(stdout);
            fflush(stderr);
        }
        sleep(10000);
        if(0) {
            break;
        }
    }
*/

    return EXIT_SUCCESS;
}

mercurial