src/server/daemon/srvctrl.c

Wed, 27 Nov 2024 23:00:07 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Wed, 27 Nov 2024 23:00:07 +0100
changeset 563
6ca97c99173e
parent 439
255e316db762
permissions
-rw-r--r--

add TODO to use a future ucx feature

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 2017 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 <sys/socket.h>
#include <sys/un.h>

#include "log.h"
#include "webserver.h"

#include "../util/systhr.h"

#include <cx/utils.h>
#include <cx/buffer.h>
#include <cx/printf.h>

#include "srvctrl.h"

#define SRVCTRL_THREAD_STACKSIZE 262144

static int srvctrl;
static cxmutstr srvctrl_path;

static WSBool srv_shutdown;

int srvctrl_init(ServerConfiguration *cfg) {
    // create srvctrl unix domain socket
    // this socket is used for stop, reconfigure and other operations
    srvctrl_path = cx_asprintf("%s/private/srvctrl.sock", cfg->tmp.ptr);
    struct sockaddr_un addr;
    if(srvctrl_path.length > sizeof(addr.sun_path)-1) {
        log_ereport(
                LOG_CATASTROPHE,
                "path '%s' too long for unix domain socket",
                srvctrl_path.ptr);
        return -1;
    }
    
    // make sure there is no old srvctrl socket file
    // otherweise bind would fail
    if(unlink(srvctrl_path.ptr)) {
        if(errno != ENOENT) {
            log_ereport(
                    LOG_CATASTROPHE,
                    "cannot unlink old srvctrl socket '%s': %s",
                    srvctrl_path.ptr,
                    strerror(errno));
            return -1;
        }
    }

    ZERO(&addr, sizeof(addr));
    addr.sun_family = AF_UNIX;
    memcpy(addr.sun_path, srvctrl_path.ptr, srvctrl_path.length);
    
    srvctrl = socket(AF_UNIX, SOCK_STREAM, 0);
    if(srvctrl == -1) {
        log_ereport(
                LOG_CATASTROPHE,
                "Cannot create server control socket: %s",
                strerror(errno));
        return -1;
    }
    if(bind(srvctrl, (struct sockaddr*)&addr, sizeof(addr))) {
        log_ereport(
                LOG_CATASTROPHE,
                "srvctrl socket bind failed: %s",
                strerror(errno));
        return -1;
    }
    
    return 0;
}

int srvctrl_wait() {
    listen(srvctrl, 8);
    
    for(;;) {
        int fd = accept(srvctrl, NULL, 0);
        if(fd < 0) {
            if(srv_shutdown) break;
            log_ereport(
                    LOG_FAILURE,
                    "srvctrl: accept failed: %s",
                    strerror(errno));
            break;
        }
        
        SrvCtrlClient *client = srvctrl_create_client(fd);
        SYS_THREAD t = systhread_start(
                SYSTHREAD_DEFAULT_PRIORITY,
                SRVCTRL_THREAD_STACKSIZE,
                (thrstartfunc)srvctrl_thread,
                client);
        systhread_detach(t);
        
    }
    
    close(srvctrl);
    unlink(srvctrl_path.ptr);
    free(srvctrl_path.ptr);
    
    return 0;
}

void srvctrl_shutdown() {
    srv_shutdown = TRUE;
    shutdown(srvctrl, SHUT_RDWR);
}

SrvCtrlClient* srvctrl_create_client(int fd) {
    SrvCtrlClient *client = malloc(sizeof(SrvCtrlClient));
    ZERO(client, sizeof(SrvCtrlClient));
    client->fd = fd;      
    return client;
}

void* srvctrl_thread(SrvCtrlClient *client) {
    LogDup log;
    log.write = (log_writefunc)srvctrl_log;
    log.cookie = client;
    log_add_logdup(&log);
    
    char buf[64];
    CxBuffer line;
    cxBufferInit(&line, NULL, 32, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS);
    
    ssize_t r;
    WSBool br = FALSE;
    while((r = read(client->fd, buf, 64)) > 0) {
        for(int i=0;i<r;i++) {
            char c = buf[i];
            if(c == '\n') {
                cxmutstr ln = cx_mutstrn(line.space, line.pos);
                if(srvctrl_handle_cmd(client, ln)) {
                    br = TRUE;
                    break;
                }
            } else {
                cxBufferPut(&line, c);
            }
        }
        if(br) {
            break;
        }
    }
    log_remove_logdup(&log);
    
    cxBufferDestroy(&line);
    close(client->fd);
    free(client);
    return NULL;
}

int srvctrl_handle_cmd(SrvCtrlClient *client, cxmutstr cmd) {
    if(!cx_strcmp(cx_strcast(cmd), cx_str("reconfig"))) {
        log_ereport(LOG_INFORM, "reconfigure server");
        if(!webserver_reconfig()) {
            log_ereport(LOG_INFORM, "reconfig: success");
        }
    } else if(!cx_strcmp(cx_strcast(cmd), cx_str("shutdown"))) {
        webserver_shutdown();
    } else if(!cx_strcmp(cx_strcast(cmd), cx_str("stat"))) {
        // TODO: implement
    } else if(!cx_strcmp(cx_strcast(cmd), cx_str("log"))) {
        return 0;
    } else {
        log_ereport(
                LOG_FAILURE,
                "unknown srvctrl command: %.*s",
                (int)cmd.length,
                cmd.ptr);
    }
    return 1;
}

void srvctrl_log(SrvCtrlClient *client, char *msg, size_t len) {
    char msgheader[4];
    msgheader[0] = SRV_MSG_LOG;
    uint16_t msglen = len;
    memcpy(&msgheader[1], &msglen, 2);
    write(client->fd, msgheader, 3);
    
    size_t pos = 0;
    ssize_t w = 0;
    while(pos < len) {
        w = write(client->fd, msg + pos, len - pos);
        if(w <= 0) {
            break;
        }
        pos += w;
    }
}

mercurial