src/server/daemon/srvctrl.c

changeset 179
ef6827505bd2
parent 178
4760f7f1b197
child 254
4784c14aa639
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/server/daemon/srvctrl.c	Mon Mar 06 17:32:26 2017 +0100
@@ -0,0 +1,220 @@
+/*
+ * 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 "../../ucx/utils.h"
+#include "../../ucx/buffer.h"
+
+#include "srvctrl.h"
+
+#define SRVCTRL_THREAD_STACKSIZE 8192
+
+static int srvctrl;
+static sstr_t 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 = ucx_sprintf("%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];
+    UcxBuffer *line = ucx_buffer_new(NULL, 32, UCX_BUFFER_AUTOEXTEND);
+    
+    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') {
+                sstr_t ln = sstrn(line->space, line->pos);
+                if(srvctrl_handle_cmd(client, ln)) {
+                    br = TRUE;
+                    break;
+                }
+            } else {
+                ucx_buffer_putc(line, c);
+            }
+        }
+        if(br) {
+            break;
+        }
+    }
+    log_remove_logdup(&log);
+    
+    ucx_buffer_free(line);
+    close(client->fd);
+    free(client);
+    return NULL;
+}
+
+int srvctrl_handle_cmd(SrvCtrlClient *client, sstr_t cmd) {
+    if(!sstrcmp(cmd, S("reconfig"))) {
+        log_ereport(LOG_INFORM, "reconfigure server");
+        if(webserver_reconfig()) {
+            log_ereport(LOG_FAILURE, "cannot reload config");
+        } else {
+            log_ereport(LOG_INFORM, "reconfig: success");
+        }
+    } else if(!sstrcmp(cmd, S("shutdown"))) {
+        webserver_shutdown();
+    } else if(!sstrcmp(cmd, S("stat"))) {
+        // TODO: implement
+    } else if(!sstrcmp(cmd, S("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