UNIXworkcode

1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright 2017 Olaf Wintermann. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <unistd.h> 32 #include <sys/socket.h> 33 #include <sys/un.h> 34 35 #include "log.h" 36 #include "webserver.h" 37 38 #include "../util/systhr.h" 39 40 #include <cx/utils.h> 41 #include <cx/buffer.h> 42 #include <cx/printf.h> 43 44 #include "srvctrl.h" 45 46 #define SRVCTRL_THREAD_STACKSIZE 262144 47 48 static int srvctrl; 49 static cxmutstr srvctrl_path; 50 51 static WSBool srv_shutdown; 52 53 int srvctrl_init(ServerConfiguration *cfg) { 54 // create srvctrl unix domain socket 55 // this socket is used for stop, reconfigure and other operations 56 srvctrl_path = cx_asprintf("%s/private/srvctrl.sock", cfg->tmp.ptr); 57 struct sockaddr_un addr; 58 if(srvctrl_path.length > sizeof(addr.sun_path)-1) { 59 log_ereport( 60 LOG_CATASTROPHE, 61 "path ''%s'' too long for unix domain socket", 62 srvctrl_path.ptr); 63 return -1; 64 } 65 66 // make sure there is no old srvctrl socket file 67 // otherweise bind would fail 68 if(unlink(srvctrl_path.ptr)) { 69 if(errno != ENOENT) { 70 log_ereport( 71 LOG_CATASTROPHE, 72 "cannot unlink old srvctrl socket ''%s'': %s", 73 srvctrl_path.ptr, 74 strerror(errno)); 75 return -1; 76 } 77 } 78 79 ZERO(&addr, sizeof(addr)); 80 addr.sun_family = AF_UNIX; 81 memcpy(addr.sun_path, srvctrl_path.ptr, srvctrl_path.length); 82 83 srvctrl = socket(AF_UNIX, SOCK_STREAM, 0); 84 if(srvctrl == -1) { 85 log_ereport( 86 LOG_CATASTROPHE, 87 "Cannot create server control socket: %s", 88 strerror(errno)); 89 return -1; 90 } 91 if(bind(srvctrl, (struct sockaddr*)&addr, sizeof(addr))) { 92 log_ereport( 93 LOG_CATASTROPHE, 94 "srvctrl socket bind failed: %s", 95 strerror(errno)); 96 return -1; 97 } 98 99 return 0; 100 } 101 102 int srvctrl_wait() { 103 listen(srvctrl, 8); 104 105 for(;;) { 106 int fd = accept(srvctrl, NULL, 0); 107 if(fd < 0) { 108 if(srv_shutdown) break; 109 log_ereport( 110 LOG_FAILURE, 111 "srvctrl: accept failed: %s", 112 strerror(errno)); 113 break; 114 } 115 116 SrvCtrlClient *client = srvctrl_create_client(fd); 117 SYS_THREAD t = systhread_start( 118 SYSTHREAD_DEFAULT_PRIORITY, 119 SRVCTRL_THREAD_STACKSIZE, 120 (thrstartfunc)srvctrl_thread, 121 client); 122 systhread_detach(t); 123 124 } 125 126 close(srvctrl); 127 unlink(srvctrl_path.ptr); 128 free(srvctrl_path.ptr); 129 130 return 0; 131 } 132 133 void srvctrl_shutdown() { 134 srv_shutdown = TRUE; 135 shutdown(srvctrl, SHUT_RDWR); 136 } 137 138 SrvCtrlClient* srvctrl_create_client(int fd) { 139 SrvCtrlClient *client = malloc(sizeof(SrvCtrlClient)); 140 ZERO(client, sizeof(SrvCtrlClient)); 141 client->fd = fd; 142 return client; 143 } 144 145 void* srvctrl_thread(SrvCtrlClient *client) { 146 LogDup log; 147 log.write = (log_writefunc)srvctrl_log; 148 log.cookie = client; 149 log_add_logdup(&log); 150 151 char buf[64]; 152 CxBuffer line; 153 cxBufferInit(&line, NULL, 32, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS); 154 155 ssize_t r; 156 WSBool br = FALSE; 157 while((r = read(client->fd, buf, 64)) > 0) { 158 for(int i=0;i<r;i++) { 159 char c = buf[i]; 160 if(c == '\n') { 161 cxmutstr ln = cx_mutstrn(line.space, line.pos); 162 if(srvctrl_handle_cmd(client, ln)) { 163 br = TRUE; 164 break; 165 } 166 } else { 167 cxBufferPut(&line, c); 168 } 169 } 170 if(br) { 171 break; 172 } 173 } 174 log_remove_logdup(&log); 175 176 cxBufferDestroy(&line); 177 close(client->fd); 178 free(client); 179 return NULL; 180 } 181 182 int srvctrl_handle_cmd(SrvCtrlClient *client, cxmutstr cmd) { 183 if(!cx_strcmp(cx_strcast(cmd), cx_str("reconfig"))) { 184 log_ereport(LOG_INFORM, "reconfigure server"); 185 if(!webserver_reconfig()) { 186 log_ereport(LOG_INFORM, "reconfig: success"); 187 } 188 } else if(!cx_strcmp(cx_strcast(cmd), cx_str("shutdown"))) { 189 webserver_shutdown(); 190 } else if(!cx_strcmp(cx_strcast(cmd), cx_str("stat"))) { 191 // TODO: implement 192 } else if(!cx_strcmp(cx_strcast(cmd), cx_str("log"))) { 193 return 0; 194 } else { 195 log_ereport( 196 LOG_FAILURE, 197 "unknown srvctrl command: %.*s", 198 (int)cmd.length, 199 cmd.ptr); 200 } 201 return 1; 202 } 203 204 void srvctrl_log(SrvCtrlClient *client, char *msg, size_t len) { 205 char msgheader[4]; 206 msgheader[0] = SRV_MSG_LOG; 207 uint16_t msglen = len; 208 memcpy(&msgheader[1], &msglen, 2); 209 write(client->fd, msgheader, 3); 210 211 size_t pos = 0; 212 ssize_t w = 0; 213 while(pos < len) { 214 w = write(client->fd, msg + pos, len - pos); 215 if(w <= 0) { 216 break; 217 } 218 pos += w; 219 } 220 } 221