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, &notimer, 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 +

Impressum / About Us