moonbridge
diff moonbridge.c @ 0:f6d3b3f70dab
Initial commit
author | jbe |
---|---|
date | Sun Jan 04 19:30:28 2015 +0100 (2015-01-04) |
parents | |
children | 6016547c6410 |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/moonbridge.c Sun Jan 04 19:30:28 2015 +0100 1.3 @@ -0,0 +1,2715 @@ 1.4 + 1.5 +/*** Compile-time configuration ***/ 1.6 + 1.7 +#define MOONBR_LUA_PANIC_BUG_WORKAROUND 1 1.8 + 1.9 + 1.10 +/*** C preprocessor macros for portability support ***/ 1.11 + 1.12 +#ifndef __has_include 1.13 +#define __has_include(x) 0 1.14 +#endif 1.15 + 1.16 + 1.17 +/*** Include directives for used system libraries ***/ 1.18 + 1.19 +#if defined(__linux__) 1.20 +#define _GNU_SOURCE 1.21 +#endif 1.22 +#include <stdlib.h> 1.23 +#include <unistd.h> 1.24 +#include <stdint.h> 1.25 +#include <errno.h> 1.26 +#include <getopt.h> 1.27 +#include <syslog.h> 1.28 +#include <string.h> 1.29 +#include <stdio.h> 1.30 +#include <time.h> 1.31 +#include <sys/time.h> 1.32 +#include <sys/socket.h> 1.33 +#include <sys/un.h> 1.34 +#include <netinet/in.h> 1.35 +#include <poll.h> 1.36 +#include <signal.h> 1.37 +#include <sys/wait.h> 1.38 +#include <sys/resource.h> 1.39 +#include <sys/file.h> 1.40 +#if defined(__FreeBSD__) || __has_include(<libutil.h>) 1.41 +#include <libutil.h> 1.42 +#endif 1.43 +#if defined(__linux__) || __has_include(<bsd/libutil.h>) 1.44 +#include <bsd/libutil.h> 1.45 +#endif 1.46 +#if defined(__linux__) || __has_include(<bsd/unistd.h>) 1.47 +#include <bsd/unistd.h> 1.48 +#endif 1.49 + 1.50 + 1.51 +/*** Fallback definitions for missing constants on some platforms ***/ 1.52 + 1.53 +/* INFTIM is used as timeout parameter for poll() */ 1.54 +#ifndef INFTIM 1.55 +#define INFTIM -1 1.56 +#endif 1.57 + 1.58 + 1.59 +/*** Include directives for Lua ***/ 1.60 + 1.61 +#include <lua.h> 1.62 +#include <lauxlib.h> 1.63 +#include <lualib.h> 1.64 + 1.65 + 1.66 +/*** Constants ***/ 1.67 + 1.68 +/* Backlog option for listen() call */ 1.69 +#define MOONBR_LISTEN_BACKLOG 1024 1.70 + 1.71 +/* Maximum length of a timestamp used for strftime() */ 1.72 +#define MOONBR_LOG_MAXTIMELEN 40 1.73 + 1.74 +/* Maximum length of a log message */ 1.75 +#define MOONBR_LOG_MAXMSGLEN 4095 1.76 + 1.77 +/* Exitcodes passed to exit() call */ 1.78 +#define MOONBR_EXITCODE_GRACEFUL 0 1.79 +#define MOONBR_EXITCODE_CMDLINEERROR 1 1.80 +#define MOONBR_EXITCODE_ALREADYRUNNING 2 1.81 +#define MOONBR_EXITCODE_STARTUPERROR 3 1.82 +#define MOONBR_EXITCODE_RUNTIMEERROR 4 1.83 + 1.84 +/* Maximum length of a line sent to stderr by child processes */ 1.85 +#define MOONBR_MAXERRORLINELEN 1024 1.86 + 1.87 +/* Maximum length of an error string returned by strerror() */ 1.88 +#define MOONBR_MAXSTRERRORLEN 80 1.89 + 1.90 +/* Status bytes exchanged between master and child processes */ 1.91 +#define MOONBR_SOCKETTYPE_INTERVAL 'I' 1.92 +#define MOONBR_SOCKETTYPE_LOCAL 'L' 1.93 +#define MOONBR_SOCKETTYPE_NETWORK 'N' 1.94 +#define MOONBR_STATUS_IDLE '1' 1.95 +#define MOONBR_COMMAND_TERMINATE '2' 1.96 +#define MOONBR_STATUS_GOODBYE '3' 1.97 + 1.98 +/* Constant file descriptors */ 1.99 +#define MOONBR_FD_STDERR 2 1.100 +#define MOONBR_FD_CONTROL 3 1.101 +#define MOONBR_FD_END 4 1.102 + 1.103 +/* Return values of moonbr_try_destroy_worker() */ 1.104 +#define MOONBR_DESTROY_NONE 0 1.105 +#define MOONBR_DESTROY_PREPARE 1 1.106 +#define MOONBR_DESTROY_IDLE_OR_ASSIGNED 2 1.107 + 1.108 + 1.109 +/*** Types ***/ 1.110 + 1.111 +/* Enum for 'moonbr_pstate' */ 1.112 +#define MOONBR_PSTATE_STARTUP 0 1.113 +#define MOONBR_PSTATE_RUNNING 1 1.114 +#define MOONBR_PSTATE_FORKED 2 1.115 + 1.116 +/* Enum for 'proto' field of struct moonbr_listener */ 1.117 +#define MOONBR_PROTO_INTERVAL 1 1.118 +#define MOONBR_PROTO_LOCAL 2 1.119 +#define MOONBR_PROTO_TCP6 3 1.120 +#define MOONBR_PROTO_TCP4 4 1.121 + 1.122 +/* Data structure for a pool's listener that can accept incoming connections */ 1.123 +struct moonbr_listener { 1.124 + struct moonbr_pool *pool; 1.125 + struct moonbr_listener *prev_listener; /* previous idle or(!) connected listener */ 1.126 + struct moonbr_listener *next_listener; /* next idle or(!) connected listener */ 1.127 + int proto; 1.128 + union { 1.129 + struct { 1.130 + char *name; /* name of interval passed to 'connect' function as 'interval' field in table */ 1.131 + int strict; /* nonzero = runtime of 'connect' function does not delay interval */ 1.132 + struct timeval delay; /* interval between invocations of 'connect' function */ 1.133 + struct timeval wakeup; /* point in time of next invocation */ 1.134 + } interval; 1.135 + struct { 1.136 + char *path; /* full path name (i.e. filename with path) of UNIX domain socket */ 1.137 + } local; 1.138 + struct { 1.139 + int port; /* port number to listen on (in host endianess) */ 1.140 + int localhost_only; /* nonzero = listen on localhost only */ 1.141 + } tcp; 1.142 + } proto_specific; 1.143 + int listenfd; /* -1 = none */ 1.144 + int pollidx; /* -1 = none */ 1.145 +}; 1.146 + 1.147 +/* Data structure for a child process that is handling incoming connections */ 1.148 +struct moonbr_worker { 1.149 + struct moonbr_pool *pool; 1.150 + struct moonbr_worker *prev_worker; 1.151 + struct moonbr_worker *next_worker; 1.152 + struct moonbr_worker *prev_idle_worker; 1.153 + struct moonbr_worker *next_idle_worker; 1.154 + int idle; /* nonzero = waiting for command from parent process */ 1.155 + int assigned; /* nonzero = currently handling a connection */ 1.156 + pid_t pid; 1.157 + int controlfd; /* socket to send/receive control message to/from child process */ 1.158 + int errorfd; /* socket to receive error output from child process' stderr */ 1.159 + char *errorlinebuf; /* optional buffer for collecting stderr data from child process */ 1.160 + int errorlinelen; /* number of bytes stored in 'errorlinebuf' */ 1.161 + int errorlineovf; /* nonzero = line length overflow */ 1.162 + struct timeval idle_expiration; /* point in time until child process may stay in idle state */ 1.163 + struct moonbr_listener *restart_interval_listener; /* set while interval listener is assigned */ 1.164 +}; 1.165 + 1.166 +/* Data structure for a pool of workers and listeners */ 1.167 +struct moonbr_pool { 1.168 + int poolnum; /* number of pool for log output */ 1.169 + struct moonbr_pool *next_pool; /* next entry in linked list starting with 'moonbr_first_pool' */ 1.170 + struct moonbr_worker *first_worker; /* first worker of pool */ 1.171 + struct moonbr_worker *last_worker; /* last worker of pool */ 1.172 + struct moonbr_worker *first_idle_worker; /* first idle worker of pool */ 1.173 + struct moonbr_worker *last_idle_worker; /* last idle worker of pool */ 1.174 + int idle_worker_count; 1.175 + int unassigned_worker_count; 1.176 + int total_worker_count; 1.177 + int worker_count_stat; /* only needed for statistics */ 1.178 + int pre_fork; /* desired minimum number of unassigned workers */ 1.179 + int min_fork; /* desired minimum number of workers in total */ 1.180 + int max_fork; /* maximum number of workers */ 1.181 + struct timeval fork_delay; /* delay after each fork() until a fork may happen again */ 1.182 + struct timeval fork_wakeup; /* point in time when a fork may happen again (unless a worker terminates before) */ 1.183 + struct timeval fork_error_delay; /* delay between fork()s when an error during fork or preparation occurred */ 1.184 + struct timeval fork_error_wakeup; /* point in time when fork may happen again if an error in preparation occurred */ 1.185 + int use_fork_error_wakeup; /* nonzero = error in preparation occured; gets reset on next fork */ 1.186 + struct timeval exit_delay; /* delay for terminating excessive workers (unassigned_worker_count > pre_fork) */ 1.187 + struct timeval exit_wakeup; /* point in time when terminating an excessive worker */ 1.188 + struct timeval idle_timeout; /* delay before an idle worker is terminated */ 1.189 + size_t memory_limit; /* maximum bytes of memory that the Lua machine may allocate */ 1.190 + int listener_count; /* total number of listeners of pool (and size of 'listener' array at end of this struct) */ 1.191 + struct moonbr_listener *first_idle_listener; /* first listener that is idle (i.e. has no waiting connection) */ 1.192 + struct moonbr_listener *last_idle_listener; /* last listener that is idle (i.e. has no waiting connection) */ 1.193 + struct moonbr_listener *first_connected_listener; /* first listener that has a pending connection */ 1.194 + struct moonbr_listener *last_connected_listener; /* last listener that has a pending connection */ 1.195 + struct moonbr_listener listener[1]; /* static array of variable(!) size to contain 'listener' structures */ 1.196 +}; 1.197 + 1.198 +/* Enum for 'channel' field of struct moonbr_poll_worker */ 1.199 +#define MOONBR_POLL_WORKER_CONTROLCHANNEL 1 1.200 +#define MOONBR_POLL_WORKER_ERRORCHANNEL 2 1.201 + 1.202 +/* Structure to refer from 'moonbr_poll_worker_fds' entry to worker structure */ 1.203 +struct moonbr_poll_worker { 1.204 + struct moonbr_worker *worker; 1.205 + int channel; /* field indicating whether file descriptor is 'controlfd' or 'errorfd' */ 1.206 +}; 1.207 + 1.208 +/* Variable indicating that clean shutdown was requested */ 1.209 +static int moonbr_shutdown_in_progress = 0; 1.210 + 1.211 + 1.212 +/*** Macros for Lua registry ***/ 1.213 + 1.214 +/* Lightuserdata keys for Lua registry to store 'prepare', 'connect', and 'finish' functions */ 1.215 +#define moonbr_luakey_prepare_func(pool) ((void *)(intptr_t)(pool) + 0) 1.216 +#define moonbr_luakey_connect_func(pool) ((void *)(intptr_t)(pool) + 1) 1.217 +#define moonbr_luakey_finish_func(pool) ((void *)(intptr_t)(pool) + 2) 1.218 + 1.219 + 1.220 +/*** Global variables ***/ 1.221 + 1.222 +/* State of process execution */ 1.223 +static int moonbr_pstate = MOONBR_PSTATE_STARTUP; 1.224 + 1.225 +/* Process ID of the main process */ 1.226 +static pid_t moonbr_masterpid; 1.227 + 1.228 +/* Condition variables set by the signal handler */ 1.229 +static volatile sig_atomic_t moonbr_cond_poll = 0; 1.230 +static volatile sig_atomic_t moonbr_cond_terminate = 0; 1.231 +static volatile sig_atomic_t moonbr_cond_interrupt = 0; 1.232 +static volatile sig_atomic_t moonbr_cond_child = 0; 1.233 + 1.234 +/* Socket pair to denote signal delivery when signal handler was called just before poll() */ 1.235 +static int moonbr_poll_signalfds[2]; 1.236 +#define moonbr_poll_signalfd_read moonbr_poll_signalfds[0] 1.237 +#define moonbr_poll_signalfd_write moonbr_poll_signalfds[1] 1.238 + 1.239 +/* Global variables for pidfile and logging */ 1.240 +static struct pidfh *moonbr_pidfh = NULL; 1.241 +static FILE *moonbr_logfile = NULL; 1.242 +static int moonbr_use_syslog = 0; 1.243 + 1.244 +/* First and last entry of linked list of all created pools during initialization */ 1.245 +static struct moonbr_pool *moonbr_first_pool = NULL; 1.246 +static struct moonbr_pool *moonbr_last_pool = NULL; 1.247 + 1.248 +/* Total count of pools */ 1.249 +static int moonbr_pool_count = 0; 1.250 + 1.251 +/* Set to a nonzero value if dynamic part of 'moonbr_poll_fds' ('moonbr_poll_worker_fds') needs an update */ 1.252 +static int moonbr_poll_refresh_needed = 0; 1.253 + 1.254 +/* Array passed to poll(), consisting of static part and dynamic part ('moonbr_poll_worker_fds') */ 1.255 +static struct pollfd *moonbr_poll_fds = NULL; /* the array */ 1.256 +static int moonbr_poll_fds_bufsize = 0; /* memory allocated for this number of elements */ 1.257 +static int moonbr_poll_fds_count = 0; /* total number of elements */ 1.258 +static int moonbr_poll_fds_static_count; /* number of elements in static part */ 1.259 + 1.260 +/* Dynamic part of 'moonbr_poll_fds' array */ 1.261 +#define moonbr_poll_worker_fds (moonbr_poll_fds+moonbr_poll_fds_static_count) 1.262 + 1.263 +/* Additional information for dynamic part of 'moonbr_poll_fds' array */ 1.264 +struct moonbr_poll_worker *moonbr_poll_workers; /* the array */ 1.265 +static int moonbr_poll_workers_bufsize = 0; /* memory allocated for this number of elements */ 1.266 +static int moonbr_poll_worker_count = 0; /* number of elements in array */ 1.267 + 1.268 +/* Variable set to nonzero value to disallow further calls of 'listen' function */ 1.269 +static int moonbr_booted = 0; 1.270 + 1.271 +/* Global variables to store information on connection socket in child process */ 1.272 +static int moonbr_child_peersocket_type; /* type of socket by MOONBR_SOCKETTYPE constant */ 1.273 +static int moonbr_child_peersocket_fd; /* Original file descriptor of peer socket */ 1.274 +static luaL_Stream *moonbr_child_peersocket_inputstream; /* Lua input stream of socket */ 1.275 +static luaL_Stream *moonbr_child_peersocket_outputstream; /* Lua output stream of socket */ 1.276 + 1.277 +/* Verbosity settings */ 1.278 +static int moonbr_debug = 0; 1.279 +static int moonbr_stat = 0; 1.280 + 1.281 +/* Memory consumption by Lua machine */ 1.282 +static size_t moonbr_memory_usage = 0; 1.283 +static size_t moonbr_memory_limit = 0; 1.284 + 1.285 + 1.286 +/*** Functions for signal handling ***/ 1.287 + 1.288 +/* Signal handler for master and child processes */ 1.289 +static void moonbr_signal(int sig) { 1.290 + if (getpid() == moonbr_masterpid) { 1.291 + /* master process */ 1.292 + switch (sig) { 1.293 + case SIGHUP: 1.294 + case SIGINT: 1.295 + /* fast shutdown requested */ 1.296 + moonbr_cond_interrupt = 1; 1.297 + break; 1.298 + case SIGTERM: 1.299 + /* clean shutdown requested */ 1.300 + moonbr_cond_terminate = 1; 1.301 + break; 1.302 + case SIGCHLD: 1.303 + /* child process terminated */ 1.304 + moonbr_cond_child = 1; 1.305 + break; 1.306 + } 1.307 + if (moonbr_cond_poll) { 1.308 + /* avoid race condition if signal handler is invoked right before poll() */ 1.309 + char buf[1] = {0}; 1.310 + write(moonbr_poll_signalfd_write, buf, 1); 1.311 + } 1.312 + } else { 1.313 + /* child process forwards certain signals to parent process */ 1.314 + switch (sig) { 1.315 + case SIGHUP: 1.316 + case SIGINT: 1.317 + case SIGTERM: 1.318 + kill(moonbr_masterpid, sig); 1.319 + } 1.320 + } 1.321 +} 1.322 + 1.323 +/* Initialize signal handling */ 1.324 +static void moonbr_signal_init(){ 1.325 + moonbr_masterpid = getpid(); 1.326 + signal(SIGHUP, moonbr_signal); 1.327 + signal(SIGINT, moonbr_signal); 1.328 + signal(SIGTERM, moonbr_signal); 1.329 + signal(SIGCHLD, moonbr_signal); 1.330 +} 1.331 + 1.332 + 1.333 +/*** Functions for logging in master process ***/ 1.334 + 1.335 +/* Logs a pre-formatted message with given syslog() priority */ 1.336 +static void moonbr_log_msg(int priority, const char *msg) { 1.337 + if (moonbr_logfile) { 1.338 + /* logging to logfile desired (timestamp is prepended in that case) */ 1.339 + time_t now_time = 0; 1.340 + struct tm now_tmstruct; 1.341 + char timestr[MOONBR_LOG_MAXTIMELEN+1]; 1.342 + time(&now_time); 1.343 + localtime_r(&now_time, &now_tmstruct); 1.344 + if (!strftime( 1.345 + timestr, MOONBR_LOG_MAXTIMELEN+1, "%Y-%m-%d %H:%M:%S %Z: ", &now_tmstruct 1.346 + )) timestr[0] = 0; 1.347 + fprintf(moonbr_logfile, "%s%s\n", timestr, msg); 1.348 + } 1.349 + if (moonbr_use_syslog) { 1.350 + /* logging through syslog desired */ 1.351 + syslog(priority, "%s", msg); 1.352 + } 1.353 +} 1.354 + 1.355 +/* Formats a message via vsnprintf() and logs it with given syslog() priority */ 1.356 +static void moonbr_log(int priority, const char *message, ...) { 1.357 + char msgbuf[MOONBR_LOG_MAXMSGLEN+1]; /* buffer of static size to store formatted message */ 1.358 + int msglen; /* length of full message (may exceed MOONBR_LOG_MAXMSGLEN) */ 1.359 + { 1.360 + /* pass variable arguments to vsnprintf() to format message */ 1.361 + va_list ap; 1.362 + va_start(ap, message); 1.363 + msglen = vsnprintf(msgbuf, MOONBR_LOG_MAXMSGLEN+1, message, ap); 1.364 + va_end(ap); 1.365 + } 1.366 + { 1.367 + /* split and log message line by line */ 1.368 + char *line = msgbuf; 1.369 + while (1) { 1.370 + char *endptr = strchr(line, '\n'); 1.371 + if (endptr) { 1.372 + /* terminate string where newline character is found */ 1.373 + *endptr = 0; 1.374 + } else if (line != msgbuf && msglen > MOONBR_LOG_MAXMSGLEN) { 1.375 + /* break if line is incomplete and not the first line */ 1.376 + break; 1.377 + } 1.378 + moonbr_log_msg(priority, line); 1.379 + if (!endptr) break; /* break if end of formatted message is reached */ 1.380 + line = endptr+1; /* otherwise continue with remaining message */ 1.381 + } 1.382 + } 1.383 + if (msglen > MOONBR_LOG_MAXMSGLEN) { 1.384 + /* print warning if message was truncated */ 1.385 + moonbr_log_msg(priority, "Previous log message has been truncated due to excessive length"); 1.386 + } 1.387 +} 1.388 + 1.389 + 1.390 +/*** Termination function ***/ 1.391 + 1.392 +/* Kill all child processes, remove PID file (if existent), and exit master process with given exitcode */ 1.393 +static void moonbr_terminate(int exitcode) { 1.394 + { 1.395 + struct moonbr_pool *pool; 1.396 + for (pool=moonbr_first_pool; pool; pool=pool->next_pool) { 1.397 + { 1.398 + struct moonbr_worker *worker; 1.399 + for (worker=pool->first_worker; worker; worker=worker->next_worker) { 1.400 + moonbr_log(LOG_INFO, "Sending SIGKILL to child with PID %i", (int)worker->pid); 1.401 + if (kill(worker->pid, SIGKILL)) { 1.402 + moonbr_log(LOG_ERR, "Error while killing child process: %s", strerror(errno)); 1.403 + } 1.404 + } 1.405 + } 1.406 + { 1.407 + int i; 1.408 + for (i=0; i<pool->listener_count; i++) { 1.409 + struct moonbr_listener *listener = &pool->listener[i]; 1.410 + if (listener->proto == MOONBR_PROTO_LOCAL) { 1.411 + moonbr_log(LOG_INFO, "Unlinking local socket \"%s\"", listener->proto_specific.local.path); 1.412 + if (unlink(listener->proto_specific.local.path)) { 1.413 + moonbr_log(LOG_ERR, "Error while unlinking local socket: %s", strerror(errno)); 1.414 + } 1.415 + } 1.416 + } 1.417 + } 1.418 + } 1.419 + } 1.420 + moonbr_log(exitcode ? LOG_ERR : LOG_NOTICE, "Terminating with exit code %i", exitcode); 1.421 + if (moonbr_pidfh && pidfile_remove(moonbr_pidfh)) { 1.422 + moonbr_log(LOG_ERR, "Error while removing PID file: %s", strerror(errno)); 1.423 + } 1.424 + exit(exitcode); 1.425 +} 1.426 + 1.427 +/* Terminate with either MOONBR_EXITCODE_STARTUPERROR or MOONBR_EXITCODE_RUNTIMEERROR */ 1.428 +#define moonbr_terminate_error() \ 1.429 + moonbr_terminate( \ 1.430 + moonbr_pstate == MOONBR_PSTATE_STARTUP ? \ 1.431 + MOONBR_EXITCODE_STARTUPERROR : \ 1.432 + MOONBR_EXITCODE_RUNTIMEERROR \ 1.433 + ) 1.434 + 1.435 + 1.436 +/*** Helper functions ***/ 1.437 + 1.438 +/* Fills a 'struct timeval' structure with the current time (using CLOCK_MONOTONIC) */ 1.439 +static void moonbr_now(struct timeval *now) { 1.440 + struct timespec ts = {0, }; 1.441 + if (clock_gettime(CLOCK_MONOTONIC, &ts)) { 1.442 + moonbr_log(LOG_CRIT, "Error in clock_gettime() call: %s", strerror(errno)); 1.443 + moonbr_terminate_error(); 1.444 + } 1.445 + *now = (struct timeval){ .tv_sec = ts.tv_sec, .tv_usec = ts.tv_nsec / 1000 }; 1.446 +} 1.447 + 1.448 +/* Formats a 'struct timeval' value (not thread-safe) */ 1.449 +static char *moonbr_format_timeval(struct timeval *t) { 1.450 + static char buf[32]; 1.451 + snprintf(buf, 32, "%ji.%06ji seconds", (intmax_t)t->tv_sec, (intmax_t)t->tv_usec); 1.452 + return buf; 1.453 +} 1.454 + 1.455 + 1.456 +/*** Functions for pool creation and startup ***/ 1.457 + 1.458 +/* Creates a 'struct moonbr_pool' structure with a given number of listeners */ 1.459 +static struct moonbr_pool *moonbr_create_pool(int listener_count) { 1.460 + struct moonbr_pool *pool; 1.461 + pool = calloc(1, 1.462 + sizeof(struct moonbr_pool) + /* size of 'struct moonbr_pool' with one listener */ 1.463 + (listener_count-1) * sizeof(struct moonbr_listener) /* size of extra listeners */ 1.464 + ); 1.465 + if (!pool) { 1.466 + moonbr_log(LOG_CRIT, "Memory allocation error"); 1.467 + moonbr_terminate_error(); 1.468 + } 1.469 + pool->listener_count = listener_count; 1.470 + { 1.471 + /* initialization of listeners */ 1.472 + int i; 1.473 + for (i=0; i<listener_count; i++) { 1.474 + struct moonbr_listener *listener = &pool->listener[i]; 1.475 + listener->pool = pool; 1.476 + listener->listenfd = -1; 1.477 + listener->pollidx = -1; 1.478 + } 1.479 + } 1.480 + return pool; 1.481 +} 1.482 + 1.483 +/* Destroys a 'struct moonbr_pool' structure before it has been started */ 1.484 +static void moonbr_destroy_pool(struct moonbr_pool *pool) { 1.485 + int i; 1.486 + for (i=0; i<pool->listener_count; i++) { 1.487 + struct moonbr_listener *listener = &pool->listener[i]; 1.488 + if ( 1.489 + listener->proto == MOONBR_PROTO_INTERVAL && 1.490 + listener->proto_specific.interval.name 1.491 + ) { 1.492 + free(listener->proto_specific.interval.name); 1.493 + } 1.494 + if ( 1.495 + listener->proto == MOONBR_PROTO_LOCAL && 1.496 + listener->proto_specific.local.path 1.497 + ) { 1.498 + free(listener->proto_specific.local.path); 1.499 + } 1.500 + } 1.501 + free(pool); 1.502 +} 1.503 + 1.504 +/* Starts a all listeners in a pool */ 1.505 +static int moonbr_start_pool(struct moonbr_pool *pool) { 1.506 + moonbr_log(LOG_INFO, "Creating pool", pool->poolnum); 1.507 + { 1.508 + int i; 1.509 + for (i=0; i<pool->listener_count; i++) { 1.510 + struct moonbr_listener *listener = &pool->listener[i]; 1.511 + switch (listener->proto) { 1.512 + case MOONBR_PROTO_INTERVAL: 1.513 + // nothing to do here: starting intervals is performed in moonbr_run() function 1.514 + if (!listener->proto_specific.interval.name) { 1.515 + moonbr_log(LOG_INFO, "Adding unnamed interval listener"); 1.516 + } else { 1.517 + moonbr_log(LOG_INFO, "Adding interval listener \"%s\"", listener->proto_specific.interval.name); 1.518 + } 1.519 + break; 1.520 + case MOONBR_PROTO_LOCAL: 1.521 + moonbr_log(LOG_INFO, "Adding local socket listener for path \"%s\"", listener->proto_specific.local.path); 1.522 + { 1.523 + struct sockaddr_un servaddr = { .sun_family = AF_UNIX }; 1.524 + const int path_maxlen = sizeof(struct sockaddr_un) - ( 1.525 + (void *)&servaddr.sun_path - (void *)&servaddr 1.526 + ); 1.527 + if ( 1.528 + snprintf( 1.529 + servaddr.sun_path, 1.530 + path_maxlen, 1.531 + "%s", 1.532 + listener->proto_specific.local.path 1.533 + ) >= path_maxlen 1.534 + ) { 1.535 + errno = ENAMETOOLONG; 1.536 + }; 1.537 + listener->listenfd = socket(PF_LOCAL, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); 1.538 + if (listener->listenfd == -1) goto moonbr_start_pool_error; 1.539 + if (!unlink(listener->proto_specific.local.path)) { 1.540 + moonbr_log(LOG_WARNING, "Unlinked named socket \"%s\" prior to listening", listener->proto_specific.local.path); 1.541 + } else { 1.542 + if (errno != ENOENT) { 1.543 + moonbr_log(LOG_ERR, "Could not unlink named socket \"%s\" prior to listening: %s", listener->proto_specific.local.path, strerror(errno)); 1.544 + } 1.545 + } 1.546 + if ( 1.547 + bind(listener->listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) 1.548 + ) goto moonbr_start_pool_error; 1.549 + if (listen(listener->listenfd, MOONBR_LISTEN_BACKLOG)) goto moonbr_start_pool_error; 1.550 + } 1.551 + break; 1.552 + case MOONBR_PROTO_TCP6: 1.553 + if (listener->proto_specific.tcp.localhost_only) { 1.554 + moonbr_log(LOG_INFO, "Adding localhost TCP/IPv6 listener on port %i", listener->proto_specific.tcp.port); 1.555 + } else { 1.556 + moonbr_log(LOG_INFO, "Adding public TCP/IPv6 listener on port %i", listener->proto_specific.tcp.port); 1.557 + } 1.558 + { 1.559 + struct sockaddr_in6 servaddr = { 1.560 + .sin6_family = AF_INET6, 1.561 + .sin6_port = htons(listener->proto_specific.tcp.port) 1.562 + }; 1.563 + listener->listenfd = socket(PF_INET6, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); 1.564 + if (listener->listenfd == -1) goto moonbr_start_pool_error; 1.565 + { 1.566 + /* avoid "Address already in use" error when restarting service */ 1.567 + static const int reuseval = 1; 1.568 + if (setsockopt( 1.569 + listener->listenfd, SOL_SOCKET, SO_REUSEADDR, &reuseval, sizeof(reuseval) 1.570 + )) goto moonbr_start_pool_error; 1.571 + } 1.572 + { 1.573 + /* default to send TCP RST when process terminates unexpectedly */ 1.574 + static const struct linger lingerval = { 1.575 + .l_onoff = 1, 1.576 + .l_linger = 0 1.577 + }; 1.578 + if (setsockopt( 1.579 + listener->listenfd, SOL_SOCKET, SO_LINGER, &lingerval, sizeof(lingerval) 1.580 + )) goto moonbr_start_pool_error; 1.581 + } 1.582 + if (listener->proto_specific.tcp.localhost_only) { 1.583 + servaddr.sin6_addr.s6_addr[15] = 1; 1.584 + } 1.585 + if ( 1.586 + bind(listener->listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) 1.587 + ) goto moonbr_start_pool_error; 1.588 + if (listen(listener->listenfd, MOONBR_LISTEN_BACKLOG)) goto moonbr_start_pool_error; 1.589 + } 1.590 + break; 1.591 + case MOONBR_PROTO_TCP4: 1.592 + if (listener->proto_specific.tcp.localhost_only) { 1.593 + moonbr_log(LOG_INFO, "Adding localhost TCP/IPv4 listener on port %i", listener->proto_specific.tcp.port); 1.594 + } else { 1.595 + moonbr_log(LOG_INFO, "Adding public TCP/IPv4 listener on port %i", listener->proto_specific.tcp.port); 1.596 + } 1.597 + { 1.598 + struct sockaddr_in servaddr = { 1.599 + .sin_family = AF_INET, 1.600 + .sin_port = htons(listener->proto_specific.tcp.port) 1.601 + }; 1.602 + listener->listenfd = socket(PF_INET, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); 1.603 + if (listener->listenfd == -1) goto moonbr_start_pool_error; 1.604 + { 1.605 + /* avoid "Address already in use" error when restarting service */ 1.606 + static const int reuseval = 1; 1.607 + if (setsockopt( 1.608 + listener->listenfd, SOL_SOCKET, SO_REUSEADDR, &reuseval, sizeof(reuseval) 1.609 + )) goto moonbr_start_pool_error; 1.610 + } 1.611 + { 1.612 + /* default to send TCP RST when process terminates unexpectedly */ 1.613 + static const struct linger lingerval = { 1.614 + .l_onoff = 1, 1.615 + .l_linger = 0 1.616 + }; 1.617 + if (setsockopt( 1.618 + listener->listenfd, SOL_SOCKET, SO_LINGER, &lingerval, sizeof(lingerval) 1.619 + )) goto moonbr_start_pool_error; 1.620 + } 1.621 + if (listener->proto_specific.tcp.localhost_only) { 1.622 + ((uint8_t *)&servaddr.sin_addr.s_addr)[0] = 127; 1.623 + ((uint8_t *)&servaddr.sin_addr.s_addr)[3] = 1; 1.624 + } 1.625 + if ( 1.626 + bind(listener->listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) 1.627 + ) goto moonbr_start_pool_error; 1.628 + if (listen(listener->listenfd, MOONBR_LISTEN_BACKLOG)) goto moonbr_start_pool_error; 1.629 + } 1.630 + break; 1.631 + default: 1.632 + moonbr_log(LOG_CRIT, "Internal error (should not happen): Unexpected value in listener.proto field"); 1.633 + moonbr_terminate_error(); 1.634 + } 1.635 + } 1.636 + goto moonbr_start_pool_ok; 1.637 + moonbr_start_pool_error: 1.638 + { 1.639 + int j = i; 1.640 + int errno2 = errno; 1.641 + for (; i>=0; i--) { 1.642 + struct moonbr_listener *listener = &pool->listener[i]; 1.643 + if (listener->listenfd != -1) close(listener->listenfd); 1.644 + } 1.645 + errno = errno2; 1.646 + return j; 1.647 + } 1.648 + } 1.649 + moonbr_start_pool_ok: 1.650 + pool->poolnum = ++moonbr_pool_count; 1.651 + moonbr_log(LOG_INFO, "Pool #%i created", pool->poolnum); 1.652 + if (moonbr_last_pool) moonbr_last_pool->next_pool = pool; 1.653 + else moonbr_first_pool = pool; 1.654 + moonbr_last_pool = pool; 1.655 + return -1; 1.656 +} 1.657 + 1.658 + 1.659 +/*** Function to send data and a file descriptor to child process */ 1.660 + 1.661 +/* Sends control message of one bye plus optional file descriptor plus optional pointer to child process */ 1.662 +static void moonbr_send_control_message(struct moonbr_worker *worker, char status, int fd, void *ptr) { 1.663 + { 1.664 + struct iovec iovector = { .iov_base = &status, .iov_len = 1 }; /* carrying status byte */ 1.665 + char control_message_buffer[CMSG_SPACE(sizeof(int))] = {0, }; /* used to transfer file descriptor */ 1.666 + struct msghdr message = { .msg_iov = &iovector, .msg_iovlen = 1 }; /* data structure passed to sendmsg() call */ 1.667 + if (moonbr_debug) { 1.668 + if (fd == -1) { 1.669 + moonbr_log(LOG_DEBUG, "Sending control message \"%c\" to child process in pool #%i (PID %i)", (int)status, worker->pool->poolnum, (int)worker->pid); 1.670 + } else { 1.671 + moonbr_log(LOG_DEBUG, "Sending control message \"%c\" with file descriptor #%i to child process in pool #%i (PID %i)", (int)status, fd, worker->pool->poolnum, (int)worker->pid); 1.672 + } 1.673 + } 1.674 + if (fd != -1) { 1.675 + /* attach control message with file descriptor */ 1.676 + message.msg_control = control_message_buffer; 1.677 + message.msg_controllen = CMSG_SPACE(sizeof(int)); 1.678 + { 1.679 + struct cmsghdr *control_message = CMSG_FIRSTHDR(&message); 1.680 + control_message->cmsg_level = SOL_SOCKET; 1.681 + control_message->cmsg_type = SCM_RIGHTS; 1.682 + control_message->cmsg_len = CMSG_LEN(sizeof(int)); 1.683 + *((int *)CMSG_DATA(control_message)) = fd; 1.684 + } 1.685 + } 1.686 + while (sendmsg(worker->controlfd, &message, MSG_NOSIGNAL) < 0) { 1.687 + if (errno == EPIPE) { 1.688 + moonbr_log(LOG_ERR, "Error while communicating with idle child process in pool #%i (PID %i): %s", worker->pool->poolnum, (int)worker->pid, strerror(errno)); 1.689 + return; /* do not close socket; socket is closed when reading from it */ 1.690 + } 1.691 + if (errno != EINTR) { 1.692 + moonbr_log(LOG_CRIT, "Unexpected error while communicating with idle child process in pool #%i (PID %i): %s", worker->pool->poolnum, (int)worker->pid, strerror(errno)); 1.693 + moonbr_terminate_error(); 1.694 + } 1.695 + } 1.696 + } 1.697 + if (ptr) { 1.698 + char buf[sizeof(void *)]; 1.699 + char *pos = buf; 1.700 + int len = sizeof(void *); 1.701 + ssize_t written; 1.702 + if (moonbr_debug) { 1.703 + moonbr_log(LOG_DEBUG, "Sending memory pointer to child process in pool #%i (PID %i)", (int)status, worker->pool->poolnum, (int)worker->pid); 1.704 + } 1.705 + *((intptr_t *)buf) = (intptr_t)ptr; 1.706 + while (len) { 1.707 + written = send(worker->controlfd, pos, len, MSG_NOSIGNAL); 1.708 + if (written > 0) { 1.709 + pos += written; 1.710 + len -= written; 1.711 + } else if (errno == EPIPE) { 1.712 + moonbr_log(LOG_ERR, "Error while communicating with idle child process in pool #%i (PID %i): %s", worker->pool->poolnum, (int)worker->pid, strerror(errno)); 1.713 + return; /* do not close socket; socket is closed when reading from it */ 1.714 + } else if (errno != EINTR) { 1.715 + moonbr_log(LOG_CRIT, "Unexpected error while communicating with idle child process in pool #%i (PID %i): %s", worker->pool->poolnum, (int)worker->pid, strerror(errno)); 1.716 + moonbr_terminate_error(); 1.717 + } 1.718 + } 1.719 + } 1.720 +} 1.721 + 1.722 + 1.723 +/*** Functions running in child process ***/ 1.724 + 1.725 +/* Logs an error in child process */ 1.726 +static void moonbr_child_log(const char *message) { 1.727 + fprintf(stderr, "%s\n", message); 1.728 +} 1.729 + 1.730 +/* Logs a fatal error in child process and terminates process with error status */ 1.731 +static void moonbr_child_log_fatal(const char *message) { 1.732 + moonbr_child_log(message); 1.733 + exit(1); 1.734 +} 1.735 + 1.736 +/* Logs an error in child process while appending error string for global errno variable */ 1.737 +static void moonbr_child_log_errno(const char *message) { 1.738 + char errmsg[MOONBR_MAXSTRERRORLEN]; 1.739 + strerror_r(errno, errmsg, MOONBR_MAXSTRERRORLEN); /* use thread-safe call in case child created threads */ 1.740 + fprintf(stderr, "%s: %s\n", message, errmsg); 1.741 +} 1.742 + 1.743 +/* Logs a fatal error in child process while appending error string for errno and terminating process */ 1.744 +static void moonbr_child_log_errno_fatal(const char *message) { 1.745 + moonbr_child_log_errno(message); 1.746 + exit(1); 1.747 +} 1.748 + 1.749 +/* Receives a control message consisting of one character plus an optional file descriptor from parent process */ 1.750 +static void moonbr_child_receive_control_message(int socketfd, char *status, int *fd) { 1.751 + struct iovec iovector = { .iov_base = status, .iov_len = 1 }; /* reference to status byte variable */ 1.752 + char control_message_buffer[CMSG_SPACE(sizeof(int))] = {0, }; /* used to receive file descriptor */ 1.753 + struct msghdr message = { /* data structure passed to recvmsg() call */ 1.754 + .msg_iov = &iovector, 1.755 + .msg_iovlen = 1, 1.756 + .msg_control = control_message_buffer, 1.757 + .msg_controllen = CMSG_SPACE(sizeof(int)) 1.758 + }; 1.759 + { 1.760 + int received; 1.761 + while ((received = recvmsg(socketfd, &message, MSG_CMSG_CLOEXEC)) < 0) { 1.762 + if (errno != EINTR) { 1.763 + moonbr_child_log_errno_fatal("Error while trying to receive connection socket from parent process"); 1.764 + } 1.765 + } 1.766 + if (!received) { 1.767 + moonbr_child_log_fatal("Unexpected EOF while trying to receive connection socket from parent process"); 1.768 + } 1.769 + } 1.770 + { 1.771 + struct cmsghdr *control_message = CMSG_FIRSTHDR(&message); 1.772 + if (control_message) { 1.773 + if (control_message->cmsg_level != SOL_SOCKET) { 1.774 + moonbr_child_log_fatal("Received control message with cmsg_level not equal to SOL_SOCKET"); 1.775 + } 1.776 + if (control_message->cmsg_type != SCM_RIGHTS) { 1.777 + moonbr_child_log_fatal("Received control message with cmsg_type not equal to SCM_RIGHTS"); 1.778 + } 1.779 + *fd = *((int *)CMSG_DATA(control_message)); 1.780 + } else { 1.781 + *fd = -1; 1.782 + } 1.783 + } 1.784 +} 1.785 + 1.786 +/* Receives a pointer from parent process */ 1.787 +static void *moonbr_child_receive_pointer(int socketfd) { 1.788 + char buf[sizeof(void *)]; 1.789 + char *pos = buf; 1.790 + int len = sizeof(void *); 1.791 + ssize_t bytes_read; 1.792 + while (len) { 1.793 + bytes_read = recv(socketfd, pos, len, 0); 1.794 + if (bytes_read > 0) { 1.795 + pos += bytes_read; 1.796 + len -= bytes_read; 1.797 + } else if (!bytes_read) { 1.798 + moonbr_child_log_fatal("Unexpected EOF while trying to receive memory pointer from parent process"); 1.799 + } else if (errno != EINTR) { 1.800 + moonbr_child_log_errno_fatal("Error while trying to receive memory pointer from parent process"); 1.801 + } 1.802 + } 1.803 + return (void *)*(intptr_t *)buf; 1.804 +} 1.805 + 1.806 +/* Throws a Lua error message with an error string for errno appended to it */ 1.807 +static void moonbr_child_lua_errno_error(lua_State *L, char *message) { 1.808 + char errmsg[MOONBR_MAXSTRERRORLEN]; 1.809 + strerror_r(errno, errmsg, MOONBR_MAXSTRERRORLEN); /* use thread-safe call in case child created threads */ 1.810 + luaL_error(L, "%s: %s", message, errmsg); 1.811 +} 1.812 + 1.813 +/* Closes the input stream from peer unless it has already been closed */ 1.814 +static int moonbr_child_close_peersocket_inputstream( 1.815 + int cleanshut, /* nonzero = use shutdown() if applicable */ 1.816 + int mark /* nonzero = mark the stream as closed for Lua */ 1.817 +) { 1.818 + int err = 0; /* nonzero = error occurred */ 1.819 + int errno2; /* stores previous errno values that take precedence */ 1.820 + if (moonbr_child_peersocket_inputstream->f) { 1.821 + if (cleanshut && moonbr_child_peersocket_type == MOONBR_SOCKETTYPE_NETWORK) { 1.822 + if (shutdown(moonbr_child_peersocket_fd, SHUT_RD)) { 1.823 + errno2 = errno; 1.824 + err = -1; 1.825 + } 1.826 + } 1.827 + if (fclose(moonbr_child_peersocket_inputstream->f)) { 1.828 + if (!err) errno2 = errno; 1.829 + err = -1; 1.830 + } 1.831 + moonbr_child_peersocket_inputstream->f = NULL; 1.832 + } 1.833 + if (mark) moonbr_child_peersocket_inputstream->closef = NULL; 1.834 + if (err) errno = errno2; 1.835 + return err; 1.836 +} 1.837 + 1.838 +/* Closes the output stream to peer unless it has already been closed */ 1.839 +static int moonbr_child_close_peersocket_outputstream( 1.840 + int cleanshut, /* nonzero = use fflush() and shutdown() if applicable */ 1.841 + int mark /* nonzero = mark the stream as closed for Lua */ 1.842 +) { 1.843 + int err = 0; /* nonzero = error occurred */ 1.844 + int errno2; /* stores previous errno values that take precedence */ 1.845 + if (moonbr_child_peersocket_outputstream->f) { 1.846 + if (moonbr_child_peersocket_type == MOONBR_SOCKETTYPE_NETWORK) { 1.847 + if (cleanshut) { 1.848 + if (fflush(moonbr_child_peersocket_outputstream->f)) { 1.849 + errno2 = errno; 1.850 + err = -1; 1.851 + } else { 1.852 + if (shutdown(moonbr_child_peersocket_fd, SHUT_WR)) { 1.853 + errno2 = errno; 1.854 + err = -1; 1.855 + } 1.856 + } 1.857 + } else { 1.858 + fpurge(moonbr_child_peersocket_outputstream->f); 1.859 + } 1.860 + } 1.861 + if (fclose(moonbr_child_peersocket_outputstream->f)) { 1.862 + if (!err) errno2 = errno; 1.863 + err = -1; 1.864 + } 1.865 + moonbr_child_peersocket_outputstream->f = NULL; 1.866 + } 1.867 + if (mark) moonbr_child_peersocket_outputstream->closef = NULL; 1.868 + if (err) errno = errno2; 1.869 + return err; 1.870 +} 1.871 + 1.872 +/* Perform a clean shutdown of input and output stream (may be called multiple times) */ 1.873 +static int moonbr_child_close_peersocket(int timeout) { 1.874 + int errprio = 0; 1.875 + int errno2; 1.876 + if (moonbr_child_peersocket_fd == -1) return 0; 1.877 + if (moonbr_child_close_peersocket_inputstream(1, 1)) { 1.878 + errprio = 1; 1.879 + errno2 = errno; 1.880 + } 1.881 + if (moonbr_child_close_peersocket_outputstream(1, 1)) { 1.882 + errprio = 4; 1.883 + errno2 = errno; 1.884 + } 1.885 + if (moonbr_child_peersocket_type == MOONBR_SOCKETTYPE_NETWORK) { 1.886 + struct linger lingerval = { 0, }; 1.887 + if (timeout && !errprio) { 1.888 + lingerval.l_onoff = 1; 1.889 + lingerval.l_linger = timeout; 1.890 + } 1.891 + if (setsockopt(moonbr_child_peersocket_fd, SOL_SOCKET, SO_LINGER, &lingerval, sizeof(lingerval))) { 1.892 + if (errprio < 2) { 1.893 + errprio = 2; 1.894 + errno2 = errno; 1.895 + } 1.896 + } 1.897 + } 1.898 + if (close(moonbr_child_peersocket_fd)) { 1.899 + if (errprio < 3) { 1.900 + errprio = 3; 1.901 + errno2 = errno; 1.902 + } 1.903 + } 1.904 + moonbr_child_peersocket_fd = -1; 1.905 + if (errprio) { 1.906 + errno = errno2; 1.907 + return -1; 1.908 + } 1.909 + return 0; 1.910 +} 1.911 + 1.912 +/* Close socket and cause reset of TCP connection (TCP RST aka "Connection reset by peer") if possible */ 1.913 +static int moonbr_child_cancel_peersocket() { 1.914 + int err = 0; 1.915 + if (moonbr_child_close_peersocket_inputstream(0, 1)) err = -1; 1.916 + if (moonbr_child_close_peersocket_outputstream(0, 1)) err = -1; 1.917 + if (close(moonbr_child_peersocket_fd)) err = -1; 1.918 + moonbr_child_peersocket_fd = -1; 1.919 + return err; 1.920 +} 1.921 + 1.922 +/* Lua method for socket object to read from input stream */ 1.923 +static int moonbr_child_lua_read_stream(lua_State *L) { 1.924 + lua_getfield(L, 1, "input"); 1.925 + lua_getfield(L, -1, "read"); 1.926 + lua_insert(L, 1); 1.927 + lua_replace(L, 2); 1.928 + lua_call(L, lua_gettop(L) - 1, LUA_MULTRET); 1.929 + return lua_gettop(L); 1.930 +} 1.931 + 1.932 +/* Lua method for socket object to read from input stream until terminator */ 1.933 +static int moonbr_child_lua_readuntil_stream(lua_State *L) { 1.934 + lua_getfield(L, 1, "input"); 1.935 + lua_getfield(L, -1, "readuntil"); 1.936 + lua_insert(L, 1); 1.937 + lua_replace(L, 2); 1.938 + lua_call(L, lua_gettop(L) - 1, LUA_MULTRET); 1.939 + return lua_gettop(L); 1.940 +} 1.941 + 1.942 +/* Lua method for socket object to iterate over input stream */ 1.943 +static int moonbr_child_lua_lines_stream(lua_State *L) { 1.944 + lua_getfield(L, 1, "input"); 1.945 + lua_getfield(L, -1, "lines"); 1.946 + lua_insert(L, 1); 1.947 + lua_replace(L, 2); 1.948 + lua_call(L, lua_gettop(L) - 1, LUA_MULTRET); 1.949 + return lua_gettop(L); 1.950 +} 1.951 + 1.952 +/* Lua method for socket object to write to output stream */ 1.953 +static int moonbr_child_lua_write_stream(lua_State *L) { 1.954 + lua_getfield(L, 1, "output"); 1.955 + lua_getfield(L, -1, "write"); 1.956 + lua_insert(L, 1); 1.957 + lua_replace(L, 2); 1.958 + lua_call(L, lua_gettop(L) - 1, LUA_MULTRET); 1.959 + return lua_gettop(L); 1.960 +} 1.961 + 1.962 +/* Lua method for socket object to flush the output stream */ 1.963 +static int moonbr_child_lua_flush_stream(lua_State *L) { 1.964 + lua_getfield(L, 1, "output"); 1.965 + lua_getfield(L, -1, "flush"); 1.966 + lua_insert(L, 1); 1.967 + lua_replace(L, 2); 1.968 + lua_call(L, lua_gettop(L) - 1, LUA_MULTRET); 1.969 + return lua_gettop(L); 1.970 +} 1.971 + 1.972 +/* Lua function to close a single stream (input or output) from/to peer */ 1.973 +static int moonbr_child_lua_close_stream(lua_State *L) { 1.974 + luaL_Stream *stream = lua_touserdata(L, 1); 1.975 + if (stream == moonbr_child_peersocket_inputstream) { 1.976 + if (moonbr_child_close_peersocket_inputstream(1, 0)) { /* don't mark as closed as it's done by Lua */ 1.977 + moonbr_child_lua_errno_error(L, "Could not close input stream"); 1.978 + } 1.979 + } else if (stream == moonbr_child_peersocket_outputstream) { 1.980 + if (moonbr_child_close_peersocket_outputstream(1, 0)) { /* don't mark as closed as it's done by Lua */ 1.981 + moonbr_child_lua_errno_error(L, "Could not close output stream"); 1.982 + } 1.983 + } else { 1.984 + luaL_argerror(L, 1, "Not a connection socket"); 1.985 + } 1.986 + return 0; 1.987 +} 1.988 + 1.989 +/* Lua function to close both input and output stream from/to peer */ 1.990 +static int moonbr_child_lua_close_both_streams(lua_State *L) { 1.991 + int timeout = 0; 1.992 + if (!lua_isnoneornil(L, 2)) timeout = luaL_checkint(L, 2); 1.993 + if (moonbr_child_peersocket_fd == -1) { 1.994 + luaL_error(L, "Connection with peer has already been explicitly closed"); 1.995 + } 1.996 + if (moonbr_child_close_peersocket(timeout)) { 1.997 + moonbr_child_lua_errno_error(L, "Could not close socket connection with peer"); 1.998 + } 1.999 + return 0; 1.1000 +} 1.1001 + 1.1002 +/* Lua function to close both input and output stream from/to peer */ 1.1003 +static int moonbr_child_lua_cancel_both_streams(lua_State *L) { 1.1004 + if (moonbr_child_peersocket_fd == -1) { 1.1005 + luaL_error(L, "Connection with peer has already been explicitly closed"); 1.1006 + } 1.1007 + if (moonbr_child_cancel_peersocket()) { 1.1008 + moonbr_child_lua_errno_error(L, "Could not cancel socket connection with peer"); 1.1009 + } 1.1010 + return 0; 1.1011 +} 1.1012 + 1.1013 +/* Methods of (bidirectional) socket object passed to handler */ 1.1014 +static luaL_Reg moonbr_child_lua_socket_functions[] = { 1.1015 + {"read", moonbr_child_lua_read_stream}, 1.1016 + {"readuntil", moonbr_child_lua_readuntil_stream}, 1.1017 + {"lines", moonbr_child_lua_lines_stream}, 1.1018 + {"write", moonbr_child_lua_write_stream}, 1.1019 + {"flush", moonbr_child_lua_flush_stream}, 1.1020 + {"close", moonbr_child_lua_close_both_streams}, 1.1021 + {"cancel", moonbr_child_lua_cancel_both_streams}, 1.1022 + {NULL, NULL} 1.1023 +}; 1.1024 + 1.1025 +/* Main function of child process to be called after fork() and file descriptor rearrangement */ 1.1026 +void moonbr_child_run(struct moonbr_pool *pool, lua_State *L) { 1.1027 + char controlmsg; 1.1028 + struct itimerval notimer = { { 0, }, { 0, } }; 1.1029 + lua_rawgetp(L, LUA_REGISTRYINDEX, moonbr_luakey_prepare_func(pool)); 1.1030 + if (lua_isnil(L, -1)) lua_pop(L, 1); 1.1031 + else if (lua_pcall(L, 0, 0, 1)) { 1.1032 + fprintf(stderr, "Error in \"prepare\" function: %s\n", lua_tostring(L, -1)); 1.1033 + exit(1); 1.1034 + } 1.1035 + while (1) { 1.1036 + struct moonbr_listener *listener; 1.1037 + if (setitimer(ITIMER_REAL, ¬imer, NULL)) { 1.1038 + moonbr_child_log_errno_fatal("Could not reset ITIMER_REAL via setitimer()"); 1.1039 + } 1.1040 + controlmsg = MOONBR_STATUS_IDLE; 1.1041 + if (write(MOONBR_FD_CONTROL, &controlmsg, 1) <= 0) { 1.1042 + moonbr_child_log_errno_fatal("Error while sending ready message to parent process"); 1.1043 + } 1.1044 + moonbr_child_receive_control_message( 1.1045 + MOONBR_FD_CONTROL, 1.1046 + &controlmsg, 1.1047 + &moonbr_child_peersocket_fd 1.1048 + ); 1.1049 + if (!( 1.1050 + (controlmsg == MOONBR_COMMAND_TERMINATE && moonbr_child_peersocket_fd == -1) || 1.1051 + (controlmsg == MOONBR_SOCKETTYPE_INTERVAL && moonbr_child_peersocket_fd == -1) || 1.1052 + (controlmsg == MOONBR_SOCKETTYPE_LOCAL && moonbr_child_peersocket_fd != -1) || 1.1053 + (controlmsg == MOONBR_SOCKETTYPE_NETWORK && moonbr_child_peersocket_fd != -1) 1.1054 + )) { 1.1055 + moonbr_child_log_fatal("Received illegal control message from parent process"); 1.1056 + } 1.1057 + if (controlmsg == MOONBR_COMMAND_TERMINATE) break; 1.1058 + listener = moonbr_child_receive_pointer(MOONBR_FD_CONTROL); 1.1059 + moonbr_child_peersocket_type = controlmsg; 1.1060 + if (moonbr_child_peersocket_fd != -1) { 1.1061 + { 1.1062 + int clonedfd; 1.1063 + clonedfd = dup(moonbr_child_peersocket_fd); 1.1064 + if (!clonedfd) { 1.1065 + moonbr_child_log_errno_fatal("Could not duplicate file descriptor for input stream"); 1.1066 + } 1.1067 + moonbr_child_peersocket_inputstream = lua_newuserdata(L, sizeof(luaL_Stream)); 1.1068 + if (!moonbr_child_peersocket_inputstream) { 1.1069 + moonbr_child_log_fatal("Memory allocation error"); 1.1070 + } 1.1071 + moonbr_child_peersocket_inputstream->f = fdopen(clonedfd, "rb"); 1.1072 + if (!moonbr_child_peersocket_inputstream->f) { 1.1073 + moonbr_child_log_errno_fatal("Could not open input stream for remote connection"); 1.1074 + } 1.1075 + moonbr_child_peersocket_inputstream->closef = moonbr_child_lua_close_stream; 1.1076 + if (luaL_newmetatable(L, LUA_FILEHANDLE)) { 1.1077 + moonbr_child_log_fatal("Lua metatable LUA_FILEHANDLE does not exist"); 1.1078 + } 1.1079 + lua_setmetatable(L, -2); 1.1080 + } 1.1081 + { 1.1082 + int clonedfd; 1.1083 + clonedfd = dup(moonbr_child_peersocket_fd); 1.1084 + if (!clonedfd) { 1.1085 + moonbr_child_log_errno_fatal("Could not duplicate file descriptor for output stream"); 1.1086 + } 1.1087 + moonbr_child_peersocket_outputstream = lua_newuserdata(L, sizeof(luaL_Stream)); 1.1088 + if (!moonbr_child_peersocket_outputstream) { 1.1089 + moonbr_child_log_fatal("Memory allocation error"); 1.1090 + } 1.1091 + moonbr_child_peersocket_outputstream->f = fdopen(clonedfd, "wb"); 1.1092 + if (!moonbr_child_peersocket_outputstream->f) { 1.1093 + moonbr_child_log_errno_fatal("Could not open output stream for remote connection"); 1.1094 + } 1.1095 + moonbr_child_peersocket_outputstream->closef = moonbr_child_lua_close_stream; 1.1096 + if (luaL_newmetatable(L, LUA_FILEHANDLE)) { 1.1097 + moonbr_child_log_fatal("Lua metatable LUA_FILEHANDLE does not exist"); 1.1098 + } 1.1099 + lua_setmetatable(L, -2); 1.1100 + } 1.1101 + } 1.1102 + lua_rawgetp(L, LUA_REGISTRYINDEX, moonbr_luakey_connect_func(pool)); 1.1103 + if (listener->proto == MOONBR_PROTO_INTERVAL) { 1.1104 + lua_newtable(L); 1.1105 + lua_pushstring(L, 1.1106 + listener->proto_specific.interval.name ? 1.1107 + listener->proto_specific.interval.name : "" 1.1108 + ); 1.1109 + lua_setfield(L, -2, "interval"); 1.1110 + } else { 1.1111 + lua_newtable(L); 1.1112 + lua_pushvalue(L, -4); 1.1113 + lua_setfield(L, -2, "input"); 1.1114 + lua_pushvalue(L, -3); 1.1115 + lua_setfield(L, -2, "output"); 1.1116 + luaL_setfuncs(L, moonbr_child_lua_socket_functions, 0); 1.1117 + if (listener->proto == MOONBR_PROTO_TCP6) { 1.1118 + struct sockaddr_in6 addr; 1.1119 + socklen_t addr_len = sizeof(struct sockaddr_in6); 1.1120 + if (getsockname(moonbr_child_peersocket_fd, (struct sockaddr *)&addr, &addr_len)) { 1.1121 + moonbr_child_log_errno("Could not get local IP address/port"); 1.1122 + } else { 1.1123 + lua_pushlstring(L, (char *)addr.sin6_addr.s6_addr, 16); 1.1124 + lua_setfield(L, -2, "local_ip6"); 1.1125 + lua_pushinteger(L, ntohs(addr.sin6_port)); 1.1126 + lua_setfield(L, -2, "local_tcpport"); 1.1127 + } 1.1128 + if (getpeername(moonbr_child_peersocket_fd, (struct sockaddr *)&addr, &addr_len)) { 1.1129 + moonbr_child_log_errno("Could not get remote IP address/port"); 1.1130 + } else { 1.1131 + lua_pushlstring(L, (char *)addr.sin6_addr.s6_addr, 16); 1.1132 + lua_setfield(L, -2, "remote_ip6"); 1.1133 + lua_pushinteger(L, ntohs(addr.sin6_port)); 1.1134 + lua_setfield(L, -2, "remote_tcpport"); 1.1135 + } 1.1136 + } else if (listener->proto == MOONBR_PROTO_TCP4) { 1.1137 + struct sockaddr_in addr; 1.1138 + socklen_t addr_len = sizeof(struct sockaddr_in); 1.1139 + if (getsockname(moonbr_child_peersocket_fd, (struct sockaddr *)&addr, &addr_len)) { 1.1140 + moonbr_child_log_errno("Could not get local IP address/port"); 1.1141 + } else { 1.1142 + lua_pushlstring(L, (char *)&addr.sin_addr.s_addr, 4); 1.1143 + lua_setfield(L, -2, "local_ip4"); 1.1144 + lua_pushinteger(L, ntohs(addr.sin_port)); 1.1145 + lua_setfield(L, -2, "local_tcpport"); 1.1146 + } 1.1147 + if (getpeername(moonbr_child_peersocket_fd, (struct sockaddr *)&addr, &addr_len)) { 1.1148 + moonbr_child_log_errno("Could not get remote IP address/port"); 1.1149 + } else { 1.1150 + lua_pushlstring(L, (char *)&addr.sin_addr.s_addr, 4); 1.1151 + lua_setfield(L, -2, "remote_ip4"); 1.1152 + lua_pushinteger(L, ntohs(addr.sin_port)); 1.1153 + lua_setfield(L, -2, "remote_tcpport"); 1.1154 + } 1.1155 + } 1.1156 + } 1.1157 + if (lua_pcall(L, 1, 1, 1)) { 1.1158 + fprintf(stderr, "Error in \"connect\" function: %s\n", lua_tostring(L, -1)); 1.1159 + exit(1); 1.1160 + } 1.1161 + if (moonbr_child_close_peersocket(0)) { 1.1162 + moonbr_child_log_errno("Could not close socket connection with peer"); 1.1163 + } 1.1164 + if (lua_type(L, -1) != LUA_TBOOLEAN || !lua_toboolean(L, -1)) break; 1.1165 +#ifdef MOONBR_LUA_PANIC_BUG_WORKAROUND 1.1166 + lua_settop(L, 2); 1.1167 +#else 1.1168 + lua_settop(L, 1); 1.1169 +#endif 1.1170 + } 1.1171 + controlmsg = MOONBR_STATUS_GOODBYE; 1.1172 + if (write(MOONBR_FD_CONTROL, &controlmsg, 1) <= 0) { 1.1173 + moonbr_child_log_errno_fatal("Error while sending goodbye message to parent process"); 1.1174 + } 1.1175 + if (close(MOONBR_FD_CONTROL) && errno != EINTR) { 1.1176 + moonbr_child_log_errno("Error while closing control socket"); 1.1177 + } 1.1178 + lua_rawgetp(L, LUA_REGISTRYINDEX, moonbr_luakey_finish_func(pool)); 1.1179 + if (lua_isnil(L, -1)) lua_pop(L, 1); 1.1180 + else if (lua_pcall(L, 0, 0, 1)) { 1.1181 + fprintf(stderr, "Error in \"finish\" function: %s\n", lua_tostring(L, -1)); 1.1182 + exit(1); 1.1183 + } 1.1184 + lua_close(L); 1.1185 + exit(0); 1.1186 +} 1.1187 + 1.1188 + 1.1189 +/*** Functions to spawn child process ***/ 1.1190 + 1.1191 +/* Helper function to send an error message to a file descriptor (not needing a file stream) */ 1.1192 +static void moonbr_child_emergency_print(int fd, char *message) { 1.1193 + size_t len = strlen(message); 1.1194 + ssize_t written; 1.1195 + while (len) { 1.1196 + written = write(fd, message, len); 1.1197 + if (written > 0) { 1.1198 + message += written; 1.1199 + len -= written; 1.1200 + } else { 1.1201 + if (written != -1 || errno != EINTR) break; 1.1202 + } 1.1203 + } 1.1204 +} 1.1205 + 1.1206 +/* Helper function to send an error message plus a text for errno to a file descriptor and terminate the process */ 1.1207 +static void moonbr_child_emergency_error(int fd, char *message) { 1.1208 + int errno2 = errno; 1.1209 + moonbr_child_emergency_print(fd, message); 1.1210 + moonbr_child_emergency_print(fd, ": "); 1.1211 + moonbr_child_emergency_print(fd, strerror(errno2)); 1.1212 + moonbr_child_emergency_print(fd, "\n"); 1.1213 + exit(1); 1.1214 +} 1.1215 + 1.1216 +/* Creates a child process and (in case of success) registers it in the 'struct moonbr_pool' structure */ 1.1217 +static int moonbr_create_worker(struct moonbr_pool *pool, lua_State *L) { 1.1218 + struct moonbr_worker *worker; 1.1219 + worker = calloc(1, sizeof(struct moonbr_worker)); 1.1220 + if (!worker) { 1.1221 + moonbr_log(LOG_CRIT, "Memory allocation error"); 1.1222 + return -1; 1.1223 + } 1.1224 + worker->pool = pool; 1.1225 + { 1.1226 + int controlfds[2]; 1.1227 + int errorfds[2]; 1.1228 + if (socketpair(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, controlfds)) { 1.1229 + moonbr_log(LOG_ERR, "Could not create control socket pair for communcation with child process: %s", strerror(errno)); 1.1230 + free(worker); 1.1231 + return -1; 1.1232 + } 1.1233 + if (socketpair(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, errorfds)) { 1.1234 + moonbr_log(LOG_ERR, "Could not create socket pair to redirect stderr of child process: %s", strerror(errno)); 1.1235 + close(controlfds[0]); 1.1236 + close(controlfds[1]); 1.1237 + free(worker); 1.1238 + return -1; 1.1239 + } 1.1240 + if (moonbr_logfile && fflush(moonbr_logfile)) { 1.1241 + moonbr_log(LOG_CRIT, "Could not flush log file prior to forking: %s", strerror(errno)); 1.1242 + moonbr_terminate_error(); 1.1243 + } 1.1244 + worker->pid = fork(); 1.1245 + if (worker->pid == -1) { 1.1246 + moonbr_log(LOG_ERR, "Could not fork: %s", strerror(errno)); 1.1247 + close(controlfds[0]); 1.1248 + close(controlfds[1]); 1.1249 + close(errorfds[0]); 1.1250 + close(errorfds[1]); 1.1251 + free(worker); 1.1252 + return -1; 1.1253 + } else if (!worker->pid) { 1.1254 + moonbr_pstate = MOONBR_PSTATE_FORKED; 1.1255 +#ifdef MOONBR_LUA_PANIC_BUG_WORKAROUND 1.1256 + lua_pushliteral(L, "Failed to pass error message due to bug in Lua panic handler (hint: not enough memory?)"); 1.1257 +#endif 1.1258 + moonbr_memory_limit = pool->memory_limit; 1.1259 + if (moonbr_pidfh && pidfile_close(moonbr_pidfh)) { 1.1260 + moonbr_child_emergency_error(errorfds[1], "Could not close PID file in forked child process"); 1.1261 + } 1.1262 + if (moonbr_logfile && moonbr_logfile != stderr && fclose(moonbr_logfile)) { 1.1263 + moonbr_child_emergency_error(errorfds[1], "Could not close log file in forked child process"); 1.1264 + } 1.1265 + if (dup2(errorfds[1], MOONBR_FD_STDERR) == -1) { 1.1266 + moonbr_child_emergency_error(errorfds[1], "Could not duplicate socket to stderr file descriptor"); 1.1267 + } 1.1268 + if (dup2(controlfds[1], MOONBR_FD_CONTROL) == -1) { 1.1269 + moonbr_child_emergency_error(errorfds[1], "Could not duplicate control socket"); 1.1270 + } 1.1271 + closefrom(MOONBR_FD_END); 1.1272 + moonbr_child_run(pool, L); 1.1273 + } 1.1274 + if (moonbr_stat) { 1.1275 + moonbr_log(LOG_INFO, "Created new worker in pool #%i with PID %i", worker->pool->poolnum, (int)worker->pid); 1.1276 + } 1.1277 + worker->controlfd = controlfds[0]; 1.1278 + worker->errorfd = errorfds[0]; 1.1279 + if (close(controlfds[1]) && errno != EINTR) { 1.1280 + moonbr_log(LOG_CRIT, "Could not close opposite end of control file descriptor after forking"); 1.1281 + moonbr_terminate_error(); 1.1282 + } 1.1283 + if (close(errorfds[1]) && errno != EINTR) { 1.1284 + moonbr_log(LOG_CRIT, "Could not close opposite end of control file descriptor after forking"); 1.1285 + moonbr_terminate_error(); 1.1286 + } 1.1287 + } 1.1288 + worker->prev_worker = pool->last_worker; 1.1289 + if (worker->prev_worker) worker->prev_worker->next_worker = worker; 1.1290 + else pool->first_worker = worker; 1.1291 + pool->last_worker = worker; 1.1292 + pool->unassigned_worker_count++; 1.1293 + pool->total_worker_count++; 1.1294 + pool->worker_count_stat = 1; 1.1295 + moonbr_poll_refresh_needed = 1; 1.1296 + return 0; /* return zero only in case of success */ 1.1297 +} 1.1298 + 1.1299 + 1.1300 +/*** Functions to handle previously created 'struct moonbr_worker' structures ***/ 1.1301 + 1.1302 +#define moonbr_try_destroy_worker_stat(str, field) \ 1.1303 + moonbr_log(LOG_INFO, "Resource usage in pool #%i for PID %i: " str " %li", worker->pool->poolnum, (int)worker->pid, (long)childusage.field); 1.1304 + 1.1305 +/* Destroys a worker structure if socket connections have been closed and child process has terminated */ 1.1306 +static int moonbr_try_destroy_worker(struct moonbr_worker *worker) { 1.1307 + if (worker->controlfd != -1 || worker->errorfd != -1) return MOONBR_DESTROY_NONE; 1.1308 + { 1.1309 + int childstatus; 1.1310 + struct rusage childusage; 1.1311 + { 1.1312 + pid_t waitedpid; 1.1313 + while ( 1.1314 + (waitedpid = wait4(worker->pid, &childstatus, WNOHANG, &childusage)) == -1 1.1315 + ) { 1.1316 + if (errno != EINTR) { 1.1317 + moonbr_log(LOG_CRIT, "Error in wait4() call: %s", strerror(errno)); 1.1318 + moonbr_terminate_error(); 1.1319 + } 1.1320 + } 1.1321 + if (!waitedpid) return 0; /* return 0 if worker couldn't be destroyed */ 1.1322 + if (waitedpid != worker->pid) { 1.1323 + moonbr_log(LOG_CRIT, "Wrong PID returned by wait4() call"); 1.1324 + moonbr_terminate_error(); 1.1325 + } 1.1326 + } 1.1327 + if (WIFEXITED(childstatus)) { 1.1328 + if (WEXITSTATUS(childstatus) || moonbr_stat) { 1.1329 + moonbr_log( 1.1330 + WEXITSTATUS(childstatus) ? LOG_WARNING : LOG_INFO, 1.1331 + "Child process in pool #%i with PID %i returned with exit code %i", worker->pool->poolnum, (int)worker->pid, WEXITSTATUS(childstatus) 1.1332 + ); 1.1333 + } 1.1334 + } else if (WIFSIGNALED(childstatus)) { 1.1335 + if (WCOREDUMP(childstatus)) { 1.1336 + moonbr_log(LOG_ERR, "Child process in pool #%i with PID %i died from signal %i (core dump was created)", worker->pool->poolnum, (int)worker->pid, WTERMSIG(childstatus)); 1.1337 + } else if (WTERMSIG(childstatus) == SIGALRM) { 1.1338 + moonbr_log(LOG_WARNING, "Child process in pool #%i with PID %i exited prematurely due to timeout", worker->pool->poolnum, (int)worker->pid); 1.1339 + } else { 1.1340 + moonbr_log(LOG_ERR, "Child process in pool #%i with PID %i died from signal %i", worker->pool->poolnum, (int)worker->pid, WTERMSIG(childstatus)); 1.1341 + } 1.1342 + } else { 1.1343 + moonbr_log(LOG_CRIT, "Illegal exit status from child process in pool #%i with PID %i", worker->pool->poolnum, (int)worker->pid); 1.1344 + moonbr_terminate_error(); 1.1345 + } 1.1346 + if (moonbr_stat) { 1.1347 + moonbr_log(LOG_INFO, "Resource usage in pool #%i for PID %i: user time %s", worker->pool->poolnum, (int)worker->pid, moonbr_format_timeval(&childusage.ru_utime)); 1.1348 + moonbr_log(LOG_INFO, "Resource usage in pool #%i for PID %i: system time %s", worker->pool->poolnum, (int)worker->pid, moonbr_format_timeval(&childusage.ru_stime)); 1.1349 + moonbr_try_destroy_worker_stat("max resident set size", ru_maxrss); 1.1350 + moonbr_try_destroy_worker_stat("integral shared memory size", ru_ixrss); 1.1351 + moonbr_try_destroy_worker_stat("integral unshared data", ru_idrss); 1.1352 + moonbr_try_destroy_worker_stat("integral unshared stack", ru_isrss); 1.1353 + moonbr_try_destroy_worker_stat("page replaims", ru_minflt); 1.1354 + moonbr_try_destroy_worker_stat("page faults", ru_majflt); 1.1355 + moonbr_try_destroy_worker_stat("swaps", ru_nswap); 1.1356 + moonbr_try_destroy_worker_stat("block input operations", ru_inblock); 1.1357 + moonbr_try_destroy_worker_stat("block output operations", ru_oublock); 1.1358 + moonbr_try_destroy_worker_stat("messages sent", ru_msgsnd); 1.1359 + moonbr_try_destroy_worker_stat("messages received", ru_msgrcv); 1.1360 + moonbr_try_destroy_worker_stat("signals received", ru_nsignals); 1.1361 + moonbr_try_destroy_worker_stat("voluntary context switches", ru_nvcsw); 1.1362 + moonbr_try_destroy_worker_stat("involuntary context switches", ru_nivcsw); 1.1363 + } 1.1364 + } 1.1365 + { 1.1366 + int retval = ( 1.1367 + (worker->idle || worker->assigned) ? 1.1368 + MOONBR_DESTROY_IDLE_OR_ASSIGNED : 1.1369 + MOONBR_DESTROY_PREPARE 1.1370 + ); 1.1371 + if (worker->prev_worker) worker->prev_worker->next_worker = worker->next_worker; 1.1372 + else worker->pool->first_worker = worker->next_worker; 1.1373 + if (worker->next_worker) worker->next_worker->prev_worker = worker->prev_worker; 1.1374 + else worker->pool->last_worker = worker->prev_worker; 1.1375 + if (worker->idle) { 1.1376 + if (worker->prev_idle_worker) worker->prev_idle_worker->next_idle_worker = worker->next_idle_worker; 1.1377 + else worker->pool->first_idle_worker = worker->next_idle_worker; 1.1378 + if (worker->next_idle_worker) worker->next_idle_worker->prev_idle_worker = worker->prev_idle_worker; 1.1379 + else worker->pool->last_idle_worker = worker->prev_idle_worker; 1.1380 + worker->pool->idle_worker_count--; 1.1381 + } 1.1382 + if (!worker->assigned) worker->pool->unassigned_worker_count--; 1.1383 + worker->pool->total_worker_count--; 1.1384 + worker->pool->worker_count_stat = 1; 1.1385 + if (worker->errorlinebuf) free(worker->errorlinebuf); 1.1386 + free(worker); 1.1387 + return retval; 1.1388 + } 1.1389 +} 1.1390 + 1.1391 +/* Marks a worker as idle and stores it in a queue, optionally setting 'idle_expiration' value */ 1.1392 +static void moonbr_add_idle_worker(struct moonbr_worker *worker) { 1.1393 + worker->prev_idle_worker = worker->pool->last_idle_worker; 1.1394 + if (worker->prev_idle_worker) worker->prev_idle_worker->next_idle_worker = worker; 1.1395 + else worker->pool->first_idle_worker = worker; 1.1396 + worker->pool->last_idle_worker = worker; 1.1397 + worker->idle = 1; 1.1398 + worker->pool->idle_worker_count++; 1.1399 + if (worker->assigned) { 1.1400 + worker->assigned = 0; 1.1401 + worker->pool->unassigned_worker_count++; 1.1402 + } 1.1403 + worker->pool->worker_count_stat = 1; 1.1404 + if (timerisset(&worker->pool->idle_timeout)) { 1.1405 + struct timeval now; 1.1406 + moonbr_now(&now); 1.1407 + timeradd(&now, &worker->pool->idle_timeout, &worker->idle_expiration); 1.1408 + } 1.1409 +} 1.1410 + 1.1411 +/* Pops a worker from the queue of idle workers (idle queue must not be empty) */ 1.1412 +static struct moonbr_worker *moonbr_pop_idle_worker(struct moonbr_pool *pool) { 1.1413 + struct moonbr_worker *worker; 1.1414 + worker = pool->first_idle_worker; 1.1415 + pool->first_idle_worker = worker->next_idle_worker; 1.1416 + if (pool->first_idle_worker) pool->first_idle_worker->prev_idle_worker = NULL; 1.1417 + else pool->last_idle_worker = NULL; 1.1418 + worker->next_idle_worker = NULL; 1.1419 + worker->idle = 0; 1.1420 + worker->pool->idle_worker_count--; 1.1421 + worker->assigned = 1; 1.1422 + worker->pool->unassigned_worker_count--; 1.1423 + worker->pool->worker_count_stat = 1; 1.1424 + return worker; 1.1425 +} 1.1426 + 1.1427 + 1.1428 +/*** Functions for queues of 'struct moonbr_listener' ***/ 1.1429 + 1.1430 +/* Appends a 'struct moonbr_listener' to the queue of idle listeners and registers it for poll() */ 1.1431 +static void moonbr_add_idle_listener(struct moonbr_listener *listener) { 1.1432 + listener->prev_listener = listener->pool->last_idle_listener; 1.1433 + if (listener->prev_listener) listener->prev_listener->next_listener = listener; 1.1434 + else listener->pool->first_idle_listener = listener; 1.1435 + listener->pool->last_idle_listener = listener; 1.1436 + if (listener->pollidx != -1) moonbr_poll_fds[listener->pollidx].events |= POLLIN; 1.1437 +} 1.1438 + 1.1439 +/* Removes a 'struct moonbr_listener' from the queue of idle listeners and unregisters it from poll() */ 1.1440 +static void moonbr_remove_idle_listener(struct moonbr_listener *listener) { 1.1441 + if (listener->prev_listener) listener->prev_listener->next_listener = listener->next_listener; 1.1442 + else listener->pool->first_idle_listener = listener->next_listener; 1.1443 + if (listener->next_listener) listener->next_listener->prev_listener = listener->prev_listener; 1.1444 + else listener->pool->last_idle_listener = listener->prev_listener; 1.1445 + listener->prev_listener = NULL; 1.1446 + listener->next_listener = NULL; 1.1447 + if (listener->pollidx != -1) moonbr_poll_fds[listener->pollidx].events &= ~POLLIN; 1.1448 +} 1.1449 + 1.1450 +/* Adds a listener to the queue of connected listeners (i.e. waiting to have their incoming connection accepted) */ 1.1451 +static void moonbr_add_connected_listener(struct moonbr_listener *listener) { 1.1452 + listener->prev_listener = listener->pool->last_connected_listener; 1.1453 + if (listener->prev_listener) listener->prev_listener->next_listener = listener; 1.1454 + else listener->pool->first_connected_listener = listener; 1.1455 + listener->pool->last_connected_listener = listener; 1.1456 +} 1.1457 + 1.1458 +/* Removes and returns the first connected listener in the queue */ 1.1459 +static struct moonbr_listener *moonbr_pop_connected_listener(struct moonbr_pool *pool) { 1.1460 + struct moonbr_listener *listener = pool->first_connected_listener; 1.1461 + listener->pool->first_connected_listener = listener->next_listener; 1.1462 + if (listener->pool->first_connected_listener) listener->pool->first_connected_listener->prev_listener = NULL; 1.1463 + else listener->pool->last_connected_listener = NULL; 1.1464 + listener->next_listener = NULL; 1.1465 + return listener; 1.1466 +} 1.1467 + 1.1468 + 1.1469 +/*** Functions to handle polling ***/ 1.1470 + 1.1471 +/* Returns an index to a new initialized entry in moonbr_poll_fds[] */ 1.1472 +int moonbr_poll_fds_nextindex() { 1.1473 + if (moonbr_poll_fds_count >= moonbr_poll_fds_bufsize) { 1.1474 + if (moonbr_poll_fds_bufsize) moonbr_poll_fds_bufsize *= 2; 1.1475 + else moonbr_poll_fds_bufsize = 1; 1.1476 + moonbr_poll_fds = realloc( 1.1477 + moonbr_poll_fds, moonbr_poll_fds_bufsize * sizeof(struct pollfd) 1.1478 + ); 1.1479 + if (!moonbr_poll_fds) { 1.1480 + moonbr_log(LOG_CRIT, "Memory allocation error"); 1.1481 + moonbr_terminate_error(); 1.1482 + } 1.1483 + } 1.1484 + moonbr_poll_fds[moonbr_poll_fds_count] = (struct pollfd){0, }; 1.1485 + return moonbr_poll_fds_count++; 1.1486 +} 1.1487 + 1.1488 +/* Returns an index to a new initialized entry in moonbr_poll_workers[] */ 1.1489 +int moonbr_poll_workers_nextindex() { 1.1490 + if (moonbr_poll_worker_count >= moonbr_poll_workers_bufsize) { 1.1491 + if (moonbr_poll_workers_bufsize) moonbr_poll_workers_bufsize *= 2; 1.1492 + else moonbr_poll_workers_bufsize = 1; 1.1493 + moonbr_poll_workers = realloc( 1.1494 + moonbr_poll_workers, moonbr_poll_workers_bufsize * sizeof(struct moonbr_poll_worker) 1.1495 + ); 1.1496 + if (!moonbr_poll_workers) { 1.1497 + moonbr_log(LOG_CRIT, "Memory allocation error"); 1.1498 + moonbr_terminate_error(); 1.1499 + } 1.1500 + } 1.1501 + moonbr_poll_workers[moonbr_poll_worker_count] = (struct moonbr_poll_worker){0, }; 1.1502 + return moonbr_poll_worker_count++; 1.1503 +} 1.1504 + 1.1505 +/* Queues all listeners as idle, and initializes static part of moonbr_poll_fds[], which is passed to poll() */ 1.1506 +static void moonbr_poll_init() { 1.1507 + if (socketpair( 1.1508 + PF_LOCAL, 1.1509 + SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 1.1510 + 0, 1.1511 + moonbr_poll_signalfds 1.1512 + )) { 1.1513 + moonbr_log(LOG_CRIT, "Could not create socket pair for signal delivery during polling: %s", strerror(errno)); 1.1514 + moonbr_terminate_error(); 1.1515 + } 1.1516 + { 1.1517 + int j = moonbr_poll_fds_nextindex(); 1.1518 + struct pollfd *pollfd = &moonbr_poll_fds[j]; 1.1519 + pollfd->fd = moonbr_poll_signalfd_read; 1.1520 + pollfd->events = POLLIN; 1.1521 + } 1.1522 + { 1.1523 + struct moonbr_pool *pool; 1.1524 + for (pool=moonbr_first_pool; pool; pool=pool->next_pool) { 1.1525 + int i; 1.1526 + for (i=0; i<pool->listener_count; i++) { 1.1527 + struct moonbr_listener *listener = &pool->listener[i]; 1.1528 + if (listener->listenfd != -1) { 1.1529 + int j = moonbr_poll_fds_nextindex(); 1.1530 + listener->pollidx = j; 1.1531 + moonbr_poll_fds[j].fd = listener->listenfd; 1.1532 + } 1.1533 + moonbr_add_idle_listener(listener); 1.1534 + } 1.1535 + } 1.1536 + } 1.1537 + moonbr_poll_fds_static_count = moonbr_poll_fds_count; /* remember size of static part of array */ 1.1538 +} 1.1539 + 1.1540 +/* Disables polling of all listeners (required for clean shutdown) */ 1.1541 +static void moonbr_poll_shutdown() { 1.1542 + int i; 1.1543 + for (i=1; i<moonbr_poll_fds_static_count; i++) { 1.1544 + moonbr_poll_fds[i].fd = -1; 1.1545 + } 1.1546 +} 1.1547 + 1.1548 +/* (Re)builds dynamic part of moonbr_poll_fds[] array, and (re)builds moonbr_poll_workers[] array */ 1.1549 +static void moonbr_poll_refresh() { 1.1550 + moonbr_poll_refresh_needed = 0; 1.1551 + moonbr_poll_fds_count = moonbr_poll_fds_static_count; 1.1552 + moonbr_poll_worker_count = 0; 1.1553 + { 1.1554 + struct moonbr_pool *pool; 1.1555 + for (pool=moonbr_first_pool; pool; pool=pool->next_pool) { 1.1556 + struct moonbr_worker *worker; 1.1557 + for (worker=pool->first_worker; worker; worker=worker->next_worker) { 1.1558 + if (worker->controlfd != -1) { 1.1559 + int j = moonbr_poll_fds_nextindex(); 1.1560 + int k = moonbr_poll_workers_nextindex(); 1.1561 + struct pollfd *pollfd = &moonbr_poll_fds[j]; 1.1562 + struct moonbr_poll_worker *poll_worker = &moonbr_poll_workers[k]; 1.1563 + pollfd->fd = worker->controlfd; 1.1564 + pollfd->events = POLLIN; 1.1565 + poll_worker->channel = MOONBR_POLL_WORKER_CONTROLCHANNEL; 1.1566 + poll_worker->worker = worker; 1.1567 + } 1.1568 + if (worker->errorfd != -1) { 1.1569 + int j = moonbr_poll_fds_nextindex(); 1.1570 + int k = moonbr_poll_workers_nextindex(); 1.1571 + struct pollfd *pollfd = &moonbr_poll_fds[j]; 1.1572 + struct moonbr_poll_worker *poll_worker = &moonbr_poll_workers[k]; 1.1573 + pollfd->fd = worker->errorfd; 1.1574 + pollfd->events = POLLIN; 1.1575 + poll_worker->channel = MOONBR_POLL_WORKER_ERRORCHANNEL; 1.1576 + poll_worker->worker = worker; 1.1577 + } 1.1578 + } 1.1579 + } 1.1580 + } 1.1581 +} 1.1582 + 1.1583 +/* resets socket and 'revents' field of moonbr_poll_fds[] for signal delivery just before poll() is called */ 1.1584 +static void moonbr_poll_reset_signal() { 1.1585 + ssize_t readcount; 1.1586 + char buf[1]; 1.1587 + moonbr_poll_fds[0].revents = 0; 1.1588 + while ((readcount = read(moonbr_poll_signalfd_read, buf, 1)) < 0) { 1.1589 + if (errno == EAGAIN) break; 1.1590 + if (errno != EINTR) { 1.1591 + moonbr_log(LOG_CRIT, "Error while reading from signal delivery socket: %s", strerror(errno)); 1.1592 + moonbr_terminate_error(); 1.1593 + } 1.1594 + } 1.1595 + if (!readcount) { 1.1596 + moonbr_log(LOG_CRIT, "Unexpected EOF when reading from signal delivery socket: %s", strerror(errno)); 1.1597 + moonbr_terminate_error(); 1.1598 + } 1.1599 +} 1.1600 + 1.1601 + 1.1602 +/*** Shutdown initiation ***/ 1.1603 + 1.1604 +/* Sets global variable 'moonbr_shutdown_in_progress', closes listeners, and demands worker termination */ 1.1605 +static void moonbr_initiate_shutdown() { 1.1606 + struct moonbr_pool *pool; 1.1607 + int i; 1.1608 + if (moonbr_shutdown_in_progress) { 1.1609 + moonbr_log(LOG_NOTICE, "Shutdown already in progress"); 1.1610 + return; 1.1611 + } 1.1612 + moonbr_shutdown_in_progress = 1; 1.1613 + moonbr_log(LOG_NOTICE, "Initiate shutdown"); 1.1614 + for (pool = moonbr_first_pool; pool; pool = pool->next_pool) { 1.1615 + for (i=0; i<pool->listener_count; i++) { 1.1616 + struct moonbr_listener *listener = &pool->listener[i]; 1.1617 + if (listener->listenfd != -1) { 1.1618 + if (close(listener->listenfd) && errno != EINTR) { 1.1619 + moonbr_log(LOG_CRIT, "Could not close listening socket: %s", strerror(errno)); 1.1620 + moonbr_terminate_error(); 1.1621 + } 1.1622 + } 1.1623 + } 1.1624 + pool->pre_fork = 0; 1.1625 + pool->min_fork = 0; 1.1626 + pool->max_fork = 0; 1.1627 + timerclear(&pool->exit_delay); 1.1628 + } 1.1629 + moonbr_poll_shutdown(); /* avoids loops due to error condition when polling closed listeners */ 1.1630 +} 1.1631 + 1.1632 + 1.1633 +/*** Functions to communicate with child processes ***/ 1.1634 + 1.1635 +/* Tells child process to terminate */ 1.1636 +static void moonbr_terminate_idle_worker(struct moonbr_worker *worker) { 1.1637 + moonbr_send_control_message(worker, MOONBR_COMMAND_TERMINATE, -1, NULL); 1.1638 +} 1.1639 + 1.1640 +/* Handles status messages from child process */ 1.1641 +static void moonbr_read_controlchannel(struct moonbr_worker *worker) { 1.1642 + char controlmsg; 1.1643 + { 1.1644 + ssize_t bytes_read; 1.1645 + while ((bytes_read = read(worker->controlfd, &controlmsg, 1)) <= 0) { 1.1646 + if (bytes_read == 0 || errno == ECONNRESET) { 1.1647 + moonbr_log(LOG_WARNING, "Child process in pool #%i with PID %i unexpectedly closed control socket", worker->pool->poolnum, (int)worker->pid); 1.1648 + if (close(worker->controlfd) && errno != EINTR) { 1.1649 + moonbr_log(LOG_CRIT, "Error while closing control socket to child process in pool #%i with PID %i: %s", worker->pool->poolnum, (int)worker->pid, strerror(errno)); 1.1650 + moonbr_terminate_error(); 1.1651 + } 1.1652 + worker->controlfd = -1; 1.1653 + moonbr_poll_refresh_needed = 1; 1.1654 + return; 1.1655 + } 1.1656 + if (errno != EINTR) { 1.1657 + moonbr_log(LOG_CRIT, "Unexpected error while reading control socket from child process in pool #%i with PID %i: %s", worker->pool->poolnum, (int)worker->pid, strerror(errno)); 1.1658 + moonbr_terminate_error(); 1.1659 + } 1.1660 + } 1.1661 + } 1.1662 + if (worker->idle) { 1.1663 + moonbr_log(LOG_CRIT, "Unexpected data from supposedly idle child process in pool #%i with PID %i", worker->pool->poolnum, (int)worker->pid); 1.1664 + moonbr_terminate_error(); 1.1665 + } 1.1666 + if (moonbr_debug) { 1.1667 + moonbr_log(LOG_DEBUG, "Received control message from child in pool #%i with PID %i: \"%c\"", worker->pool->poolnum, (int)worker->pid, (int)controlmsg); 1.1668 + } 1.1669 + switch (controlmsg) { 1.1670 + case MOONBR_STATUS_IDLE: 1.1671 + if (moonbr_stat) { 1.1672 + moonbr_log(LOG_INFO, "Child process in pool #%i with PID %i reports as idle", worker->pool->poolnum, (int)worker->pid); 1.1673 + } 1.1674 + moonbr_add_idle_worker(worker); 1.1675 + break; 1.1676 + case MOONBR_STATUS_GOODBYE: 1.1677 + if (moonbr_stat) { 1.1678 + moonbr_log(LOG_INFO, "Child process in pool #%i with PID %i announced termination", worker->pool->poolnum, (int)worker->pid); 1.1679 + } 1.1680 + if (close(worker->controlfd) && errno != EINTR) { 1.1681 + moonbr_log(LOG_CRIT, "Error while closing control socket to child process in pool #%i with PID %i: %s", worker->pool->poolnum, (int)worker->pid, strerror(errno)); 1.1682 + moonbr_terminate_error(); 1.1683 + } 1.1684 + worker->controlfd = -1; 1.1685 + moonbr_poll_refresh_needed = 1; 1.1686 + break; 1.1687 + default: 1.1688 + moonbr_log(LOG_CRIT, "Received illegal data (\"%c\") while reading control socket from child process in pool #%i with PID %i", (int)controlmsg, worker->pool->poolnum, (int)worker->pid); 1.1689 + moonbr_terminate_error(); 1.1690 + } 1.1691 +} 1.1692 + 1.1693 +/* Handles stderr stream from child process */ 1.1694 +static void moonbr_read_errorchannel(struct moonbr_worker *worker) { 1.1695 + char staticbuf[MOONBR_MAXERRORLINELEN+1]; 1.1696 + char *buf = worker->errorlinebuf; 1.1697 + if (!buf) buf = staticbuf; 1.1698 + { 1.1699 + ssize_t bytes_read; 1.1700 + while ( 1.1701 + (bytes_read = read( 1.1702 + worker->errorfd, 1.1703 + buf + worker->errorlinelen, 1.1704 + MOONBR_MAXERRORLINELEN+1 - worker->errorlinelen 1.1705 + )) <= 0 1.1706 + ) { 1.1707 + if (bytes_read == 0 || errno == ECONNRESET) { 1.1708 + if (moonbr_debug) { 1.1709 + moonbr_log(LOG_DEBUG, "Child process in pool #%i with PID %i closed stderr socket", worker->pool->poolnum, (int)worker->pid); 1.1710 + } 1.1711 + if (close(worker->errorfd) && errno != EINTR) { 1.1712 + moonbr_log(LOG_CRIT, "Error while closing stderr socket to child process in pool #%i with PID %i: %s", worker->pool->poolnum, (int)worker->pid, strerror(errno)); 1.1713 + moonbr_terminate_error(); 1.1714 + } 1.1715 + worker->errorfd = -1; 1.1716 + moonbr_poll_refresh_needed = 1; 1.1717 + break; 1.1718 + } 1.1719 + if (errno != EINTR) { 1.1720 + moonbr_log(LOG_CRIT, "Unexpected error while reading stderr from child process in pool #%i with PID %i: %s", worker->pool->poolnum, (int)worker->pid, strerror(errno)); 1.1721 + moonbr_terminate_error(); 1.1722 + } 1.1723 + } 1.1724 + worker->errorlinelen += bytes_read; 1.1725 + } 1.1726 + { 1.1727 + int i; 1.1728 + for (i=0; i<worker->errorlinelen; i++) { 1.1729 + if (buf[i] == '\n') buf[i] = 0; 1.1730 + if (!buf[i]) { 1.1731 + if (worker->errorlineovf) { 1.1732 + worker->errorlineovf = 0; 1.1733 + } else { 1.1734 + moonbr_log(LOG_WARNING, "Error log from process in pool #%i with PID %i: %s", worker->pool->poolnum, (int)worker->pid, buf); 1.1735 + } 1.1736 + worker->errorlinelen -= i+1; 1.1737 + memmove(buf, buf+i+1, worker->errorlinelen); 1.1738 + i = -1; 1.1739 + } 1.1740 + } 1.1741 + if (i > MOONBR_MAXERRORLINELEN) { 1.1742 + buf[MOONBR_MAXERRORLINELEN] = 0; 1.1743 + if (!worker->errorlineovf) { 1.1744 + moonbr_log(LOG_WARNING, "Error log from process in pool #%i with PID %i (line has been truncated): %s", worker->pool->poolnum, (int)worker->pid, buf); 1.1745 + } 1.1746 + worker->errorlinelen = 0; 1.1747 + worker->errorlineovf = 1; 1.1748 + } 1.1749 + } 1.1750 + if (!worker->errorlinebuf && worker->errorlinelen) { /* allocate buffer on heap only if necessary */ 1.1751 + worker->errorlinebuf = malloc((MOONBR_MAXERRORLINELEN+1) * sizeof(char)); 1.1752 + if (!worker->errorlinebuf) { 1.1753 + moonbr_log(LOG_CRIT, "Memory allocation error"); 1.1754 + moonbr_terminate_error(); 1.1755 + } 1.1756 + memcpy(worker->errorlinebuf, staticbuf, worker->errorlinelen); 1.1757 + } 1.1758 +} 1.1759 + 1.1760 + 1.1761 +/*** Handler for incoming connections ***/ 1.1762 + 1.1763 +/* Accepts one or more incoming connections on listener socket and passes it to worker(s) popped from idle queue */ 1.1764 +static void moonbr_connect(struct moonbr_pool *pool) { 1.1765 + struct moonbr_listener *listener = moonbr_pop_connected_listener(pool); 1.1766 + struct moonbr_worker *worker; 1.1767 + switch (listener->proto) { 1.1768 + case MOONBR_PROTO_INTERVAL: 1.1769 + worker = moonbr_pop_idle_worker(pool); 1.1770 + if (moonbr_stat) { 1.1771 + moonbr_log(LOG_INFO, "Dispatching interval timer \"%s\" of pool #%i to PID %i", listener->proto_specific.interval.name, listener->pool->poolnum, (int)worker->pid); 1.1772 + } 1.1773 + worker->restart_interval_listener = listener; 1.1774 + moonbr_send_control_message(worker, MOONBR_SOCKETTYPE_INTERVAL, -1, listener); 1.1775 + /* do not push listener to queue of idle listeners yet */ 1.1776 + break; 1.1777 + case MOONBR_PROTO_LOCAL: 1.1778 + do { 1.1779 + int peerfd; 1.1780 + struct sockaddr_un peeraddr; 1.1781 + socklen_t peeraddr_len = sizeof(struct sockaddr_un); 1.1782 + peerfd = accept4( 1.1783 + listener->listenfd, 1.1784 + (struct sockaddr *)&peeraddr, 1.1785 + &peeraddr_len, 1.1786 + SOCK_CLOEXEC 1.1787 + ); 1.1788 + if (peerfd == -1) { 1.1789 + if (errno == EWOULDBLOCK) { 1.1790 + break; 1.1791 + } else if (errno == ECONNABORTED) { 1.1792 + moonbr_log(LOG_WARNING, "Connection aborted before accepting it (proto=\"local\", path=\"%s\")", listener->proto_specific.local.path); 1.1793 + break; 1.1794 + } else if (errno != EINTR) { 1.1795 + moonbr_log(LOG_ERR, "Could not accept socket connection: %s", strerror(errno)); 1.1796 + moonbr_terminate_error(); 1.1797 + } 1.1798 + } else { 1.1799 + worker = moonbr_pop_idle_worker(pool); 1.1800 + if (moonbr_stat) { 1.1801 + moonbr_log(LOG_INFO, "Dispatching local socket connection on path \"%s\" for pool #%i to PID %i", listener->proto_specific.local.path, listener->pool->poolnum, (int)worker->pid); 1.1802 + } 1.1803 + moonbr_send_control_message(worker, MOONBR_SOCKETTYPE_LOCAL, peerfd, listener); 1.1804 + if (close(peerfd) && errno != EINTR) { 1.1805 + moonbr_log(LOG_ERR, "Could not close incoming socket connection in parent process: %s", strerror(errno)); 1.1806 + moonbr_terminate_error(); 1.1807 + } 1.1808 + } 1.1809 + } while (pool->first_idle_worker); 1.1810 + moonbr_add_idle_listener(listener); 1.1811 + break; 1.1812 + case MOONBR_PROTO_TCP6: 1.1813 + do { 1.1814 + int peerfd; 1.1815 + struct sockaddr_in6 peeraddr; 1.1816 + socklen_t peeraddr_len = sizeof(struct sockaddr_in6); 1.1817 + peerfd = accept4( 1.1818 + listener->listenfd, 1.1819 + (struct sockaddr *)&peeraddr, 1.1820 + &peeraddr_len, 1.1821 + SOCK_CLOEXEC 1.1822 + ); 1.1823 + if (peerfd == -1) { 1.1824 + if (errno == EWOULDBLOCK) { 1.1825 + break; 1.1826 + } else if (errno == ECONNABORTED) { 1.1827 + moonbr_log(LOG_WARNING, "Connection aborted before accepting it (proto=\"tcp6\", port=%i)", listener->proto_specific.tcp.port); 1.1828 + break; 1.1829 + } else if (errno != EINTR) { 1.1830 + moonbr_log(LOG_ERR, "Could not accept socket connection: %s", strerror(errno)); 1.1831 + moonbr_terminate_error(); 1.1832 + } 1.1833 + } else { 1.1834 + worker = moonbr_pop_idle_worker(pool); 1.1835 + if (moonbr_stat) { 1.1836 + moonbr_log(LOG_INFO, "Dispatching TCP/IPv6 connection for pool #%i on port %i to PID %i", listener->pool->poolnum, listener->proto_specific.tcp.port, (int)worker->pid); 1.1837 + } 1.1838 + moonbr_send_control_message(worker, MOONBR_SOCKETTYPE_NETWORK, peerfd, listener); 1.1839 + if (close(peerfd) && errno != EINTR) { 1.1840 + moonbr_log(LOG_ERR, "Could not close incoming socket connection in parent process: %s", strerror(errno)); 1.1841 + moonbr_terminate_error(); 1.1842 + } 1.1843 + } 1.1844 + } while (pool->first_idle_worker); 1.1845 + moonbr_add_idle_listener(listener); 1.1846 + break; 1.1847 + case MOONBR_PROTO_TCP4: 1.1848 + do { 1.1849 + int peerfd; 1.1850 + struct sockaddr_in peeraddr; 1.1851 + socklen_t peeraddr_len = sizeof(struct sockaddr_in); 1.1852 + peerfd = accept4( 1.1853 + listener->listenfd, 1.1854 + (struct sockaddr *)&peeraddr, 1.1855 + &peeraddr_len, 1.1856 + SOCK_CLOEXEC 1.1857 + ); 1.1858 + if (peerfd == -1) { 1.1859 + if (errno == EWOULDBLOCK) { 1.1860 + break; 1.1861 + } else if (errno == ECONNABORTED) { 1.1862 + moonbr_log(LOG_WARNING, "Connection aborted before accepting it (proto=\"tcp4\", port=%i)", listener->proto_specific.tcp.port); 1.1863 + break; 1.1864 + } else if (errno != EINTR) { 1.1865 + moonbr_log(LOG_ERR, "Could not accept socket connection: %s", strerror(errno)); 1.1866 + moonbr_terminate_error(); 1.1867 + } 1.1868 + } else { 1.1869 + worker = moonbr_pop_idle_worker(pool); 1.1870 + if (moonbr_stat) { 1.1871 + moonbr_log(LOG_INFO, "Dispatching TCP/IPv4 connection for pool #%i on port %i to PID %i", listener->pool->poolnum, listener->proto_specific.tcp.port, (int)worker->pid); 1.1872 + } 1.1873 + moonbr_send_control_message(worker, MOONBR_SOCKETTYPE_NETWORK, peerfd, listener); 1.1874 + if (close(peerfd) && errno != EINTR) { 1.1875 + moonbr_log(LOG_ERR, "Could not close incoming socket connection in parent process: %s", strerror(errno)); 1.1876 + moonbr_terminate_error(); 1.1877 + } 1.1878 + } 1.1879 + } while (pool->first_idle_worker); 1.1880 + moonbr_add_idle_listener(listener); 1.1881 + break; 1.1882 + default: 1.1883 + moonbr_log(LOG_ERR, "Internal error (should not happen): Unexpected value in listener.proto field"); 1.1884 + moonbr_terminate_error(); 1.1885 + } 1.1886 +} 1.1887 + 1.1888 + 1.1889 +/*** Functions to initialize and restart interval timers ***/ 1.1890 + 1.1891 +/* Initializes all interval timers */ 1.1892 +static void moonbr_interval_initialize() { 1.1893 + struct timeval now; 1.1894 + struct moonbr_pool *pool; 1.1895 + moonbr_now(&now); 1.1896 + for (pool=moonbr_first_pool; pool; pool=pool->next_pool) { 1.1897 + int i; 1.1898 + for (i=0; i<pool->listener_count; i++) { 1.1899 + struct moonbr_listener *listener = &pool->listener[i]; 1.1900 + if (listener->proto == MOONBR_PROTO_INTERVAL) { 1.1901 + timeradd( 1.1902 + &now, 1.1903 + &listener->proto_specific.interval.delay, 1.1904 + &listener->proto_specific.interval.wakeup 1.1905 + ); 1.1906 + } 1.1907 + } 1.1908 + } 1.1909 +} 1.1910 + 1.1911 +/* If necessary, restarts interval timers and queues interval listener as idle after a worker changed status */ 1.1912 +static void moonbr_interval_restart( 1.1913 + struct moonbr_worker *worker, 1.1914 + struct timeval *now /* passed to synchronize with moonbr_run() function */ 1.1915 +) { 1.1916 + struct moonbr_listener *listener = worker->restart_interval_listener; 1.1917 + if (listener) { 1.1918 + moonbr_add_idle_listener(listener); 1.1919 + worker->restart_interval_listener = NULL; 1.1920 + if (listener->proto_specific.interval.strict) { 1.1921 + timeradd( 1.1922 + &listener->proto_specific.interval.wakeup, 1.1923 + &listener->proto_specific.interval.delay, 1.1924 + &listener->proto_specific.interval.wakeup 1.1925 + ); 1.1926 + if (timercmp(&listener->proto_specific.interval.wakeup, now, <)) { 1.1927 + listener->proto_specific.interval.wakeup = *now; 1.1928 + } 1.1929 + } else { 1.1930 + timeradd( 1.1931 + now, 1.1932 + &listener->proto_specific.interval.delay, 1.1933 + &listener->proto_specific.interval.wakeup 1.1934 + ); 1.1935 + } 1.1936 + } 1.1937 +} 1.1938 + 1.1939 + 1.1940 +/*** Main loop and helper functions ***/ 1.1941 + 1.1942 +/* Stores the earliest required wakeup time in 'wait' variable */ 1.1943 +static void moonbr_calc_wait(struct timeval *wait, struct timeval *wakeup) { 1.1944 + if (!timerisset(wait) || timercmp(wakeup, wait, <)) *wait = *wakeup; 1.1945 +} 1.1946 + 1.1947 +/* Main loop of Moonbridge system (including initialization of signal handlers and polling structures) */ 1.1948 +static void moonbr_run(lua_State *L) { 1.1949 + struct timeval now; 1.1950 + struct moonbr_pool *pool; 1.1951 + struct moonbr_worker *worker; 1.1952 + struct moonbr_worker *next_worker; /* needed when worker is removed during iteration of workers */ 1.1953 + struct moonbr_listener *listener; 1.1954 + struct moonbr_listener *next_listener; /* needed when listener is removed during iteration of listeners */ 1.1955 + int i; 1.1956 + moonbr_poll_init(); /* must be executed before moonbr_signal_init() */ 1.1957 + moonbr_signal_init(); 1.1958 + moonbr_interval_initialize(); 1.1959 + moonbr_pstate = MOONBR_PSTATE_RUNNING; 1.1960 + while (1) { 1.1961 + struct timeval wait = {0, }; /* point in time when premature wakeup of poll() is required */ 1.1962 + if (moonbr_cond_interrupt) { 1.1963 + moonbr_log(LOG_WARNING, "Fast shutdown requested"); 1.1964 + moonbr_terminate(MOONBR_EXITCODE_GRACEFUL); 1.1965 + } 1.1966 + if (moonbr_cond_terminate) { 1.1967 + moonbr_initiate_shutdown(); 1.1968 + moonbr_cond_terminate = 0; 1.1969 + } 1.1970 + moonbr_cond_child = 0; /* must not be reset between moonbr_try_destroy_worker() and poll() */ 1.1971 + moonbr_now(&now); 1.1972 + for (pool=moonbr_first_pool; pool; pool=pool->next_pool) { 1.1973 + int terminated_worker_count = 0; /* allows shortcut for new worker creation */ 1.1974 + /* terminate idle workers when expired */ 1.1975 + if (timerisset(&pool->idle_timeout)) { 1.1976 + while ((worker = pool->first_idle_worker) != NULL) { 1.1977 + if (timercmp(&worker->idle_expiration, &now, >)) break; 1.1978 + moonbr_pop_idle_worker(pool); 1.1979 + moonbr_terminate_idle_worker(worker); 1.1980 + } 1.1981 + } 1.1982 + /* mark listeners as connected when incoming connection is pending */ 1.1983 + for (listener=pool->first_idle_listener; listener; listener=next_listener) { 1.1984 + next_listener = listener->next_listener; /* extra variable necessary due to changing list */ 1.1985 + if (listener->pollidx != -1) { 1.1986 + if (moonbr_poll_fds[listener->pollidx].revents) { 1.1987 + moonbr_poll_fds[listener->pollidx].revents = 0; 1.1988 + moonbr_remove_idle_listener(listener); 1.1989 + moonbr_add_connected_listener(listener); 1.1990 + } 1.1991 + } else if (listener->proto == MOONBR_PROTO_INTERVAL) { 1.1992 + if (!timercmp(&listener->proto_specific.interval.wakeup, &now, >)) { 1.1993 + moonbr_remove_idle_listener(listener); 1.1994 + moonbr_add_connected_listener(listener); 1.1995 + } 1.1996 + } else { 1.1997 + moonbr_log(LOG_CRIT, "Internal error (should not happen): Listener is neither an interval timer nor has the 'pollidx' value set"); 1.1998 + moonbr_terminate_error(); 1.1999 + } 1.2000 + } 1.2001 + /* process input from child processes */ 1.2002 + for (i=0; i<moonbr_poll_worker_count; i++) { 1.2003 + if (moonbr_poll_worker_fds[i].revents) { 1.2004 + moonbr_poll_worker_fds[i].revents = 0; 1.2005 + struct moonbr_poll_worker *poll_worker = &moonbr_poll_workers[i]; 1.2006 + switch (poll_worker->channel) { 1.2007 + case MOONBR_POLL_WORKER_CONTROLCHANNEL: 1.2008 + moonbr_read_controlchannel(poll_worker->worker); 1.2009 + moonbr_interval_restart(poll_worker->worker, &now); 1.2010 + break; 1.2011 + case MOONBR_POLL_WORKER_ERRORCHANNEL: 1.2012 + moonbr_read_errorchannel(poll_worker->worker); 1.2013 + break; 1.2014 + } 1.2015 + } 1.2016 + } 1.2017 + /* collect dead child processes */ 1.2018 + for (worker=pool->first_worker; worker; worker=next_worker) { 1.2019 + next_worker = worker->next_worker; /* extra variable necessary due to changing list */ 1.2020 + switch (moonbr_try_destroy_worker(worker)) { 1.2021 + case MOONBR_DESTROY_PREPARE: 1.2022 + pool->use_fork_error_wakeup = 1; 1.2023 + break; 1.2024 + case MOONBR_DESTROY_IDLE_OR_ASSIGNED: 1.2025 + terminated_worker_count++; 1.2026 + break; 1.2027 + } 1.2028 + } 1.2029 + /* connect listeners with idle workers */ 1.2030 + if (!moonbr_shutdown_in_progress) { 1.2031 + while (pool->first_connected_listener && pool->first_idle_worker) { 1.2032 + moonbr_connect(pool); 1.2033 + } 1.2034 + } 1.2035 + /* create new worker processes */ 1.2036 + while ( 1.2037 + pool->total_worker_count < pool->max_fork && ( 1.2038 + pool->unassigned_worker_count < pool->pre_fork || 1.2039 + pool->total_worker_count < pool->min_fork 1.2040 + ) 1.2041 + ) { 1.2042 + if (pool->use_fork_error_wakeup) { 1.2043 + if (timercmp(&pool->fork_error_wakeup, &now, >)) { 1.2044 + moonbr_calc_wait(&wait, &pool->fork_error_wakeup); 1.2045 + break; 1.2046 + } 1.2047 + } else { 1.2048 + if (terminated_worker_count) { 1.2049 + terminated_worker_count--; 1.2050 + } else if (timercmp(&pool->fork_wakeup, &now, >)) { 1.2051 + moonbr_calc_wait(&wait, &pool->fork_wakeup); 1.2052 + break; 1.2053 + } 1.2054 + } 1.2055 + if (moonbr_create_worker(pool, L)) { 1.2056 + /* on error, enforce error delay */ 1.2057 + timeradd(&now, &pool->fork_error_delay, &pool->fork_error_wakeup); 1.2058 + pool->use_fork_error_wakeup = 1; 1.2059 + moonbr_calc_wait(&wait, &pool->fork_error_wakeup); 1.2060 + break; 1.2061 + } else { 1.2062 + /* normal fork delay on success */ 1.2063 + timeradd(&now, &pool->fork_delay, &pool->fork_wakeup); 1.2064 + timeradd(&now, &pool->fork_error_delay, &pool->fork_error_wakeup); 1.2065 + pool->use_fork_error_wakeup = 0; /* gets set later if error occures during preparation */ 1.2066 + } 1.2067 + } 1.2068 + /* terminate excessive worker processes */ 1.2069 + while ( 1.2070 + pool->total_worker_count > pool->min_fork && 1.2071 + pool->idle_worker_count > pool->pre_fork 1.2072 + ) { 1.2073 + if (timerisset(&pool->exit_wakeup)) { 1.2074 + if (timercmp(&pool->exit_wakeup, &now, >)) { 1.2075 + moonbr_calc_wait(&wait, &pool->exit_wakeup); 1.2076 + break; 1.2077 + } 1.2078 + moonbr_terminate_idle_worker(moonbr_pop_idle_worker(pool)); 1.2079 + timeradd(&now, &pool->exit_delay, &pool->exit_wakeup); 1.2080 + } else { 1.2081 + timeradd(&now, &pool->exit_delay, &pool->exit_wakeup); 1.2082 + break; 1.2083 + } 1.2084 + } 1.2085 + if (!( 1.2086 + pool->total_worker_count > pool->min_fork && 1.2087 + pool->idle_worker_count > pool->pre_fork 1.2088 + )) { 1.2089 + timerclear(&pool->exit_wakeup); /* timer gets restarted later when there are excessive workers */ 1.2090 + } 1.2091 + /* optionally output worker count stats */ 1.2092 + if (moonbr_stat && pool->worker_count_stat) { 1.2093 + pool->worker_count_stat = 0; 1.2094 + moonbr_log( 1.2095 + LOG_INFO, 1.2096 + "Worker count for pool #%i: %i idle, %i assigned, %i total", 1.2097 + pool->poolnum, pool->idle_worker_count, 1.2098 + pool->total_worker_count - pool->unassigned_worker_count, 1.2099 + pool->total_worker_count); 1.2100 + } 1.2101 + /* calculate wakeup time for interval listeners */ 1.2102 + for (listener=pool->first_idle_listener; listener; listener=listener->next_listener) { 1.2103 + if (listener->proto == MOONBR_PROTO_INTERVAL) { 1.2104 + moonbr_calc_wait(&wait, &listener->proto_specific.interval.wakeup); 1.2105 + } 1.2106 + } 1.2107 + /* calculate wakeup time for idle workers (only first idle worker is significant) */ 1.2108 + if (timerisset(&pool->idle_timeout) && pool->first_idle_worker) { 1.2109 + moonbr_calc_wait(&wait, &pool->first_idle_worker->idle_expiration); 1.2110 + } 1.2111 + } 1.2112 + /* check if shutdown is complete */ 1.2113 + if (moonbr_shutdown_in_progress) { 1.2114 + for (pool=moonbr_first_pool; pool; pool=pool->next_pool) { 1.2115 + if (pool->first_worker) break; 1.2116 + } 1.2117 + if (!pool) { 1.2118 + moonbr_log(LOG_INFO, "All worker threads have terminated"); 1.2119 + moonbr_terminate(MOONBR_EXITCODE_GRACEFUL); 1.2120 + } 1.2121 + } 1.2122 + if (moonbr_poll_refresh_needed) moonbr_poll_refresh(); 1.2123 + moonbr_cond_poll = 1; 1.2124 + if (!moonbr_cond_child && !moonbr_cond_terminate && !moonbr_cond_interrupt) { 1.2125 + int timeout; 1.2126 + if (timerisset(&wait)) { 1.2127 + if (timercmp(&wait, &now, <)) { 1.2128 + moonbr_log(LOG_CRIT, "Internal error (should not happen): Future is in the past"); 1.2129 + moonbr_terminate_error(); 1.2130 + } 1.2131 + timersub(&wait, &now, &wait); 1.2132 + timeout = wait.tv_sec * 1000 + wait.tv_usec / 1000; 1.2133 + } else { 1.2134 + timeout = INFTIM; 1.2135 + } 1.2136 + if (moonbr_debug) { 1.2137 + moonbr_log(LOG_DEBUG, "Waiting for I/O"); 1.2138 + } 1.2139 + poll(moonbr_poll_fds, moonbr_poll_fds_count, timeout); 1.2140 + } else { 1.2141 + if (moonbr_debug) { 1.2142 + moonbr_log(LOG_DEBUG, "Do not wait for I/O"); 1.2143 + } 1.2144 + } 1.2145 + moonbr_cond_poll = 0; 1.2146 + moonbr_poll_reset_signal(); 1.2147 + } 1.2148 +} 1.2149 + 1.2150 + 1.2151 +/*** Lua interface ***/ 1.2152 + 1.2153 +static int moonbr_lua_panic(lua_State *L) { 1.2154 + const char *errmsg; 1.2155 + errmsg = lua_tostring(L, -1); 1.2156 + if (!errmsg) { 1.2157 + if (lua_isnoneornil(L, -1)) errmsg = "(error message is nil)"; 1.2158 + else errmsg = "(error message is not a string)"; 1.2159 + } 1.2160 + if (moonbr_pstate == MOONBR_PSTATE_FORKED) { 1.2161 + fprintf(stderr, "Uncaught Lua error: %s\n", errmsg); 1.2162 + exit(1); 1.2163 + } else { 1.2164 + moonbr_log(LOG_CRIT, "Uncaught Lua error: %s", errmsg); 1.2165 + moonbr_terminate_error(); 1.2166 + } 1.2167 + return 0; 1.2168 +} 1.2169 + 1.2170 +static int moonbr_addtraceback(lua_State *L) { 1.2171 + luaL_traceback(L, L, luaL_tolstring(L, 1, NULL), 1); 1.2172 + return 1; 1.2173 +} 1.2174 + 1.2175 +/* Memory allocator that allows limiting memory consumption */ 1.2176 +static void *moonbr_alloc (void *ud, void *ptr, size_t osize, size_t nsize) { 1.2177 + (void)ud; /* not used */ 1.2178 + if (nsize == 0) { 1.2179 + if (ptr) { 1.2180 + moonbr_memory_usage -= osize; 1.2181 + free(ptr); 1.2182 + } 1.2183 + return NULL; 1.2184 + } else if (ptr) { 1.2185 + if ( 1.2186 + moonbr_memory_limit && 1.2187 + nsize > osize && 1.2188 + moonbr_memory_usage + (nsize - osize) > moonbr_memory_limit 1.2189 + ) { 1.2190 + return NULL; 1.2191 + } else { 1.2192 + ptr = realloc(ptr, nsize); 1.2193 + if (ptr) moonbr_memory_usage += nsize - osize; 1.2194 + } 1.2195 + } else { 1.2196 + if ( 1.2197 + moonbr_memory_limit && 1.2198 + moonbr_memory_usage + nsize > moonbr_memory_limit 1.2199 + ) { 1.2200 + return NULL; 1.2201 + } else { 1.2202 + ptr = realloc(ptr, nsize); 1.2203 + if (ptr) moonbr_memory_usage += nsize; 1.2204 + } 1.2205 + } 1.2206 + return ptr; 1.2207 +} 1.2208 + 1.2209 +/* New method for Lua file objects: read until terminator or length exceeded */ 1.2210 +static int moonbr_readuntil(lua_State *L) { 1.2211 + luaL_Stream *stream; 1.2212 + FILE *file; 1.2213 + const char *terminatorstr; 1.2214 + size_t terminatorlen; 1.2215 + luaL_Buffer buf; 1.2216 + lua_Integer maxlen; 1.2217 + char terminator; 1.2218 + int byte; 1.2219 + stream = luaL_checkudata(L, 1, LUA_FILEHANDLE); 1.2220 + terminatorstr = luaL_checklstring(L, 2, &terminatorlen); 1.2221 + luaL_argcheck(L, terminatorlen == 1, 2, "single byte expected"); 1.2222 + maxlen = luaL_optinteger(L, 3, 0); 1.2223 + if (!stream->closef) luaL_error(L, "attempt to use a closed file"); 1.2224 + file = stream->f; 1.2225 + luaL_buffinit(L, &buf); 1.2226 + if (!maxlen) maxlen = -1; 1.2227 + terminator = terminatorstr[0]; 1.2228 + while (maxlen > 0 ? maxlen-- : maxlen) { 1.2229 + byte = fgetc(file); 1.2230 + if (byte == EOF) { 1.2231 + if (ferror(file)) { 1.2232 + char errmsg[MOONBR_MAXSTRERRORLEN]; 1.2233 + strerror_r(errno, errmsg, MOONBR_MAXSTRERRORLEN); /* use thread-safe call in case child created threads */ 1.2234 + luaL_error(L, "%s", errmsg); 1.2235 + } else { 1.2236 + break; 1.2237 + } 1.2238 + } 1.2239 + luaL_addchar(&buf, byte); 1.2240 + if (byte == terminator) break; 1.2241 + } 1.2242 + luaL_pushresult(&buf); 1.2243 + if (!lua_rawlen(L, -1)) lua_pushnil(L); 1.2244 + return 1; 1.2245 +} 1.2246 + 1.2247 +static int moonbr_lua_tonatural(lua_State *L, int idx) { 1.2248 + int isnum; 1.2249 + lua_Number n; 1.2250 + n = lua_tonumberx(L, idx, &isnum); 1.2251 + if (isnum && n>=0 && n<INT_MAX && (lua_Number)(int)n == n) return n; 1.2252 + else return -1; 1.2253 +} 1.2254 + 1.2255 +static int moonbr_lua_totimeval(lua_State *L, int idx, struct timeval *value) { 1.2256 + int isnum; 1.2257 + lua_Number n; 1.2258 + n = lua_tonumberx(L, idx, &isnum); 1.2259 + if (isnum && n>=0 && n<=100000000) { 1.2260 + value->tv_sec = n; 1.2261 + value->tv_usec = 1e6 * (n - value->tv_sec); 1.2262 + return 1; 1.2263 + } else { 1.2264 + return 0; 1.2265 + } 1.2266 +} 1.2267 + 1.2268 +static int moonbr_timeout(lua_State *L) { 1.2269 + struct itimerval oldval; 1.2270 + if (lua_isnoneornil(L, 1) && lua_isnoneornil(L, 2)) { 1.2271 + getitimer(ITIMER_REAL, &oldval); 1.2272 + } else { 1.2273 + struct itimerval newval = {}; 1.2274 + if (lua_toboolean(L, 1)) { 1.2275 + luaL_argcheck( 1.2276 + L, moonbr_lua_totimeval(L, 1, &newval.it_value), 1, 1.2277 + "interval in seconds expected" 1.2278 + ); 1.2279 + } 1.2280 + if (lua_isnoneornil(L, 2)) { 1.2281 + if (setitimer(ITIMER_REAL, &newval, &oldval)) { 1.2282 + moonbr_log(LOG_CRIT, "Could not set ITIMER_REAL via setitimer()"); 1.2283 + moonbr_terminate_error(); 1.2284 + } 1.2285 + } else { 1.2286 + getitimer(ITIMER_REAL, &oldval); 1.2287 + if (timercmp(&newval.it_value, &oldval.it_value, <)) { 1.2288 + struct itimerval remval; 1.2289 + if (setitimer(ITIMER_REAL, &newval, NULL)) { 1.2290 + moonbr_log(LOG_CRIT, "Could not set ITIMER_REAL via setitimer()"); 1.2291 + moonbr_terminate_error(); 1.2292 + } 1.2293 + lua_call(L, lua_gettop(L) - 2, LUA_MULTRET); 1.2294 + getitimer(ITIMER_REAL, &remval); 1.2295 + timersub(&oldval.it_value, &newval.it_value, &newval.it_value); 1.2296 + timeradd(&newval.it_value, &remval.it_value, &newval.it_value); 1.2297 + if (setitimer(ITIMER_REAL, &newval, NULL)) { 1.2298 + moonbr_log(LOG_CRIT, "Could not set ITIMER_REAL via setitimer()"); 1.2299 + moonbr_terminate_error(); 1.2300 + } 1.2301 + } else { 1.2302 + lua_call(L, lua_gettop(L) - 2, LUA_MULTRET); 1.2303 + } 1.2304 + return lua_gettop(L) - 1; 1.2305 + } 1.2306 + } 1.2307 + lua_pushnumber(L, oldval.it_value.tv_sec + 1e-6 * oldval.it_value.tv_usec); 1.2308 + return 1; 1.2309 +} 1.2310 + 1.2311 +#define moonbr_listen_init_pool_forkoption(luaname, cname, defval) { \ 1.2312 + lua_getfield(L, 2, luaname); \ 1.2313 + pool->cname = lua_isnil(L, -1) ? (defval) : moonbr_lua_tonatural(L, -1); \ 1.2314 +} while(0) 1.2315 + 1.2316 +#define moonbr_listen_init_pool_timeoption(luaname, cname, defval, defvalu) ( \ 1.2317 + lua_getfield(L, 2, luaname), \ 1.2318 + lua_isnil(L, -1) ? ( \ 1.2319 + pool->cname.tv_sec = (defval), pool->cname.tv_usec = (defvalu), \ 1.2320 + 1 \ 1.2321 + ) : ( \ 1.2322 + (lua_isboolean(L, -1) && !lua_toboolean(L, -1)) ? ( \ 1.2323 + pool->cname.tv_sec = 0, pool->cname.tv_usec = 0, \ 1.2324 + 1 \ 1.2325 + ) : ( \ 1.2326 + moonbr_lua_totimeval(L, -1, &pool->cname) \ 1.2327 + ) \ 1.2328 + ) \ 1.2329 +) 1.2330 + 1.2331 +static int moonbr_listen_init_pool(lua_State *L) { 1.2332 + struct moonbr_pool *pool; 1.2333 + const char *proto; 1.2334 + int i; 1.2335 + pool = lua_touserdata(L, 1); 1.2336 + for (i=0; i<pool->listener_count; i++) { 1.2337 + struct moonbr_listener *listener = &pool->listener[i]; 1.2338 + lua_settop(L, 2); 1.2339 + lua_pushinteger(L, i+1); 1.2340 + lua_gettable(L, 2); 1.2341 + lua_getfield(L, 3, "proto"); 1.2342 + proto = lua_tostring(L, -1); 1.2343 + if (proto && !strcmp(proto, "interval")) { 1.2344 + listener->proto = MOONBR_PROTO_INTERVAL; 1.2345 + lua_getfield(L, 3, "name"); 1.2346 + { 1.2347 + const char *name = lua_tostring(L, -1); 1.2348 + if (name) { 1.2349 + if (asprintf(&listener->proto_specific.interval.name, "%s", name) < 0) { 1.2350 + moonbr_log(LOG_CRIT, "Memory allocation_error"); 1.2351 + moonbr_terminate_error(); 1.2352 + } 1.2353 + } 1.2354 + } 1.2355 + lua_getfield(L, 3, "delay"); 1.2356 + if ( 1.2357 + !moonbr_lua_totimeval(L, -1, &listener->proto_specific.interval.delay) || 1.2358 + !timerisset(&listener->proto_specific.interval.delay) 1.2359 + ) { 1.2360 + luaL_error(L, "No valid interval delay specified; use listen{{proto=\"interval\", delay=...}, ...}"); 1.2361 + } 1.2362 + lua_getfield(L, 3, "strict"); 1.2363 + if (!lua_isnil(L, -1)) { 1.2364 + if (lua_isboolean(L, -1)) { 1.2365 + if (lua_toboolean(L, -1)) listener->proto_specific.interval.strict = 1; 1.2366 + } else { 1.2367 + luaL_error(L, "Option \"strict\" must be a boolean if set; use listen{{proto=\"interval\", strict=true, ...}, ...}"); 1.2368 + } 1.2369 + } 1.2370 + } else if (proto && !strcmp(proto, "local")) { 1.2371 + listener->proto = MOONBR_PROTO_LOCAL; 1.2372 + lua_getfield(L, 3, "path"); 1.2373 + { 1.2374 + const char *path = lua_tostring(L, -1); 1.2375 + if (!path) { 1.2376 + luaL_error(L, "No valid path specified for local socket; use listen{{proto=\"local\", path=...}, ...}"); 1.2377 + } 1.2378 + if (asprintf(&listener->proto_specific.local.path, "%s", path) < 0) { 1.2379 + moonbr_log(LOG_CRIT, "Memory allocation_error"); 1.2380 + moonbr_terminate_error(); 1.2381 + } 1.2382 + } 1.2383 + } else if (proto && !strcmp(proto, "tcp6")) { 1.2384 + listener->proto = MOONBR_PROTO_TCP6; 1.2385 + lua_getfield(L, 3, "port"); 1.2386 + listener->proto_specific.tcp.port = lua_tointeger(L, -1); 1.2387 + if ( 1.2388 + listener->proto_specific.tcp.port < 1 || 1.2389 + listener->proto_specific.tcp.port > 65535 1.2390 + ) { 1.2391 + luaL_error(L, "No valid port number specified; use listen{{proto=\"tcp6\", port=...}, ...}"); 1.2392 + } 1.2393 + lua_getfield(L, 3, "localhost"); 1.2394 + if (!lua_isnil(L, -1)) { 1.2395 + if (lua_isboolean(L, -1)) { 1.2396 + if (lua_toboolean(L, -1)) listener->proto_specific.tcp.localhost_only = 1; 1.2397 + } else { 1.2398 + luaL_error(L, "Option \"localhost\" must be a boolean if set; use listen{{proto=\"tcp6\", localhost=true, ...}, ...}"); 1.2399 + } 1.2400 + } 1.2401 + } else if (proto && !strcmp(proto, "tcp4")) { 1.2402 + listener->proto = MOONBR_PROTO_TCP4; 1.2403 + lua_getfield(L, 3, "port"); 1.2404 + listener->proto_specific.tcp.port = lua_tointeger(L, -1); 1.2405 + if ( 1.2406 + listener->proto_specific.tcp.port < 1 || 1.2407 + listener->proto_specific.tcp.port > 65535 1.2408 + ) { 1.2409 + luaL_error(L, "No valid port number specified; use listen{{proto=\"tcp4\", port=...}, ...}"); 1.2410 + } 1.2411 + lua_getfield(L, 3, "localhost"); 1.2412 + if (!lua_isnil(L, -1)) { 1.2413 + if (lua_isboolean(L, -1)) { 1.2414 + if (lua_toboolean(L, -1)) listener->proto_specific.tcp.localhost_only = 1; 1.2415 + } else { 1.2416 + luaL_error(L, "Option \"localhost\" must be a boolean if set; use listen{{proto=\"tcp4\", localhost=true, ...}, ...}"); 1.2417 + } 1.2418 + } 1.2419 + } 1.2420 + } 1.2421 + lua_settop(L, 2); 1.2422 + moonbr_listen_init_pool_forkoption("pre_fork", pre_fork, 1); 1.2423 + moonbr_listen_init_pool_forkoption("min_fork", min_fork, pool->pre_fork > 2 ? pool->pre_fork : 2); 1.2424 + moonbr_listen_init_pool_forkoption("max_fork", max_fork, pool->min_fork > 16 ? pool->min_fork : 16); 1.2425 + if (!moonbr_listen_init_pool_timeoption("fork_delay", fork_delay, 1, 0)) { 1.2426 + luaL_error(L, "Option \"fork_delay\" is expected to be a non-negative number"); 1.2427 + } 1.2428 + if (!moonbr_listen_init_pool_timeoption("fork_error_delay", fork_error_delay, 2, 0)) { 1.2429 + luaL_error(L, "Option \"fork_error_delay\" is expected to be a non-negative number"); 1.2430 + } 1.2431 + if (!moonbr_listen_init_pool_timeoption("exit_delay", exit_delay, 60, 0)) { 1.2432 + luaL_error(L, "Option \"exit_delay\" is expected to be a non-negative number"); 1.2433 + } 1.2434 + if (timercmp(&pool->fork_error_delay, &pool->fork_delay, <)) { 1.2435 + pool->fork_error_delay = pool->fork_delay; 1.2436 + } 1.2437 + if (!moonbr_listen_init_pool_timeoption("idle_timeout", idle_timeout, 0, 0)) { 1.2438 + luaL_error(L, "Option \"idle_timeout\" is expected to be a non-negative number"); 1.2439 + } 1.2440 + lua_getfield(L, 2, "memory_limit"); 1.2441 + if (!lua_isnil(L, -1)) { 1.2442 + int isnum; 1.2443 + lua_Number n; 1.2444 + n = lua_tonumberx(L, -1, &isnum); 1.2445 + if (n < 0 || !isnum) { 1.2446 + luaL_error(L, "Option \"memory_limit\" is expected to be a non-negative number"); 1.2447 + } 1.2448 + pool->memory_limit = n; 1.2449 + } 1.2450 + lua_settop(L, 2); 1.2451 + lua_getfield(L, 2, "prepare"); 1.2452 + if (!lua_isnil(L, -1) && !lua_isfunction(L, -1)) { 1.2453 + luaL_error(L, "Option \"prepare\" must be nil or a function"); 1.2454 + } 1.2455 + lua_rawsetp(L, LUA_REGISTRYINDEX, moonbr_luakey_prepare_func(pool)); 1.2456 + lua_getfield(L, 2, "connect"); 1.2457 + if (!lua_isfunction(L, -1)) { 1.2458 + luaL_error(L, "Option \"connect\" must be a function; use listen{{...}, {...}, connect=function(socket) ... end, ...}"); 1.2459 + } 1.2460 + lua_rawsetp(L, LUA_REGISTRYINDEX, moonbr_luakey_connect_func(pool)); 1.2461 + lua_getfield(L, 2, "finish"); 1.2462 + if (!lua_isnil(L, -1) && !lua_isfunction(L, -1)) { 1.2463 + luaL_error(L, "Option \"finish\" must be nil or a function"); 1.2464 + } 1.2465 + lua_rawsetp(L, LUA_REGISTRYINDEX, moonbr_luakey_finish_func(pool)); 1.2466 + return 0; 1.2467 +} 1.2468 + 1.2469 +static int moonbr_listen(lua_State *L) { 1.2470 + struct moonbr_pool *pool; 1.2471 + lua_Integer listener_count; 1.2472 + if (moonbr_booted) luaL_error(L, "Moonbridge bootup is already complete"); 1.2473 + luaL_checktype(L, 1, LUA_TTABLE); 1.2474 + listener_count = luaL_len(L, 1); 1.2475 + if (!listener_count) luaL_error(L, "No listen ports specified; use listen{{proto=..., port=...},...}"); 1.2476 + if (listener_count > 100) luaL_error(L, "Too many listeners"); 1.2477 + pool = moonbr_create_pool(listener_count); 1.2478 + lua_pushcfunction(L, moonbr_listen_init_pool); 1.2479 + lua_pushlightuserdata(L, pool); 1.2480 + lua_pushvalue(L, 1); 1.2481 + if (lua_pcall(L, 2, 0, 0)) goto moonbr_listen_error; 1.2482 + { 1.2483 + int i; 1.2484 + i = moonbr_start_pool(pool); 1.2485 + if (i >= 0) { 1.2486 + struct moonbr_listener *listener = &pool->listener[i]; 1.2487 + switch (listener->proto) { 1.2488 + case MOONBR_PROTO_INTERVAL: 1.2489 + lua_pushfstring(L, "Could not initialize listener #%d (proto=\"interval\"): %s", i+1, strerror(errno)); 1.2490 + break; 1.2491 + case MOONBR_PROTO_LOCAL: 1.2492 + lua_pushfstring(L, "Could not initialize listener #%d (proto=\"local\", path=\"%s\"): %s", i+1, listener->proto_specific.local.path, strerror(errno)); 1.2493 + break; 1.2494 + case MOONBR_PROTO_TCP6: 1.2495 + lua_pushfstring(L, "Could not initialize listener #%d (proto=\"tcp6\", port=%d): %s", i+1, listener->proto_specific.tcp.port, strerror(errno)); 1.2496 + break; 1.2497 + case MOONBR_PROTO_TCP4: 1.2498 + lua_pushfstring(L, "Could not initialize listener #%d (proto=\"tcp4\", port=%d): %s", i+1, listener->proto_specific.tcp.port, strerror(errno)); 1.2499 + break; 1.2500 + default: 1.2501 + moonbr_log(LOG_ERR, "Internal error (should not happen): Unexpected value in listener.proto field"); 1.2502 + moonbr_terminate_error(); 1.2503 + } 1.2504 + goto moonbr_listen_error; 1.2505 + } 1.2506 + } 1.2507 + return 0; 1.2508 + moonbr_listen_error: 1.2509 + moonbr_destroy_pool(pool); 1.2510 + lua_pushnil(L); 1.2511 + lua_rawsetp(L, LUA_REGISTRYINDEX, moonbr_luakey_prepare_func(pool)); 1.2512 + lua_pushnil(L); 1.2513 + lua_rawsetp(L, LUA_REGISTRYINDEX, moonbr_luakey_connect_func(pool)); 1.2514 + lua_pushnil(L); 1.2515 + lua_rawsetp(L, LUA_REGISTRYINDEX, moonbr_luakey_finish_func(pool)); 1.2516 + lua_error(L); 1.2517 + return 0; // avoid compiler warning 1.2518 +} 1.2519 + 1.2520 + 1.2521 +/*** Main function and command line invokation ***/ 1.2522 + 1.2523 +static void moonbr_usage(int err, const char *cmd) { 1.2524 + FILE *out; 1.2525 + out = err ? stderr : stdout; 1.2526 + if (!cmd) cmd = "moonbridge"; 1.2527 + fprintf(out, "Get this help message: %s {-h|--help}\n", cmd); 1.2528 + fprintf(out, "Usage: %s \\\n", cmd); 1.2529 + fprintf(out, " [-b|--background] \\\n"); 1.2530 + fprintf(out, " [-d|--debug] \\\n"); 1.2531 + fprintf(out, " [-f|--logfacility {DAEMON|USER|0|1|...|7}] \\\n"); 1.2532 + fprintf(out, " [-i|--logident <syslog ident> \\\n"); 1.2533 + fprintf(out, " [-l|--logfile <logfile>] \\\n"); 1.2534 + fprintf(out, " [-p|--pidfile <pidfile>] \\\n"); 1.2535 + fprintf(out, " [-s|--stats] \\\n"); 1.2536 + fprintf(out, " <Lua file> [<Lua file> ...]\n"); 1.2537 + exit(err); 1.2538 +} 1.2539 + 1.2540 +#define moonbr_usage_error() moonbr_usage(MOONBR_EXITCODE_CMDLINEERROR, argc ? argv[0] : NULL) 1.2541 + 1.2542 +int main(int argc, char **argv) { 1.2543 + { 1.2544 + int daemonize = 0; 1.2545 + int log_facility = LOG_USER; 1.2546 + const char *log_ident = "moonbridge"; 1.2547 + const char *log_filename = NULL; 1.2548 + const char *pid_filename = NULL; 1.2549 + int option; 1.2550 + struct option longopts[] = { 1.2551 + { "background", no_argument, NULL, 'b' }, 1.2552 + { "debug", no_argument, NULL, 'd' }, 1.2553 + { "logfacility", required_argument, NULL, 'f' }, 1.2554 + { "help", no_argument, NULL, 'h' }, 1.2555 + { "logident", required_argument, NULL, 'i' }, 1.2556 + { "logfile", required_argument, NULL, 'l' }, 1.2557 + { "pidfile", required_argument, NULL, 'p' }, 1.2558 + { "stats", no_argument, NULL, 's' } 1.2559 + }; 1.2560 + while ((option = getopt_long(argc, argv, "bdf:hi:l:p:s", longopts, NULL)) != -1) { 1.2561 + switch (option) { 1.2562 + case 'b': 1.2563 + daemonize = 1; 1.2564 + break; 1.2565 + case 'd': 1.2566 + moonbr_debug = 1; 1.2567 + moonbr_stat = 1; 1.2568 + break; 1.2569 + case 'f': 1.2570 + if (!strcmp(optarg, "DAEMON")) { 1.2571 + log_facility = LOG_DAEMON; 1.2572 + } else if (!strcmp(optarg, "USER")) { 1.2573 + log_facility = LOG_USER; 1.2574 + } else if (!strcmp(optarg, "0")) { 1.2575 + log_facility = LOG_LOCAL0; 1.2576 + } else if (!strcmp(optarg, "1")) { 1.2577 + log_facility = LOG_LOCAL1; 1.2578 + } else if (!strcmp(optarg, "2")) { 1.2579 + log_facility = LOG_LOCAL2; 1.2580 + } else if (!strcmp(optarg, "3")) { 1.2581 + log_facility = LOG_LOCAL3; 1.2582 + } else if (!strcmp(optarg, "4")) { 1.2583 + log_facility = LOG_LOCAL4; 1.2584 + } else if (!strcmp(optarg, "5")) { 1.2585 + log_facility = LOG_LOCAL5; 1.2586 + } else if (!strcmp(optarg, "6")) { 1.2587 + log_facility = LOG_LOCAL6; 1.2588 + } else if (!strcmp(optarg, "7")) { 1.2589 + log_facility = LOG_LOCAL7; 1.2590 + } else { 1.2591 + moonbr_usage_error(); 1.2592 + } 1.2593 + moonbr_use_syslog = 1; 1.2594 + break; 1.2595 + case 'h': 1.2596 + moonbr_usage(MOONBR_EXITCODE_GRACEFUL, argv[0]); 1.2597 + break; 1.2598 + case 'i': 1.2599 + log_ident = optarg; 1.2600 + moonbr_use_syslog = 1; 1.2601 + break; 1.2602 + case 'l': 1.2603 + log_filename = optarg; 1.2604 + break; 1.2605 + case 'p': 1.2606 + pid_filename = optarg; 1.2607 + break; 1.2608 + case 's': 1.2609 + moonbr_stat = 1; 1.2610 + break; 1.2611 + default: 1.2612 + moonbr_usage_error(); 1.2613 + } 1.2614 + } 1.2615 + if (argc - optind <= 0) moonbr_usage_error(); 1.2616 + if (pid_filename) { 1.2617 + pid_t otherpid; 1.2618 + while ((moonbr_pidfh = pidfile_open(pid_filename, 0644, &otherpid)) == NULL) { 1.2619 + if (errno == EEXIST) { 1.2620 + if (otherpid == -1) { 1.2621 + fprintf(stderr, "PID file \"%s\" is already locked\n", pid_filename); 1.2622 + } else { 1.2623 + fprintf(stderr, "PID file \"%s\" is already locked by process with PID: %i\n", pid_filename, (int)otherpid); 1.2624 + } 1.2625 + exit(MOONBR_EXITCODE_ALREADYRUNNING); 1.2626 + } else if (errno != EINTR) { 1.2627 + fprintf(stderr, "Could not write PID file \"%s\": %s\n", pid_filename, strerror(errno)); 1.2628 + exit(MOONBR_EXITCODE_STARTUPERROR); 1.2629 + } 1.2630 + } 1.2631 + } 1.2632 + if (log_filename) { 1.2633 + int logfd; 1.2634 + while ( 1.2635 + ( logfd = flopen( 1.2636 + log_filename, 1.2637 + O_WRONLY|O_NONBLOCK|O_CREAT|O_APPEND|O_CLOEXEC, 1.2638 + 0640 1.2639 + ) 1.2640 + ) < 0 1.2641 + ) { 1.2642 + if (errno == EWOULDBLOCK) { 1.2643 + fprintf(stderr, "Logfile \"%s\" is locked\n", log_filename); 1.2644 + exit(MOONBR_EXITCODE_ALREADYRUNNING); 1.2645 + } else if (errno != EINTR) { 1.2646 + fprintf(stderr, "Could not open logfile \"%s\": %s\n", log_filename, strerror(errno)); 1.2647 + exit(MOONBR_EXITCODE_STARTUPERROR); 1.2648 + } 1.2649 + } 1.2650 + moonbr_logfile = fdopen(logfd, "a"); 1.2651 + if (!moonbr_logfile) { 1.2652 + fprintf(stderr, "Could not open write stream to logfile \"%s\": %s\n", log_filename, strerror(errno)); 1.2653 + exit(MOONBR_EXITCODE_STARTUPERROR); 1.2654 + } 1.2655 + } 1.2656 + if (daemonize == 0 && !moonbr_logfile) moonbr_logfile = stderr; 1.2657 + if (moonbr_logfile) setlinebuf(moonbr_logfile); 1.2658 + else moonbr_use_syslog = 1; 1.2659 + if (moonbr_use_syslog) openlog(log_ident, LOG_NDELAY | LOG_PID, log_facility); 1.2660 + if (daemonize) { 1.2661 + if (daemon(1, 0)) { 1.2662 + moonbr_log(LOG_ERR, "Could not daemonize moonbridge process"); 1.2663 + moonbr_terminate_error(); 1.2664 + } 1.2665 + } 1.2666 + } 1.2667 + moonbr_log(LOG_NOTICE, "Starting moonbridge server"); 1.2668 + if (moonbr_pidfh && pidfile_write(moonbr_pidfh)) { 1.2669 + moonbr_log(LOG_ERR, "Could not write pidfile (after locking)"); 1.2670 + } 1.2671 + { 1.2672 + lua_State *L; 1.2673 + L = lua_newstate(moonbr_alloc, NULL); 1.2674 + if (!L) { 1.2675 + moonbr_log(LOG_CRIT, "Could not initialize Lua state"); 1.2676 + moonbr_terminate_error(); 1.2677 + } 1.2678 + lua_atpanic(L, moonbr_lua_panic); 1.2679 + luaL_openlibs(L); 1.2680 + if (luaL_newmetatable(L, LUA_FILEHANDLE)) { 1.2681 + moonbr_log(LOG_CRIT, "Lua metatable LUA_FILEHANDLE does not exist"); 1.2682 + moonbr_terminate_error(); 1.2683 + } 1.2684 + lua_getfield(L, -1, "__index"); 1.2685 + lua_pushcfunction(L, moonbr_readuntil); 1.2686 + lua_setfield(L, -2, "readuntil"); 1.2687 + lua_pop(L, 2); 1.2688 + lua_pushcfunction(L, moonbr_timeout); 1.2689 + lua_setglobal(L, "timeout"); 1.2690 + lua_pushcfunction(L, moonbr_listen); 1.2691 + lua_setglobal(L, "listen"); 1.2692 + lua_pushcfunction(L, moonbr_addtraceback); // on stack position 1 1.2693 + { 1.2694 + int i; 1.2695 + for (i=optind; i<argc; i++) { 1.2696 + moonbr_log(LOG_INFO, "Loading \"%s\"", argv[i]); 1.2697 + if (luaL_loadfile(L, argv[i])) { 1.2698 + moonbr_log(LOG_ERR, "Error while loading \"%s\": %s", argv[i], lua_tostring(L, -1)); 1.2699 + moonbr_terminate_error(); 1.2700 + } 1.2701 + if (lua_pcall(L, 0, 0, 1)) { 1.2702 + moonbr_log(LOG_ERR, "Error while executing \"%s\": %s", argv[i], lua_tostring(L, -1)); 1.2703 + moonbr_terminate_error(); 1.2704 + } 1.2705 + } 1.2706 + } 1.2707 + lua_getglobal(L, "listen"); 1.2708 + lua_pushcfunction(L, moonbr_listen); 1.2709 + if (lua_compare(L, -2, -1, LUA_OPEQ)) { 1.2710 + lua_pushnil(L); 1.2711 + lua_setglobal(L, "listen"); 1.2712 + } 1.2713 + lua_settop(L, 1); 1.2714 + moonbr_run(L); 1.2715 + } 1.2716 + return 0; 1.2717 +} 1.2718 +