UNIXworkcode

1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright 2013 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 30 #include <stdio.h> 31 #include <stdlib.h> 32 #include <unistd.h> 33 #include <signal.h> 34 #include <errno.h> 35 #include <pthread.h> 36 #include <fcntl.h> 37 #include <poll.h> 38 #include <semaphore.h> 39 40 #include "../util/pool.h" 41 #include "../public/nsapi.h" 42 #include "../util/plist.h" 43 #include "../util/date.h" 44 45 #include <cx/string.h> 46 47 #include "webserver.h" 48 #include "log.h" 49 #include "httprequest.h" 50 #include "httplistener.h" 51 #include "srvctrl.h" 52 53 #include "configmanager.h" 54 55 #define LOG_THREAD_STACK_SIZE 32768 56 #define LOG_THREAD_MAX_POLL_FAILS 10 57 #define LOG_THREAD_READ_BUF 2048 58 59 static int std_out[2]; 60 static int std_err[2]; 61 static WSBool is_daemon; 62 63 void test() { 64 time_t t = time(NULL); 65 pool_handle_t *pool = pool_create(); 66 cxmutstr date = date_format_http(t, pool); 67 printf("%s\n", date.ptr); 68 } 69 70 71 WSBool main_is_daemon(void) { 72 return is_daemon; 73 } 74 75 /* 76 * SIGUSR1: reload the configuration files 77 */ 78 void sig_usr1_reload(int sig) { 79 log_ereport(LOG_INFORM, "sig reload"); 80 (void)webserver_reconfig(); 81 signal(SIGUSR1, sig_usr1_reload); 82 } 83 84 /* 85 * SIGTERM: stop the server 86 */ 87 void sig_term(int sig) { 88 webserver_shutdown(); 89 //exit(EXIT_SUCCESS); 90 } 91 92 static void set_pipe_nonblocking(int fd) { 93 int flags = 0; 94 flags = fcntl(fd, F_GETFL, 0); 95 fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); 96 } 97 98 static char log_pipe_readbuf[LOG_THREAD_READ_BUF]; 99 static char log_pipe_stdout_buf[LOG_THREAD_READ_BUF]; 100 static char log_pipe_stderr_buf[LOG_THREAD_READ_BUF]; 101 static size_t log_pipe_stdout_tmp_pos = 0; 102 static size_t log_pipe_stderr_tmp_pos = 0; 103 104 // pipe used for startup 105 // after fork, the parent process waits, until the daemon is fully started 106 // to get potential error and log messages 107 static int daemon_start_pipe[2]; 108 109 // used for sending log messages from the daemon process to the parent process 110 // in the startup phase 111 static int parent_log_pipe[2]; 112 113 static LogDup startup_log; 114 115 static void finish_daemon_startup(int status) { 116 if(status != 0) { 117 log_ereport(LOG_FAILURE, "daemon startup failed"); 118 if(status > 127) { 119 status = 127; 120 } 121 } 122 char s = (char)status; 123 124 // notify parent process about startup status 125 write(daemon_start_pipe[1], &s, 1); 126 close(daemon_start_pipe[1]); 127 128 // remove logdup 129 log_remove_logdup(&startup_log); 130 close(parent_log_pipe[0]); 131 close(parent_log_pipe[1]); 132 } 133 134 static int log_pipe(const char *name, int fd, char *buf, size_t *pos) { 135 ssize_t r = read(fd, log_pipe_readbuf, 2); 136 if(r <= 0) { 137 return r == 0 ? 0 : 1; 138 } 139 140 char *tmp = buf; 141 int tmplen = *pos; 142 143 int s = 0; 144 for(int i=0;i<r;i++) { 145 if(log_pipe_readbuf[i] == '\n') { 146 if(tmplen + i-s > 0) { 147 log_message(name, "%.*s%.*s", tmplen, tmp, i-s, log_pipe_readbuf + s); 148 } 149 tmplen = 0; 150 *pos = 0; 151 s = i+1; 152 } 153 } 154 155 int remaining = r - s; 156 if(tmplen + remaining >= LOG_THREAD_READ_BUF) { 157 log_message(name, "%.*s%.*s", tmplen, tmp, remaining, log_pipe_readbuf + s); 158 *pos = 0; 159 } else if(remaining > 0) { 160 memcpy(buf + *pos, log_pipe_readbuf + s, remaining); 161 *pos += remaining; 162 } 163 164 return 0; 165 } 166 167 void* log_pipe_thread(void *data) { 168 set_pipe_nonblocking(std_out[0]); 169 set_pipe_nonblocking(std_err[0]); 170 171 struct pollfd fds[2]; 172 fds[0].fd = std_out[0]; 173 fds[0].events = POLLIN; 174 fds[1].fd = std_err[0]; 175 fds[1].events = POLLIN; 176 177 int poll_fails = 0; 178 for(;;) { 179 if(poll(fds, 1, 1000000) < 0) { 180 if(errno == EINTR) { 181 continue; 182 } 183 log_ereport(LOG_FAILURE, "log thread poll failed: %s", strerror(errno)); 184 if(poll_fails++ > LOG_THREAD_MAX_POLL_FAILS) { 185 break; 186 } 187 } 188 189 // check stdout 190 if(fds[0].revents & POLLIN) { 191 if(log_pipe("stdout", fds[0].fd, log_pipe_stdout_buf, &log_pipe_stdout_tmp_pos)) { 192 log_ereport(LOG_WARN, "log_pipe stdout failed"); 193 break; 194 } 195 } 196 197 // check stderr 198 if(fds[1].revents & POLLIN) { 199 if(log_pipe("stderr", fds[0].fd, log_pipe_stderr_buf, &log_pipe_stderr_tmp_pos)) { 200 log_ereport(LOG_WARN, "log_pipe stderr failed"); 201 break; 202 } 203 } 204 } 205 206 log_ereport(LOG_INFORM, "log thread end"); 207 208 return NULL; 209 } 210 211 212 // read (blocking) 1 byte from daemon_start_pipe and return the value 213 // this is used to wait in the parent process, until the daemon process 214 // is started 215 // the returning byte is used as the process return value 216 static int main_daemon_startup_wait(void) { 217 // receive log messages from daemon 218 char buf[2048]; 219 ssize_t r; 220 while((r = read(parent_log_pipe[0], buf, 2048)) > 0) { 221 ssize_t pos = 0; 222 while(r > 0) { 223 ssize_t w = write(STDOUT_FILENO, buf+pos, r-pos); 224 pos += w; 225 r -= w; 226 break; 227 } 228 } 229 // log pipe closed, daemon_start_pipe should contain the status code now 230 231 char ret; 232 if(read(daemon_start_pipe[0], &ret, 1) != 1) { 233 return 255; 234 } 235 return ret; 236 } 237 238 static void startup_log_write(void *cookie, char *msg, size_t length) { 239 write(parent_log_pipe[1], msg, length); 240 } 241 242 int main(int argc, char **argv) { 243 //test(); 244 245 // if the -c parameter is specified, we don't create a daemon 246 is_daemon = 1; 247 for(int i=0;i<argc;i++) { 248 char *p = argv[i]; 249 if(p[0] == '-' && p[1] == 'c') { 250 is_daemon = 0; 251 break; 252 } 253 } 254 255 if(init_logging()) { 256 fprintf(stderr, "OOM\n"); 257 return 1; 258 } 259 260 if(is_daemon) { 261 // initialize startup pipes 262 if(pipe(daemon_start_pipe)) { 263 perror("pipe"); 264 return EXIT_FAILURE; 265 } 266 if(pipe(parent_log_pipe)) { 267 perror("pipe"); 268 return EXIT_FAILURE; 269 } 270 271 log_ereport(LOG_INFORM, "start daemon"); 272 273 // register parent process as LogDup to receive startup log messages 274 // in the parent process, because stdout/stderr will be closed 275 // after fork 276 startup_log.write = startup_log_write; 277 log_add_logdup(&startup_log); 278 279 // create daemon 280 pid_t pid = fork(); 281 if(pid < 0) { 282 perror("fork"); 283 return EXIT_FAILURE; 284 } else if(pid > 0) { 285 close(daemon_start_pipe[1]); 286 close(parent_log_pipe[1]); 287 return main_daemon_startup_wait(); 288 } 289 290 if(setsid() < 0) { 291 fprintf(stderr, "setsid failed\n"); 292 return EXIT_FAILURE; 293 } 294 295 // close read-end of pipe in the daemon process 296 close(daemon_start_pipe[0]); 297 298 // stdio redirection 299 // create pipes 300 if(pipe(std_out) != 0) { 301 perror("pipe"); 302 return EXIT_FAILURE; 303 } 304 if(pipe(std_err) != 0) { 305 perror("pipe"); 306 return EXIT_FAILURE; 307 } 308 309 for(int i=0;i<3;i++) { 310 close(i); 311 } 312 313 dup2(std_out[1], 1); 314 dup2(std_err[1], 2); 315 close(std_out[1]); 316 close(std_err[1]); 317 318 // set log thread stack size 319 pthread_attr_t tattr; 320 pthread_attr_init(&tattr); 321 pthread_attr_setstacksize(&tattr, LOG_THREAD_STACK_SIZE); 322 323 pthread_t tid; 324 pthread_create(&tid, &tattr, log_pipe_thread, NULL); 325 } 326 327 pool_init(NULL, NULL, NULL); 328 329 // add signal handler 330 signal(SIGUSR1, sig_usr1_reload); 331 signal(SIGTERM, sig_term); 332 signal(SIGINT, sig_term); 333 334 struct sigaction act; 335 ZERO(&act, sizeof(struct sigaction)); 336 act.sa_handler = SIG_IGN; 337 sigaction(SIGPIPE, &act, NULL); 338 339 // start webserver 340 log_ereport(LOG_INFORM, "startup"); 341 342 int status; 343 status = webserver_init(); 344 if(status != 0) { 345 log_ereport(LOG_FAILURE, "cannot initialize server."); 346 return EXIT_FAILURE; 347 } 348 349 status = webserver_run(); 350 if(is_daemon) { 351 finish_daemon_startup(status); 352 } 353 if(status != 0) { 354 log_ereport(LOG_FAILURE, "cannot run server."); 355 return EXIT_FAILURE; 356 } 357 358 if(srvctrl_wait()) { 359 return EXIT_FAILURE; 360 } 361 webserver_end(); 362 log_ereport(LOG_INFORM, "webserver: exit"); 363 364 return EXIT_SUCCESS; 365 } 366 367