--- /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; + } +}