moonbridge
annotate moonbridge.c @ 124:61a2f55b3538
Do not announce socket/listener type when communicating with the child process
(unnecessary because pointer to listener struct is passed)
(unnecessary because pointer to listener struct is passed)
| author | jbe |
|---|---|
| date | Sun Apr 12 00:33:08 2015 +0200 (2015-04-12) |
| parents | 84aa2b8dcf79 |
| children | d9cc81641175 |
| rev | line source |
|---|---|
| jbe@58 | 1 |
| jbe@58 | 2 /*** Version ***/ |
| jbe@58 | 3 #define MOONBR_VERSION_STRING "0.4.0" |
| jbe@58 | 4 |
| jbe@0 | 5 |
| jbe@0 | 6 /*** Compile-time configuration ***/ |
| jbe@0 | 7 |
| jbe@0 | 8 #define MOONBR_LUA_PANIC_BUG_WORKAROUND 1 |
| jbe@0 | 9 |
| jbe@0 | 10 |
| jbe@0 | 11 /*** C preprocessor macros for portability support ***/ |
| jbe@0 | 12 |
| jbe@0 | 13 #ifndef __has_include |
| jbe@0 | 14 #define __has_include(x) 0 |
| jbe@0 | 15 #endif |
| jbe@0 | 16 |
| jbe@0 | 17 |
| jbe@0 | 18 /*** Include directives for used system libraries ***/ |
| jbe@0 | 19 |
| jbe@0 | 20 #if defined(__linux__) |
| jbe@0 | 21 #define _GNU_SOURCE |
| jbe@0 | 22 #endif |
| jbe@0 | 23 #include <stdlib.h> |
| jbe@0 | 24 #include <unistd.h> |
| jbe@0 | 25 #include <stdint.h> |
| jbe@0 | 26 #include <errno.h> |
| jbe@0 | 27 #include <getopt.h> |
| jbe@0 | 28 #include <syslog.h> |
| jbe@0 | 29 #include <string.h> |
| jbe@0 | 30 #include <stdio.h> |
| jbe@0 | 31 #include <time.h> |
| jbe@0 | 32 #include <sys/time.h> |
| jbe@0 | 33 #include <sys/socket.h> |
| jbe@0 | 34 #include <sys/un.h> |
| jbe@0 | 35 #include <netinet/in.h> |
| jbe@0 | 36 #include <poll.h> |
| jbe@0 | 37 #include <signal.h> |
| jbe@0 | 38 #include <sys/wait.h> |
| jbe@0 | 39 #include <sys/resource.h> |
| jbe@0 | 40 #include <sys/file.h> |
| jbe@0 | 41 #if defined(__FreeBSD__) || __has_include(<libutil.h>) |
| jbe@0 | 42 #include <libutil.h> |
| jbe@0 | 43 #endif |
| jbe@22 | 44 #if defined(__linux__) || __has_include(<bsd/stdio.h>) |
| jbe@22 | 45 #include <bsd/stdio.h> |
| jbe@22 | 46 #endif |
| jbe@0 | 47 #if defined(__linux__) || __has_include(<bsd/libutil.h>) |
| jbe@0 | 48 #include <bsd/libutil.h> |
| jbe@0 | 49 #endif |
| jbe@0 | 50 #if defined(__linux__) || __has_include(<bsd/unistd.h>) |
| jbe@0 | 51 #include <bsd/unistd.h> |
| jbe@0 | 52 #endif |
| jbe@0 | 53 |
| jbe@0 | 54 |
| jbe@0 | 55 /*** Fallback definitions for missing constants on some platforms ***/ |
| jbe@0 | 56 |
| jbe@0 | 57 /* INFTIM is used as timeout parameter for poll() */ |
| jbe@0 | 58 #ifndef INFTIM |
| jbe@0 | 59 #define INFTIM -1 |
| jbe@0 | 60 #endif |
| jbe@0 | 61 |
| jbe@0 | 62 |
| jbe@0 | 63 /*** Include directives for Lua ***/ |
| jbe@0 | 64 |
| jbe@0 | 65 #include <lua.h> |
| jbe@0 | 66 #include <lauxlib.h> |
| jbe@0 | 67 #include <lualib.h> |
| jbe@0 | 68 |
| jbe@0 | 69 |
| jbe@79 | 70 /*** Include directive for moonbridge_io library ***/ |
| jbe@79 | 71 |
| jbe@79 | 72 #include "moonbridge_io.h" |
| jbe@79 | 73 |
| jbe@79 | 74 |
| jbe@0 | 75 /*** Constants ***/ |
| jbe@0 | 76 |
| jbe@0 | 77 /* Backlog option for listen() call */ |
| jbe@0 | 78 #define MOONBR_LISTEN_BACKLOG 1024 |
| jbe@0 | 79 |
| jbe@0 | 80 /* Maximum length of a timestamp used for strftime() */ |
| jbe@0 | 81 #define MOONBR_LOG_MAXTIMELEN 40 |
| jbe@0 | 82 |
| jbe@0 | 83 /* Maximum length of a log message */ |
| jbe@0 | 84 #define MOONBR_LOG_MAXMSGLEN 4095 |
| jbe@0 | 85 |
| jbe@0 | 86 /* Exitcodes passed to exit() call */ |
| jbe@0 | 87 #define MOONBR_EXITCODE_GRACEFUL 0 |
| jbe@0 | 88 #define MOONBR_EXITCODE_CMDLINEERROR 1 |
| jbe@0 | 89 #define MOONBR_EXITCODE_ALREADYRUNNING 2 |
| jbe@0 | 90 #define MOONBR_EXITCODE_STARTUPERROR 3 |
| jbe@0 | 91 #define MOONBR_EXITCODE_RUNTIMEERROR 4 |
| jbe@0 | 92 |
| jbe@0 | 93 /* Maximum length of a line sent to stderr by child processes */ |
| jbe@0 | 94 #define MOONBR_MAXERRORLINELEN 1024 |
| jbe@0 | 95 |
| jbe@0 | 96 /* Maximum length of an error string returned by strerror() */ |
| jbe@0 | 97 #define MOONBR_MAXSTRERRORLEN 80 |
| jbe@0 | 98 |
| jbe@0 | 99 /* Status bytes exchanged between master and child processes */ |
| jbe@124 | 100 #define MOONBR_STATUS_IDLE 'I' |
| jbe@124 | 101 #define MOONBR_COMMAND_CONNECT 'C' |
| jbe@124 | 102 #define MOONBR_COMMAND_TERMINATE 'T' |
| jbe@124 | 103 #define MOONBR_STATUS_GOODBYE 'B' |
| jbe@0 | 104 |
| jbe@0 | 105 /* Constant file descriptors */ |
| jbe@0 | 106 #define MOONBR_FD_STDERR 2 |
| jbe@0 | 107 #define MOONBR_FD_CONTROL 3 |
| jbe@0 | 108 #define MOONBR_FD_END 4 |
| jbe@0 | 109 |
| jbe@0 | 110 /* Return values of moonbr_try_destroy_worker() */ |
| jbe@0 | 111 #define MOONBR_DESTROY_NONE 0 |
| jbe@0 | 112 #define MOONBR_DESTROY_PREPARE 1 |
| jbe@0 | 113 #define MOONBR_DESTROY_IDLE_OR_ASSIGNED 2 |
| jbe@0 | 114 |
| jbe@0 | 115 |
| jbe@0 | 116 /*** Types ***/ |
| jbe@0 | 117 |
| jbe@0 | 118 /* Enum for 'moonbr_pstate' */ |
| jbe@0 | 119 #define MOONBR_PSTATE_STARTUP 0 |
| jbe@0 | 120 #define MOONBR_PSTATE_RUNNING 1 |
| jbe@0 | 121 #define MOONBR_PSTATE_FORKED 2 |
| jbe@0 | 122 |
| jbe@0 | 123 /* Enum for 'proto' field of struct moonbr_listener */ |
| jbe@0 | 124 #define MOONBR_PROTO_INTERVAL 1 |
| jbe@0 | 125 #define MOONBR_PROTO_LOCAL 2 |
| jbe@0 | 126 #define MOONBR_PROTO_TCP6 3 |
| jbe@0 | 127 #define MOONBR_PROTO_TCP4 4 |
| jbe@0 | 128 |
| jbe@0 | 129 /* Data structure for a pool's listener that can accept incoming connections */ |
| jbe@0 | 130 struct moonbr_listener { |
| jbe@0 | 131 struct moonbr_pool *pool; |
| jbe@0 | 132 struct moonbr_listener *prev_listener; /* previous idle or(!) connected listener */ |
| jbe@0 | 133 struct moonbr_listener *next_listener; /* next idle or(!) connected listener */ |
| jbe@0 | 134 int proto; |
| jbe@0 | 135 union { |
| jbe@0 | 136 struct { |
| jbe@0 | 137 char *name; /* name of interval passed to 'connect' function as 'interval' field in table */ |
| jbe@0 | 138 int strict; /* nonzero = runtime of 'connect' function does not delay interval */ |
| jbe@0 | 139 struct timeval delay; /* interval between invocations of 'connect' function */ |
| jbe@0 | 140 struct timeval wakeup; /* point in time of next invocation */ |
| jbe@0 | 141 } interval; |
| jbe@0 | 142 struct { |
| jbe@0 | 143 char *path; /* full path name (i.e. filename with path) of UNIX domain socket */ |
| jbe@0 | 144 } local; |
| jbe@0 | 145 struct { |
| jbe@0 | 146 int port; /* port number to listen on (in host endianess) */ |
| jbe@0 | 147 int localhost_only; /* nonzero = listen on localhost only */ |
| jbe@0 | 148 } tcp; |
| jbe@0 | 149 } proto_specific; |
| jbe@0 | 150 int listenfd; /* -1 = none */ |
| jbe@0 | 151 int pollidx; /* -1 = none */ |
| jbe@0 | 152 }; |
| jbe@0 | 153 |
| jbe@0 | 154 /* Data structure for a child process that is handling incoming connections */ |
| jbe@0 | 155 struct moonbr_worker { |
| jbe@0 | 156 struct moonbr_pool *pool; |
| jbe@0 | 157 struct moonbr_worker *prev_worker; |
| jbe@0 | 158 struct moonbr_worker *next_worker; |
| jbe@0 | 159 struct moonbr_worker *prev_idle_worker; |
| jbe@0 | 160 struct moonbr_worker *next_idle_worker; |
| jbe@0 | 161 int idle; /* nonzero = waiting for command from parent process */ |
| jbe@0 | 162 int assigned; /* nonzero = currently handling a connection */ |
| jbe@0 | 163 pid_t pid; |
| jbe@0 | 164 int controlfd; /* socket to send/receive control message to/from child process */ |
| jbe@0 | 165 int errorfd; /* socket to receive error output from child process' stderr */ |
| jbe@0 | 166 char *errorlinebuf; /* optional buffer for collecting stderr data from child process */ |
| jbe@0 | 167 int errorlinelen; /* number of bytes stored in 'errorlinebuf' */ |
| jbe@0 | 168 int errorlineovf; /* nonzero = line length overflow */ |
| jbe@0 | 169 struct timeval idle_expiration; /* point in time until child process may stay in idle state */ |
| jbe@0 | 170 struct moonbr_listener *restart_interval_listener; /* set while interval listener is assigned */ |
| jbe@0 | 171 }; |
| jbe@0 | 172 |
| jbe@0 | 173 /* Data structure for a pool of workers and listeners */ |
| jbe@0 | 174 struct moonbr_pool { |
| jbe@0 | 175 int poolnum; /* number of pool for log output */ |
| jbe@0 | 176 struct moonbr_pool *next_pool; /* next entry in linked list starting with 'moonbr_first_pool' */ |
| jbe@0 | 177 struct moonbr_worker *first_worker; /* first worker of pool */ |
| jbe@0 | 178 struct moonbr_worker *last_worker; /* last worker of pool */ |
| jbe@0 | 179 struct moonbr_worker *first_idle_worker; /* first idle worker of pool */ |
| jbe@0 | 180 struct moonbr_worker *last_idle_worker; /* last idle worker of pool */ |
| jbe@0 | 181 int idle_worker_count; |
| jbe@0 | 182 int unassigned_worker_count; |
| jbe@0 | 183 int total_worker_count; |
| jbe@0 | 184 int worker_count_stat; /* only needed for statistics */ |
| jbe@0 | 185 int pre_fork; /* desired minimum number of unassigned workers */ |
| jbe@0 | 186 int min_fork; /* desired minimum number of workers in total */ |
| jbe@0 | 187 int max_fork; /* maximum number of workers */ |
| jbe@0 | 188 struct timeval fork_delay; /* delay after each fork() until a fork may happen again */ |
| jbe@0 | 189 struct timeval fork_wakeup; /* point in time when a fork may happen again (unless a worker terminates before) */ |
| jbe@0 | 190 struct timeval fork_error_delay; /* delay between fork()s when an error during fork or preparation occurred */ |
| jbe@0 | 191 struct timeval fork_error_wakeup; /* point in time when fork may happen again if an error in preparation occurred */ |
| jbe@0 | 192 int use_fork_error_wakeup; /* nonzero = error in preparation occured; gets reset on next fork */ |
| jbe@0 | 193 struct timeval exit_delay; /* delay for terminating excessive workers (unassigned_worker_count > pre_fork) */ |
| jbe@0 | 194 struct timeval exit_wakeup; /* point in time when terminating an excessive worker */ |
| jbe@0 | 195 struct timeval idle_timeout; /* delay before an idle worker is terminated */ |
| jbe@0 | 196 size_t memory_limit; /* maximum bytes of memory that the Lua machine may allocate */ |
| jbe@0 | 197 int listener_count; /* total number of listeners of pool (and size of 'listener' array at end of this struct) */ |
| jbe@0 | 198 struct moonbr_listener *first_idle_listener; /* first listener that is idle (i.e. has no waiting connection) */ |
| jbe@0 | 199 struct moonbr_listener *last_idle_listener; /* last listener that is idle (i.e. has no waiting connection) */ |
| jbe@0 | 200 struct moonbr_listener *first_connected_listener; /* first listener that has a pending connection */ |
| jbe@0 | 201 struct moonbr_listener *last_connected_listener; /* last listener that has a pending connection */ |
| jbe@0 | 202 struct moonbr_listener listener[1]; /* static array of variable(!) size to contain 'listener' structures */ |
| jbe@0 | 203 }; |
| jbe@0 | 204 |
| jbe@0 | 205 /* Enum for 'channel' field of struct moonbr_poll_worker */ |
| jbe@0 | 206 #define MOONBR_POLL_WORKER_CONTROLCHANNEL 1 |
| jbe@0 | 207 #define MOONBR_POLL_WORKER_ERRORCHANNEL 2 |
| jbe@0 | 208 |
| jbe@0 | 209 /* Structure to refer from 'moonbr_poll_worker_fds' entry to worker structure */ |
| jbe@0 | 210 struct moonbr_poll_worker { |
| jbe@0 | 211 struct moonbr_worker *worker; |
| jbe@0 | 212 int channel; /* field indicating whether file descriptor is 'controlfd' or 'errorfd' */ |
| jbe@0 | 213 }; |
| jbe@0 | 214 |
| jbe@0 | 215 /* Variable indicating that clean shutdown was requested */ |
| jbe@0 | 216 static int moonbr_shutdown_in_progress = 0; |
| jbe@0 | 217 |
| jbe@0 | 218 |
| jbe@0 | 219 /*** Macros for Lua registry ***/ |
| jbe@0 | 220 |
| jbe@0 | 221 /* Lightuserdata keys for Lua registry to store 'prepare', 'connect', and 'finish' functions */ |
| jbe@0 | 222 #define moonbr_luakey_prepare_func(pool) ((void *)(intptr_t)(pool) + 0) |
| jbe@0 | 223 #define moonbr_luakey_connect_func(pool) ((void *)(intptr_t)(pool) + 1) |
| jbe@0 | 224 #define moonbr_luakey_finish_func(pool) ((void *)(intptr_t)(pool) + 2) |
| jbe@0 | 225 |
| jbe@0 | 226 |
| jbe@0 | 227 /*** Global variables ***/ |
| jbe@0 | 228 |
| jbe@0 | 229 /* State of process execution */ |
| jbe@0 | 230 static int moonbr_pstate = MOONBR_PSTATE_STARTUP; |
| jbe@0 | 231 |
| jbe@0 | 232 /* Process ID of the main process */ |
| jbe@0 | 233 static pid_t moonbr_masterpid; |
| jbe@0 | 234 |
| jbe@0 | 235 /* Condition variables set by the signal handler */ |
| jbe@0 | 236 static volatile sig_atomic_t moonbr_cond_poll = 0; |
| jbe@0 | 237 static volatile sig_atomic_t moonbr_cond_terminate = 0; |
| jbe@0 | 238 static volatile sig_atomic_t moonbr_cond_interrupt = 0; |
| jbe@0 | 239 static volatile sig_atomic_t moonbr_cond_child = 0; |
| jbe@0 | 240 |
| jbe@0 | 241 /* Socket pair to denote signal delivery when signal handler was called just before poll() */ |
| jbe@0 | 242 static int moonbr_poll_signalfds[2]; |
| jbe@0 | 243 #define moonbr_poll_signalfd_read moonbr_poll_signalfds[0] |
| jbe@0 | 244 #define moonbr_poll_signalfd_write moonbr_poll_signalfds[1] |
| jbe@0 | 245 |
| jbe@0 | 246 /* Global variables for pidfile and logging */ |
| jbe@0 | 247 static struct pidfh *moonbr_pidfh = NULL; |
| jbe@0 | 248 static FILE *moonbr_logfile = NULL; |
| jbe@0 | 249 static int moonbr_use_syslog = 0; |
| jbe@0 | 250 |
| jbe@0 | 251 /* First and last entry of linked list of all created pools during initialization */ |
| jbe@0 | 252 static struct moonbr_pool *moonbr_first_pool = NULL; |
| jbe@0 | 253 static struct moonbr_pool *moonbr_last_pool = NULL; |
| jbe@0 | 254 |
| jbe@0 | 255 /* Total count of pools */ |
| jbe@0 | 256 static int moonbr_pool_count = 0; |
| jbe@0 | 257 |
| jbe@0 | 258 /* Set to a nonzero value if dynamic part of 'moonbr_poll_fds' ('moonbr_poll_worker_fds') needs an update */ |
| jbe@0 | 259 static int moonbr_poll_refresh_needed = 0; |
| jbe@0 | 260 |
| jbe@0 | 261 /* Array passed to poll(), consisting of static part and dynamic part ('moonbr_poll_worker_fds') */ |
| jbe@0 | 262 static struct pollfd *moonbr_poll_fds = NULL; /* the array */ |
| jbe@0 | 263 static int moonbr_poll_fds_bufsize = 0; /* memory allocated for this number of elements */ |
| jbe@0 | 264 static int moonbr_poll_fds_count = 0; /* total number of elements */ |
| jbe@0 | 265 static int moonbr_poll_fds_static_count; /* number of elements in static part */ |
| jbe@0 | 266 |
| jbe@0 | 267 /* Dynamic part of 'moonbr_poll_fds' array */ |
| jbe@0 | 268 #define moonbr_poll_worker_fds (moonbr_poll_fds+moonbr_poll_fds_static_count) |
| jbe@0 | 269 |
| jbe@0 | 270 /* Additional information for dynamic part of 'moonbr_poll_fds' array */ |
| jbe@0 | 271 struct moonbr_poll_worker *moonbr_poll_workers; /* the array */ |
| jbe@0 | 272 static int moonbr_poll_workers_bufsize = 0; /* memory allocated for this number of elements */ |
| jbe@0 | 273 static int moonbr_poll_worker_count = 0; /* number of elements in array */ |
| jbe@0 | 274 |
| jbe@0 | 275 /* Variable set to nonzero value to disallow further calls of 'listen' function */ |
| jbe@0 | 276 static int moonbr_booted = 0; |
| jbe@0 | 277 |
| jbe@0 | 278 /* Verbosity settings */ |
| jbe@0 | 279 static int moonbr_debug = 0; |
| jbe@0 | 280 static int moonbr_stat = 0; |
| jbe@0 | 281 |
| jbe@0 | 282 /* Memory consumption by Lua machine */ |
| jbe@0 | 283 static size_t moonbr_memory_usage = 0; |
| jbe@0 | 284 static size_t moonbr_memory_limit = 0; |
| jbe@0 | 285 |
| jbe@0 | 286 |
| jbe@0 | 287 /*** Functions for signal handling ***/ |
| jbe@0 | 288 |
| jbe@0 | 289 /* Signal handler for master and child processes */ |
| jbe@0 | 290 static void moonbr_signal(int sig) { |
| jbe@0 | 291 if (getpid() == moonbr_masterpid) { |
| jbe@0 | 292 /* master process */ |
| jbe@0 | 293 switch (sig) { |
| jbe@0 | 294 case SIGHUP: |
| jbe@0 | 295 case SIGINT: |
| jbe@0 | 296 /* fast shutdown requested */ |
| jbe@0 | 297 moonbr_cond_interrupt = 1; |
| jbe@0 | 298 break; |
| jbe@0 | 299 case SIGTERM: |
| jbe@0 | 300 /* clean shutdown requested */ |
| jbe@0 | 301 moonbr_cond_terminate = 1; |
| jbe@0 | 302 break; |
| jbe@0 | 303 case SIGCHLD: |
| jbe@0 | 304 /* child process terminated */ |
| jbe@0 | 305 moonbr_cond_child = 1; |
| jbe@0 | 306 break; |
| jbe@0 | 307 } |
| jbe@0 | 308 if (moonbr_cond_poll) { |
| jbe@0 | 309 /* avoid race condition if signal handler is invoked right before poll() */ |
| jbe@0 | 310 char buf[1] = {0}; |
| jbe@20 | 311 write(moonbr_poll_signalfd_write, buf, 1); |
| jbe@0 | 312 } |
| jbe@0 | 313 } else { |
| jbe@0 | 314 /* child process forwards certain signals to parent process */ |
| jbe@0 | 315 switch (sig) { |
| jbe@0 | 316 case SIGHUP: |
| jbe@0 | 317 case SIGINT: |
| jbe@0 | 318 case SIGTERM: |
| jbe@0 | 319 kill(moonbr_masterpid, sig); |
| jbe@0 | 320 } |
| jbe@0 | 321 } |
| jbe@0 | 322 } |
| jbe@0 | 323 |
| jbe@0 | 324 /* Initialize signal handling */ |
| jbe@0 | 325 static void moonbr_signal_init(){ |
| jbe@0 | 326 moonbr_masterpid = getpid(); |
| jbe@0 | 327 signal(SIGHUP, moonbr_signal); |
| jbe@0 | 328 signal(SIGINT, moonbr_signal); |
| jbe@0 | 329 signal(SIGTERM, moonbr_signal); |
| jbe@0 | 330 signal(SIGCHLD, moonbr_signal); |
| jbe@0 | 331 } |
| jbe@0 | 332 |
| jbe@0 | 333 |
| jbe@0 | 334 /*** Functions for logging in master process ***/ |
| jbe@0 | 335 |
| jbe@0 | 336 /* Logs a pre-formatted message with given syslog() priority */ |
| jbe@0 | 337 static void moonbr_log_msg(int priority, const char *msg) { |
| jbe@0 | 338 if (moonbr_logfile) { |
| jbe@0 | 339 /* logging to logfile desired (timestamp is prepended in that case) */ |
| jbe@0 | 340 time_t now_time = 0; |
| jbe@0 | 341 struct tm now_tmstruct; |
| jbe@0 | 342 char timestr[MOONBR_LOG_MAXTIMELEN+1]; |
| jbe@0 | 343 time(&now_time); |
| jbe@0 | 344 localtime_r(&now_time, &now_tmstruct); |
| jbe@0 | 345 if (!strftime( |
| jbe@0 | 346 timestr, MOONBR_LOG_MAXTIMELEN+1, "%Y-%m-%d %H:%M:%S %Z: ", &now_tmstruct |
| jbe@0 | 347 )) timestr[0] = 0; |
| jbe@0 | 348 fprintf(moonbr_logfile, "%s%s\n", timestr, msg); |
| jbe@0 | 349 } |
| jbe@0 | 350 if (moonbr_use_syslog) { |
| jbe@0 | 351 /* logging through syslog desired */ |
| jbe@0 | 352 syslog(priority, "%s", msg); |
| jbe@0 | 353 } |
| jbe@0 | 354 } |
| jbe@0 | 355 |
| jbe@0 | 356 /* Formats a message via vsnprintf() and logs it with given syslog() priority */ |
| jbe@0 | 357 static void moonbr_log(int priority, const char *message, ...) { |
| jbe@0 | 358 char msgbuf[MOONBR_LOG_MAXMSGLEN+1]; /* buffer of static size to store formatted message */ |
| jbe@0 | 359 int msglen; /* length of full message (may exceed MOONBR_LOG_MAXMSGLEN) */ |
| jbe@0 | 360 { |
| jbe@0 | 361 /* pass variable arguments to vsnprintf() to format message */ |
| jbe@0 | 362 va_list ap; |
| jbe@0 | 363 va_start(ap, message); |
| jbe@0 | 364 msglen = vsnprintf(msgbuf, MOONBR_LOG_MAXMSGLEN+1, message, ap); |
| jbe@0 | 365 va_end(ap); |
| jbe@0 | 366 } |
| jbe@0 | 367 { |
| jbe@0 | 368 /* split and log message line by line */ |
| jbe@0 | 369 char *line = msgbuf; |
| jbe@0 | 370 while (1) { |
| jbe@0 | 371 char *endptr = strchr(line, '\n'); |
| jbe@0 | 372 if (endptr) { |
| jbe@0 | 373 /* terminate string where newline character is found */ |
| jbe@0 | 374 *endptr = 0; |
| jbe@0 | 375 } else if (line != msgbuf && msglen > MOONBR_LOG_MAXMSGLEN) { |
| jbe@0 | 376 /* break if line is incomplete and not the first line */ |
| jbe@0 | 377 break; |
| jbe@0 | 378 } |
| jbe@0 | 379 moonbr_log_msg(priority, line); |
| jbe@0 | 380 if (!endptr) break; /* break if end of formatted message is reached */ |
| jbe@0 | 381 line = endptr+1; /* otherwise continue with remaining message */ |
| jbe@0 | 382 } |
| jbe@0 | 383 } |
| jbe@0 | 384 if (msglen > MOONBR_LOG_MAXMSGLEN) { |
| jbe@0 | 385 /* print warning if message was truncated */ |
| jbe@0 | 386 moonbr_log_msg(priority, "Previous log message has been truncated due to excessive length"); |
| jbe@0 | 387 } |
| jbe@0 | 388 } |
| jbe@0 | 389 |
| jbe@0 | 390 |
| jbe@0 | 391 /*** Termination function ***/ |
| jbe@0 | 392 |
| jbe@0 | 393 /* Kill all child processes, remove PID file (if existent), and exit master process with given exitcode */ |
| jbe@0 | 394 static void moonbr_terminate(int exitcode) { |
| jbe@0 | 395 { |
| jbe@0 | 396 struct moonbr_pool *pool; |
| jbe@0 | 397 for (pool=moonbr_first_pool; pool; pool=pool->next_pool) { |
| jbe@0 | 398 { |
| jbe@0 | 399 struct moonbr_worker *worker; |
| jbe@0 | 400 for (worker=pool->first_worker; worker; worker=worker->next_worker) { |
| jbe@0 | 401 moonbr_log(LOG_INFO, "Sending SIGKILL to child with PID %i", (int)worker->pid); |
| jbe@0 | 402 if (kill(worker->pid, SIGKILL)) { |
| jbe@0 | 403 moonbr_log(LOG_ERR, "Error while killing child process: %s", strerror(errno)); |
| jbe@0 | 404 } |
| jbe@0 | 405 } |
| jbe@0 | 406 } |
| jbe@0 | 407 { |
| jbe@0 | 408 int i; |
| jbe@0 | 409 for (i=0; i<pool->listener_count; i++) { |
| jbe@0 | 410 struct moonbr_listener *listener = &pool->listener[i]; |
| jbe@0 | 411 if (listener->proto == MOONBR_PROTO_LOCAL) { |
| jbe@0 | 412 moonbr_log(LOG_INFO, "Unlinking local socket \"%s\"", listener->proto_specific.local.path); |
| jbe@0 | 413 if (unlink(listener->proto_specific.local.path)) { |
| jbe@0 | 414 moonbr_log(LOG_ERR, "Error while unlinking local socket: %s", strerror(errno)); |
| jbe@0 | 415 } |
| jbe@0 | 416 } |
| jbe@0 | 417 } |
| jbe@0 | 418 } |
| jbe@0 | 419 } |
| jbe@0 | 420 } |
| jbe@0 | 421 moonbr_log(exitcode ? LOG_ERR : LOG_NOTICE, "Terminating with exit code %i", exitcode); |
| jbe@0 | 422 if (moonbr_pidfh && pidfile_remove(moonbr_pidfh)) { |
| jbe@0 | 423 moonbr_log(LOG_ERR, "Error while removing PID file: %s", strerror(errno)); |
| jbe@0 | 424 } |
| jbe@0 | 425 exit(exitcode); |
| jbe@0 | 426 } |
| jbe@0 | 427 |
| jbe@0 | 428 /* Terminate with either MOONBR_EXITCODE_STARTUPERROR or MOONBR_EXITCODE_RUNTIMEERROR */ |
| jbe@0 | 429 #define moonbr_terminate_error() \ |
| jbe@0 | 430 moonbr_terminate( \ |
| jbe@0 | 431 moonbr_pstate == MOONBR_PSTATE_STARTUP ? \ |
| jbe@0 | 432 MOONBR_EXITCODE_STARTUPERROR : \ |
| jbe@0 | 433 MOONBR_EXITCODE_RUNTIMEERROR \ |
| jbe@0 | 434 ) |
| jbe@0 | 435 |
| jbe@0 | 436 |
| jbe@0 | 437 /*** Helper functions ***/ |
| jbe@0 | 438 |
| jbe@0 | 439 /* Fills a 'struct timeval' structure with the current time (using CLOCK_MONOTONIC) */ |
| jbe@0 | 440 static void moonbr_now(struct timeval *now) { |
| jbe@0 | 441 struct timespec ts = {0, }; |
| jbe@0 | 442 if (clock_gettime(CLOCK_MONOTONIC, &ts)) { |
| jbe@0 | 443 moonbr_log(LOG_CRIT, "Error in clock_gettime() call: %s", strerror(errno)); |
| jbe@0 | 444 moonbr_terminate_error(); |
| jbe@0 | 445 } |
| jbe@0 | 446 *now = (struct timeval){ .tv_sec = ts.tv_sec, .tv_usec = ts.tv_nsec / 1000 }; |
| jbe@0 | 447 } |
| jbe@0 | 448 |
| jbe@0 | 449 /* Formats a 'struct timeval' value (not thread-safe) */ |
| jbe@0 | 450 static char *moonbr_format_timeval(struct timeval *t) { |
| jbe@0 | 451 static char buf[32]; |
| jbe@0 | 452 snprintf(buf, 32, "%ji.%06ji seconds", (intmax_t)t->tv_sec, (intmax_t)t->tv_usec); |
| jbe@0 | 453 return buf; |
| jbe@0 | 454 } |
| jbe@0 | 455 |
| jbe@0 | 456 |
| jbe@0 | 457 /*** Functions for pool creation and startup ***/ |
| jbe@0 | 458 |
| jbe@0 | 459 /* Creates a 'struct moonbr_pool' structure with a given number of listeners */ |
| jbe@0 | 460 static struct moonbr_pool *moonbr_create_pool(int listener_count) { |
| jbe@0 | 461 struct moonbr_pool *pool; |
| jbe@0 | 462 pool = calloc(1, |
| jbe@0 | 463 sizeof(struct moonbr_pool) + /* size of 'struct moonbr_pool' with one listener */ |
| jbe@0 | 464 (listener_count-1) * sizeof(struct moonbr_listener) /* size of extra listeners */ |
| jbe@0 | 465 ); |
| jbe@0 | 466 if (!pool) { |
| jbe@0 | 467 moonbr_log(LOG_CRIT, "Memory allocation error"); |
| jbe@0 | 468 moonbr_terminate_error(); |
| jbe@0 | 469 } |
| jbe@0 | 470 pool->listener_count = listener_count; |
| jbe@0 | 471 { |
| jbe@0 | 472 /* initialization of listeners */ |
| jbe@0 | 473 int i; |
| jbe@0 | 474 for (i=0; i<listener_count; i++) { |
| jbe@0 | 475 struct moonbr_listener *listener = &pool->listener[i]; |
| jbe@0 | 476 listener->pool = pool; |
| jbe@0 | 477 listener->listenfd = -1; |
| jbe@0 | 478 listener->pollidx = -1; |
| jbe@0 | 479 } |
| jbe@0 | 480 } |
| jbe@0 | 481 return pool; |
| jbe@0 | 482 } |
| jbe@0 | 483 |
| jbe@0 | 484 /* Destroys a 'struct moonbr_pool' structure before it has been started */ |
| jbe@0 | 485 static void moonbr_destroy_pool(struct moonbr_pool *pool) { |
| jbe@0 | 486 int i; |
| jbe@0 | 487 for (i=0; i<pool->listener_count; i++) { |
| jbe@0 | 488 struct moonbr_listener *listener = &pool->listener[i]; |
| jbe@0 | 489 if ( |
| jbe@0 | 490 listener->proto == MOONBR_PROTO_INTERVAL && |
| jbe@0 | 491 listener->proto_specific.interval.name |
| jbe@0 | 492 ) { |
| jbe@0 | 493 free(listener->proto_specific.interval.name); |
| jbe@0 | 494 } |
| jbe@0 | 495 if ( |
| jbe@0 | 496 listener->proto == MOONBR_PROTO_LOCAL && |
| jbe@0 | 497 listener->proto_specific.local.path |
| jbe@0 | 498 ) { |
| jbe@0 | 499 free(listener->proto_specific.local.path); |
| jbe@0 | 500 } |
| jbe@0 | 501 } |
| jbe@0 | 502 free(pool); |
| jbe@0 | 503 } |
| jbe@0 | 504 |
| jbe@0 | 505 /* Starts a all listeners in a pool */ |
| jbe@0 | 506 static int moonbr_start_pool(struct moonbr_pool *pool) { |
| jbe@0 | 507 moonbr_log(LOG_INFO, "Creating pool", pool->poolnum); |
| jbe@0 | 508 { |
| jbe@0 | 509 int i; |
| jbe@0 | 510 for (i=0; i<pool->listener_count; i++) { |
| jbe@0 | 511 struct moonbr_listener *listener = &pool->listener[i]; |
| jbe@0 | 512 switch (listener->proto) { |
| jbe@0 | 513 case MOONBR_PROTO_INTERVAL: |
| jbe@17 | 514 /* nothing to do here: starting intervals is performed in moonbr_run() function */ |
| jbe@0 | 515 if (!listener->proto_specific.interval.name) { |
| jbe@0 | 516 moonbr_log(LOG_INFO, "Adding unnamed interval listener"); |
| jbe@0 | 517 } else { |
| jbe@0 | 518 moonbr_log(LOG_INFO, "Adding interval listener \"%s\"", listener->proto_specific.interval.name); |
| jbe@0 | 519 } |
| jbe@0 | 520 break; |
| jbe@0 | 521 case MOONBR_PROTO_LOCAL: |
| jbe@0 | 522 moonbr_log(LOG_INFO, "Adding local socket listener for path \"%s\"", listener->proto_specific.local.path); |
| jbe@0 | 523 { |
| jbe@0 | 524 struct sockaddr_un servaddr = { .sun_family = AF_UNIX }; |
| jbe@0 | 525 const int path_maxlen = sizeof(struct sockaddr_un) - ( |
| jbe@110 | 526 (void *)servaddr.sun_path - (void *)&servaddr |
| jbe@0 | 527 ); |
| jbe@0 | 528 if ( |
| jbe@0 | 529 snprintf( |
| jbe@0 | 530 servaddr.sun_path, |
| jbe@0 | 531 path_maxlen, |
| jbe@0 | 532 "%s", |
| jbe@0 | 533 listener->proto_specific.local.path |
| jbe@0 | 534 ) >= path_maxlen |
| jbe@0 | 535 ) { |
| jbe@0 | 536 errno = ENAMETOOLONG; |
| jbe@0 | 537 }; |
| jbe@0 | 538 listener->listenfd = socket(PF_LOCAL, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); |
| jbe@0 | 539 if (listener->listenfd == -1) goto moonbr_start_pool_error; |
| jbe@0 | 540 if (!unlink(listener->proto_specific.local.path)) { |
| jbe@0 | 541 moonbr_log(LOG_WARNING, "Unlinked named socket \"%s\" prior to listening", listener->proto_specific.local.path); |
| jbe@0 | 542 } else { |
| jbe@0 | 543 if (errno != ENOENT) { |
| jbe@0 | 544 moonbr_log(LOG_ERR, "Could not unlink named socket \"%s\" prior to listening: %s", listener->proto_specific.local.path, strerror(errno)); |
| jbe@0 | 545 } |
| jbe@0 | 546 } |
| jbe@0 | 547 if ( |
| jbe@0 | 548 bind(listener->listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) |
| jbe@0 | 549 ) goto moonbr_start_pool_error; |
| jbe@0 | 550 if (listen(listener->listenfd, MOONBR_LISTEN_BACKLOG)) goto moonbr_start_pool_error; |
| jbe@0 | 551 } |
| jbe@0 | 552 break; |
| jbe@0 | 553 case MOONBR_PROTO_TCP6: |
| jbe@0 | 554 if (listener->proto_specific.tcp.localhost_only) { |
| jbe@0 | 555 moonbr_log(LOG_INFO, "Adding localhost TCP/IPv6 listener on port %i", listener->proto_specific.tcp.port); |
| jbe@0 | 556 } else { |
| jbe@0 | 557 moonbr_log(LOG_INFO, "Adding public TCP/IPv6 listener on port %i", listener->proto_specific.tcp.port); |
| jbe@0 | 558 } |
| jbe@0 | 559 { |
| jbe@0 | 560 struct sockaddr_in6 servaddr = { |
| jbe@0 | 561 .sin6_family = AF_INET6, |
| jbe@0 | 562 .sin6_port = htons(listener->proto_specific.tcp.port) |
| jbe@0 | 563 }; |
| jbe@0 | 564 listener->listenfd = socket(PF_INET6, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); |
| jbe@0 | 565 if (listener->listenfd == -1) goto moonbr_start_pool_error; |
| jbe@0 | 566 { |
| jbe@0 | 567 /* avoid "Address already in use" error when restarting service */ |
| jbe@0 | 568 static const int reuseval = 1; |
| jbe@0 | 569 if (setsockopt( |
| jbe@0 | 570 listener->listenfd, SOL_SOCKET, SO_REUSEADDR, &reuseval, sizeof(reuseval) |
| jbe@0 | 571 )) goto moonbr_start_pool_error; |
| jbe@0 | 572 } |
| jbe@0 | 573 { |
| jbe@0 | 574 /* default to send TCP RST when process terminates unexpectedly */ |
| jbe@0 | 575 static const struct linger lingerval = { |
| jbe@0 | 576 .l_onoff = 1, |
| jbe@0 | 577 .l_linger = 0 |
| jbe@0 | 578 }; |
| jbe@0 | 579 if (setsockopt( |
| jbe@0 | 580 listener->listenfd, SOL_SOCKET, SO_LINGER, &lingerval, sizeof(lingerval) |
| jbe@0 | 581 )) goto moonbr_start_pool_error; |
| jbe@0 | 582 } |
| jbe@0 | 583 if (listener->proto_specific.tcp.localhost_only) { |
| jbe@0 | 584 servaddr.sin6_addr.s6_addr[15] = 1; |
| jbe@0 | 585 } |
| jbe@0 | 586 if ( |
| jbe@0 | 587 bind(listener->listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) |
| jbe@0 | 588 ) goto moonbr_start_pool_error; |
| jbe@0 | 589 if (listen(listener->listenfd, MOONBR_LISTEN_BACKLOG)) goto moonbr_start_pool_error; |
| jbe@0 | 590 } |
| jbe@0 | 591 break; |
| jbe@0 | 592 case MOONBR_PROTO_TCP4: |
| jbe@0 | 593 if (listener->proto_specific.tcp.localhost_only) { |
| jbe@0 | 594 moonbr_log(LOG_INFO, "Adding localhost TCP/IPv4 listener on port %i", listener->proto_specific.tcp.port); |
| jbe@0 | 595 } else { |
| jbe@0 | 596 moonbr_log(LOG_INFO, "Adding public TCP/IPv4 listener on port %i", listener->proto_specific.tcp.port); |
| jbe@0 | 597 } |
| jbe@0 | 598 { |
| jbe@0 | 599 struct sockaddr_in servaddr = { |
| jbe@0 | 600 .sin_family = AF_INET, |
| jbe@0 | 601 .sin_port = htons(listener->proto_specific.tcp.port) |
| jbe@0 | 602 }; |
| jbe@0 | 603 listener->listenfd = socket(PF_INET, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); |
| jbe@0 | 604 if (listener->listenfd == -1) goto moonbr_start_pool_error; |
| jbe@0 | 605 { |
| jbe@0 | 606 /* avoid "Address already in use" error when restarting service */ |
| jbe@0 | 607 static const int reuseval = 1; |
| jbe@0 | 608 if (setsockopt( |
| jbe@0 | 609 listener->listenfd, SOL_SOCKET, SO_REUSEADDR, &reuseval, sizeof(reuseval) |
| jbe@0 | 610 )) goto moonbr_start_pool_error; |
| jbe@0 | 611 } |
| jbe@0 | 612 { |
| jbe@0 | 613 /* default to send TCP RST when process terminates unexpectedly */ |
| jbe@0 | 614 static const struct linger lingerval = { |
| jbe@0 | 615 .l_onoff = 1, |
| jbe@0 | 616 .l_linger = 0 |
| jbe@0 | 617 }; |
| jbe@0 | 618 if (setsockopt( |
| jbe@0 | 619 listener->listenfd, SOL_SOCKET, SO_LINGER, &lingerval, sizeof(lingerval) |
| jbe@0 | 620 )) goto moonbr_start_pool_error; |
| jbe@0 | 621 } |
| jbe@0 | 622 if (listener->proto_specific.tcp.localhost_only) { |
| jbe@0 | 623 ((uint8_t *)&servaddr.sin_addr.s_addr)[0] = 127; |
| jbe@0 | 624 ((uint8_t *)&servaddr.sin_addr.s_addr)[3] = 1; |
| jbe@0 | 625 } |
| jbe@0 | 626 if ( |
| jbe@0 | 627 bind(listener->listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) |
| jbe@0 | 628 ) goto moonbr_start_pool_error; |
| jbe@0 | 629 if (listen(listener->listenfd, MOONBR_LISTEN_BACKLOG)) goto moonbr_start_pool_error; |
| jbe@0 | 630 } |
| jbe@0 | 631 break; |
| jbe@0 | 632 default: |
| jbe@0 | 633 moonbr_log(LOG_CRIT, "Internal error (should not happen): Unexpected value in listener.proto field"); |
| jbe@0 | 634 moonbr_terminate_error(); |
| jbe@0 | 635 } |
| jbe@0 | 636 } |
| jbe@0 | 637 goto moonbr_start_pool_ok; |
| jbe@0 | 638 moonbr_start_pool_error: |
| jbe@0 | 639 { |
| jbe@0 | 640 int j = i; |
| jbe@0 | 641 int errno2 = errno; |
| jbe@0 | 642 for (; i>=0; i--) { |
| jbe@0 | 643 struct moonbr_listener *listener = &pool->listener[i]; |
| jbe@0 | 644 if (listener->listenfd != -1) close(listener->listenfd); |
| jbe@0 | 645 } |
| jbe@0 | 646 errno = errno2; |
| jbe@0 | 647 return j; |
| jbe@0 | 648 } |
| jbe@0 | 649 } |
| jbe@0 | 650 moonbr_start_pool_ok: |
| jbe@0 | 651 pool->poolnum = ++moonbr_pool_count; |
| jbe@0 | 652 moonbr_log(LOG_INFO, "Pool #%i created", pool->poolnum); |
| jbe@0 | 653 if (moonbr_last_pool) moonbr_last_pool->next_pool = pool; |
| jbe@0 | 654 else moonbr_first_pool = pool; |
| jbe@0 | 655 moonbr_last_pool = pool; |
| jbe@0 | 656 return -1; |
| jbe@0 | 657 } |
| jbe@0 | 658 |
| jbe@0 | 659 |
| jbe@0 | 660 /*** Function to send data and a file descriptor to child process */ |
| jbe@0 | 661 |
| jbe@0 | 662 /* Sends control message of one bye plus optional file descriptor plus optional pointer to child process */ |
| jbe@0 | 663 static void moonbr_send_control_message(struct moonbr_worker *worker, char status, int fd, void *ptr) { |
| jbe@0 | 664 { |
| jbe@0 | 665 struct iovec iovector = { .iov_base = &status, .iov_len = 1 }; /* carrying status byte */ |
| jbe@0 | 666 char control_message_buffer[CMSG_SPACE(sizeof(int))] = {0, }; /* used to transfer file descriptor */ |
| jbe@0 | 667 struct msghdr message = { .msg_iov = &iovector, .msg_iovlen = 1 }; /* data structure passed to sendmsg() call */ |
| jbe@0 | 668 if (moonbr_debug) { |
| jbe@0 | 669 if (fd == -1) { |
| jbe@0 | 670 moonbr_log(LOG_DEBUG, "Sending control message \"%c\" to child process in pool #%i (PID %i)", (int)status, worker->pool->poolnum, (int)worker->pid); |
| jbe@0 | 671 } else { |
| jbe@0 | 672 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); |
| jbe@0 | 673 } |
| jbe@0 | 674 } |
| jbe@0 | 675 if (fd != -1) { |
| jbe@0 | 676 /* attach control message with file descriptor */ |
| jbe@0 | 677 message.msg_control = control_message_buffer; |
| jbe@0 | 678 message.msg_controllen = CMSG_SPACE(sizeof(int)); |
| jbe@0 | 679 { |
| jbe@0 | 680 struct cmsghdr *control_message = CMSG_FIRSTHDR(&message); |
| jbe@0 | 681 control_message->cmsg_level = SOL_SOCKET; |
| jbe@0 | 682 control_message->cmsg_type = SCM_RIGHTS; |
| jbe@0 | 683 control_message->cmsg_len = CMSG_LEN(sizeof(int)); |
| jbe@18 | 684 memcpy(CMSG_DATA(control_message), &fd, sizeof(int)); |
| jbe@0 | 685 } |
| jbe@0 | 686 } |
| jbe@0 | 687 while (sendmsg(worker->controlfd, &message, MSG_NOSIGNAL) < 0) { |
| jbe@0 | 688 if (errno == EPIPE) { |
| jbe@0 | 689 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)); |
| jbe@0 | 690 return; /* do not close socket; socket is closed when reading from it */ |
| jbe@0 | 691 } |
| jbe@0 | 692 if (errno != EINTR) { |
| jbe@0 | 693 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)); |
| jbe@0 | 694 moonbr_terminate_error(); |
| jbe@0 | 695 } |
| jbe@0 | 696 } |
| jbe@0 | 697 } |
| jbe@0 | 698 if (ptr) { |
| jbe@0 | 699 char buf[sizeof(void *)]; |
| jbe@0 | 700 char *pos = buf; |
| jbe@0 | 701 int len = sizeof(void *); |
| jbe@0 | 702 ssize_t written; |
| jbe@0 | 703 if (moonbr_debug) { |
| jbe@0 | 704 moonbr_log(LOG_DEBUG, "Sending memory pointer to child process in pool #%i (PID %i)", (int)status, worker->pool->poolnum, (int)worker->pid); |
| jbe@0 | 705 } |
| jbe@18 | 706 memcpy(buf, &ptr, sizeof(void *)); |
| jbe@0 | 707 while (len) { |
| jbe@0 | 708 written = send(worker->controlfd, pos, len, MSG_NOSIGNAL); |
| jbe@0 | 709 if (written > 0) { |
| jbe@0 | 710 pos += written; |
| jbe@0 | 711 len -= written; |
| jbe@0 | 712 } else if (errno == EPIPE) { |
| jbe@0 | 713 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)); |
| jbe@0 | 714 return; /* do not close socket; socket is closed when reading from it */ |
| jbe@0 | 715 } else if (errno != EINTR) { |
| jbe@0 | 716 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)); |
| jbe@0 | 717 moonbr_terminate_error(); |
| jbe@0 | 718 } |
| jbe@0 | 719 } |
| jbe@0 | 720 } |
| jbe@0 | 721 } |
| jbe@0 | 722 |
| jbe@0 | 723 |
| jbe@0 | 724 /*** Functions running in child process ***/ |
| jbe@0 | 725 |
| jbe@0 | 726 /* Logs an error in child process */ |
| jbe@0 | 727 static void moonbr_child_log(const char *message) { |
| jbe@0 | 728 fprintf(stderr, "%s\n", message); |
| jbe@0 | 729 } |
| jbe@0 | 730 |
| jbe@0 | 731 /* Logs a fatal error in child process and terminates process with error status */ |
| jbe@0 | 732 static void moonbr_child_log_fatal(const char *message) { |
| jbe@0 | 733 moonbr_child_log(message); |
| jbe@0 | 734 exit(1); |
| jbe@0 | 735 } |
| jbe@0 | 736 |
| jbe@0 | 737 /* Logs an error in child process while appending error string for global errno variable */ |
| jbe@0 | 738 static void moonbr_child_log_errno(const char *message) { |
| jbe@0 | 739 char errmsg[MOONBR_MAXSTRERRORLEN]; |
| jbe@20 | 740 strerror_r(errno, errmsg, MOONBR_MAXSTRERRORLEN); /* use thread-safe call in case child created threads */ |
| jbe@0 | 741 fprintf(stderr, "%s: %s\n", message, errmsg); |
| jbe@0 | 742 } |
| jbe@0 | 743 |
| jbe@0 | 744 /* Logs a fatal error in child process while appending error string for errno and terminating process */ |
| jbe@0 | 745 static void moonbr_child_log_errno_fatal(const char *message) { |
| jbe@0 | 746 moonbr_child_log_errno(message); |
| jbe@0 | 747 exit(1); |
| jbe@0 | 748 } |
| jbe@0 | 749 |
| jbe@0 | 750 /* Receives a control message consisting of one character plus an optional file descriptor from parent process */ |
| jbe@0 | 751 static void moonbr_child_receive_control_message(int socketfd, char *status, int *fd) { |
| jbe@0 | 752 struct iovec iovector = { .iov_base = status, .iov_len = 1 }; /* reference to status byte variable */ |
| jbe@0 | 753 char control_message_buffer[CMSG_SPACE(sizeof(int))] = {0, }; /* used to receive file descriptor */ |
| jbe@0 | 754 struct msghdr message = { /* data structure passed to recvmsg() call */ |
| jbe@0 | 755 .msg_iov = &iovector, |
| jbe@0 | 756 .msg_iovlen = 1, |
| jbe@0 | 757 .msg_control = control_message_buffer, |
| jbe@0 | 758 .msg_controllen = CMSG_SPACE(sizeof(int)) |
| jbe@0 | 759 }; |
| jbe@0 | 760 { |
| jbe@0 | 761 int received; |
| jbe@0 | 762 while ((received = recvmsg(socketfd, &message, MSG_CMSG_CLOEXEC)) < 0) { |
| jbe@0 | 763 if (errno != EINTR) { |
| jbe@0 | 764 moonbr_child_log_errno_fatal("Error while trying to receive connection socket from parent process"); |
| jbe@0 | 765 } |
| jbe@0 | 766 } |
| jbe@0 | 767 if (!received) { |
| jbe@0 | 768 moonbr_child_log_fatal("Unexpected EOF while trying to receive connection socket from parent process"); |
| jbe@0 | 769 } |
| jbe@0 | 770 } |
| jbe@0 | 771 { |
| jbe@0 | 772 struct cmsghdr *control_message = CMSG_FIRSTHDR(&message); |
| jbe@0 | 773 if (control_message) { |
| jbe@0 | 774 if (control_message->cmsg_level != SOL_SOCKET) { |
| jbe@0 | 775 moonbr_child_log_fatal("Received control message with cmsg_level not equal to SOL_SOCKET"); |
| jbe@0 | 776 } |
| jbe@0 | 777 if (control_message->cmsg_type != SCM_RIGHTS) { |
| jbe@0 | 778 moonbr_child_log_fatal("Received control message with cmsg_type not equal to SCM_RIGHTS"); |
| jbe@0 | 779 } |
| jbe@18 | 780 memcpy(fd, CMSG_DATA(control_message), sizeof(int)); |
| jbe@0 | 781 } else { |
| jbe@0 | 782 *fd = -1; |
| jbe@0 | 783 } |
| jbe@0 | 784 } |
| jbe@0 | 785 } |
| jbe@0 | 786 |
| jbe@0 | 787 /* Receives a pointer from parent process */ |
| jbe@0 | 788 static void *moonbr_child_receive_pointer(int socketfd) { |
| jbe@0 | 789 char buf[sizeof(void *)]; |
| jbe@0 | 790 char *pos = buf; |
| jbe@0 | 791 int len = sizeof(void *); |
| jbe@0 | 792 ssize_t bytes_read; |
| jbe@0 | 793 while (len) { |
| jbe@0 | 794 bytes_read = recv(socketfd, pos, len, 0); |
| jbe@0 | 795 if (bytes_read > 0) { |
| jbe@0 | 796 pos += bytes_read; |
| jbe@0 | 797 len -= bytes_read; |
| jbe@0 | 798 } else if (!bytes_read) { |
| jbe@0 | 799 moonbr_child_log_fatal("Unexpected EOF while trying to receive memory pointer from parent process"); |
| jbe@0 | 800 } else if (errno != EINTR) { |
| jbe@0 | 801 moonbr_child_log_errno_fatal("Error while trying to receive memory pointer from parent process"); |
| jbe@0 | 802 } |
| jbe@0 | 803 } |
| jbe@18 | 804 { |
| jbe@18 | 805 void *ptr; /* avoid breaking strict-aliasing rules */ |
| jbe@18 | 806 memcpy(&ptr, buf, sizeof(void *)); |
| jbe@18 | 807 return ptr; |
| jbe@18 | 808 } |
| jbe@0 | 809 } |
| jbe@0 | 810 |
| jbe@0 | 811 /* Main function of child process to be called after fork() and file descriptor rearrangement */ |
| jbe@0 | 812 void moonbr_child_run(struct moonbr_pool *pool, lua_State *L) { |
| jbe@0 | 813 char controlmsg; |
| jbe@88 | 814 int fd; |
| jbe@0 | 815 struct itimerval notimer = { { 0, }, { 0, } }; |
| jbe@0 | 816 lua_rawgetp(L, LUA_REGISTRYINDEX, moonbr_luakey_prepare_func(pool)); |
| jbe@0 | 817 if (lua_isnil(L, -1)) lua_pop(L, 1); |
| jbe@0 | 818 else if (lua_pcall(L, 0, 0, 1)) { |
| jbe@0 | 819 fprintf(stderr, "Error in \"prepare\" function: %s\n", lua_tostring(L, -1)); |
| jbe@0 | 820 exit(1); |
| jbe@0 | 821 } |
| jbe@0 | 822 while (1) { |
| jbe@0 | 823 struct moonbr_listener *listener; |
| jbe@0 | 824 if (setitimer(ITIMER_REAL, ¬imer, NULL)) { |
| jbe@0 | 825 moonbr_child_log_errno_fatal("Could not reset ITIMER_REAL via setitimer()"); |
| jbe@0 | 826 } |
| jbe@0 | 827 controlmsg = MOONBR_STATUS_IDLE; |
| jbe@0 | 828 if (write(MOONBR_FD_CONTROL, &controlmsg, 1) <= 0) { |
| jbe@0 | 829 moonbr_child_log_errno_fatal("Error while sending ready message to parent process"); |
| jbe@0 | 830 } |
| jbe@88 | 831 moonbr_child_receive_control_message(MOONBR_FD_CONTROL, &controlmsg, &fd); |
| jbe@0 | 832 if (!( |
| jbe@124 | 833 (controlmsg == MOONBR_COMMAND_TERMINATE && fd == -1) || |
| jbe@124 | 834 (controlmsg == MOONBR_COMMAND_CONNECT) |
| jbe@0 | 835 )) { |
| jbe@0 | 836 moonbr_child_log_fatal("Received illegal control message from parent process"); |
| jbe@0 | 837 } |
| jbe@0 | 838 if (controlmsg == MOONBR_COMMAND_TERMINATE) break; |
| jbe@0 | 839 listener = moonbr_child_receive_pointer(MOONBR_FD_CONTROL); |
| jbe@124 | 840 if (listener->proto == MOONBR_PROTO_INTERVAL && fd >= 0) { |
| jbe@124 | 841 moonbr_child_log_fatal("Received unexpected file descriptor from parent process"); |
| jbe@124 | 842 } else if (listener->proto != MOONBR_PROTO_INTERVAL && fd < 0) { |
| jbe@124 | 843 moonbr_child_log_fatal("Missing file descriptor from parent process"); |
| jbe@124 | 844 } |
| jbe@119 | 845 if (fd >= 0) moonbr_io_pushhandle(L, fd); |
| jbe@0 | 846 lua_rawgetp(L, LUA_REGISTRYINDEX, moonbr_luakey_connect_func(pool)); |
| jbe@119 | 847 if (fd < 0) { |
| jbe@0 | 848 lua_newtable(L); |
| jbe@0 | 849 lua_pushstring(L, |
| jbe@0 | 850 listener->proto_specific.interval.name ? |
| jbe@0 | 851 listener->proto_specific.interval.name : "" |
| jbe@0 | 852 ); |
| jbe@0 | 853 lua_setfield(L, -2, "interval"); |
| jbe@0 | 854 } else { |
| jbe@88 | 855 lua_pushvalue(L, -2); |
| jbe@0 | 856 } |
| jbe@0 | 857 if (lua_pcall(L, 1, 1, 1)) { |
| jbe@0 | 858 fprintf(stderr, "Error in \"connect\" function: %s\n", lua_tostring(L, -1)); |
| jbe@0 | 859 exit(1); |
| jbe@0 | 860 } |
| jbe@119 | 861 if (fd >= 0) moonbr_io_closehandle(L, -2, 0); /* attemt clean close */ |
| jbe@0 | 862 if (lua_type(L, -1) != LUA_TBOOLEAN || !lua_toboolean(L, -1)) break; |
| jbe@0 | 863 #ifdef MOONBR_LUA_PANIC_BUG_WORKAROUND |
| jbe@0 | 864 lua_settop(L, 2); |
| jbe@0 | 865 #else |
| jbe@0 | 866 lua_settop(L, 1); |
| jbe@0 | 867 #endif |
| jbe@0 | 868 } |
| jbe@0 | 869 controlmsg = MOONBR_STATUS_GOODBYE; |
| jbe@0 | 870 if (write(MOONBR_FD_CONTROL, &controlmsg, 1) <= 0) { |
| jbe@0 | 871 moonbr_child_log_errno_fatal("Error while sending goodbye message to parent process"); |
| jbe@0 | 872 } |
| jbe@0 | 873 if (close(MOONBR_FD_CONTROL) && errno != EINTR) { |
| jbe@0 | 874 moonbr_child_log_errno("Error while closing control socket"); |
| jbe@0 | 875 } |
| jbe@0 | 876 lua_rawgetp(L, LUA_REGISTRYINDEX, moonbr_luakey_finish_func(pool)); |
| jbe@0 | 877 if (lua_isnil(L, -1)) lua_pop(L, 1); |
| jbe@0 | 878 else if (lua_pcall(L, 0, 0, 1)) { |
| jbe@0 | 879 fprintf(stderr, "Error in \"finish\" function: %s\n", lua_tostring(L, -1)); |
| jbe@0 | 880 exit(1); |
| jbe@0 | 881 } |
| jbe@0 | 882 lua_close(L); |
| jbe@0 | 883 exit(0); |
| jbe@0 | 884 } |
| jbe@0 | 885 |
| jbe@0 | 886 |
| jbe@0 | 887 /*** Functions to spawn child process ***/ |
| jbe@0 | 888 |
| jbe@0 | 889 /* Helper function to send an error message to a file descriptor (not needing a file stream) */ |
| jbe@0 | 890 static void moonbr_child_emergency_print(int fd, char *message) { |
| jbe@0 | 891 size_t len = strlen(message); |
| jbe@0 | 892 ssize_t written; |
| jbe@0 | 893 while (len) { |
| jbe@0 | 894 written = write(fd, message, len); |
| jbe@0 | 895 if (written > 0) { |
| jbe@0 | 896 message += written; |
| jbe@0 | 897 len -= written; |
| jbe@0 | 898 } else { |
| jbe@0 | 899 if (written != -1 || errno != EINTR) break; |
| jbe@0 | 900 } |
| jbe@0 | 901 } |
| jbe@0 | 902 } |
| jbe@0 | 903 |
| jbe@0 | 904 /* Helper function to send an error message plus a text for errno to a file descriptor and terminate the process */ |
| jbe@0 | 905 static void moonbr_child_emergency_error(int fd, char *message) { |
| jbe@0 | 906 int errno2 = errno; |
| jbe@0 | 907 moonbr_child_emergency_print(fd, message); |
| jbe@0 | 908 moonbr_child_emergency_print(fd, ": "); |
| jbe@0 | 909 moonbr_child_emergency_print(fd, strerror(errno2)); |
| jbe@0 | 910 moonbr_child_emergency_print(fd, "\n"); |
| jbe@0 | 911 exit(1); |
| jbe@0 | 912 } |
| jbe@0 | 913 |
| jbe@0 | 914 /* Creates a child process and (in case of success) registers it in the 'struct moonbr_pool' structure */ |
| jbe@0 | 915 static int moonbr_create_worker(struct moonbr_pool *pool, lua_State *L) { |
| jbe@0 | 916 struct moonbr_worker *worker; |
| jbe@0 | 917 worker = calloc(1, sizeof(struct moonbr_worker)); |
| jbe@0 | 918 if (!worker) { |
| jbe@0 | 919 moonbr_log(LOG_CRIT, "Memory allocation error"); |
| jbe@0 | 920 return -1; |
| jbe@0 | 921 } |
| jbe@0 | 922 worker->pool = pool; |
| jbe@0 | 923 { |
| jbe@0 | 924 int controlfds[2]; |
| jbe@0 | 925 int errorfds[2]; |
| jbe@0 | 926 if (socketpair(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, controlfds)) { |
| jbe@0 | 927 moonbr_log(LOG_ERR, "Could not create control socket pair for communcation with child process: %s", strerror(errno)); |
| jbe@0 | 928 free(worker); |
| jbe@0 | 929 return -1; |
| jbe@0 | 930 } |
| jbe@0 | 931 if (socketpair(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, errorfds)) { |
| jbe@0 | 932 moonbr_log(LOG_ERR, "Could not create socket pair to redirect stderr of child process: %s", strerror(errno)); |
| jbe@0 | 933 close(controlfds[0]); |
| jbe@0 | 934 close(controlfds[1]); |
| jbe@0 | 935 free(worker); |
| jbe@0 | 936 return -1; |
| jbe@0 | 937 } |
| jbe@0 | 938 if (moonbr_logfile && fflush(moonbr_logfile)) { |
| jbe@0 | 939 moonbr_log(LOG_CRIT, "Could not flush log file prior to forking: %s", strerror(errno)); |
| jbe@0 | 940 moonbr_terminate_error(); |
| jbe@0 | 941 } |
| jbe@0 | 942 worker->pid = fork(); |
| jbe@0 | 943 if (worker->pid == -1) { |
| jbe@0 | 944 moonbr_log(LOG_ERR, "Could not fork: %s", strerror(errno)); |
| jbe@0 | 945 close(controlfds[0]); |
| jbe@0 | 946 close(controlfds[1]); |
| jbe@0 | 947 close(errorfds[0]); |
| jbe@0 | 948 close(errorfds[1]); |
| jbe@0 | 949 free(worker); |
| jbe@0 | 950 return -1; |
| jbe@0 | 951 } else if (!worker->pid) { |
| jbe@0 | 952 moonbr_pstate = MOONBR_PSTATE_FORKED; |
| jbe@0 | 953 #ifdef MOONBR_LUA_PANIC_BUG_WORKAROUND |
| jbe@0 | 954 lua_pushliteral(L, "Failed to pass error message due to bug in Lua panic handler (hint: not enough memory?)"); |
| jbe@0 | 955 #endif |
| jbe@0 | 956 moonbr_memory_limit = pool->memory_limit; |
| jbe@0 | 957 if (moonbr_pidfh && pidfile_close(moonbr_pidfh)) { |
| jbe@0 | 958 moonbr_child_emergency_error(errorfds[1], "Could not close PID file in forked child process"); |
| jbe@0 | 959 } |
| jbe@0 | 960 if (moonbr_logfile && moonbr_logfile != stderr && fclose(moonbr_logfile)) { |
| jbe@0 | 961 moonbr_child_emergency_error(errorfds[1], "Could not close log file in forked child process"); |
| jbe@0 | 962 } |
| jbe@0 | 963 if (dup2(errorfds[1], MOONBR_FD_STDERR) == -1) { |
| jbe@0 | 964 moonbr_child_emergency_error(errorfds[1], "Could not duplicate socket to stderr file descriptor"); |
| jbe@0 | 965 } |
| jbe@0 | 966 if (dup2(controlfds[1], MOONBR_FD_CONTROL) == -1) { |
| jbe@0 | 967 moonbr_child_emergency_error(errorfds[1], "Could not duplicate control socket"); |
| jbe@0 | 968 } |
| jbe@0 | 969 closefrom(MOONBR_FD_END); |
| jbe@0 | 970 moonbr_child_run(pool, L); |
| jbe@0 | 971 } |
| jbe@0 | 972 if (moonbr_stat) { |
| jbe@0 | 973 moonbr_log(LOG_INFO, "Created new worker in pool #%i with PID %i", worker->pool->poolnum, (int)worker->pid); |
| jbe@0 | 974 } |
| jbe@0 | 975 worker->controlfd = controlfds[0]; |
| jbe@0 | 976 worker->errorfd = errorfds[0]; |
| jbe@0 | 977 if (close(controlfds[1]) && errno != EINTR) { |
| jbe@0 | 978 moonbr_log(LOG_CRIT, "Could not close opposite end of control file descriptor after forking"); |
| jbe@0 | 979 moonbr_terminate_error(); |
| jbe@0 | 980 } |
| jbe@0 | 981 if (close(errorfds[1]) && errno != EINTR) { |
| jbe@0 | 982 moonbr_log(LOG_CRIT, "Could not close opposite end of control file descriptor after forking"); |
| jbe@0 | 983 moonbr_terminate_error(); |
| jbe@0 | 984 } |
| jbe@0 | 985 } |
| jbe@0 | 986 worker->prev_worker = pool->last_worker; |
| jbe@0 | 987 if (worker->prev_worker) worker->prev_worker->next_worker = worker; |
| jbe@0 | 988 else pool->first_worker = worker; |
| jbe@0 | 989 pool->last_worker = worker; |
| jbe@0 | 990 pool->unassigned_worker_count++; |
| jbe@0 | 991 pool->total_worker_count++; |
| jbe@0 | 992 pool->worker_count_stat = 1; |
| jbe@0 | 993 moonbr_poll_refresh_needed = 1; |
| jbe@0 | 994 return 0; /* return zero only in case of success */ |
| jbe@0 | 995 } |
| jbe@0 | 996 |
| jbe@0 | 997 |
| jbe@0 | 998 /*** Functions to handle previously created 'struct moonbr_worker' structures ***/ |
| jbe@0 | 999 |
| jbe@0 | 1000 #define moonbr_try_destroy_worker_stat(str, field) \ |
| jbe@0 | 1001 moonbr_log(LOG_INFO, "Resource usage in pool #%i for PID %i: " str " %li", worker->pool->poolnum, (int)worker->pid, (long)childusage.field); |
| jbe@0 | 1002 |
| jbe@0 | 1003 /* Destroys a worker structure if socket connections have been closed and child process has terminated */ |
| jbe@0 | 1004 static int moonbr_try_destroy_worker(struct moonbr_worker *worker) { |
| jbe@0 | 1005 if (worker->controlfd != -1 || worker->errorfd != -1) return MOONBR_DESTROY_NONE; |
| jbe@0 | 1006 { |
| jbe@0 | 1007 int childstatus; |
| jbe@0 | 1008 struct rusage childusage; |
| jbe@0 | 1009 { |
| jbe@0 | 1010 pid_t waitedpid; |
| jbe@0 | 1011 while ( |
| jbe@0 | 1012 (waitedpid = wait4(worker->pid, &childstatus, WNOHANG, &childusage)) == -1 |
| jbe@0 | 1013 ) { |
| jbe@0 | 1014 if (errno != EINTR) { |
| jbe@0 | 1015 moonbr_log(LOG_CRIT, "Error in wait4() call: %s", strerror(errno)); |
| jbe@0 | 1016 moonbr_terminate_error(); |
| jbe@0 | 1017 } |
| jbe@0 | 1018 } |
| jbe@0 | 1019 if (!waitedpid) return 0; /* return 0 if worker couldn't be destroyed */ |
| jbe@0 | 1020 if (waitedpid != worker->pid) { |
| jbe@0 | 1021 moonbr_log(LOG_CRIT, "Wrong PID returned by wait4() call"); |
| jbe@0 | 1022 moonbr_terminate_error(); |
| jbe@0 | 1023 } |
| jbe@0 | 1024 } |
| jbe@0 | 1025 if (WIFEXITED(childstatus)) { |
| jbe@0 | 1026 if (WEXITSTATUS(childstatus) || moonbr_stat) { |
| jbe@0 | 1027 moonbr_log( |
| jbe@0 | 1028 WEXITSTATUS(childstatus) ? LOG_WARNING : LOG_INFO, |
| jbe@0 | 1029 "Child process in pool #%i with PID %i returned with exit code %i", worker->pool->poolnum, (int)worker->pid, WEXITSTATUS(childstatus) |
| jbe@0 | 1030 ); |
| jbe@0 | 1031 } |
| jbe@0 | 1032 } else if (WIFSIGNALED(childstatus)) { |
| jbe@0 | 1033 if (WCOREDUMP(childstatus)) { |
| jbe@0 | 1034 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)); |
| jbe@0 | 1035 } else if (WTERMSIG(childstatus) == SIGALRM) { |
| jbe@0 | 1036 moonbr_log(LOG_WARNING, "Child process in pool #%i with PID %i exited prematurely due to timeout", worker->pool->poolnum, (int)worker->pid); |
| jbe@0 | 1037 } else { |
| jbe@0 | 1038 moonbr_log(LOG_ERR, "Child process in pool #%i with PID %i died from signal %i", worker->pool->poolnum, (int)worker->pid, WTERMSIG(childstatus)); |
| jbe@0 | 1039 } |
| jbe@0 | 1040 } else { |
| jbe@0 | 1041 moonbr_log(LOG_CRIT, "Illegal exit status from child process in pool #%i with PID %i", worker->pool->poolnum, (int)worker->pid); |
| jbe@0 | 1042 moonbr_terminate_error(); |
| jbe@0 | 1043 } |
| jbe@0 | 1044 if (moonbr_stat) { |
| jbe@0 | 1045 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)); |
| jbe@0 | 1046 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)); |
| jbe@0 | 1047 moonbr_try_destroy_worker_stat("max resident set size", ru_maxrss); |
| jbe@0 | 1048 moonbr_try_destroy_worker_stat("integral shared memory size", ru_ixrss); |
| jbe@0 | 1049 moonbr_try_destroy_worker_stat("integral unshared data", ru_idrss); |
| jbe@0 | 1050 moonbr_try_destroy_worker_stat("integral unshared stack", ru_isrss); |
| jbe@0 | 1051 moonbr_try_destroy_worker_stat("page replaims", ru_minflt); |
| jbe@0 | 1052 moonbr_try_destroy_worker_stat("page faults", ru_majflt); |
| jbe@0 | 1053 moonbr_try_destroy_worker_stat("swaps", ru_nswap); |
| jbe@0 | 1054 moonbr_try_destroy_worker_stat("block input operations", ru_inblock); |
| jbe@0 | 1055 moonbr_try_destroy_worker_stat("block output operations", ru_oublock); |
| jbe@0 | 1056 moonbr_try_destroy_worker_stat("messages sent", ru_msgsnd); |
| jbe@0 | 1057 moonbr_try_destroy_worker_stat("messages received", ru_msgrcv); |
| jbe@0 | 1058 moonbr_try_destroy_worker_stat("signals received", ru_nsignals); |
| jbe@0 | 1059 moonbr_try_destroy_worker_stat("voluntary context switches", ru_nvcsw); |
| jbe@0 | 1060 moonbr_try_destroy_worker_stat("involuntary context switches", ru_nivcsw); |
| jbe@0 | 1061 } |
| jbe@0 | 1062 } |
| jbe@0 | 1063 { |
| jbe@0 | 1064 int retval = ( |
| jbe@0 | 1065 (worker->idle || worker->assigned) ? |
| jbe@0 | 1066 MOONBR_DESTROY_IDLE_OR_ASSIGNED : |
| jbe@0 | 1067 MOONBR_DESTROY_PREPARE |
| jbe@0 | 1068 ); |
| jbe@0 | 1069 if (worker->prev_worker) worker->prev_worker->next_worker = worker->next_worker; |
| jbe@0 | 1070 else worker->pool->first_worker = worker->next_worker; |
| jbe@0 | 1071 if (worker->next_worker) worker->next_worker->prev_worker = worker->prev_worker; |
| jbe@0 | 1072 else worker->pool->last_worker = worker->prev_worker; |
| jbe@0 | 1073 if (worker->idle) { |
| jbe@0 | 1074 if (worker->prev_idle_worker) worker->prev_idle_worker->next_idle_worker = worker->next_idle_worker; |
| jbe@0 | 1075 else worker->pool->first_idle_worker = worker->next_idle_worker; |
| jbe@0 | 1076 if (worker->next_idle_worker) worker->next_idle_worker->prev_idle_worker = worker->prev_idle_worker; |
| jbe@0 | 1077 else worker->pool->last_idle_worker = worker->prev_idle_worker; |
| jbe@0 | 1078 worker->pool->idle_worker_count--; |
| jbe@0 | 1079 } |
| jbe@0 | 1080 if (!worker->assigned) worker->pool->unassigned_worker_count--; |
| jbe@0 | 1081 worker->pool->total_worker_count--; |
| jbe@0 | 1082 worker->pool->worker_count_stat = 1; |
| jbe@0 | 1083 if (worker->errorlinebuf) free(worker->errorlinebuf); |
| jbe@0 | 1084 free(worker); |
| jbe@0 | 1085 return retval; |
| jbe@0 | 1086 } |
| jbe@0 | 1087 } |
| jbe@0 | 1088 |
| jbe@0 | 1089 /* Marks a worker as idle and stores it in a queue, optionally setting 'idle_expiration' value */ |
| jbe@0 | 1090 static void moonbr_add_idle_worker(struct moonbr_worker *worker) { |
| jbe@0 | 1091 worker->prev_idle_worker = worker->pool->last_idle_worker; |
| jbe@0 | 1092 if (worker->prev_idle_worker) worker->prev_idle_worker->next_idle_worker = worker; |
| jbe@0 | 1093 else worker->pool->first_idle_worker = worker; |
| jbe@0 | 1094 worker->pool->last_idle_worker = worker; |
| jbe@0 | 1095 worker->idle = 1; |
| jbe@0 | 1096 worker->pool->idle_worker_count++; |
| jbe@0 | 1097 if (worker->assigned) { |
| jbe@0 | 1098 worker->assigned = 0; |
| jbe@0 | 1099 worker->pool->unassigned_worker_count++; |
| jbe@0 | 1100 } |
| jbe@0 | 1101 worker->pool->worker_count_stat = 1; |
| jbe@0 | 1102 if (timerisset(&worker->pool->idle_timeout)) { |
| jbe@0 | 1103 struct timeval now; |
| jbe@0 | 1104 moonbr_now(&now); |
| jbe@0 | 1105 timeradd(&now, &worker->pool->idle_timeout, &worker->idle_expiration); |
| jbe@0 | 1106 } |
| jbe@0 | 1107 } |
| jbe@0 | 1108 |
| jbe@0 | 1109 /* Pops a worker from the queue of idle workers (idle queue must not be empty) */ |
| jbe@0 | 1110 static struct moonbr_worker *moonbr_pop_idle_worker(struct moonbr_pool *pool) { |
| jbe@0 | 1111 struct moonbr_worker *worker; |
| jbe@0 | 1112 worker = pool->first_idle_worker; |
| jbe@0 | 1113 pool->first_idle_worker = worker->next_idle_worker; |
| jbe@0 | 1114 if (pool->first_idle_worker) pool->first_idle_worker->prev_idle_worker = NULL; |
| jbe@0 | 1115 else pool->last_idle_worker = NULL; |
| jbe@0 | 1116 worker->next_idle_worker = NULL; |
| jbe@0 | 1117 worker->idle = 0; |
| jbe@0 | 1118 worker->pool->idle_worker_count--; |
| jbe@0 | 1119 worker->assigned = 1; |
| jbe@0 | 1120 worker->pool->unassigned_worker_count--; |
| jbe@0 | 1121 worker->pool->worker_count_stat = 1; |
| jbe@0 | 1122 return worker; |
| jbe@0 | 1123 } |
| jbe@0 | 1124 |
| jbe@0 | 1125 |
| jbe@0 | 1126 /*** Functions for queues of 'struct moonbr_listener' ***/ |
| jbe@0 | 1127 |
| jbe@0 | 1128 /* Appends a 'struct moonbr_listener' to the queue of idle listeners and registers it for poll() */ |
| jbe@0 | 1129 static void moonbr_add_idle_listener(struct moonbr_listener *listener) { |
| jbe@0 | 1130 listener->prev_listener = listener->pool->last_idle_listener; |
| jbe@0 | 1131 if (listener->prev_listener) listener->prev_listener->next_listener = listener; |
| jbe@0 | 1132 else listener->pool->first_idle_listener = listener; |
| jbe@0 | 1133 listener->pool->last_idle_listener = listener; |
| jbe@0 | 1134 if (listener->pollidx != -1) moonbr_poll_fds[listener->pollidx].events |= POLLIN; |
| jbe@0 | 1135 } |
| jbe@0 | 1136 |
| jbe@0 | 1137 /* Removes a 'struct moonbr_listener' from the queue of idle listeners and unregisters it from poll() */ |
| jbe@0 | 1138 static void moonbr_remove_idle_listener(struct moonbr_listener *listener) { |
| jbe@0 | 1139 if (listener->prev_listener) listener->prev_listener->next_listener = listener->next_listener; |
| jbe@0 | 1140 else listener->pool->first_idle_listener = listener->next_listener; |
| jbe@0 | 1141 if (listener->next_listener) listener->next_listener->prev_listener = listener->prev_listener; |
| jbe@0 | 1142 else listener->pool->last_idle_listener = listener->prev_listener; |
| jbe@0 | 1143 listener->prev_listener = NULL; |
| jbe@0 | 1144 listener->next_listener = NULL; |
| jbe@0 | 1145 if (listener->pollidx != -1) moonbr_poll_fds[listener->pollidx].events &= ~POLLIN; |
| jbe@0 | 1146 } |
| jbe@0 | 1147 |
| jbe@0 | 1148 /* Adds a listener to the queue of connected listeners (i.e. waiting to have their incoming connection accepted) */ |
| jbe@0 | 1149 static void moonbr_add_connected_listener(struct moonbr_listener *listener) { |
| jbe@0 | 1150 listener->prev_listener = listener->pool->last_connected_listener; |
| jbe@0 | 1151 if (listener->prev_listener) listener->prev_listener->next_listener = listener; |
| jbe@0 | 1152 else listener->pool->first_connected_listener = listener; |
| jbe@0 | 1153 listener->pool->last_connected_listener = listener; |
| jbe@0 | 1154 } |
| jbe@0 | 1155 |
| jbe@0 | 1156 /* Removes and returns the first connected listener in the queue */ |
| jbe@0 | 1157 static struct moonbr_listener *moonbr_pop_connected_listener(struct moonbr_pool *pool) { |
| jbe@0 | 1158 struct moonbr_listener *listener = pool->first_connected_listener; |
| jbe@0 | 1159 listener->pool->first_connected_listener = listener->next_listener; |
| jbe@0 | 1160 if (listener->pool->first_connected_listener) listener->pool->first_connected_listener->prev_listener = NULL; |
| jbe@0 | 1161 else listener->pool->last_connected_listener = NULL; |
| jbe@0 | 1162 listener->next_listener = NULL; |
| jbe@0 | 1163 return listener; |
| jbe@0 | 1164 } |
| jbe@0 | 1165 |
| jbe@0 | 1166 |
| jbe@0 | 1167 /*** Functions to handle polling ***/ |
| jbe@0 | 1168 |
| jbe@0 | 1169 /* Returns an index to a new initialized entry in moonbr_poll_fds[] */ |
| jbe@0 | 1170 int moonbr_poll_fds_nextindex() { |
| jbe@0 | 1171 if (moonbr_poll_fds_count >= moonbr_poll_fds_bufsize) { |
| jbe@0 | 1172 if (moonbr_poll_fds_bufsize) moonbr_poll_fds_bufsize *= 2; |
| jbe@0 | 1173 else moonbr_poll_fds_bufsize = 1; |
| jbe@0 | 1174 moonbr_poll_fds = realloc( |
| jbe@0 | 1175 moonbr_poll_fds, moonbr_poll_fds_bufsize * sizeof(struct pollfd) |
| jbe@0 | 1176 ); |
| jbe@0 | 1177 if (!moonbr_poll_fds) { |
| jbe@0 | 1178 moonbr_log(LOG_CRIT, "Memory allocation error"); |
| jbe@0 | 1179 moonbr_terminate_error(); |
| jbe@0 | 1180 } |
| jbe@0 | 1181 } |
| jbe@0 | 1182 moonbr_poll_fds[moonbr_poll_fds_count] = (struct pollfd){0, }; |
| jbe@0 | 1183 return moonbr_poll_fds_count++; |
| jbe@0 | 1184 } |
| jbe@0 | 1185 |
| jbe@0 | 1186 /* Returns an index to a new initialized entry in moonbr_poll_workers[] */ |
| jbe@0 | 1187 int moonbr_poll_workers_nextindex() { |
| jbe@0 | 1188 if (moonbr_poll_worker_count >= moonbr_poll_workers_bufsize) { |
| jbe@0 | 1189 if (moonbr_poll_workers_bufsize) moonbr_poll_workers_bufsize *= 2; |
| jbe@0 | 1190 else moonbr_poll_workers_bufsize = 1; |
| jbe@0 | 1191 moonbr_poll_workers = realloc( |
| jbe@0 | 1192 moonbr_poll_workers, moonbr_poll_workers_bufsize * sizeof(struct moonbr_poll_worker) |
| jbe@0 | 1193 ); |
| jbe@0 | 1194 if (!moonbr_poll_workers) { |
| jbe@0 | 1195 moonbr_log(LOG_CRIT, "Memory allocation error"); |
| jbe@0 | 1196 moonbr_terminate_error(); |
| jbe@0 | 1197 } |
| jbe@0 | 1198 } |
| jbe@0 | 1199 moonbr_poll_workers[moonbr_poll_worker_count] = (struct moonbr_poll_worker){0, }; |
| jbe@0 | 1200 return moonbr_poll_worker_count++; |
| jbe@0 | 1201 } |
| jbe@0 | 1202 |
| jbe@0 | 1203 /* Queues all listeners as idle, and initializes static part of moonbr_poll_fds[], which is passed to poll() */ |
| jbe@0 | 1204 static void moonbr_poll_init() { |
| jbe@0 | 1205 if (socketpair( |
| jbe@0 | 1206 PF_LOCAL, |
| jbe@0 | 1207 SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, |
| jbe@0 | 1208 0, |
| jbe@0 | 1209 moonbr_poll_signalfds |
| jbe@0 | 1210 )) { |
| jbe@0 | 1211 moonbr_log(LOG_CRIT, "Could not create socket pair for signal delivery during polling: %s", strerror(errno)); |
| jbe@0 | 1212 moonbr_terminate_error(); |
| jbe@0 | 1213 } |
| jbe@0 | 1214 { |
| jbe@0 | 1215 int j = moonbr_poll_fds_nextindex(); |
| jbe@0 | 1216 struct pollfd *pollfd = &moonbr_poll_fds[j]; |
| jbe@0 | 1217 pollfd->fd = moonbr_poll_signalfd_read; |
| jbe@0 | 1218 pollfd->events = POLLIN; |
| jbe@0 | 1219 } |
| jbe@0 | 1220 { |
| jbe@0 | 1221 struct moonbr_pool *pool; |
| jbe@0 | 1222 for (pool=moonbr_first_pool; pool; pool=pool->next_pool) { |
| jbe@0 | 1223 int i; |
| jbe@0 | 1224 for (i=0; i<pool->listener_count; i++) { |
| jbe@0 | 1225 struct moonbr_listener *listener = &pool->listener[i]; |
| jbe@0 | 1226 if (listener->listenfd != -1) { |
| jbe@0 | 1227 int j = moonbr_poll_fds_nextindex(); |
| jbe@0 | 1228 listener->pollidx = j; |
| jbe@0 | 1229 moonbr_poll_fds[j].fd = listener->listenfd; |
| jbe@0 | 1230 } |
| jbe@0 | 1231 moonbr_add_idle_listener(listener); |
| jbe@0 | 1232 } |
| jbe@0 | 1233 } |
| jbe@0 | 1234 } |
| jbe@0 | 1235 moonbr_poll_fds_static_count = moonbr_poll_fds_count; /* remember size of static part of array */ |
| jbe@0 | 1236 } |
| jbe@0 | 1237 |
| jbe@0 | 1238 /* Disables polling of all listeners (required for clean shutdown) */ |
| jbe@0 | 1239 static void moonbr_poll_shutdown() { |
| jbe@0 | 1240 int i; |
| jbe@0 | 1241 for (i=1; i<moonbr_poll_fds_static_count; i++) { |
| jbe@0 | 1242 moonbr_poll_fds[i].fd = -1; |
| jbe@0 | 1243 } |
| jbe@0 | 1244 } |
| jbe@0 | 1245 |
| jbe@0 | 1246 /* (Re)builds dynamic part of moonbr_poll_fds[] array, and (re)builds moonbr_poll_workers[] array */ |
| jbe@0 | 1247 static void moonbr_poll_refresh() { |
| jbe@0 | 1248 moonbr_poll_refresh_needed = 0; |
| jbe@0 | 1249 moonbr_poll_fds_count = moonbr_poll_fds_static_count; |
| jbe@0 | 1250 moonbr_poll_worker_count = 0; |
| jbe@0 | 1251 { |
| jbe@0 | 1252 struct moonbr_pool *pool; |
| jbe@0 | 1253 for (pool=moonbr_first_pool; pool; pool=pool->next_pool) { |
| jbe@0 | 1254 struct moonbr_worker *worker; |
| jbe@0 | 1255 for (worker=pool->first_worker; worker; worker=worker->next_worker) { |
| jbe@0 | 1256 if (worker->controlfd != -1) { |
| jbe@0 | 1257 int j = moonbr_poll_fds_nextindex(); |
| jbe@0 | 1258 int k = moonbr_poll_workers_nextindex(); |
| jbe@0 | 1259 struct pollfd *pollfd = &moonbr_poll_fds[j]; |
| jbe@0 | 1260 struct moonbr_poll_worker *poll_worker = &moonbr_poll_workers[k]; |
| jbe@0 | 1261 pollfd->fd = worker->controlfd; |
| jbe@0 | 1262 pollfd->events = POLLIN; |
| jbe@0 | 1263 poll_worker->channel = MOONBR_POLL_WORKER_CONTROLCHANNEL; |
| jbe@0 | 1264 poll_worker->worker = worker; |
| jbe@0 | 1265 } |
| jbe@0 | 1266 if (worker->errorfd != -1) { |
| jbe@0 | 1267 int j = moonbr_poll_fds_nextindex(); |
| jbe@0 | 1268 int k = moonbr_poll_workers_nextindex(); |
| jbe@0 | 1269 struct pollfd *pollfd = &moonbr_poll_fds[j]; |
| jbe@0 | 1270 struct moonbr_poll_worker *poll_worker = &moonbr_poll_workers[k]; |
| jbe@0 | 1271 pollfd->fd = worker->errorfd; |
| jbe@0 | 1272 pollfd->events = POLLIN; |
| jbe@0 | 1273 poll_worker->channel = MOONBR_POLL_WORKER_ERRORCHANNEL; |
| jbe@0 | 1274 poll_worker->worker = worker; |
| jbe@0 | 1275 } |
| jbe@0 | 1276 } |
| jbe@0 | 1277 } |
| jbe@0 | 1278 } |
| jbe@0 | 1279 } |
| jbe@0 | 1280 |
| jbe@0 | 1281 /* resets socket and 'revents' field of moonbr_poll_fds[] for signal delivery just before poll() is called */ |
| jbe@0 | 1282 static void moonbr_poll_reset_signal() { |
| jbe@0 | 1283 ssize_t readcount; |
| jbe@0 | 1284 char buf[1]; |
| jbe@0 | 1285 moonbr_poll_fds[0].revents = 0; |
| jbe@0 | 1286 while ((readcount = read(moonbr_poll_signalfd_read, buf, 1)) < 0) { |
| jbe@0 | 1287 if (errno == EAGAIN) break; |
| jbe@0 | 1288 if (errno != EINTR) { |
| jbe@0 | 1289 moonbr_log(LOG_CRIT, "Error while reading from signal delivery socket: %s", strerror(errno)); |
| jbe@0 | 1290 moonbr_terminate_error(); |
| jbe@0 | 1291 } |
| jbe@0 | 1292 } |
| jbe@0 | 1293 if (!readcount) { |
| jbe@0 | 1294 moonbr_log(LOG_CRIT, "Unexpected EOF when reading from signal delivery socket: %s", strerror(errno)); |
| jbe@0 | 1295 moonbr_terminate_error(); |
| jbe@0 | 1296 } |
| jbe@0 | 1297 } |
| jbe@0 | 1298 |
| jbe@0 | 1299 |
| jbe@0 | 1300 /*** Shutdown initiation ***/ |
| jbe@0 | 1301 |
| jbe@0 | 1302 /* Sets global variable 'moonbr_shutdown_in_progress', closes listeners, and demands worker termination */ |
| jbe@0 | 1303 static void moonbr_initiate_shutdown() { |
| jbe@0 | 1304 struct moonbr_pool *pool; |
| jbe@0 | 1305 int i; |
| jbe@0 | 1306 if (moonbr_shutdown_in_progress) { |
| jbe@0 | 1307 moonbr_log(LOG_NOTICE, "Shutdown already in progress"); |
| jbe@0 | 1308 return; |
| jbe@0 | 1309 } |
| jbe@0 | 1310 moonbr_shutdown_in_progress = 1; |
| jbe@0 | 1311 moonbr_log(LOG_NOTICE, "Initiate shutdown"); |
| jbe@0 | 1312 for (pool = moonbr_first_pool; pool; pool = pool->next_pool) { |
| jbe@0 | 1313 for (i=0; i<pool->listener_count; i++) { |
| jbe@0 | 1314 struct moonbr_listener *listener = &pool->listener[i]; |
| jbe@0 | 1315 if (listener->listenfd != -1) { |
| jbe@0 | 1316 if (close(listener->listenfd) && errno != EINTR) { |
| jbe@0 | 1317 moonbr_log(LOG_CRIT, "Could not close listening socket: %s", strerror(errno)); |
| jbe@0 | 1318 moonbr_terminate_error(); |
| jbe@0 | 1319 } |
| jbe@0 | 1320 } |
| jbe@0 | 1321 } |
| jbe@0 | 1322 pool->pre_fork = 0; |
| jbe@0 | 1323 pool->min_fork = 0; |
| jbe@0 | 1324 pool->max_fork = 0; |
| jbe@0 | 1325 timerclear(&pool->exit_delay); |
| jbe@0 | 1326 } |
| jbe@0 | 1327 moonbr_poll_shutdown(); /* avoids loops due to error condition when polling closed listeners */ |
| jbe@0 | 1328 } |
| jbe@0 | 1329 |
| jbe@0 | 1330 |
| jbe@0 | 1331 /*** Functions to communicate with child processes ***/ |
| jbe@0 | 1332 |
| jbe@0 | 1333 /* Tells child process to terminate */ |
| jbe@0 | 1334 static void moonbr_terminate_idle_worker(struct moonbr_worker *worker) { |
| jbe@0 | 1335 moonbr_send_control_message(worker, MOONBR_COMMAND_TERMINATE, -1, NULL); |
| jbe@0 | 1336 } |
| jbe@0 | 1337 |
| jbe@0 | 1338 /* Handles status messages from child process */ |
| jbe@0 | 1339 static void moonbr_read_controlchannel(struct moonbr_worker *worker) { |
| jbe@0 | 1340 char controlmsg; |
| jbe@0 | 1341 { |
| jbe@0 | 1342 ssize_t bytes_read; |
| jbe@0 | 1343 while ((bytes_read = read(worker->controlfd, &controlmsg, 1)) <= 0) { |
| jbe@0 | 1344 if (bytes_read == 0 || errno == ECONNRESET) { |
| jbe@0 | 1345 moonbr_log(LOG_WARNING, "Child process in pool #%i with PID %i unexpectedly closed control socket", worker->pool->poolnum, (int)worker->pid); |
| jbe@0 | 1346 if (close(worker->controlfd) && errno != EINTR) { |
| jbe@0 | 1347 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)); |
| jbe@0 | 1348 moonbr_terminate_error(); |
| jbe@0 | 1349 } |
| jbe@0 | 1350 worker->controlfd = -1; |
| jbe@0 | 1351 moonbr_poll_refresh_needed = 1; |
| jbe@0 | 1352 return; |
| jbe@0 | 1353 } |
| jbe@0 | 1354 if (errno != EINTR) { |
| jbe@0 | 1355 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)); |
| jbe@0 | 1356 moonbr_terminate_error(); |
| jbe@0 | 1357 } |
| jbe@0 | 1358 } |
| jbe@0 | 1359 } |
| jbe@0 | 1360 if (worker->idle) { |
| jbe@0 | 1361 moonbr_log(LOG_CRIT, "Unexpected data from supposedly idle child process in pool #%i with PID %i", worker->pool->poolnum, (int)worker->pid); |
| jbe@0 | 1362 moonbr_terminate_error(); |
| jbe@0 | 1363 } |
| jbe@0 | 1364 if (moonbr_debug) { |
| jbe@0 | 1365 moonbr_log(LOG_DEBUG, "Received control message from child in pool #%i with PID %i: \"%c\"", worker->pool->poolnum, (int)worker->pid, (int)controlmsg); |
| jbe@0 | 1366 } |
| jbe@0 | 1367 switch (controlmsg) { |
| jbe@0 | 1368 case MOONBR_STATUS_IDLE: |
| jbe@0 | 1369 if (moonbr_stat) { |
| jbe@0 | 1370 moonbr_log(LOG_INFO, "Child process in pool #%i with PID %i reports as idle", worker->pool->poolnum, (int)worker->pid); |
| jbe@0 | 1371 } |
| jbe@0 | 1372 moonbr_add_idle_worker(worker); |
| jbe@0 | 1373 break; |
| jbe@0 | 1374 case MOONBR_STATUS_GOODBYE: |
| jbe@0 | 1375 if (moonbr_stat) { |
| jbe@0 | 1376 moonbr_log(LOG_INFO, "Child process in pool #%i with PID %i announced termination", worker->pool->poolnum, (int)worker->pid); |
| jbe@0 | 1377 } |
| jbe@0 | 1378 if (close(worker->controlfd) && errno != EINTR) { |
| jbe@0 | 1379 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)); |
| jbe@0 | 1380 moonbr_terminate_error(); |
| jbe@0 | 1381 } |
| jbe@0 | 1382 worker->controlfd = -1; |
| jbe@0 | 1383 moonbr_poll_refresh_needed = 1; |
| jbe@0 | 1384 break; |
| jbe@0 | 1385 default: |
| jbe@0 | 1386 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); |
| jbe@0 | 1387 moonbr_terminate_error(); |
| jbe@0 | 1388 } |
| jbe@0 | 1389 } |
| jbe@0 | 1390 |
| jbe@0 | 1391 /* Handles stderr stream from child process */ |
| jbe@0 | 1392 static void moonbr_read_errorchannel(struct moonbr_worker *worker) { |
| jbe@0 | 1393 char staticbuf[MOONBR_MAXERRORLINELEN+1]; |
| jbe@0 | 1394 char *buf = worker->errorlinebuf; |
| jbe@0 | 1395 if (!buf) buf = staticbuf; |
| jbe@0 | 1396 { |
| jbe@0 | 1397 ssize_t bytes_read; |
| jbe@0 | 1398 while ( |
| jbe@0 | 1399 (bytes_read = read( |
| jbe@0 | 1400 worker->errorfd, |
| jbe@0 | 1401 buf + worker->errorlinelen, |
| jbe@0 | 1402 MOONBR_MAXERRORLINELEN+1 - worker->errorlinelen |
| jbe@0 | 1403 )) <= 0 |
| jbe@0 | 1404 ) { |
| jbe@0 | 1405 if (bytes_read == 0 || errno == ECONNRESET) { |
| jbe@0 | 1406 if (moonbr_debug) { |
| jbe@0 | 1407 moonbr_log(LOG_DEBUG, "Child process in pool #%i with PID %i closed stderr socket", worker->pool->poolnum, (int)worker->pid); |
| jbe@0 | 1408 } |
| jbe@0 | 1409 if (close(worker->errorfd) && errno != EINTR) { |
| jbe@0 | 1410 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)); |
| jbe@0 | 1411 moonbr_terminate_error(); |
| jbe@0 | 1412 } |
| jbe@0 | 1413 worker->errorfd = -1; |
| jbe@0 | 1414 moonbr_poll_refresh_needed = 1; |
| jbe@0 | 1415 break; |
| jbe@0 | 1416 } |
| jbe@0 | 1417 if (errno != EINTR) { |
| jbe@0 | 1418 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)); |
| jbe@0 | 1419 moonbr_terminate_error(); |
| jbe@0 | 1420 } |
| jbe@0 | 1421 } |
| jbe@0 | 1422 worker->errorlinelen += bytes_read; |
| jbe@0 | 1423 } |
| jbe@0 | 1424 { |
| jbe@0 | 1425 int i; |
| jbe@0 | 1426 for (i=0; i<worker->errorlinelen; i++) { |
| jbe@0 | 1427 if (buf[i] == '\n') buf[i] = 0; |
| jbe@0 | 1428 if (!buf[i]) { |
| jbe@0 | 1429 if (worker->errorlineovf) { |
| jbe@0 | 1430 worker->errorlineovf = 0; |
| jbe@0 | 1431 } else { |
| jbe@0 | 1432 moonbr_log(LOG_WARNING, "Error log from process in pool #%i with PID %i: %s", worker->pool->poolnum, (int)worker->pid, buf); |
| jbe@0 | 1433 } |
| jbe@0 | 1434 worker->errorlinelen -= i+1; |
| jbe@0 | 1435 memmove(buf, buf+i+1, worker->errorlinelen); |
| jbe@0 | 1436 i = -1; |
| jbe@0 | 1437 } |
| jbe@0 | 1438 } |
| jbe@0 | 1439 if (i > MOONBR_MAXERRORLINELEN) { |
| jbe@0 | 1440 buf[MOONBR_MAXERRORLINELEN] = 0; |
| jbe@0 | 1441 if (!worker->errorlineovf) { |
| jbe@0 | 1442 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); |
| jbe@0 | 1443 } |
| jbe@0 | 1444 worker->errorlinelen = 0; |
| jbe@0 | 1445 worker->errorlineovf = 1; |
| jbe@0 | 1446 } |
| jbe@0 | 1447 } |
| jbe@0 | 1448 if (!worker->errorlinebuf && worker->errorlinelen) { /* allocate buffer on heap only if necessary */ |
| jbe@0 | 1449 worker->errorlinebuf = malloc((MOONBR_MAXERRORLINELEN+1) * sizeof(char)); |
| jbe@0 | 1450 if (!worker->errorlinebuf) { |
| jbe@0 | 1451 moonbr_log(LOG_CRIT, "Memory allocation error"); |
| jbe@0 | 1452 moonbr_terminate_error(); |
| jbe@0 | 1453 } |
| jbe@0 | 1454 memcpy(worker->errorlinebuf, staticbuf, worker->errorlinelen); |
| jbe@0 | 1455 } |
| jbe@0 | 1456 } |
| jbe@0 | 1457 |
| jbe@0 | 1458 |
| jbe@0 | 1459 /*** Handler for incoming connections ***/ |
| jbe@0 | 1460 |
| jbe@0 | 1461 /* Accepts one or more incoming connections on listener socket and passes it to worker(s) popped from idle queue */ |
| jbe@0 | 1462 static void moonbr_connect(struct moonbr_pool *pool) { |
| jbe@0 | 1463 struct moonbr_listener *listener = moonbr_pop_connected_listener(pool); |
| jbe@0 | 1464 struct moonbr_worker *worker; |
| jbe@0 | 1465 switch (listener->proto) { |
| jbe@0 | 1466 case MOONBR_PROTO_INTERVAL: |
| jbe@0 | 1467 worker = moonbr_pop_idle_worker(pool); |
| jbe@0 | 1468 if (moonbr_stat) { |
| jbe@0 | 1469 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); |
| jbe@0 | 1470 } |
| jbe@0 | 1471 worker->restart_interval_listener = listener; |
| jbe@124 | 1472 moonbr_send_control_message(worker, MOONBR_COMMAND_CONNECT, -1, listener); |
| jbe@0 | 1473 /* do not push listener to queue of idle listeners yet */ |
| jbe@0 | 1474 break; |
| jbe@0 | 1475 case MOONBR_PROTO_LOCAL: |
| jbe@0 | 1476 do { |
| jbe@0 | 1477 int peerfd; |
| jbe@0 | 1478 struct sockaddr_un peeraddr; |
| jbe@0 | 1479 socklen_t peeraddr_len = sizeof(struct sockaddr_un); |
| jbe@0 | 1480 peerfd = accept4( |
| jbe@0 | 1481 listener->listenfd, |
| jbe@0 | 1482 (struct sockaddr *)&peeraddr, |
| jbe@0 | 1483 &peeraddr_len, |
| jbe@0 | 1484 SOCK_CLOEXEC |
| jbe@0 | 1485 ); |
| jbe@0 | 1486 if (peerfd == -1) { |
| jbe@0 | 1487 if (errno == EWOULDBLOCK) { |
| jbe@0 | 1488 break; |
| jbe@0 | 1489 } else if (errno == ECONNABORTED) { |
| jbe@0 | 1490 moonbr_log(LOG_WARNING, "Connection aborted before accepting it (proto=\"local\", path=\"%s\")", listener->proto_specific.local.path); |
| jbe@0 | 1491 break; |
| jbe@0 | 1492 } else if (errno != EINTR) { |
| jbe@0 | 1493 moonbr_log(LOG_ERR, "Could not accept socket connection: %s", strerror(errno)); |
| jbe@0 | 1494 moonbr_terminate_error(); |
| jbe@0 | 1495 } |
| jbe@0 | 1496 } else { |
| jbe@0 | 1497 worker = moonbr_pop_idle_worker(pool); |
| jbe@0 | 1498 if (moonbr_stat) { |
| jbe@0 | 1499 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); |
| jbe@0 | 1500 } |
| jbe@124 | 1501 moonbr_send_control_message(worker, MOONBR_COMMAND_CONNECT, peerfd, listener); |
| jbe@0 | 1502 if (close(peerfd) && errno != EINTR) { |
| jbe@0 | 1503 moonbr_log(LOG_ERR, "Could not close incoming socket connection in parent process: %s", strerror(errno)); |
| jbe@0 | 1504 moonbr_terminate_error(); |
| jbe@0 | 1505 } |
| jbe@0 | 1506 } |
| jbe@0 | 1507 } while (pool->first_idle_worker); |
| jbe@0 | 1508 moonbr_add_idle_listener(listener); |
| jbe@0 | 1509 break; |
| jbe@0 | 1510 case MOONBR_PROTO_TCP6: |
| jbe@0 | 1511 do { |
| jbe@0 | 1512 int peerfd; |
| jbe@0 | 1513 struct sockaddr_in6 peeraddr; |
| jbe@0 | 1514 socklen_t peeraddr_len = sizeof(struct sockaddr_in6); |
| jbe@0 | 1515 peerfd = accept4( |
| jbe@0 | 1516 listener->listenfd, |
| jbe@0 | 1517 (struct sockaddr *)&peeraddr, |
| jbe@0 | 1518 &peeraddr_len, |
| jbe@0 | 1519 SOCK_CLOEXEC |
| jbe@0 | 1520 ); |
| jbe@0 | 1521 if (peerfd == -1) { |
| jbe@0 | 1522 if (errno == EWOULDBLOCK) { |
| jbe@0 | 1523 break; |
| jbe@0 | 1524 } else if (errno == ECONNABORTED) { |
| jbe@0 | 1525 moonbr_log(LOG_WARNING, "Connection aborted before accepting it (proto=\"tcp6\", port=%i)", listener->proto_specific.tcp.port); |
| jbe@0 | 1526 break; |
| jbe@0 | 1527 } else if (errno != EINTR) { |
| jbe@0 | 1528 moonbr_log(LOG_ERR, "Could not accept socket connection: %s", strerror(errno)); |
| jbe@0 | 1529 moonbr_terminate_error(); |
| jbe@0 | 1530 } |
| jbe@0 | 1531 } else { |
| jbe@0 | 1532 worker = moonbr_pop_idle_worker(pool); |
| jbe@0 | 1533 if (moonbr_stat) { |
| jbe@0 | 1534 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); |
| jbe@0 | 1535 } |
| jbe@124 | 1536 moonbr_send_control_message(worker, MOONBR_COMMAND_CONNECT, peerfd, listener); |
| jbe@0 | 1537 if (close(peerfd) && errno != EINTR) { |
| jbe@0 | 1538 moonbr_log(LOG_ERR, "Could not close incoming socket connection in parent process: %s", strerror(errno)); |
| jbe@0 | 1539 moonbr_terminate_error(); |
| jbe@0 | 1540 } |
| jbe@0 | 1541 } |
| jbe@0 | 1542 } while (pool->first_idle_worker); |
| jbe@0 | 1543 moonbr_add_idle_listener(listener); |
| jbe@0 | 1544 break; |
| jbe@0 | 1545 case MOONBR_PROTO_TCP4: |
| jbe@0 | 1546 do { |
| jbe@0 | 1547 int peerfd; |
| jbe@0 | 1548 struct sockaddr_in peeraddr; |
| jbe@0 | 1549 socklen_t peeraddr_len = sizeof(struct sockaddr_in); |
| jbe@0 | 1550 peerfd = accept4( |
| jbe@0 | 1551 listener->listenfd, |
| jbe@0 | 1552 (struct sockaddr *)&peeraddr, |
| jbe@0 | 1553 &peeraddr_len, |
| jbe@0 | 1554 SOCK_CLOEXEC |
| jbe@0 | 1555 ); |
| jbe@0 | 1556 if (peerfd == -1) { |
| jbe@0 | 1557 if (errno == EWOULDBLOCK) { |
| jbe@0 | 1558 break; |
| jbe@0 | 1559 } else if (errno == ECONNABORTED) { |
| jbe@0 | 1560 moonbr_log(LOG_WARNING, "Connection aborted before accepting it (proto=\"tcp4\", port=%i)", listener->proto_specific.tcp.port); |
| jbe@0 | 1561 break; |
| jbe@0 | 1562 } else if (errno != EINTR) { |
| jbe@0 | 1563 moonbr_log(LOG_ERR, "Could not accept socket connection: %s", strerror(errno)); |
| jbe@0 | 1564 moonbr_terminate_error(); |
| jbe@0 | 1565 } |
| jbe@0 | 1566 } else { |
| jbe@0 | 1567 worker = moonbr_pop_idle_worker(pool); |
| jbe@0 | 1568 if (moonbr_stat) { |
| jbe@0 | 1569 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); |
| jbe@0 | 1570 } |
| jbe@124 | 1571 moonbr_send_control_message(worker, MOONBR_COMMAND_CONNECT, peerfd, listener); |
| jbe@0 | 1572 if (close(peerfd) && errno != EINTR) { |
| jbe@0 | 1573 moonbr_log(LOG_ERR, "Could not close incoming socket connection in parent process: %s", strerror(errno)); |
| jbe@0 | 1574 moonbr_terminate_error(); |
| jbe@0 | 1575 } |
| jbe@0 | 1576 } |
| jbe@0 | 1577 } while (pool->first_idle_worker); |
| jbe@0 | 1578 moonbr_add_idle_listener(listener); |
| jbe@0 | 1579 break; |
| jbe@0 | 1580 default: |
| jbe@0 | 1581 moonbr_log(LOG_ERR, "Internal error (should not happen): Unexpected value in listener.proto field"); |
| jbe@0 | 1582 moonbr_terminate_error(); |
| jbe@0 | 1583 } |
| jbe@0 | 1584 } |
| jbe@0 | 1585 |
| jbe@0 | 1586 |
| jbe@0 | 1587 /*** Functions to initialize and restart interval timers ***/ |
| jbe@0 | 1588 |
| jbe@0 | 1589 /* Initializes all interval timers */ |
| jbe@0 | 1590 static void moonbr_interval_initialize() { |
| jbe@0 | 1591 struct timeval now; |
| jbe@0 | 1592 struct moonbr_pool *pool; |
| jbe@0 | 1593 moonbr_now(&now); |
| jbe@0 | 1594 for (pool=moonbr_first_pool; pool; pool=pool->next_pool) { |
| jbe@0 | 1595 int i; |
| jbe@0 | 1596 for (i=0; i<pool->listener_count; i++) { |
| jbe@0 | 1597 struct moonbr_listener *listener = &pool->listener[i]; |
| jbe@0 | 1598 if (listener->proto == MOONBR_PROTO_INTERVAL) { |
| jbe@0 | 1599 timeradd( |
| jbe@0 | 1600 &now, |
| jbe@0 | 1601 &listener->proto_specific.interval.delay, |
| jbe@0 | 1602 &listener->proto_specific.interval.wakeup |
| jbe@0 | 1603 ); |
| jbe@0 | 1604 } |
| jbe@0 | 1605 } |
| jbe@0 | 1606 } |
| jbe@0 | 1607 } |
| jbe@0 | 1608 |
| jbe@0 | 1609 /* If necessary, restarts interval timers and queues interval listener as idle after a worker changed status */ |
| jbe@0 | 1610 static void moonbr_interval_restart( |
| jbe@0 | 1611 struct moonbr_worker *worker, |
| jbe@0 | 1612 struct timeval *now /* passed to synchronize with moonbr_run() function */ |
| jbe@0 | 1613 ) { |
| jbe@0 | 1614 struct moonbr_listener *listener = worker->restart_interval_listener; |
| jbe@0 | 1615 if (listener) { |
| jbe@0 | 1616 moonbr_add_idle_listener(listener); |
| jbe@0 | 1617 worker->restart_interval_listener = NULL; |
| jbe@0 | 1618 if (listener->proto_specific.interval.strict) { |
| jbe@0 | 1619 timeradd( |
| jbe@0 | 1620 &listener->proto_specific.interval.wakeup, |
| jbe@0 | 1621 &listener->proto_specific.interval.delay, |
| jbe@0 | 1622 &listener->proto_specific.interval.wakeup |
| jbe@0 | 1623 ); |
| jbe@0 | 1624 if (timercmp(&listener->proto_specific.interval.wakeup, now, <)) { |
| jbe@0 | 1625 listener->proto_specific.interval.wakeup = *now; |
| jbe@0 | 1626 } |
| jbe@0 | 1627 } else { |
| jbe@0 | 1628 timeradd( |
| jbe@0 | 1629 now, |
| jbe@0 | 1630 &listener->proto_specific.interval.delay, |
| jbe@0 | 1631 &listener->proto_specific.interval.wakeup |
| jbe@0 | 1632 ); |
| jbe@0 | 1633 } |
| jbe@0 | 1634 } |
| jbe@0 | 1635 } |
| jbe@0 | 1636 |
| jbe@0 | 1637 |
| jbe@0 | 1638 /*** Main loop and helper functions ***/ |
| jbe@0 | 1639 |
| jbe@0 | 1640 /* Stores the earliest required wakeup time in 'wait' variable */ |
| jbe@0 | 1641 static void moonbr_calc_wait(struct timeval *wait, struct timeval *wakeup) { |
| jbe@0 | 1642 if (!timerisset(wait) || timercmp(wakeup, wait, <)) *wait = *wakeup; |
| jbe@0 | 1643 } |
| jbe@0 | 1644 |
| jbe@0 | 1645 /* Main loop of Moonbridge system (including initialization of signal handlers and polling structures) */ |
| jbe@0 | 1646 static void moonbr_run(lua_State *L) { |
| jbe@0 | 1647 struct timeval now; |
| jbe@0 | 1648 struct moonbr_pool *pool; |
| jbe@0 | 1649 struct moonbr_worker *worker; |
| jbe@0 | 1650 struct moonbr_worker *next_worker; /* needed when worker is removed during iteration of workers */ |
| jbe@0 | 1651 struct moonbr_listener *listener; |
| jbe@0 | 1652 struct moonbr_listener *next_listener; /* needed when listener is removed during iteration of listeners */ |
| jbe@0 | 1653 int i; |
| jbe@0 | 1654 moonbr_poll_init(); /* must be executed before moonbr_signal_init() */ |
| jbe@0 | 1655 moonbr_signal_init(); |
| jbe@0 | 1656 moonbr_interval_initialize(); |
| jbe@0 | 1657 moonbr_pstate = MOONBR_PSTATE_RUNNING; |
| jbe@0 | 1658 while (1) { |
| jbe@0 | 1659 struct timeval wait = {0, }; /* point in time when premature wakeup of poll() is required */ |
| jbe@0 | 1660 if (moonbr_cond_interrupt) { |
| jbe@0 | 1661 moonbr_log(LOG_WARNING, "Fast shutdown requested"); |
| jbe@0 | 1662 moonbr_terminate(MOONBR_EXITCODE_GRACEFUL); |
| jbe@0 | 1663 } |
| jbe@0 | 1664 if (moonbr_cond_terminate) { |
| jbe@0 | 1665 moonbr_initiate_shutdown(); |
| jbe@0 | 1666 moonbr_cond_terminate = 0; |
| jbe@0 | 1667 } |
| jbe@0 | 1668 moonbr_cond_child = 0; /* must not be reset between moonbr_try_destroy_worker() and poll() */ |
| jbe@0 | 1669 moonbr_now(&now); |
| jbe@0 | 1670 for (pool=moonbr_first_pool; pool; pool=pool->next_pool) { |
| jbe@0 | 1671 int terminated_worker_count = 0; /* allows shortcut for new worker creation */ |
| jbe@0 | 1672 /* terminate idle workers when expired */ |
| jbe@0 | 1673 if (timerisset(&pool->idle_timeout)) { |
| jbe@0 | 1674 while ((worker = pool->first_idle_worker) != NULL) { |
| jbe@0 | 1675 if (timercmp(&worker->idle_expiration, &now, >)) break; |
| jbe@0 | 1676 moonbr_pop_idle_worker(pool); |
| jbe@0 | 1677 moonbr_terminate_idle_worker(worker); |
| jbe@0 | 1678 } |
| jbe@0 | 1679 } |
| jbe@0 | 1680 /* mark listeners as connected when incoming connection is pending */ |
| jbe@0 | 1681 for (listener=pool->first_idle_listener; listener; listener=next_listener) { |
| jbe@0 | 1682 next_listener = listener->next_listener; /* extra variable necessary due to changing list */ |
| jbe@0 | 1683 if (listener->pollidx != -1) { |
| jbe@0 | 1684 if (moonbr_poll_fds[listener->pollidx].revents) { |
| jbe@0 | 1685 moonbr_poll_fds[listener->pollidx].revents = 0; |
| jbe@0 | 1686 moonbr_remove_idle_listener(listener); |
| jbe@0 | 1687 moonbr_add_connected_listener(listener); |
| jbe@0 | 1688 } |
| jbe@0 | 1689 } else if (listener->proto == MOONBR_PROTO_INTERVAL) { |
| jbe@0 | 1690 if (!timercmp(&listener->proto_specific.interval.wakeup, &now, >)) { |
| jbe@0 | 1691 moonbr_remove_idle_listener(listener); |
| jbe@0 | 1692 moonbr_add_connected_listener(listener); |
| jbe@0 | 1693 } |
| jbe@0 | 1694 } else { |
| jbe@0 | 1695 moonbr_log(LOG_CRIT, "Internal error (should not happen): Listener is neither an interval timer nor has the 'pollidx' value set"); |
| jbe@0 | 1696 moonbr_terminate_error(); |
| jbe@0 | 1697 } |
| jbe@0 | 1698 } |
| jbe@0 | 1699 /* process input from child processes */ |
| jbe@0 | 1700 for (i=0; i<moonbr_poll_worker_count; i++) { |
| jbe@0 | 1701 if (moonbr_poll_worker_fds[i].revents) { |
| jbe@0 | 1702 moonbr_poll_worker_fds[i].revents = 0; |
| jbe@0 | 1703 struct moonbr_poll_worker *poll_worker = &moonbr_poll_workers[i]; |
| jbe@0 | 1704 switch (poll_worker->channel) { |
| jbe@0 | 1705 case MOONBR_POLL_WORKER_CONTROLCHANNEL: |
| jbe@0 | 1706 moonbr_read_controlchannel(poll_worker->worker); |
| jbe@0 | 1707 moonbr_interval_restart(poll_worker->worker, &now); |
| jbe@0 | 1708 break; |
| jbe@0 | 1709 case MOONBR_POLL_WORKER_ERRORCHANNEL: |
| jbe@0 | 1710 moonbr_read_errorchannel(poll_worker->worker); |
| jbe@0 | 1711 break; |
| jbe@0 | 1712 } |
| jbe@0 | 1713 } |
| jbe@0 | 1714 } |
| jbe@0 | 1715 /* collect dead child processes */ |
| jbe@0 | 1716 for (worker=pool->first_worker; worker; worker=next_worker) { |
| jbe@0 | 1717 next_worker = worker->next_worker; /* extra variable necessary due to changing list */ |
| jbe@0 | 1718 switch (moonbr_try_destroy_worker(worker)) { |
| jbe@0 | 1719 case MOONBR_DESTROY_PREPARE: |
| jbe@0 | 1720 pool->use_fork_error_wakeup = 1; |
| jbe@0 | 1721 break; |
| jbe@0 | 1722 case MOONBR_DESTROY_IDLE_OR_ASSIGNED: |
| jbe@0 | 1723 terminated_worker_count++; |
| jbe@0 | 1724 break; |
| jbe@0 | 1725 } |
| jbe@0 | 1726 } |
| jbe@0 | 1727 /* connect listeners with idle workers */ |
| jbe@0 | 1728 if (!moonbr_shutdown_in_progress) { |
| jbe@0 | 1729 while (pool->first_connected_listener && pool->first_idle_worker) { |
| jbe@0 | 1730 moonbr_connect(pool); |
| jbe@0 | 1731 } |
| jbe@0 | 1732 } |
| jbe@0 | 1733 /* create new worker processes */ |
| jbe@0 | 1734 while ( |
| jbe@0 | 1735 pool->total_worker_count < pool->max_fork && ( |
| jbe@0 | 1736 pool->unassigned_worker_count < pool->pre_fork || |
| jbe@0 | 1737 pool->total_worker_count < pool->min_fork |
| jbe@0 | 1738 ) |
| jbe@0 | 1739 ) { |
| jbe@0 | 1740 if (pool->use_fork_error_wakeup) { |
| jbe@0 | 1741 if (timercmp(&pool->fork_error_wakeup, &now, >)) { |
| jbe@0 | 1742 moonbr_calc_wait(&wait, &pool->fork_error_wakeup); |
| jbe@0 | 1743 break; |
| jbe@0 | 1744 } |
| jbe@0 | 1745 } else { |
| jbe@0 | 1746 if (terminated_worker_count) { |
| jbe@0 | 1747 terminated_worker_count--; |
| jbe@0 | 1748 } else if (timercmp(&pool->fork_wakeup, &now, >)) { |
| jbe@0 | 1749 moonbr_calc_wait(&wait, &pool->fork_wakeup); |
| jbe@0 | 1750 break; |
| jbe@0 | 1751 } |
| jbe@0 | 1752 } |
| jbe@0 | 1753 if (moonbr_create_worker(pool, L)) { |
| jbe@0 | 1754 /* on error, enforce error delay */ |
| jbe@0 | 1755 timeradd(&now, &pool->fork_error_delay, &pool->fork_error_wakeup); |
| jbe@0 | 1756 pool->use_fork_error_wakeup = 1; |
| jbe@0 | 1757 moonbr_calc_wait(&wait, &pool->fork_error_wakeup); |
| jbe@0 | 1758 break; |
| jbe@0 | 1759 } else { |
| jbe@0 | 1760 /* normal fork delay on success */ |
| jbe@0 | 1761 timeradd(&now, &pool->fork_delay, &pool->fork_wakeup); |
| jbe@0 | 1762 timeradd(&now, &pool->fork_error_delay, &pool->fork_error_wakeup); |
| jbe@0 | 1763 pool->use_fork_error_wakeup = 0; /* gets set later if error occures during preparation */ |
| jbe@0 | 1764 } |
| jbe@0 | 1765 } |
| jbe@0 | 1766 /* terminate excessive worker processes */ |
| jbe@0 | 1767 while ( |
| jbe@0 | 1768 pool->total_worker_count > pool->min_fork && |
| jbe@0 | 1769 pool->idle_worker_count > pool->pre_fork |
| jbe@0 | 1770 ) { |
| jbe@0 | 1771 if (timerisset(&pool->exit_wakeup)) { |
| jbe@0 | 1772 if (timercmp(&pool->exit_wakeup, &now, >)) { |
| jbe@0 | 1773 moonbr_calc_wait(&wait, &pool->exit_wakeup); |
| jbe@0 | 1774 break; |
| jbe@0 | 1775 } |
| jbe@0 | 1776 moonbr_terminate_idle_worker(moonbr_pop_idle_worker(pool)); |
| jbe@0 | 1777 timeradd(&now, &pool->exit_delay, &pool->exit_wakeup); |
| jbe@0 | 1778 } else { |
| jbe@0 | 1779 timeradd(&now, &pool->exit_delay, &pool->exit_wakeup); |
| jbe@0 | 1780 break; |
| jbe@0 | 1781 } |
| jbe@0 | 1782 } |
| jbe@0 | 1783 if (!( |
| jbe@0 | 1784 pool->total_worker_count > pool->min_fork && |
| jbe@0 | 1785 pool->idle_worker_count > pool->pre_fork |
| jbe@0 | 1786 )) { |
| jbe@0 | 1787 timerclear(&pool->exit_wakeup); /* timer gets restarted later when there are excessive workers */ |
| jbe@0 | 1788 } |
| jbe@0 | 1789 /* optionally output worker count stats */ |
| jbe@0 | 1790 if (moonbr_stat && pool->worker_count_stat) { |
| jbe@0 | 1791 pool->worker_count_stat = 0; |
| jbe@0 | 1792 moonbr_log( |
| jbe@0 | 1793 LOG_INFO, |
| jbe@0 | 1794 "Worker count for pool #%i: %i idle, %i assigned, %i total", |
| jbe@0 | 1795 pool->poolnum, pool->idle_worker_count, |
| jbe@0 | 1796 pool->total_worker_count - pool->unassigned_worker_count, |
| jbe@0 | 1797 pool->total_worker_count); |
| jbe@0 | 1798 } |
| jbe@0 | 1799 /* calculate wakeup time for interval listeners */ |
| jbe@0 | 1800 for (listener=pool->first_idle_listener; listener; listener=listener->next_listener) { |
| jbe@0 | 1801 if (listener->proto == MOONBR_PROTO_INTERVAL) { |
| jbe@0 | 1802 moonbr_calc_wait(&wait, &listener->proto_specific.interval.wakeup); |
| jbe@0 | 1803 } |
| jbe@0 | 1804 } |
| jbe@0 | 1805 /* calculate wakeup time for idle workers (only first idle worker is significant) */ |
| jbe@0 | 1806 if (timerisset(&pool->idle_timeout) && pool->first_idle_worker) { |
| jbe@0 | 1807 moonbr_calc_wait(&wait, &pool->first_idle_worker->idle_expiration); |
| jbe@0 | 1808 } |
| jbe@0 | 1809 } |
| jbe@0 | 1810 /* check if shutdown is complete */ |
| jbe@0 | 1811 if (moonbr_shutdown_in_progress) { |
| jbe@0 | 1812 for (pool=moonbr_first_pool; pool; pool=pool->next_pool) { |
| jbe@0 | 1813 if (pool->first_worker) break; |
| jbe@0 | 1814 } |
| jbe@0 | 1815 if (!pool) { |
| jbe@0 | 1816 moonbr_log(LOG_INFO, "All worker threads have terminated"); |
| jbe@0 | 1817 moonbr_terminate(MOONBR_EXITCODE_GRACEFUL); |
| jbe@0 | 1818 } |
| jbe@0 | 1819 } |
| jbe@0 | 1820 if (moonbr_poll_refresh_needed) moonbr_poll_refresh(); |
| jbe@0 | 1821 moonbr_cond_poll = 1; |
| jbe@0 | 1822 if (!moonbr_cond_child && !moonbr_cond_terminate && !moonbr_cond_interrupt) { |
| jbe@0 | 1823 int timeout; |
| jbe@0 | 1824 if (timerisset(&wait)) { |
| jbe@0 | 1825 if (timercmp(&wait, &now, <)) { |
| jbe@0 | 1826 moonbr_log(LOG_CRIT, "Internal error (should not happen): Future is in the past"); |
| jbe@0 | 1827 moonbr_terminate_error(); |
| jbe@0 | 1828 } |
| jbe@0 | 1829 timersub(&wait, &now, &wait); |
| jbe@0 | 1830 timeout = wait.tv_sec * 1000 + wait.tv_usec / 1000; |
| jbe@0 | 1831 } else { |
| jbe@0 | 1832 timeout = INFTIM; |
| jbe@0 | 1833 } |
| jbe@0 | 1834 if (moonbr_debug) { |
| jbe@0 | 1835 moonbr_log(LOG_DEBUG, "Waiting for I/O"); |
| jbe@0 | 1836 } |
| jbe@0 | 1837 poll(moonbr_poll_fds, moonbr_poll_fds_count, timeout); |
| jbe@0 | 1838 } else { |
| jbe@0 | 1839 if (moonbr_debug) { |
| jbe@0 | 1840 moonbr_log(LOG_DEBUG, "Do not wait for I/O"); |
| jbe@0 | 1841 } |
| jbe@0 | 1842 } |
| jbe@0 | 1843 moonbr_cond_poll = 0; |
| jbe@0 | 1844 moonbr_poll_reset_signal(); |
| jbe@0 | 1845 } |
| jbe@0 | 1846 } |
| jbe@0 | 1847 |
| jbe@0 | 1848 |
| jbe@0 | 1849 /*** Lua interface ***/ |
| jbe@0 | 1850 |
| jbe@0 | 1851 static int moonbr_lua_panic(lua_State *L) { |
| jbe@0 | 1852 const char *errmsg; |
| jbe@0 | 1853 errmsg = lua_tostring(L, -1); |
| jbe@0 | 1854 if (!errmsg) { |
| jbe@0 | 1855 if (lua_isnoneornil(L, -1)) errmsg = "(error message is nil)"; |
| jbe@0 | 1856 else errmsg = "(error message is not a string)"; |
| jbe@0 | 1857 } |
| jbe@0 | 1858 if (moonbr_pstate == MOONBR_PSTATE_FORKED) { |
| jbe@0 | 1859 fprintf(stderr, "Uncaught Lua error: %s\n", errmsg); |
| jbe@0 | 1860 exit(1); |
| jbe@0 | 1861 } else { |
| jbe@0 | 1862 moonbr_log(LOG_CRIT, "Uncaught Lua error: %s", errmsg); |
| jbe@0 | 1863 moonbr_terminate_error(); |
| jbe@0 | 1864 } |
| jbe@0 | 1865 return 0; |
| jbe@0 | 1866 } |
| jbe@0 | 1867 |
| jbe@0 | 1868 static int moonbr_addtraceback(lua_State *L) { |
| jbe@0 | 1869 luaL_traceback(L, L, luaL_tolstring(L, 1, NULL), 1); |
| jbe@0 | 1870 return 1; |
| jbe@0 | 1871 } |
| jbe@0 | 1872 |
| jbe@0 | 1873 /* Memory allocator that allows limiting memory consumption */ |
| jbe@0 | 1874 static void *moonbr_alloc (void *ud, void *ptr, size_t osize, size_t nsize) { |
| jbe@0 | 1875 (void)ud; /* not used */ |
| jbe@0 | 1876 if (nsize == 0) { |
| jbe@0 | 1877 if (ptr) { |
| jbe@0 | 1878 moonbr_memory_usage -= osize; |
| jbe@0 | 1879 free(ptr); |
| jbe@0 | 1880 } |
| jbe@0 | 1881 return NULL; |
| jbe@0 | 1882 } else if (ptr) { |
| jbe@0 | 1883 if ( |
| jbe@0 | 1884 moonbr_memory_limit && |
| jbe@0 | 1885 nsize > osize && |
| jbe@0 | 1886 moonbr_memory_usage + (nsize - osize) > moonbr_memory_limit |
| jbe@0 | 1887 ) { |
| jbe@0 | 1888 return NULL; |
| jbe@0 | 1889 } else { |
| jbe@0 | 1890 ptr = realloc(ptr, nsize); |
| jbe@0 | 1891 if (ptr) moonbr_memory_usage += nsize - osize; |
| jbe@0 | 1892 } |
| jbe@0 | 1893 } else { |
| jbe@0 | 1894 if ( |
| jbe@0 | 1895 moonbr_memory_limit && |
| jbe@0 | 1896 moonbr_memory_usage + nsize > moonbr_memory_limit |
| jbe@0 | 1897 ) { |
| jbe@0 | 1898 return NULL; |
| jbe@0 | 1899 } else { |
| jbe@0 | 1900 ptr = realloc(ptr, nsize); |
| jbe@0 | 1901 if (ptr) moonbr_memory_usage += nsize; |
| jbe@0 | 1902 } |
| jbe@0 | 1903 } |
| jbe@0 | 1904 return ptr; |
| jbe@0 | 1905 } |
| jbe@0 | 1906 |
| jbe@78 | 1907 /* New method for Lua file objects: read until terminator or length exceeded */ |
| jbe@78 | 1908 static int moonbr_readuntil(lua_State *L) { |
| jbe@78 | 1909 luaL_Stream *stream; |
| jbe@78 | 1910 FILE *file; |
| jbe@78 | 1911 const char *terminatorstr; |
| jbe@78 | 1912 size_t terminatorlen; |
| jbe@78 | 1913 luaL_Buffer buf; |
| jbe@78 | 1914 lua_Integer maxlen; |
| jbe@78 | 1915 char terminator; |
| jbe@78 | 1916 int byte; |
| jbe@78 | 1917 stream = luaL_checkudata(L, 1, LUA_FILEHANDLE); |
| jbe@78 | 1918 terminatorstr = luaL_checklstring(L, 2, &terminatorlen); |
| jbe@78 | 1919 luaL_argcheck(L, terminatorlen == 1, 2, "single byte expected"); |
| jbe@78 | 1920 maxlen = luaL_optinteger(L, 3, 0); |
| jbe@78 | 1921 if (!stream->closef) luaL_error(L, "attempt to use a closed file"); |
| jbe@78 | 1922 file = stream->f; |
| jbe@78 | 1923 luaL_buffinit(L, &buf); |
| jbe@78 | 1924 if (!maxlen) maxlen = -1; |
| jbe@78 | 1925 terminator = terminatorstr[0]; |
| jbe@78 | 1926 while (maxlen > 0 ? maxlen-- : maxlen) { |
| jbe@78 | 1927 byte = fgetc(file); |
| jbe@78 | 1928 if (byte == EOF) { |
| jbe@78 | 1929 if (ferror(file)) { |
| jbe@78 | 1930 char errmsg[MOONBR_MAXSTRERRORLEN]; |
| jbe@78 | 1931 strerror_r(errno, errmsg, MOONBR_MAXSTRERRORLEN); /* use thread-safe call in case child created threads */ |
| jbe@78 | 1932 lua_pushnil(L); |
| jbe@78 | 1933 lua_pushstring(L, errmsg); |
| jbe@78 | 1934 return 2; |
| jbe@78 | 1935 } else { |
| jbe@78 | 1936 break; |
| jbe@78 | 1937 } |
| jbe@78 | 1938 } |
| jbe@78 | 1939 luaL_addchar(&buf, byte); |
| jbe@78 | 1940 if (byte == terminator) break; |
| jbe@78 | 1941 } |
| jbe@78 | 1942 luaL_pushresult(&buf); |
| jbe@78 | 1943 if (!lua_rawlen(L, -1)) lua_pushnil(L); |
| jbe@78 | 1944 return 1; |
| jbe@78 | 1945 } |
| jbe@78 | 1946 |
| jbe@66 | 1947 static int moonbr_lua_tonatural(lua_State *L, int idx) { |
| jbe@66 | 1948 int isnum; |
| jbe@66 | 1949 lua_Number n; |
| jbe@66 | 1950 n = lua_tonumberx(L, idx, &isnum); |
| jbe@66 | 1951 if (isnum && n>=0 && n<INT_MAX && (lua_Number)(int)n == n) return n; |
| jbe@66 | 1952 else return -1; |
| jbe@66 | 1953 } |
| jbe@66 | 1954 |
| jbe@66 | 1955 static int moonbr_lua_totimeval(lua_State *L, int idx, struct timeval *value) { |
| jbe@66 | 1956 int isnum; |
| jbe@66 | 1957 lua_Number n; |
| jbe@66 | 1958 n = lua_tonumberx(L, idx, &isnum); |
| jbe@66 | 1959 if (isnum && n>=0 && n<=100000000) { |
| jbe@66 | 1960 value->tv_sec = n; |
| jbe@66 | 1961 value->tv_usec = 1e6 * (n - value->tv_sec); |
| jbe@66 | 1962 return 1; |
| jbe@66 | 1963 } else { |
| jbe@66 | 1964 return 0; |
| jbe@66 | 1965 } |
| jbe@66 | 1966 } |
| jbe@66 | 1967 |
| jbe@0 | 1968 static int moonbr_timeout(lua_State *L) { |
| jbe@0 | 1969 struct itimerval oldval; |
| jbe@0 | 1970 if (lua_isnoneornil(L, 1) && lua_isnoneornil(L, 2)) { |
| jbe@0 | 1971 getitimer(ITIMER_REAL, &oldval); |
| jbe@0 | 1972 } else { |
| jbe@0 | 1973 struct itimerval newval = {}; |
| jbe@39 | 1974 timerclear(&newval.it_interval); |
| jbe@39 | 1975 timerclear(&newval.it_value); |
| jbe@0 | 1976 if (lua_toboolean(L, 1)) { |
| jbe@0 | 1977 luaL_argcheck( |
| jbe@0 | 1978 L, moonbr_lua_totimeval(L, 1, &newval.it_value), 1, |
| jbe@0 | 1979 "interval in seconds expected" |
| jbe@0 | 1980 ); |
| jbe@0 | 1981 } |
| jbe@0 | 1982 if (lua_isnoneornil(L, 2)) { |
| jbe@0 | 1983 if (setitimer(ITIMER_REAL, &newval, &oldval)) { |
| jbe@0 | 1984 moonbr_log(LOG_CRIT, "Could not set ITIMER_REAL via setitimer()"); |
| jbe@0 | 1985 moonbr_terminate_error(); |
| jbe@0 | 1986 } |
| jbe@0 | 1987 } else { |
| jbe@0 | 1988 getitimer(ITIMER_REAL, &oldval); |
| jbe@39 | 1989 if (!timerisset(&oldval.it_value)) { |
| jbe@39 | 1990 if (setitimer(ITIMER_REAL, &newval, NULL)) { |
| jbe@39 | 1991 moonbr_log(LOG_CRIT, "Could not set ITIMER_REAL via setitimer()"); |
| jbe@39 | 1992 moonbr_terminate_error(); |
| jbe@39 | 1993 } |
| jbe@39 | 1994 lua_call(L, lua_gettop(L) - 2, LUA_MULTRET); |
| jbe@39 | 1995 timerclear(&newval.it_value); |
| jbe@39 | 1996 if (setitimer(ITIMER_REAL, &newval, NULL)) { |
| jbe@39 | 1997 moonbr_log(LOG_CRIT, "Could not set ITIMER_REAL via setitimer()"); |
| jbe@39 | 1998 moonbr_terminate_error(); |
| jbe@39 | 1999 } |
| jbe@39 | 2000 } else if (timercmp(&newval.it_value, &oldval.it_value, <)) { |
| jbe@0 | 2001 struct itimerval remval; |
| jbe@0 | 2002 if (setitimer(ITIMER_REAL, &newval, NULL)) { |
| jbe@0 | 2003 moonbr_log(LOG_CRIT, "Could not set ITIMER_REAL via setitimer()"); |
| jbe@0 | 2004 moonbr_terminate_error(); |
| jbe@0 | 2005 } |
| jbe@0 | 2006 lua_call(L, lua_gettop(L) - 2, LUA_MULTRET); |
| jbe@0 | 2007 getitimer(ITIMER_REAL, &remval); |
| jbe@0 | 2008 timersub(&oldval.it_value, &newval.it_value, &newval.it_value); |
| jbe@0 | 2009 timeradd(&newval.it_value, &remval.it_value, &newval.it_value); |
| jbe@0 | 2010 if (setitimer(ITIMER_REAL, &newval, NULL)) { |
| jbe@0 | 2011 moonbr_log(LOG_CRIT, "Could not set ITIMER_REAL via setitimer()"); |
| jbe@0 | 2012 moonbr_terminate_error(); |
| jbe@0 | 2013 } |
| jbe@0 | 2014 } else { |
| jbe@0 | 2015 lua_call(L, lua_gettop(L) - 2, LUA_MULTRET); |
| jbe@0 | 2016 } |
| jbe@0 | 2017 return lua_gettop(L) - 1; |
| jbe@0 | 2018 } |
| jbe@0 | 2019 } |
| jbe@115 | 2020 lua_pushnumber(L, oldval.it_value.tv_sec + 1e-6 * oldval.it_value.tv_usec); |
| jbe@0 | 2021 return 1; |
| jbe@0 | 2022 } |
| jbe@0 | 2023 |
| jbe@0 | 2024 #define moonbr_listen_init_pool_forkoption(luaname, cname, defval) { \ |
| jbe@0 | 2025 lua_getfield(L, 2, luaname); \ |
| jbe@0 | 2026 pool->cname = lua_isnil(L, -1) ? (defval) : moonbr_lua_tonatural(L, -1); \ |
| jbe@0 | 2027 } while(0) |
| jbe@0 | 2028 |
| jbe@0 | 2029 #define moonbr_listen_init_pool_timeoption(luaname, cname, defval, defvalu) ( \ |
| jbe@0 | 2030 lua_getfield(L, 2, luaname), \ |
| jbe@0 | 2031 lua_isnil(L, -1) ? ( \ |
| jbe@0 | 2032 pool->cname.tv_sec = (defval), pool->cname.tv_usec = (defvalu), \ |
| jbe@0 | 2033 1 \ |
| jbe@0 | 2034 ) : ( \ |
| jbe@0 | 2035 (lua_isboolean(L, -1) && !lua_toboolean(L, -1)) ? ( \ |
| jbe@0 | 2036 pool->cname.tv_sec = 0, pool->cname.tv_usec = 0, \ |
| jbe@0 | 2037 1 \ |
| jbe@0 | 2038 ) : ( \ |
| jbe@0 | 2039 moonbr_lua_totimeval(L, -1, &pool->cname) \ |
| jbe@0 | 2040 ) \ |
| jbe@0 | 2041 ) \ |
| jbe@0 | 2042 ) |
| jbe@0 | 2043 |
| jbe@0 | 2044 static int moonbr_listen_init_pool(lua_State *L) { |
| jbe@0 | 2045 struct moonbr_pool *pool; |
| jbe@0 | 2046 const char *proto; |
| jbe@0 | 2047 int i; |
| jbe@0 | 2048 pool = lua_touserdata(L, 1); |
| jbe@0 | 2049 for (i=0; i<pool->listener_count; i++) { |
| jbe@0 | 2050 struct moonbr_listener *listener = &pool->listener[i]; |
| jbe@0 | 2051 lua_settop(L, 2); |
| jbe@3 | 2052 #if LUA_VERSION_NUM >= 503 |
| jbe@3 | 2053 lua_geti(L, 2, i+1); |
| jbe@3 | 2054 #else |
| jbe@0 | 2055 lua_pushinteger(L, i+1); |
| jbe@0 | 2056 lua_gettable(L, 2); |
| jbe@3 | 2057 #endif |
| jbe@0 | 2058 lua_getfield(L, 3, "proto"); |
| jbe@0 | 2059 proto = lua_tostring(L, -1); |
| jbe@0 | 2060 if (proto && !strcmp(proto, "interval")) { |
| jbe@0 | 2061 listener->proto = MOONBR_PROTO_INTERVAL; |
| jbe@0 | 2062 lua_getfield(L, 3, "name"); |
| jbe@0 | 2063 { |
| jbe@0 | 2064 const char *name = lua_tostring(L, -1); |
| jbe@0 | 2065 if (name) { |
| jbe@0 | 2066 if (asprintf(&listener->proto_specific.interval.name, "%s", name) < 0) { |
| jbe@0 | 2067 moonbr_log(LOG_CRIT, "Memory allocation_error"); |
| jbe@0 | 2068 moonbr_terminate_error(); |
| jbe@0 | 2069 } |
| jbe@0 | 2070 } |
| jbe@0 | 2071 } |
| jbe@0 | 2072 lua_getfield(L, 3, "delay"); |
| jbe@0 | 2073 if ( |
| jbe@0 | 2074 !moonbr_lua_totimeval(L, -1, &listener->proto_specific.interval.delay) || |
| jbe@0 | 2075 !timerisset(&listener->proto_specific.interval.delay) |
| jbe@0 | 2076 ) { |
| jbe@0 | 2077 luaL_error(L, "No valid interval delay specified; use listen{{proto=\"interval\", delay=...}, ...}"); |
| jbe@0 | 2078 } |
| jbe@0 | 2079 lua_getfield(L, 3, "strict"); |
| jbe@0 | 2080 if (!lua_isnil(L, -1)) { |
| jbe@0 | 2081 if (lua_isboolean(L, -1)) { |
| jbe@0 | 2082 if (lua_toboolean(L, -1)) listener->proto_specific.interval.strict = 1; |
| jbe@0 | 2083 } else { |
| jbe@0 | 2084 luaL_error(L, "Option \"strict\" must be a boolean if set; use listen{{proto=\"interval\", strict=true, ...}, ...}"); |
| jbe@0 | 2085 } |
| jbe@0 | 2086 } |
| jbe@0 | 2087 } else if (proto && !strcmp(proto, "local")) { |
| jbe@0 | 2088 listener->proto = MOONBR_PROTO_LOCAL; |
| jbe@0 | 2089 lua_getfield(L, 3, "path"); |
| jbe@0 | 2090 { |
| jbe@0 | 2091 const char *path = lua_tostring(L, -1); |
| jbe@0 | 2092 if (!path) { |
| jbe@0 | 2093 luaL_error(L, "No valid path specified for local socket; use listen{{proto=\"local\", path=...}, ...}"); |
| jbe@0 | 2094 } |
| jbe@0 | 2095 if (asprintf(&listener->proto_specific.local.path, "%s", path) < 0) { |
| jbe@0 | 2096 moonbr_log(LOG_CRIT, "Memory allocation_error"); |
| jbe@0 | 2097 moonbr_terminate_error(); |
| jbe@0 | 2098 } |
| jbe@0 | 2099 } |
| jbe@0 | 2100 } else if (proto && !strcmp(proto, "tcp6")) { |
| jbe@0 | 2101 listener->proto = MOONBR_PROTO_TCP6; |
| jbe@0 | 2102 lua_getfield(L, 3, "port"); |
| jbe@0 | 2103 listener->proto_specific.tcp.port = lua_tointeger(L, -1); |
| jbe@0 | 2104 if ( |
| jbe@0 | 2105 listener->proto_specific.tcp.port < 1 || |
| jbe@0 | 2106 listener->proto_specific.tcp.port > 65535 |
| jbe@0 | 2107 ) { |
| jbe@0 | 2108 luaL_error(L, "No valid port number specified; use listen{{proto=\"tcp6\", port=...}, ...}"); |
| jbe@0 | 2109 } |
| jbe@0 | 2110 lua_getfield(L, 3, "localhost"); |
| jbe@0 | 2111 if (!lua_isnil(L, -1)) { |
| jbe@0 | 2112 if (lua_isboolean(L, -1)) { |
| jbe@0 | 2113 if (lua_toboolean(L, -1)) listener->proto_specific.tcp.localhost_only = 1; |
| jbe@0 | 2114 } else { |
| jbe@0 | 2115 luaL_error(L, "Option \"localhost\" must be a boolean if set; use listen{{proto=\"tcp6\", localhost=true, ...}, ...}"); |
| jbe@0 | 2116 } |
| jbe@0 | 2117 } |
| jbe@0 | 2118 } else if (proto && !strcmp(proto, "tcp4")) { |
| jbe@0 | 2119 listener->proto = MOONBR_PROTO_TCP4; |
| jbe@0 | 2120 lua_getfield(L, 3, "port"); |
| jbe@0 | 2121 listener->proto_specific.tcp.port = lua_tointeger(L, -1); |
| jbe@0 | 2122 if ( |
| jbe@0 | 2123 listener->proto_specific.tcp.port < 1 || |
| jbe@0 | 2124 listener->proto_specific.tcp.port > 65535 |
| jbe@0 | 2125 ) { |
| jbe@0 | 2126 luaL_error(L, "No valid port number specified; use listen{{proto=\"tcp4\", port=...}, ...}"); |
| jbe@0 | 2127 } |
| jbe@0 | 2128 lua_getfield(L, 3, "localhost"); |
| jbe@0 | 2129 if (!lua_isnil(L, -1)) { |
| jbe@0 | 2130 if (lua_isboolean(L, -1)) { |
| jbe@0 | 2131 if (lua_toboolean(L, -1)) listener->proto_specific.tcp.localhost_only = 1; |
| jbe@0 | 2132 } else { |
| jbe@0 | 2133 luaL_error(L, "Option \"localhost\" must be a boolean if set; use listen{{proto=\"tcp4\", localhost=true, ...}, ...}"); |
| jbe@0 | 2134 } |
| jbe@0 | 2135 } |
| jbe@0 | 2136 } |
| jbe@0 | 2137 } |
| jbe@0 | 2138 lua_settop(L, 2); |
| jbe@0 | 2139 moonbr_listen_init_pool_forkoption("pre_fork", pre_fork, 1); |
| jbe@0 | 2140 moonbr_listen_init_pool_forkoption("min_fork", min_fork, pool->pre_fork > 2 ? pool->pre_fork : 2); |
| jbe@0 | 2141 moonbr_listen_init_pool_forkoption("max_fork", max_fork, pool->min_fork > 16 ? pool->min_fork : 16); |
| jbe@61 | 2142 if (!moonbr_listen_init_pool_timeoption("fork_delay", fork_delay, 0, 250000)) { |
| jbe@0 | 2143 luaL_error(L, "Option \"fork_delay\" is expected to be a non-negative number"); |
| jbe@0 | 2144 } |
| jbe@0 | 2145 if (!moonbr_listen_init_pool_timeoption("fork_error_delay", fork_error_delay, 2, 0)) { |
| jbe@0 | 2146 luaL_error(L, "Option \"fork_error_delay\" is expected to be a non-negative number"); |
| jbe@0 | 2147 } |
| jbe@0 | 2148 if (!moonbr_listen_init_pool_timeoption("exit_delay", exit_delay, 60, 0)) { |
| jbe@0 | 2149 luaL_error(L, "Option \"exit_delay\" is expected to be a non-negative number"); |
| jbe@0 | 2150 } |
| jbe@0 | 2151 if (timercmp(&pool->fork_error_delay, &pool->fork_delay, <)) { |
| jbe@0 | 2152 pool->fork_error_delay = pool->fork_delay; |
| jbe@0 | 2153 } |
| jbe@0 | 2154 if (!moonbr_listen_init_pool_timeoption("idle_timeout", idle_timeout, 0, 0)) { |
| jbe@0 | 2155 luaL_error(L, "Option \"idle_timeout\" is expected to be a non-negative number"); |
| jbe@0 | 2156 } |
| jbe@0 | 2157 lua_getfield(L, 2, "memory_limit"); |
| jbe@0 | 2158 if (!lua_isnil(L, -1)) { |
| jbe@0 | 2159 int isnum; |
| jbe@0 | 2160 lua_Number n; |
| jbe@0 | 2161 n = lua_tonumberx(L, -1, &isnum); |
| jbe@0 | 2162 if (n < 0 || !isnum) { |
| jbe@0 | 2163 luaL_error(L, "Option \"memory_limit\" is expected to be a non-negative number"); |
| jbe@0 | 2164 } |
| jbe@0 | 2165 pool->memory_limit = n; |
| jbe@0 | 2166 } |
| jbe@0 | 2167 lua_settop(L, 2); |
| jbe@0 | 2168 lua_getfield(L, 2, "prepare"); |
| jbe@0 | 2169 if (!lua_isnil(L, -1) && !lua_isfunction(L, -1)) { |
| jbe@0 | 2170 luaL_error(L, "Option \"prepare\" must be nil or a function"); |
| jbe@0 | 2171 } |
| jbe@0 | 2172 lua_rawsetp(L, LUA_REGISTRYINDEX, moonbr_luakey_prepare_func(pool)); |
| jbe@0 | 2173 lua_getfield(L, 2, "connect"); |
| jbe@0 | 2174 if (!lua_isfunction(L, -1)) { |
| jbe@0 | 2175 luaL_error(L, "Option \"connect\" must be a function; use listen{{...}, {...}, connect=function(socket) ... end, ...}"); |
| jbe@0 | 2176 } |
| jbe@0 | 2177 lua_rawsetp(L, LUA_REGISTRYINDEX, moonbr_luakey_connect_func(pool)); |
| jbe@0 | 2178 lua_getfield(L, 2, "finish"); |
| jbe@0 | 2179 if (!lua_isnil(L, -1) && !lua_isfunction(L, -1)) { |
| jbe@0 | 2180 luaL_error(L, "Option \"finish\" must be nil or a function"); |
| jbe@0 | 2181 } |
| jbe@0 | 2182 lua_rawsetp(L, LUA_REGISTRYINDEX, moonbr_luakey_finish_func(pool)); |
| jbe@0 | 2183 return 0; |
| jbe@0 | 2184 } |
| jbe@0 | 2185 |
| jbe@0 | 2186 static int moonbr_listen(lua_State *L) { |
| jbe@0 | 2187 struct moonbr_pool *pool; |
| jbe@0 | 2188 lua_Integer listener_count; |
| jbe@0 | 2189 if (moonbr_booted) luaL_error(L, "Moonbridge bootup is already complete"); |
| jbe@0 | 2190 luaL_checktype(L, 1, LUA_TTABLE); |
| jbe@0 | 2191 listener_count = luaL_len(L, 1); |
| jbe@0 | 2192 if (!listener_count) luaL_error(L, "No listen ports specified; use listen{{proto=..., port=...},...}"); |
| jbe@0 | 2193 if (listener_count > 100) luaL_error(L, "Too many listeners"); |
| jbe@0 | 2194 pool = moonbr_create_pool(listener_count); |
| jbe@0 | 2195 lua_pushcfunction(L, moonbr_listen_init_pool); |
| jbe@0 | 2196 lua_pushlightuserdata(L, pool); |
| jbe@0 | 2197 lua_pushvalue(L, 1); |
| jbe@0 | 2198 if (lua_pcall(L, 2, 0, 0)) goto moonbr_listen_error; |
| jbe@0 | 2199 { |
| jbe@0 | 2200 int i; |
| jbe@0 | 2201 i = moonbr_start_pool(pool); |
| jbe@0 | 2202 if (i >= 0) { |
| jbe@0 | 2203 struct moonbr_listener *listener = &pool->listener[i]; |
| jbe@0 | 2204 switch (listener->proto) { |
| jbe@0 | 2205 case MOONBR_PROTO_INTERVAL: |
| jbe@0 | 2206 lua_pushfstring(L, "Could not initialize listener #%d (proto=\"interval\"): %s", i+1, strerror(errno)); |
| jbe@0 | 2207 break; |
| jbe@0 | 2208 case MOONBR_PROTO_LOCAL: |
| jbe@0 | 2209 lua_pushfstring(L, "Could not initialize listener #%d (proto=\"local\", path=\"%s\"): %s", i+1, listener->proto_specific.local.path, strerror(errno)); |
| jbe@0 | 2210 break; |
| jbe@0 | 2211 case MOONBR_PROTO_TCP6: |
| jbe@0 | 2212 lua_pushfstring(L, "Could not initialize listener #%d (proto=\"tcp6\", port=%d): %s", i+1, listener->proto_specific.tcp.port, strerror(errno)); |
| jbe@0 | 2213 break; |
| jbe@0 | 2214 case MOONBR_PROTO_TCP4: |
| jbe@0 | 2215 lua_pushfstring(L, "Could not initialize listener #%d (proto=\"tcp4\", port=%d): %s", i+1, listener->proto_specific.tcp.port, strerror(errno)); |
| jbe@0 | 2216 break; |
| jbe@0 | 2217 default: |
| jbe@0 | 2218 moonbr_log(LOG_ERR, "Internal error (should not happen): Unexpected value in listener.proto field"); |
| jbe@0 | 2219 moonbr_terminate_error(); |
| jbe@0 | 2220 } |
| jbe@0 | 2221 goto moonbr_listen_error; |
| jbe@0 | 2222 } |
| jbe@0 | 2223 } |
| jbe@0 | 2224 return 0; |
| jbe@0 | 2225 moonbr_listen_error: |
| jbe@0 | 2226 moonbr_destroy_pool(pool); |
| jbe@0 | 2227 lua_pushnil(L); |
| jbe@0 | 2228 lua_rawsetp(L, LUA_REGISTRYINDEX, moonbr_luakey_prepare_func(pool)); |
| jbe@0 | 2229 lua_pushnil(L); |
| jbe@0 | 2230 lua_rawsetp(L, LUA_REGISTRYINDEX, moonbr_luakey_connect_func(pool)); |
| jbe@0 | 2231 lua_pushnil(L); |
| jbe@0 | 2232 lua_rawsetp(L, LUA_REGISTRYINDEX, moonbr_luakey_finish_func(pool)); |
| jbe@0 | 2233 lua_error(L); |
| jbe@17 | 2234 return 0; /* avoid compiler warning */ |
| jbe@0 | 2235 } |
| jbe@0 | 2236 |
| jbe@0 | 2237 |
| jbe@9 | 2238 /*** Function to modify Lua's library path and/or cpath ***/ |
| jbe@9 | 2239 |
| jbe@9 | 2240 #if defined(MOONBR_LUA_PATH) || defined(MOONBR_LUA_CPATH) |
| jbe@9 | 2241 static void moonbr_modify_path(lua_State *L, char *key, char *value) { |
| jbe@9 | 2242 int stackbase; |
| jbe@9 | 2243 stackbase = lua_gettop(L); |
| jbe@9 | 2244 lua_getglobal(L, "package"); |
| jbe@9 | 2245 lua_getfield(L, stackbase+1, key); |
| jbe@9 | 2246 { |
| jbe@9 | 2247 const char *current_str; |
| jbe@9 | 2248 size_t current_strlen; |
| jbe@9 | 2249 luaL_Buffer buf; |
| jbe@9 | 2250 current_str = lua_tolstring(L, stackbase+2, ¤t_strlen); |
| jbe@9 | 2251 luaL_buffinit(L, &buf); |
| jbe@9 | 2252 if (current_str) { |
| jbe@9 | 2253 lua_pushvalue(L, stackbase+2); |
| jbe@9 | 2254 luaL_addvalue(&buf); |
| jbe@9 | 2255 if (current_strlen && current_str[current_strlen-1] != ';') { |
| jbe@9 | 2256 luaL_addchar(&buf, ';'); |
| jbe@9 | 2257 } |
| jbe@9 | 2258 } |
| jbe@9 | 2259 luaL_addstring(&buf, value); |
| jbe@9 | 2260 luaL_pushresult(&buf); |
| jbe@9 | 2261 } |
| jbe@9 | 2262 lua_setfield(L, stackbase+1, key); |
| jbe@9 | 2263 lua_settop(L, stackbase); |
| jbe@9 | 2264 } |
| jbe@9 | 2265 #endif |
| jbe@9 | 2266 |
| jbe@9 | 2267 |
| jbe@0 | 2268 /*** Main function and command line invokation ***/ |
| jbe@0 | 2269 |
| jbe@0 | 2270 static void moonbr_usage(int err, const char *cmd) { |
| jbe@0 | 2271 FILE *out; |
| jbe@0 | 2272 out = err ? stderr : stdout; |
| jbe@0 | 2273 if (!cmd) cmd = "moonbridge"; |
| jbe@0 | 2274 fprintf(out, "Get this help message: %s {-h|--help}\n", cmd); |
| jbe@0 | 2275 fprintf(out, "Usage: %s \\\n", cmd); |
| jbe@0 | 2276 fprintf(out, " [-b|--background] \\\n"); |
| jbe@0 | 2277 fprintf(out, " [-d|--debug] \\\n"); |
| jbe@0 | 2278 fprintf(out, " [-f|--logfacility {DAEMON|USER|0|1|...|7}] \\\n"); |
| jbe@0 | 2279 fprintf(out, " [-i|--logident <syslog ident> \\\n"); |
| jbe@0 | 2280 fprintf(out, " [-l|--logfile <logfile>] \\\n"); |
| jbe@0 | 2281 fprintf(out, " [-p|--pidfile <pidfile>] \\\n"); |
| jbe@0 | 2282 fprintf(out, " [-s|--stats] \\\n"); |
| jbe@2 | 2283 fprintf(out, " -- <Lua script> [<cmdline options for Lua script>]\n"); |
| jbe@0 | 2284 exit(err); |
| jbe@0 | 2285 } |
| jbe@0 | 2286 |
| jbe@0 | 2287 #define moonbr_usage_error() moonbr_usage(MOONBR_EXITCODE_CMDLINEERROR, argc ? argv[0] : NULL) |
| jbe@0 | 2288 |
| jbe@0 | 2289 int main(int argc, char **argv) { |
| jbe@0 | 2290 { |
| jbe@0 | 2291 int daemonize = 0; |
| jbe@0 | 2292 int log_facility = LOG_USER; |
| jbe@0 | 2293 const char *log_ident = "moonbridge"; |
| jbe@0 | 2294 const char *log_filename = NULL; |
| jbe@0 | 2295 const char *pid_filename = NULL; |
| jbe@0 | 2296 int option; |
| jbe@0 | 2297 struct option longopts[] = { |
| jbe@0 | 2298 { "background", no_argument, NULL, 'b' }, |
| jbe@0 | 2299 { "debug", no_argument, NULL, 'd' }, |
| jbe@0 | 2300 { "logfacility", required_argument, NULL, 'f' }, |
| jbe@0 | 2301 { "help", no_argument, NULL, 'h' }, |
| jbe@0 | 2302 { "logident", required_argument, NULL, 'i' }, |
| jbe@0 | 2303 { "logfile", required_argument, NULL, 'l' }, |
| jbe@0 | 2304 { "pidfile", required_argument, NULL, 'p' }, |
| jbe@0 | 2305 { "stats", no_argument, NULL, 's' } |
| jbe@0 | 2306 }; |
| jbe@0 | 2307 while ((option = getopt_long(argc, argv, "bdf:hi:l:p:s", longopts, NULL)) != -1) { |
| jbe@0 | 2308 switch (option) { |
| jbe@0 | 2309 case 'b': |
| jbe@0 | 2310 daemonize = 1; |
| jbe@0 | 2311 break; |
| jbe@0 | 2312 case 'd': |
| jbe@0 | 2313 moonbr_debug = 1; |
| jbe@0 | 2314 moonbr_stat = 1; |
| jbe@0 | 2315 break; |
| jbe@0 | 2316 case 'f': |
| jbe@0 | 2317 if (!strcmp(optarg, "DAEMON")) { |
| jbe@0 | 2318 log_facility = LOG_DAEMON; |
| jbe@0 | 2319 } else if (!strcmp(optarg, "USER")) { |
| jbe@0 | 2320 log_facility = LOG_USER; |
| jbe@0 | 2321 } else if (!strcmp(optarg, "0")) { |
| jbe@0 | 2322 log_facility = LOG_LOCAL0; |
| jbe@0 | 2323 } else if (!strcmp(optarg, "1")) { |
| jbe@0 | 2324 log_facility = LOG_LOCAL1; |
| jbe@0 | 2325 } else if (!strcmp(optarg, "2")) { |
| jbe@0 | 2326 log_facility = LOG_LOCAL2; |
| jbe@0 | 2327 } else if (!strcmp(optarg, "3")) { |
| jbe@0 | 2328 log_facility = LOG_LOCAL3; |
| jbe@0 | 2329 } else if (!strcmp(optarg, "4")) { |
| jbe@0 | 2330 log_facility = LOG_LOCAL4; |
| jbe@0 | 2331 } else if (!strcmp(optarg, "5")) { |
| jbe@0 | 2332 log_facility = LOG_LOCAL5; |
| jbe@0 | 2333 } else if (!strcmp(optarg, "6")) { |
| jbe@0 | 2334 log_facility = LOG_LOCAL6; |
| jbe@0 | 2335 } else if (!strcmp(optarg, "7")) { |
| jbe@0 | 2336 log_facility = LOG_LOCAL7; |
| jbe@0 | 2337 } else { |
| jbe@0 | 2338 moonbr_usage_error(); |
| jbe@0 | 2339 } |
| jbe@0 | 2340 moonbr_use_syslog = 1; |
| jbe@0 | 2341 break; |
| jbe@0 | 2342 case 'h': |
| jbe@0 | 2343 moonbr_usage(MOONBR_EXITCODE_GRACEFUL, argv[0]); |
| jbe@0 | 2344 break; |
| jbe@0 | 2345 case 'i': |
| jbe@0 | 2346 log_ident = optarg; |
| jbe@0 | 2347 moonbr_use_syslog = 1; |
| jbe@0 | 2348 break; |
| jbe@0 | 2349 case 'l': |
| jbe@0 | 2350 log_filename = optarg; |
| jbe@0 | 2351 break; |
| jbe@0 | 2352 case 'p': |
| jbe@0 | 2353 pid_filename = optarg; |
| jbe@0 | 2354 break; |
| jbe@0 | 2355 case 's': |
| jbe@0 | 2356 moonbr_stat = 1; |
| jbe@0 | 2357 break; |
| jbe@0 | 2358 default: |
| jbe@0 | 2359 moonbr_usage_error(); |
| jbe@0 | 2360 } |
| jbe@0 | 2361 } |
| jbe@2 | 2362 if (argc - optind < 1) moonbr_usage_error(); |
| jbe@0 | 2363 if (pid_filename) { |
| jbe@0 | 2364 pid_t otherpid; |
| jbe@0 | 2365 while ((moonbr_pidfh = pidfile_open(pid_filename, 0644, &otherpid)) == NULL) { |
| jbe@0 | 2366 if (errno == EEXIST) { |
| jbe@0 | 2367 if (otherpid == -1) { |
| jbe@0 | 2368 fprintf(stderr, "PID file \"%s\" is already locked\n", pid_filename); |
| jbe@0 | 2369 } else { |
| jbe@0 | 2370 fprintf(stderr, "PID file \"%s\" is already locked by process with PID: %i\n", pid_filename, (int)otherpid); |
| jbe@0 | 2371 } |
| jbe@0 | 2372 exit(MOONBR_EXITCODE_ALREADYRUNNING); |
| jbe@0 | 2373 } else if (errno != EINTR) { |
| jbe@0 | 2374 fprintf(stderr, "Could not write PID file \"%s\": %s\n", pid_filename, strerror(errno)); |
| jbe@0 | 2375 exit(MOONBR_EXITCODE_STARTUPERROR); |
| jbe@0 | 2376 } |
| jbe@0 | 2377 } |
| jbe@0 | 2378 } |
| jbe@0 | 2379 if (log_filename) { |
| jbe@0 | 2380 int logfd; |
| jbe@0 | 2381 while ( |
| jbe@0 | 2382 ( logfd = flopen( |
| jbe@0 | 2383 log_filename, |
| jbe@0 | 2384 O_WRONLY|O_NONBLOCK|O_CREAT|O_APPEND|O_CLOEXEC, |
| jbe@0 | 2385 0640 |
| jbe@0 | 2386 ) |
| jbe@0 | 2387 ) < 0 |
| jbe@0 | 2388 ) { |
| jbe@0 | 2389 if (errno == EWOULDBLOCK) { |
| jbe@0 | 2390 fprintf(stderr, "Logfile \"%s\" is locked\n", log_filename); |
| jbe@0 | 2391 exit(MOONBR_EXITCODE_ALREADYRUNNING); |
| jbe@0 | 2392 } else if (errno != EINTR) { |
| jbe@0 | 2393 fprintf(stderr, "Could not open logfile \"%s\": %s\n", log_filename, strerror(errno)); |
| jbe@0 | 2394 exit(MOONBR_EXITCODE_STARTUPERROR); |
| jbe@0 | 2395 } |
| jbe@0 | 2396 } |
| jbe@0 | 2397 moonbr_logfile = fdopen(logfd, "a"); |
| jbe@0 | 2398 if (!moonbr_logfile) { |
| jbe@0 | 2399 fprintf(stderr, "Could not open write stream to logfile \"%s\": %s\n", log_filename, strerror(errno)); |
| jbe@0 | 2400 exit(MOONBR_EXITCODE_STARTUPERROR); |
| jbe@0 | 2401 } |
| jbe@0 | 2402 } |
| jbe@0 | 2403 if (daemonize == 0 && !moonbr_logfile) moonbr_logfile = stderr; |
| jbe@0 | 2404 if (moonbr_logfile) setlinebuf(moonbr_logfile); |
| jbe@0 | 2405 else moonbr_use_syslog = 1; |
| jbe@0 | 2406 if (moonbr_use_syslog) openlog(log_ident, LOG_NDELAY | LOG_PID, log_facility); |
| jbe@0 | 2407 if (daemonize) { |
| jbe@0 | 2408 if (daemon(1, 0)) { |
| jbe@0 | 2409 moonbr_log(LOG_ERR, "Could not daemonize moonbridge process"); |
| jbe@0 | 2410 moonbr_terminate_error(); |
| jbe@0 | 2411 } |
| jbe@0 | 2412 } |
| jbe@0 | 2413 } |
| jbe@0 | 2414 moonbr_log(LOG_NOTICE, "Starting moonbridge server"); |
| jbe@0 | 2415 if (moonbr_pidfh && pidfile_write(moonbr_pidfh)) { |
| jbe@0 | 2416 moonbr_log(LOG_ERR, "Could not write pidfile (after locking)"); |
| jbe@0 | 2417 } |
| jbe@0 | 2418 { |
| jbe@0 | 2419 lua_State *L; |
| jbe@0 | 2420 L = lua_newstate(moonbr_alloc, NULL); |
| jbe@0 | 2421 if (!L) { |
| jbe@0 | 2422 moonbr_log(LOG_CRIT, "Could not initialize Lua state"); |
| jbe@0 | 2423 moonbr_terminate_error(); |
| jbe@0 | 2424 } |
| jbe@0 | 2425 lua_atpanic(L, moonbr_lua_panic); |
| jbe@58 | 2426 lua_pushliteral(L, MOONBR_VERSION_STRING); |
| jbe@58 | 2427 lua_setglobal(L, "_MOONBRIDGE_VERSION"); |
| jbe@0 | 2428 luaL_openlibs(L); |
| jbe@89 | 2429 luaL_requiref(L, "moonbridge_io", luaopen_moonbridge_io, 1); |
| jbe@89 | 2430 lua_pop(L, 1); |
| jbe@8 | 2431 #ifdef MOONBR_LUA_PATH |
| jbe@9 | 2432 moonbr_modify_path(L, "path", MOONBR_LUA_PATH); |
| jbe@9 | 2433 #endif |
| jbe@9 | 2434 #ifdef MOONBR_LUA_CPATH |
| jbe@9 | 2435 moonbr_modify_path(L, "cpath", MOONBR_LUA_CPATH); |
| jbe@8 | 2436 #endif |
| jbe@0 | 2437 if (luaL_newmetatable(L, LUA_FILEHANDLE)) { |
| jbe@0 | 2438 moonbr_log(LOG_CRIT, "Lua metatable LUA_FILEHANDLE does not exist"); |
| jbe@0 | 2439 moonbr_terminate_error(); |
| jbe@0 | 2440 } |
| jbe@0 | 2441 lua_getfield(L, -1, "__index"); |
| jbe@78 | 2442 lua_pushcfunction(L, moonbr_readuntil); |
| jbe@78 | 2443 lua_setfield(L, -2, "readuntil"); |
| jbe@0 | 2444 lua_pop(L, 2); |
| jbe@0 | 2445 lua_pushcfunction(L, moonbr_timeout); |
| jbe@0 | 2446 lua_setglobal(L, "timeout"); |
| jbe@0 | 2447 lua_pushcfunction(L, moonbr_listen); |
| jbe@0 | 2448 lua_setglobal(L, "listen"); |
| jbe@17 | 2449 lua_pushcfunction(L, moonbr_addtraceback); /* on stack position 1 */ |
| jbe@2 | 2450 moonbr_log(LOG_INFO, "Loading \"%s\"", argv[optind]); |
| jbe@2 | 2451 if (luaL_loadfile(L, argv[optind])) { |
| jbe@2 | 2452 moonbr_log(LOG_ERR, "Error while loading \"%s\": %s", argv[optind], lua_tostring(L, -1)); |
| jbe@2 | 2453 moonbr_terminate_error(); |
| jbe@2 | 2454 } |
| jbe@2 | 2455 { int i; for (i=optind+1; i<argc; i++) lua_pushstring(L, argv[i]); } |
| jbe@2 | 2456 if (lua_pcall(L, argc-(optind+1), 0, 1)) { |
| jbe@2 | 2457 moonbr_log(LOG_ERR, "Error while executing \"%s\": %s", argv[optind], lua_tostring(L, -1)); |
| jbe@2 | 2458 moonbr_terminate_error(); |
| jbe@0 | 2459 } |
| jbe@6 | 2460 if (!moonbr_first_pool) { |
| jbe@6 | 2461 moonbr_log(LOG_WARNING, "No listener initialized."); |
| jbe@6 | 2462 moonbr_terminate_error(); |
| jbe@6 | 2463 } |
| jbe@0 | 2464 lua_getglobal(L, "listen"); |
| jbe@0 | 2465 lua_pushcfunction(L, moonbr_listen); |
| jbe@0 | 2466 if (lua_compare(L, -2, -1, LUA_OPEQ)) { |
| jbe@0 | 2467 lua_pushnil(L); |
| jbe@0 | 2468 lua_setglobal(L, "listen"); |
| jbe@0 | 2469 } |
| jbe@0 | 2470 lua_settop(L, 1); |
| jbe@56 | 2471 lua_gc(L, LUA_GCCOLLECT, 0); // collect garbage before forking later |
| jbe@0 | 2472 moonbr_run(L); |
| jbe@0 | 2473 } |
| jbe@0 | 2474 return 0; |
| jbe@0 | 2475 } |
| jbe@0 | 2476 |