Wed, 27 Nov 2024 23:00:07 +0100
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; } }