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 "../../ucx/utils.h" 41 #include "../../ucx/buffer.h" 42 43 #include "srvctrl.h" 44 45 #define SRVCTRL_THREAD_STACKSIZE 8192 46 47 static int srvctrl; 48 static sstr_t srvctrl_path; 49 50 static WSBool srv_shutdown; 51 52 int srvctrl_init(ServerConfiguration *cfg) { 53 // create srvctrl unix domain socket 54 // this socket is used for stop, reconfigure and other operations 55 srvctrl_path = ucx_sprintf("%s/private/srvctrl.sock", cfg->tmp.ptr); 56 struct sockaddr_un addr; 57 if(srvctrl_path.length > sizeof(addr.sun_path)-1) { 58 log_ereport( 59 LOG_CATASTROPHE, 60 "path ''%s'' too long for unix domain socket", 61 srvctrl_path.ptr); 62 return -1; 63 } 64 65 // make sure there is no old srvctrl socket file 66 // otherweise bind would fail 67 if(unlink(srvctrl_path.ptr)) { 68 if(errno != ENOENT) { 69 log_ereport( 70 LOG_CATASTROPHE, 71 "cannot unlink old srvctrl socket ''%s'': %s", 72 srvctrl_path.ptr, 73 strerror(errno)); 74 return -1; 75 } 76 } 77 78 ZERO(&addr, sizeof(addr)); 79 addr.sun_family = AF_UNIX; 80 memcpy(addr.sun_path, srvctrl_path.ptr, srvctrl_path.length); 81 82 srvctrl = socket(AF_UNIX, SOCK_STREAM, 0); 83 if(srvctrl == -1) { 84 log_ereport( 85 LOG_CATASTROPHE, 86 "Cannot create server control socket: %s", 87 strerror(errno)); 88 return -1; 89 } 90 if(bind(srvctrl, (struct sockaddr*)&addr, sizeof(addr))) { 91 log_ereport( 92 LOG_CATASTROPHE, 93 "srvctrl socket bind failed: %s", 94 strerror(errno)); 95 return -1; 96 } 97 98 return 0; 99 } 100 101 int srvctrl_wait() { 102 listen(srvctrl, 8); 103 104 for(;;) { 105 int fd = accept(srvctrl, NULL, 0); 106 if(fd < 0) { 107 if(srv_shutdown) break; 108 log_ereport( 109 LOG_FAILURE, 110 "srvctrl: accept failed: %s", 111 strerror(errno)); 112 break; 113 } 114 115 SrvCtrlClient *client = srvctrl_create_client(fd); 116 SYS_THREAD t = systhread_start( 117 SYSTHREAD_DEFAULT_PRIORITY, 118 SRVCTRL_THREAD_STACKSIZE, 119 (thrstartfunc)srvctrl_thread, 120 client); 121 systhread_detach(t); 122 123 } 124 125 close(srvctrl); 126 unlink(srvctrl_path.ptr); 127 free(srvctrl_path.ptr); 128 129 return 0; 130 } 131 132 void srvctrl_shutdown() { 133 srv_shutdown = TRUE; 134 shutdown(srvctrl, SHUT_RDWR); 135 } 136 137 SrvCtrlClient* srvctrl_create_client(int fd) { 138 SrvCtrlClient *client = malloc(sizeof(SrvCtrlClient)); 139 ZERO(client, sizeof(SrvCtrlClient)); 140 client->fd = fd; 141 return client; 142 } 143 144 void* srvctrl_thread(SrvCtrlClient *client) { 145 LogDup log; 146 log.write = (log_writefunc)srvctrl_log; 147 log.cookie = client; 148 log_add_logdup(&log); 149 150 char buf[64]; 151 UcxBuffer *line = ucx_buffer_new(NULL, 32, UCX_BUFFER_AUTOEXTEND); 152 153 ssize_t r; 154 WSBool br = FALSE; 155 while((r = read(client->fd, buf, 64)) > 0) { 156 for(int i=0;i<r;i++) { 157 char c = buf[i]; 158 if(c == '\n') { 159 sstr_t ln = sstrn(line->space, line->pos); 160 if(srvctrl_handle_cmd(client, ln)) { 161 br = TRUE; 162 break; 163 } 164 } else { 165 ucx_buffer_putc(line, c); 166 } 167 } 168 if(br) { 169 break; 170 } 171 } 172 log_remove_logdup(&log); 173 174 ucx_buffer_free(line); 175 close(client->fd); 176 free(client); 177 return NULL; 178 } 179 180 int srvctrl_handle_cmd(SrvCtrlClient *client, sstr_t cmd) { 181 if(!sstrcmp(cmd, S("reconfig"))) { 182 log_ereport(LOG_INFORM, "reconfigure server"); 183 if(webserver_reconfig()) { 184 log_ereport(LOG_FAILURE, "cannot reload config"); 185 } else { 186 log_ereport(LOG_INFORM, "reconfig: success"); 187 } 188 } else if(!sstrcmp(cmd, S("shutdown"))) { 189 webserver_shutdown(); 190 } else if(!sstrcmp(cmd, S("stat"))) { 191 // TODO: implement 192 } else if(!sstrcmp(cmd, S("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