moonbridge
changeset 0:f6d3b3f70dab v0.1
Initial commit
author | jbe |
---|---|
date | Sun Jan 04 19:30:28 2015 +0100 (2015-01-04) |
parents | |
children | a556c89270fe |
files | LICENSE Makefile README example_application.lua example_webpage.css example_webpage.html http.lua moonbridge.c reference.txt |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/LICENSE Sun Jan 04 19:30:28 2015 +0100 1.3 @@ -0,0 +1,19 @@ 1.4 +Copyright (c) 2015 Public Software Group e. V., Berlin, Germany 1.5 + 1.6 +Permission is hereby granted, free of charge, to any person obtaining a 1.7 +copy of this software and associated documentation files (the "Software"), 1.8 +to deal in the Software without restriction, including without limitation 1.9 +the rights to use, copy, modify, merge, publish, distribute, sublicense, 1.10 +and/or sell copies of the Software, and to permit persons to whom the 1.11 +Software is furnished to do so, subject to the following conditions: 1.12 + 1.13 +The above copyright notice and this permission notice shall be included in 1.14 +all copies or substantial portions of the Software. 1.15 + 1.16 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1.17 +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1.18 +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 1.19 +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 1.20 +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 1.21 +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 1.22 +DEALINGS IN THE SOFTWARE.
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 2.2 +++ b/Makefile Sun Jan 04 19:30:28 2015 +0100 2.3 @@ -0,0 +1,34 @@ 2.4 +# BSD Makefile 2.5 +# On GNU systems, use bmake. 2.6 + 2.7 +.if $(:!uname!) == "FreeBSD" 2.8 +# Default configuration for FreeBSD 2.9 +LUA_INCLUDE ?= /usr/local/include/lua52 2.10 +LUA_LIBDIR ?= /usr/local/lib 2.11 +LUA_LIBRARY ?= lua-5.2 2.12 +UTIL_FLAGS ?= -lutil 2.13 + 2.14 +.elif $(:!uname!) == "Linux" 2.15 +# Default configuration for Linux 2.16 +LUA_INCLUDE ?= /usr/include 2.17 +LUA_LIBDIR ?= /usr/lib 2.18 +LUA_LIBRARY ?= lua 2.19 +UTIL_FLAGS ?= -ldl -lbsd 2.20 + 2.21 +.else 2.22 +# Default configuration for other systems 2.23 +LUA_INCLUDE ?= /usr/include 2.24 +LUA_LIBDIR ?= /usr/lib 2.25 +LUA_LIBRARY ?= lua 2.26 +UTIL_FLAGS ?= -lutil 2.27 + 2.28 +.endif 2.29 + 2.30 +all:: moonbridge 2.31 + 2.32 +moonbridge: moonbridge.c 2.33 + cc -Wall -O2 -Wl,-E -I $(LUA_INCLUDE) -L $(LUA_LIBDIR) -o moonbridge moonbridge.c -lm -l$(LUA_LIBRARY) $(UTIL_FLAGS) 2.34 + 2.35 +clean:: 2.36 + rm -f moonbridge 2.37 +
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 3.2 +++ b/README Sun Jan 04 19:30:28 2015 +0100 3.3 @@ -0,0 +1,11 @@ 3.4 +Quickstart guide: 3.5 + 3.6 +$ make # hint: use bmake on GNU systems 3.7 +$ ./moonbridge example_application.lua 3.8 + 3.9 +Then connect to http://localhost:8080/ 3.10 + 3.11 +To learn more, check example_application.lua and reference.txt files. If you 3.12 +experence any touble during compilation, please edit the Makefile to match 3.13 +your system. 3.14 +
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 4.2 +++ b/example_application.lua Sun Jan 04 19:30:28 2015 +0100 4.3 @@ -0,0 +1,150 @@ 4.4 +-- Moonbridge example application 4.5 +-- invoke with ./moonbridge example_application.lua 4.6 + 4.7 +local http = require "http" 4.8 + 4.9 +local documents = {"example_webpage.html", "example_webpage.css"} 4.10 + 4.11 +listen{ 4.12 + -- listen to a tcp version 4 socket 4.13 + { proto = "tcp4", port = 8080, localhost = true }, 4.14 + 4.15 + -- listen to a tcp version 6 socket 4.16 + { proto = "tcp6", port = 8080, localhost = true }, 4.17 + 4.18 + -- listen to a unix domain socket 4.19 + --{ proto = "local", path = 'socket' }, 4.20 + 4.21 + -- execute the listener regularly (without incoming connection) 4.22 + --{ proto = "interval", name = "myint", delay = 10, strict = false }, 4.23 + 4.24 + -- desired number of spare (idle) processes 4.25 + pre_fork = 1, -- number of forks 4.26 + 4.27 + -- minimum number of processes 4.28 + min_fork = 2, -- number of forks 4.29 + 4.30 + -- maximum number of processes (hard limit) 4.31 + max_fork = 16, -- number of forks 4.32 + 4.33 + -- delay between creation of spare processes 4.34 + fork_delay = 1, -- seconds 4.35 + 4.36 + -- delay before retry of failed process creation 4.37 + fork_error_delay = 2, -- seconds 4.38 + 4.39 + -- delay between destruction of excessive spare processes 4.40 + exit_delay = 60, -- seconds 4.41 + 4.42 + -- idle time after a fork gets terminated 4.43 + idle_timeout = 0, -- seconds (0 for no timeout) 4.44 + 4.45 + -- maximum memory consumption before process gets terminated 4.46 + memory_limit = 1024*1024, -- bytes 4.47 + 4.48 + -- preparation of process (executed before fork) 4.49 + prepare = function() 4.50 + for i, document in ipairs(documents) do 4.51 + local file = assert(io.open(document)) 4.52 + documents[document] = file:read("*a") 4.53 + file:close() 4.54 + end 4.55 + end, 4.56 + 4.57 + -- connection handler 4.58 + connect = http.generate_handler( 4.59 + { 4.60 + static_headers = {"Server: Moonbridge Example Server"}, 4.61 + request_body_size_limit = 16*1024*1024*1024 -- allow big file uploads 4.62 + }, 4.63 + function(request) 4.64 + 4.65 + if request.method == "GET" or request.method == "HEAD" then 4.66 + 4.67 + if request.path == "/" then 4.68 + request:send_status("303 See Other") 4.69 + request:send_header("Location", "http://" .. request.headers_value.host .. "/example_webpage.html") 4.70 + 4.71 + else 4.72 + local document_name = string.match(request.path, "^/(.*)$") 4.73 + local document_extension = string.match(document_name, "%.([^.])$") 4.74 + local document = documents[string.match(request.path, "^/(.*)$")] 4.75 + if document then 4.76 + request:send_status("200 OK") 4.77 + 4.78 + if document_extension == "html" then 4.79 + request:send_header("Content-Type", "text/html; charset=UTF-8") 4.80 + elseif document_extension == "css" then 4.81 + request:send_header("Content-Type", "text/css; charset=UTF-8") 4.82 + end 4.83 + request:send_data(document) 4.84 + else 4.85 + request:send_status("404 Not Found") 4.86 + request:send_header("Content-Type", "text/html; chatset=UTF-8") 4.87 + request:send_data("<html><head><title>404 Not Found</title></head><body><h1>404 Not Found</h1></body></html>") 4.88 + end 4.89 + 4.90 + end 4.91 + 4.92 + elseif request.method == "POST" then 4.93 + 4.94 + if request.path == "/post_example" then 4.95 + local files = {} 4.96 + do 4.97 + local file 4.98 + request:stream_post_param("files", function(chunk, field_name, meta) 4.99 + if meta then 4.100 + file = { 4.101 + file_name = meta.file_name, 4.102 + content_type = meta.content_type, 4.103 + length = 0 4.104 + } 4.105 + end 4.106 + if chunk then 4.107 + file.length = file.length + #chunk 4.108 + else 4.109 + files[#files+1] = file 4.110 + end 4.111 + end) 4.112 + end 4.113 + 4.114 + request:send_status("200 OK") 4.115 + request:send_header("Content-Type", "text/html; chatset=UTF-8") 4.116 + request:send_data("<html>\n<head>\n") 4.117 + request:send_data('<link href="example_webpage.css" rel="stylesheet" type="text/css">\n') 4.118 + request:send_data("<title>Moonbridge Network Server for Lua Applications – Example Application</title>\n") 4.119 + request:send_data("</head>\n<body>\n") 4.120 + request:send_data("<h1>Moonbridge Network Server for Lua – Example Application</h1>\n") 4.121 + request:send_data("<h2>POST request successful</h2>\n") 4.122 + request:send_data('<table>\n<thead><th>File name</th><th>Content type</th><th class="numeric">Bytes received</th></thead>\n<tbody>\n') 4.123 + for i, file in ipairs(files) do 4.124 + request:send_data("<tr>") 4.125 + request:send_data("<td>", http.encode_html(file.file_name or "(unknown)"), "</td>") 4.126 + request:send_data("<td>", http.encode_html(file.content_type or "(unknown)"), "</td>") 4.127 + request:send_data('<td class="numeric">', http.encode_html(tostring(file.length)), "</td>") 4.128 + request:send_data("</tr>\n") 4.129 + end 4.130 + request:send_data("</tbody>\n</table>\n") 4.131 + request:send_data("<p>Submitted comment: ", http.encode_html(request.post_params.comment), "</p>\n") 4.132 + request:send_data("</body>\n</html>\n") 4.133 + 4.134 + else 4.135 + request:send_status("404 Not Found") 4.136 + request:send_data("<html><head><title>404 Not Found</title></head><body><h1>404 Not Found</h1></body></html>") 4.137 + 4.138 + end 4.139 + 4.140 + else 4.141 + request:send_status("405 Method not allowed") 4.142 + 4.143 + end 4.144 + 4.145 + -- returning false causes termination of current process (and re-forking) 4.146 + return true 4.147 + end), 4.148 + 4.149 + -- executed on process termination 4.150 + finish = function() 4.151 + end 4.152 +} 4.153 +
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 5.2 +++ b/example_webpage.css Sun Jan 04 19:30:28 2015 +0100 5.3 @@ -0,0 +1,31 @@ 5.4 +body { 5.5 + background-color: #bfc; 5.6 + font-family: sans-serif; 5.7 +} 5.8 + 5.9 +table { 5.10 + border-spacing: 4px; 5.11 +} 5.12 + 5.13 +table th { 5.14 + text-align: left; 5.15 +} 5.16 + 5.17 +table th, table td { 5.18 + padding: 1ex 0.5em; 5.19 + background: #f0fff4; 5.20 +} 5.21 + 5.22 +table .numeric { 5.23 + text-align: right; 5.24 +} 5.25 + 5.26 +input[type=text] { 5.27 + background: #f0fff4; 5.28 + border: none; 5.29 +} 5.30 + 5.31 +input { 5.32 + padding: 0.5ex 0.5em; 5.33 +} 5.34 +
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 6.2 +++ b/example_webpage.html Sun Jan 04 19:30:28 2015 +0100 6.3 @@ -0,0 +1,16 @@ 6.4 +<html lang="en"> 6.5 + <head> 6.6 + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 6.7 + <link href="example_webpage.css" rel="stylesheet" type="text/css"> 6.8 + <title>Moonbridge Network Server for Lua Applications – Example Application</title> 6.9 + </head> 6.10 + <body> 6.11 + <h1>Moonbridge Network Server for Lua – Example Application</h1> 6.12 + <h2>Test POST request with file upload</h2> 6.13 + <form action="/post_example" method="POST" enctype="multipart/form-data"> 6.14 + Files: <input type="file" name="files" multiple> 6.15 + Comment: <input type="text" name="comment"> 6.16 + <input type="submit"> 6.17 + </form> 6.18 + </body> 6.19 +</html>
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 7.2 +++ b/http.lua Sun Jan 04 19:30:28 2015 +0100 7.3 @@ -0,0 +1,883 @@ 7.4 +#!/usr/bin/env lua 7.5 + 7.6 +-- module preamble 7.7 +local _G, _M = _ENV, {} 7.8 +_ENV = setmetatable({}, { 7.9 + __index = function(self, key) 7.10 + local value = _M[key]; if value ~= nil then return value end 7.11 + return _G[key] 7.12 + end, 7.13 + __newindex = function(self, key, value) _M[key] = value end 7.14 +}) 7.15 + 7.16 +-- function that encodes certain HTML entities: 7.17 +-- (not used by the library itself) 7.18 +function encode_html(text) 7.19 + return ( 7.20 + string.gsub( 7.21 + text, '[<>&"]', 7.22 + function(char) 7.23 + if char == '<' then 7.24 + return "<" 7.25 + elseif char == '>' then 7.26 + return ">" 7.27 + elseif char == '&' then 7.28 + return "&" 7.29 + elseif char == '"' then 7.30 + return """ 7.31 + end 7.32 + end 7.33 + ) 7.34 + ) 7.35 + 7.36 +end 7.37 + 7.38 +-- function that encodes special characters for URIs: 7.39 +-- (not used by the library itself) 7.40 +function encode_uri(text) 7.41 + return ( 7.42 + string.gsub(text, "[^0-9A-Za-z_%.~-]", 7.43 + function (char) 7.44 + return string.format("%%%02x", string.byte(char)) 7.45 + end 7.46 + ) 7.47 + ) 7.48 +end 7.49 + 7.50 +-- function undoing URL encoding: 7.51 +do 7.52 + local b0 = string.byte("0") 7.53 + local b9 = string.byte("9") 7.54 + local bA = string.byte("A") 7.55 + local bF = string.byte("F") 7.56 + local ba = string.byte("a") 7.57 + local bf = string.byte("f") 7.58 + function decode_uri(str) 7.59 + return ( 7.60 + string.gsub( 7.61 + string.gsub(str, "%+", " "), 7.62 + "%%([0-9A-Fa-f][0-9A-Fa-f])", 7.63 + function(hex) 7.64 + local n1, n2 = string.byte(hex, 1, 2) 7.65 + if n1 >= b0 and n1 <= b9 then n1 = n1 - b0 7.66 + elseif n1 >= bA and n1 <= bF then n1 = n1 - bA + 10 7.67 + elseif n1 >= ba and n1 <= bf then n1 = n1 - ba + 10 7.68 + else error("Assertion failed") end 7.69 + if n2 >= b0 and n2 <= b9 then n2 = n2 - b0 7.70 + elseif n2 >= bA and n2 <= bF then n2 = n2 - bA + 10 7.71 + elseif n2 >= ba and n2 <= bf then n2 = n2 - ba + 10 7.72 + else error("Assertion failed") end 7.73 + return string.char(n1 * 16 + n2) 7.74 + end 7.75 + ) 7.76 + ) 7.77 + end 7.78 +end 7.79 + 7.80 +-- status codes that carry no response body (in addition to 1xx): 7.81 +-- (set to "zero_content_length" if Content-Length header is required) 7.82 +status_without_response_body = { 7.83 + ["101"] = true, 7.84 + ["204"] = true, 7.85 + ["205"] = "zero_content_length", 7.86 + ["304"] = true 7.87 +} 7.88 + 7.89 +-- handling of GET/POST param tables: 7.90 +local new_params_list -- defined later 7.91 +do 7.92 + local params_list_mapping = setmetatable({}, {__mode="k"}) 7.93 + local params_list_metatable = { 7.94 + __index = function(self, key) 7.95 + local tbl = {} 7.96 + self[key] = tbl 7.97 + return tbl 7.98 + end 7.99 + } 7.100 + local params_metatable = { 7.101 + __index = function(self, key) 7.102 + local value = params_list_mapping[self][key][1] 7.103 + self[key] = value 7.104 + return value 7.105 + end 7.106 + } 7.107 + -- returns a table to store key value-list pairs (i.e. multiple values), 7.108 + -- and a second table automatically mapping keys to the first value 7.109 + -- using the key value-list pairs in the first table: 7.110 + new_params_list = function() 7.111 + local params_list = setmetatable({}, params_list_metatable) 7.112 + local params = setmetatable({}, params_metatable) 7.113 + params_list_mapping[params] = params_list 7.114 + return params_list, params 7.115 + end 7.116 +end 7.117 +-- parses URL encoded form data and stores it in 7.118 +-- a key value-list pairs structure that has to be 7.119 +-- previously obtained by calling by new_params_list(): 7.120 +local function read_urlencoded_form(tbl, data) 7.121 + for rawkey, rawvalue in string.gmatch(data, "([^=&]*)=([^=&]*)") do 7.122 + local subtbl = tbl[decode_uri(rawkey)] 7.123 + subtbl[#subtbl+1] = decode_uri(rawvalue) 7.124 + end 7.125 +end 7.126 + 7.127 +-- function creating a HTTP handler: 7.128 +function generate_handler(handler, options) 7.129 + -- swap arguments if necessary (for convenience): 7.130 + if type(handler) ~= "function" and type(options) == "function" then 7.131 + handler, options = options, handler 7.132 + end 7.133 + -- process options: 7.134 + options = options or {} 7.135 + local preamble = "" -- preamble sent with every(!) HTTP response 7.136 + do 7.137 + -- named arg "static_headers" is used to create the preamble: 7.138 + local s = options.static_headers 7.139 + local t = {} 7.140 + if s then 7.141 + if type(s) == "string" then 7.142 + for line in string.gmatch(s, "[^\r\n]+") do 7.143 + t[#t+1] = line 7.144 + end 7.145 + else 7.146 + for i, kv in ipairs(options.static_headers) do 7.147 + if type(kv) == "string" then 7.148 + t[#t+1] = kv 7.149 + else 7.150 + t[#t+1] = kv[1] .. ": " .. kv[2] 7.151 + end 7.152 + end 7.153 + end 7.154 + end 7.155 + t[#t+1] = "" 7.156 + preamble = table.concat(t, "\r\n") 7.157 + end 7.158 + -- return connect handler: 7.159 + return function(socket) 7.160 + local survive = true -- set to false if process shall be terminated later 7.161 + while true do 7.162 + -- desired chunk sizes: 7.163 + local input_chunk_size = options.maximum_input_chunk_size or options.chunk_size or 16384 7.164 + local output_chunk_size = options.minimum_output_chunk_size or options.chunk_size or 1024 7.165 + -- process named arguments "request_header_size_limit" and "request_body_size_limit": 7.166 + local remaining_header_size_limit = options.request_header_size_limit or 1024*1024 7.167 + local remaining_body_size_limit = options.request_body_size_limit or 64*1024*1024 7.168 + -- state variables for request handling: 7.169 + local output_state = "no_status_sent" -- one of: 7.170 + -- "no_status_sent" (initial state) 7.171 + -- "info_status_sent" (1xx status code has been sent) 7.172 + -- "bodyless_status_sent" (204/304 status code has been sent) 7.173 + -- "status_sent" (regular status code has been sent) 7.174 + -- "headers_sent" (headers have been terminated) 7.175 + -- "finished" (request has been answered completely) 7.176 + local content_length -- value of Content-Length header sent 7.177 + local bytes_sent = 0 -- number of bytes sent if Content-Length is set 7.178 + local chunk_parts = {} -- list of chunks to send 7.179 + local chunk_bytes = 0 -- sum of lengths of chunks to send 7.180 + local connection_close_requested = false 7.181 + local connection_close_responded = false 7.182 + local headers_value_nil = {} -- header values that are nil 7.183 + local request_body_content_length -- Content-Length of request body 7.184 + local input_state = "pending" -- one of: 7.185 + -- "pending" (request body has not been processed yet) 7.186 + -- "deferred" (request body processing is deferred) 7.187 + -- "reading" (request body is currently being read) 7.188 + -- "finished" (request body has been read) 7.189 + local streamed_post_params = {} -- mapping from POST field name to stream function 7.190 + local streamed_post_param_patterns = {} -- list of POST field pattern and stream function pairs 7.191 + -- object passed to handler (with methods, GET/POST params, etc.): 7.192 + local request 7.193 + -- reads a number of bytes from the socket, 7.194 + -- optionally feeding these bytes chunk-wise 7.195 + -- into a callback function: 7.196 + local function read_body_bytes(remaining, callback) 7.197 + while remaining > 0 do 7.198 + local limit 7.199 + if remaining > input_chunk_size then 7.200 + limit = input_chunk_size 7.201 + else 7.202 + limit = remaining 7.203 + end 7.204 + local chunk = socket:read(limit) 7.205 + if not chunk or #chunk ~= limit then 7.206 + error("Unexpected EOF while reading chunk of request body") 7.207 + end 7.208 + remaining = remaining - limit 7.209 + if callback then 7.210 + callback(chunk) 7.211 + end 7.212 + end 7.213 + end 7.214 + -- flushes or closes the socket (depending on 7.215 + -- whether "Connection: close" header was sent): 7.216 + local function finish_response() 7.217 + if connection_close_responded then 7.218 + -- close output stream: 7.219 + socket.output:close() 7.220 + -- wait for EOF of peer to avoid immediate TCP RST condition: 7.221 + timeout(2, function() 7.222 + while socket.input:read(input_chunk_size) do end 7.223 + end) 7.224 + -- fully close socket: 7.225 + socket:close() 7.226 + else 7.227 + socket:flush() 7.228 + request:stream_request_body() 7.229 + end 7.230 + end 7.231 + -- writes out buffered chunks (without flushing the socket): 7.232 + local function send_chunk() 7.233 + if chunk_bytes > 0 then 7.234 + socket:write(string.format("%x\r\n", chunk_bytes)) 7.235 + for i = 1, #chunk_parts do 7.236 + socket:write(chunk_parts[i]) 7.237 + end 7.238 + chunk_parts = {} 7.239 + chunk_bytes = 0 7.240 + socket:write("\r\n") 7.241 + end 7.242 + end 7.243 + -- terminate header section in response, optionally flushing: 7.244 + -- (may be called multiple times unless response is finished) 7.245 + local function finish_headers(flush) 7.246 + if output_state == "no_status_sent" then 7.247 + error("HTTP status has not been sent yet") 7.248 + elseif output_state == "finished" then 7.249 + error("Response has already been finished") 7.250 + elseif output_state == "info_status_sent" then 7.251 + socket:write("\r\n") 7.252 + socket:flush() 7.253 + output_state = "no_status_sent" 7.254 + elseif output_state == "bodyless_status_sent" then 7.255 + if connection_close_requested and not connection_close_responded then 7.256 + request:send_header("Connection", "close") 7.257 + end 7.258 + socket:write("\r\n") 7.259 + finish_response() 7.260 + output_state = "finished" 7.261 + elseif output_state == "status_sent" then 7.262 + if not content_length then 7.263 + socket:write("Transfer-Encoding: chunked\r\n") 7.264 + end 7.265 + if connection_close_requested and not connection_close_responded then 7.266 + request:send_header("Connection", "close") 7.267 + end 7.268 + socket:write("\r\n") 7.269 + if request.method == "HEAD" then 7.270 + finish_response() 7.271 + elseif flush then 7.272 + socket:flush() 7.273 + end 7.274 + output_state = "headers_sent" 7.275 + elseif output_state ~= "headers_sent" then 7.276 + error("Unexpected internal status in HTTP engine") 7.277 + end 7.278 + end 7.279 + -- create request object and set several functions and values: 7.280 + request = { 7.281 + -- allow raw socket access: 7.282 + socket = socket, 7.283 + -- parsed cookies: 7.284 + cookies = {}, 7.285 + -- send a HTTP response status (e.g. "200 OK"): 7.286 + send_status = function(self, value) 7.287 + if input_state == "pending" then 7.288 + request:process_request_body() 7.289 + end 7.290 + if output_state == "info_status_sent" then 7.291 + socket:write("\r\n") 7.292 + socket:flush() 7.293 + elseif output_state ~= "no_status_sent" then 7.294 + error("HTTP status has already been sent") 7.295 + end 7.296 + local status1 = string.sub(value, 1, 1) 7.297 + local status3 = string.sub(value, 1, 3) 7.298 + socket:write("HTTP/1.1 ", value, "\r\n", preamble) 7.299 + local without_response_body = status_without_response_body[status3] 7.300 + if without_response_body then 7.301 + output_state = "bodyless_status_sent" 7.302 + if without_response_body == "zero_content_length" then 7.303 + request:send_header("Content-Length", 0) 7.304 + end 7.305 + elseif status1 == "1" then 7.306 + output_state = "info_status_sent" 7.307 + else 7.308 + output_state = "status_sent" 7.309 + end 7.310 + end, 7.311 + -- send a HTTP response header 7.312 + -- (key and value as separate args): 7.313 + send_header = function(self, key, value) 7.314 + if output_state == "no_status_sent" then 7.315 + error("HTTP status has not been sent yet") 7.316 + elseif 7.317 + output_state ~= "info_status_sent" and 7.318 + output_state ~= "bodyless_status_sent" and 7.319 + output_state ~= "status_sent" 7.320 + then 7.321 + error("All HTTP headers have already been sent") 7.322 + end 7.323 + local key_lower = string.lower(key) 7.324 + if key_lower == "content-length" then 7.325 + if output_state == "info_status_sent" then 7.326 + error("Cannot set Content-Length for informational status response") 7.327 + end 7.328 + local new_content_length = assert(tonumber(value), "Invalid content-length") 7.329 + if content_length == nil then 7.330 + content_length = new_content_length 7.331 + elseif content_length == new_content_length then 7.332 + return 7.333 + else 7.334 + error("Content-Length has been set multiple times with different values") 7.335 + end 7.336 + elseif key_lower == "connection" then 7.337 + for entry in string.gmatch(string.lower(value), "[^,]+") do 7.338 + if string.match(entry, "^[ \t]*close[ \t]*$") then 7.339 + if output_state == "info_status_sent" then 7.340 + error("Cannot set \"Connection: close\" for informational status response") 7.341 + end 7.342 + if connection_close_responded then 7.343 + return 7.344 + else 7.345 + connection_close_responded = true 7.346 + break 7.347 + end 7.348 + end 7.349 + end 7.350 + end 7.351 + socket:write(key, ": ", value, "\r\n") 7.352 + end, 7.353 + -- method to finish and flush headers: 7.354 + finish_headers = function() 7.355 + finish_headers(true) 7.356 + end, 7.357 + -- send data for response body: 7.358 + send_data = function(self, ...) 7.359 + if output_state == "info_status_sent" then 7.360 + error("No (non-informational) HTTP status has been sent yet") 7.361 + elseif output_state == "bodyless_status_sent" then 7.362 + error("Cannot send response data for body-less status message") 7.363 + end 7.364 + finish_headers(false) 7.365 + if output_state ~= "headers_sent" then 7.366 + error("Unexpected internal status in HTTP engine") 7.367 + end 7.368 + if request.method == "HEAD" then 7.369 + return 7.370 + end 7.371 + for i = 1, select("#", ...) do 7.372 + local str = tostring(select(i, ...)) 7.373 + if #str > 0 then 7.374 + if content_length then 7.375 + local bytes_to_send = #str 7.376 + if bytes_sent + bytes_to_send > content_length then 7.377 + socket:write(string.sub(str, 1, content_length - bytes_sent)) 7.378 + bytes_sent = content_length 7.379 + error("Content length exceeded") 7.380 + else 7.381 + socket:write(str) 7.382 + bytes_sent = bytes_sent + bytes_to_send 7.383 + end 7.384 + else 7.385 + chunk_bytes = chunk_bytes + #str 7.386 + chunk_parts[#chunk_parts+1] = str 7.387 + end 7.388 + end 7.389 + end 7.390 + if chunk_bytes >= output_chunk_size then 7.391 + send_chunk() 7.392 + end 7.393 + end, 7.394 + -- flush output buffer: 7.395 + flush = function(self) 7.396 + send_chunk() 7.397 + socket:flush() 7.398 + end, 7.399 + -- finish response: 7.400 + finish = function(self) 7.401 + if output_state == "finished" then 7.402 + return 7.403 + elseif output_state == "info_status_sent" then 7.404 + error("Informational HTTP response can be finished with :finish_headers() method") 7.405 + end 7.406 + finish_headers(false) 7.407 + if output_state == "headers_sent" then 7.408 + if request.method ~= "HEAD" then 7.409 + if content_length then 7.410 + if bytes_sent ~= content_length then 7.411 + error("Content length not used") 7.412 + end 7.413 + else 7.414 + send_chunk() 7.415 + socket:write("0\r\n\r\n") 7.416 + end 7.417 + finish_response() 7.418 + end 7.419 + output_state = "finished" 7.420 + elseif output_state ~= finished then 7.421 + error("Unexpected internal status in HTTP engine") 7.422 + end 7.423 + end, 7.424 + -- table mapping header field names to value-lists 7.425 + -- (raw access): 7.426 + headers = setmetatable({}, { 7.427 + __index = function(self, key) 7.428 + local lowerkey = string.lower(key) 7.429 + if lowerkey == key then 7.430 + return 7.431 + end 7.432 + local result = rawget(self, lowerkey) 7.433 + if result == nil then 7.434 + result = {} 7.435 + end 7.436 + self[lowerkey] = result 7.437 + self[key] = result 7.438 + return result 7.439 + end 7.440 + }), 7.441 + -- table mapping header field names to value-lists 7.442 + -- (for headers with comma separated values): 7.443 + headers_csv_table = setmetatable({}, { 7.444 + __index = function(self, key) 7.445 + local result = {} 7.446 + for i, line in ipairs(request.headers[key]) do 7.447 + for entry in string.gmatch(line, "[^,]+") do 7.448 + local value = string.match(entry, "^[ \t]*(..-)[ \t]*$") 7.449 + if value then 7.450 + result[#result+1] = value 7.451 + end 7.452 + end 7.453 + end 7.454 + self[key] = result 7.455 + return result 7.456 + end 7.457 + }), 7.458 + -- table mapping header field names to a comma separated string 7.459 + -- (for headers with comma separated values): 7.460 + headers_csv_string = setmetatable({}, { 7.461 + __index = function(self, key) 7.462 + local result = {} 7.463 + for i, line in ipairs(request.headers[key]) do 7.464 + result[#result+1] = line 7.465 + end 7.466 + result = string.concat(result, ", ") 7.467 + self[key] = result 7.468 + return result 7.469 + end 7.470 + }), 7.471 + -- table mapping header field names to a single string value 7.472 + -- (or false if header has been sent multiple times): 7.473 + headers_value = setmetatable({}, { 7.474 + __index = function(self, key) 7.475 + if headers_value_nil[key] then 7.476 + return nil 7.477 + end 7.478 + local result = nil 7.479 + local values = request.headers_csv_table[key] 7.480 + if #values == 0 then 7.481 + headers_value_nil[key] = true 7.482 + elseif #values == 1 then 7.483 + result = values[1] 7.484 + else 7.485 + result = false 7.486 + end 7.487 + self[key] = result 7.488 + return result 7.489 + end 7.490 + }), 7.491 + -- table mapping header field names to a flag table, 7.492 + -- indicating if the comma separated value contains certain entries: 7.493 + headers_flags = setmetatable({}, { 7.494 + __index = function(self, key) 7.495 + local result = setmetatable({}, { 7.496 + __index = function(self, key) 7.497 + local lowerkey = string.lower(key) 7.498 + local result = rawget(self, lowerkey) or false 7.499 + self[lowerkey] = result 7.500 + self[key] = result 7.501 + return result 7.502 + end 7.503 + }) 7.504 + for i, value in ipairs(request.headers_csv_table[key]) do 7.505 + result[string.lower(value)] = true 7.506 + end 7.507 + self[key] = result 7.508 + return result 7.509 + end 7.510 + }), 7.511 + -- register POST param stream handler for a single field name: 7.512 + stream_post_param = function(self, field_name, callback) 7.513 + if input_state == "inprogress" or input_state == "finished" then 7.514 + error("Cannot register POST param streaming function if request body is already processed") 7.515 + end 7.516 + streamed_post_params[field_name] = callback 7.517 + end, 7.518 + -- register POST param stream handler for a field name pattern: 7.519 + stream_post_params = function(self, pattern, callback) 7.520 + if input_state == "inprogress" or input_state == "finished" then 7.521 + error("Cannot register POST param streaming function if request body is already processed") 7.522 + end 7.523 + streamed_post_param_patterns[#streamed_post_param_patterns+1] = {pattern, callback} 7.524 + end, 7.525 + -- disables automatic request body processing on write 7.526 + -- (use with caution): 7.527 + defer_reading = function(self) 7.528 + if input_state == "pending" then 7.529 + input_state = "deferred" 7.530 + end 7.531 + end, 7.532 + -- processes the request body and sets the request.post_params, 7.533 + -- request.post_params_list, request.meta_post_params, and 7.534 + -- request.meta_post_params_list values (can be called manually or 7.535 + -- automatically if post_params are accessed or data is written out) 7.536 + process_request_body = function(self) 7.537 + if input_state == "finished" then 7.538 + return 7.539 + end 7.540 + local post_params_list, post_params = new_params_list() 7.541 + local content_type = request.headers_value["Content-Type"] 7.542 + if content_type then 7.543 + if 7.544 + content_type == "application/x-www-form-urlencoded" or 7.545 + string.match(content_type, "^application/x%-www%-form%-urlencoded *;") 7.546 + then 7.547 + read_urlencoded_form(post_params_list, request.body) 7.548 + else 7.549 + local boundary = string.match( 7.550 + content_type, 7.551 + '^multipart/form%-data[ \t]*[;,][ \t]*boundary="([^"]+)"$' 7.552 + ) or string.match( 7.553 + content_type, 7.554 + '^multipart/form%-data[ \t]*[;,][ \t]*boundary=([^"; \t]+)$' 7.555 + ) 7.556 + if boundary then 7.557 + local post_metadata_list, post_metadata = new_params_list() 7.558 + boundary = "--" .. boundary 7.559 + local headerdata = "" 7.560 + local streamer 7.561 + local field_name 7.562 + local metadata = {} 7.563 + local value_parts 7.564 + local function default_streamer(chunk) 7.565 + value_parts[#value_parts+1] = chunk 7.566 + end 7.567 + local function stream_part_finish() 7.568 + if streamer == default_streamer then 7.569 + local value = table.concat(value_parts) 7.570 + value_parts = nil 7.571 + if field_name then 7.572 + local values = post_params_list[field_name] 7.573 + values[#values+1] = value 7.574 + local metadata_entries = post_metadata_list[field_name] 7.575 + metadata_entries[#metadata_entries+1] = metadata 7.576 + end 7.577 + else 7.578 + streamer() 7.579 + end 7.580 + headerdata = "" 7.581 + streamer = nil 7.582 + field_name = nil 7.583 + metadata = {} 7.584 + end 7.585 + local function stream_part_chunk(chunk) 7.586 + if streamer then 7.587 + streamer(chunk) 7.588 + else 7.589 + headerdata = headerdata .. chunk 7.590 + while true do 7.591 + local line, remaining = string.match(headerdata, "^(.-)\r?\n(.*)$") 7.592 + if not line then 7.593 + break 7.594 + end 7.595 + if line == "" then 7.596 + streamer = streamed_post_params[field_name] 7.597 + if not streamer then 7.598 + for i, rule in ipairs(streamed_post_param_patterns) do 7.599 + if string.match(field_name, rule[1]) then 7.600 + streamer = rule[2] 7.601 + break 7.602 + end 7.603 + end 7.604 + end 7.605 + if not streamer then 7.606 + value_parts = {} 7.607 + streamer = default_streamer 7.608 + end 7.609 + streamer(remaining, field_name, metadata) 7.610 + return 7.611 + end 7.612 + headerdata = remaining 7.613 + local header_key, header_value = string.match(line, "^([^:]*):[ \t]*(.-)[ \t]*$") 7.614 + if not header_key then 7.615 + error("Invalid header in multipart/form-data part") 7.616 + end 7.617 + header_key = string.lower(header_key) 7.618 + if header_key == "content-disposition" then 7.619 + local escaped_header_value = string.gsub(header_value, '"[^"]*"', function(str) 7.620 + return string.gsub(str, "=", "==") 7.621 + end) 7.622 + field_name = string.match(escaped_header_value, ';[ \t]*name="([^"]*)"') 7.623 + if field_name then 7.624 + field_name = string.gsub(field_name, "==", "=") 7.625 + else 7.626 + field_name = string.match(header_value, ';[ \t]*name=([^"; \t]+)') 7.627 + end 7.628 + metadata.file_name = string.match(escaped_header_value, ';[ \t]*filename="([^"]*)"') 7.629 + if metadata.file_name then 7.630 + metadata.file_name = string.gsub(metadata.file_name, "==", "=") 7.631 + else 7.632 + string.match(header_value, ';[ \t]*filename=([^"; \t]+)') 7.633 + end 7.634 + elseif header_key == "content-type" then 7.635 + metadata.content_type = header_value 7.636 + elseif header_key == "content-transfer-encoding" then 7.637 + error("Content-transfer-encoding not supported by multipart/form-data parser") 7.638 + end 7.639 + end 7.640 + end 7.641 + end 7.642 + local skippart = true -- ignore data until first boundary 7.643 + local afterbound = false -- interpret 2 bytes after boundary ("\r\n" or "--") 7.644 + local terminated = false -- final boundary read 7.645 + local bigchunk = "" 7.646 + request:stream_request_body(function(chunk) 7.647 + if terminated then 7.648 + return 7.649 + end 7.650 + bigchunk = bigchunk .. chunk 7.651 + while true do 7.652 + if afterbound then 7.653 + if #bigchunk <= 2 then 7.654 + return 7.655 + end 7.656 + local terminator = string.sub(bigchunk, 1, 2) 7.657 + if terminator == "\r\n" then 7.658 + afterbound = false 7.659 + bigchunk = string.sub(bigchunk, 3) 7.660 + elseif terminator == "--" then 7.661 + terminated = true 7.662 + bigchunk = nil 7.663 + return 7.664 + else 7.665 + error("Error while parsing multipart body (expected CRLF or double minus)") 7.666 + end 7.667 + end 7.668 + local pos1, pos2 = string.find(bigchunk, boundary, 1, true) 7.669 + if not pos1 then 7.670 + if not skippart then 7.671 + local safe = #bigchunk-#boundary 7.672 + if safe > 0 then 7.673 + stream_part_chunk(string.sub(bigchunk, 1, safe)) 7.674 + bigchunk = string.sub(bigchunk, safe+1) 7.675 + end 7.676 + end 7.677 + return 7.678 + end 7.679 + if not skippart then 7.680 + stream_part_chunk(string.sub(bigchunk, 1, pos1 - 1)) 7.681 + stream_part_finish() 7.682 + else 7.683 + boundary = "\r\n" .. boundary 7.684 + skippart = false 7.685 + end 7.686 + bigchunk = string.sub(bigchunk, pos2 + 1) 7.687 + afterbound = true 7.688 + end 7.689 + end) 7.690 + if not terminated then 7.691 + error("Premature end of multipart/form-data request body") 7.692 + end 7.693 + request.post_metadata_list, request.post_metadata = post_metadata_list, post_metadata 7.694 + else 7.695 + error("Unknown Content-Type of request body") 7.696 + end 7.697 + end 7.698 + end 7.699 + request.post_params_list, request.post_params = post_params_list, post_params 7.700 + end, 7.701 + -- stream request body to an (optional) callback function 7.702 + -- without processing it otherwise: 7.703 + stream_request_body = function(self, callback) 7.704 + if input_state ~= "pending" and input_state ~= "deferred" then 7.705 + if callback then 7.706 + if input_state == "inprogress" then 7.707 + error("Request body is already being processed") 7.708 + else 7.709 + error("Request body has already been processed") 7.710 + end 7.711 + end 7.712 + return 7.713 + end 7.714 + input_state = "inprogress" 7.715 + if request.headers_flags["Expect"]["100-continue"] then 7.716 + request:send_status("100 Continue") 7.717 + request:finish_headers() 7.718 + end 7.719 + if request.headers_flags["Transfer-Encoding"]["chunked"] then 7.720 + while true do 7.721 + local line = socket:readuntil("\n", 32 + remaining_body_size_limit) 7.722 + if not line then 7.723 + error("Unexpected EOF while reading next chunk of request body") 7.724 + end 7.725 + local zeros, lenstr = string.match(line, "^(0*)([1-9A-Fa-f]+[0-9A-Fa-f]*)\r?\n$") 7.726 + local chunkext 7.727 + if lenstr then 7.728 + chunkext = "" 7.729 + else 7.730 + zeros, lenstr, chunkext = string.match(line, "^(0*)([1-9A-Fa-f]+[0-9A-Fa-f]*)([ \t;].-)\r?\n$") 7.731 + end 7.732 + if not lenstr or #lenstr > 13 then 7.733 + error("Encoding error or unexpected EOF while reading chunk of request body") 7.734 + end 7.735 + local len = tonumber("0x" .. lenstr) 7.736 + remaining_body_size_limit = remaining_body_size_limit - (#zeros + #chunkext + len) 7.737 + if remaining_body_size_limit < 0 then 7.738 + error("Request body size limit exceeded") 7.739 + end 7.740 + if len == 0 then break end 7.741 + read_body_bytes(len, callback) 7.742 + local term = socket:readuntil("\n", 2) 7.743 + if term ~= "\r\n" and term ~= "\n" then 7.744 + error("Encoding error while reading chunk of request body") 7.745 + end 7.746 + end 7.747 + while true do 7.748 + local line = socket:readuntil("\n", 2 + remaining_body_size_limit) 7.749 + if line == "\r\n" or line == "\n" then break end 7.750 + remaining_body_size_limit = remaining_body_size_limit - #line 7.751 + if remaining_body_size_limit < 0 then 7.752 + error("Request body size limit exceeded while reading trailer section of chunked request body") 7.753 + end 7.754 + end 7.755 + elseif request_body_content_length then 7.756 + read_body_bytes(request_body_content_length, callback) 7.757 + end 7.758 + input_state = "finished" 7.759 + end 7.760 + } 7.761 + -- initialize tables for GET params in request object: 7.762 + request.get_params_list, request.get_params = new_params_list() 7.763 + -- add meta table to request object to allow access to "body" and POST params: 7.764 + setmetatable(request, { 7.765 + __index = function(self, key) 7.766 + if key == "body" then 7.767 + local chunks = {} 7.768 + request:stream_request_body(function(chunk) 7.769 + chunks[#chunks+1] = chunk 7.770 + end) 7.771 + self.body = table.concat(chunks) 7.772 + return self.body 7.773 + elseif 7.774 + key == "post_params_list" or key == "post_params" or 7.775 + key == "post_metadata_list" or key == "post_metadata" 7.776 + then 7.777 + request:process_request_body() 7.778 + return request[key] 7.779 + end 7.780 + end 7.781 + }) 7.782 + -- low level HTTP error response (for malformed requests, etc.): 7.783 + local function error_response(status, text) 7.784 + request:send_status(status) 7.785 + request:send_header("Content-Type", "text/plain") 7.786 + if not connection_close_responded then 7.787 + request:send_header("Connection", "close") 7.788 + end 7.789 + request:send_data(status, "\n") 7.790 + if text then 7.791 + request:send_data("\n", text, "\n") 7.792 + end 7.793 + request:finish() 7.794 + return survive 7.795 + end 7.796 + -- read and parse request line: 7.797 + local line = socket:readuntil("\n", remaining_header_size_limit) 7.798 + if not line then return survive end 7.799 + remaining_header_size_limit = remaining_header_size_limit - #line 7.800 + if remaining_header_size_limit == 0 then 7.801 + return error_response("413 Request Entity Too Large", "Request line too long") 7.802 + end 7.803 + local proto 7.804 + request.method, request.url, proto = 7.805 + line:match("^([^ \t\r]+)[ \t]+([^ \t\r]+)[ \t]*([^ \t\r]*)[ \t]*\r?\n$") 7.806 + if not request.method then 7.807 + return error_response("400 Bad Request") 7.808 + elseif proto ~= "HTTP/1.1" then 7.809 + return error_response("505 HTTP Version Not Supported") 7.810 + else 7.811 + -- read and parse headers: 7.812 + while true do 7.813 + local line = socket:readuntil("\n", remaining_header_size_limit); 7.814 + remaining_header_size_limit = remaining_header_size_limit - #line 7.815 + if not line then 7.816 + return error_response("400 Bad Request") 7.817 + end 7.818 + if line == "\r\n" or line == "\n" then 7.819 + break 7.820 + end 7.821 + if remaining_header_size_limit == 0 then 7.822 + return error_response("413 Request Entity Too Large", "Headers too long") 7.823 + end 7.824 + local key, value = string.match(line, "^([^ \t\r]+):[ \t]*(.-)[ \t]*\r?\n$") 7.825 + if not key then 7.826 + return error_response("400 Bad Request") 7.827 + end 7.828 + local values = request.headers[key] 7.829 + values[#values+1] = value 7.830 + end 7.831 + -- process "Connection: close" header if existent: 7.832 + connection_close_requested = request.headers_flags["Connection"]["close"] 7.833 + -- process "Content-Length" header if existent: 7.834 + do 7.835 + local values = request.headers_csv_table["Content-Length"] 7.836 + if #values > 0 then 7.837 + request_body_content_length = tonumber(values[1]) 7.838 + local proper_value = tostring(request_body_content_length) 7.839 + for i, value in ipairs(values) do 7.840 + value = string.match(value, "^0*(.*)") 7.841 + if value ~= proper_value then 7.842 + return error_response("400 Bad Request", "Content-Length header(s) invalid") 7.843 + end 7.844 + end 7.845 + if request_body_content_length > remaining_body_size_limit then 7.846 + return error_response("413 Request Entity Too Large", "Request body too big") 7.847 + end 7.848 + end 7.849 + end 7.850 + -- process "Transfer-Encoding" header if existent: 7.851 + do 7.852 + local flag = request.headers_flags["Transfer-Encoding"]["chunked"] 7.853 + local list = request.headers_csv_table["Transfer-Encoding"] 7.854 + if (flag and #list ~= 1) or (not flag and #list ~= 0) then 7.855 + return error_response("400 Bad Request", "Unexpected Transfer-Encoding") 7.856 + end 7.857 + end 7.858 + -- process "Expect" header if existent: 7.859 + for i, value in ipairs(request.headers_csv_table["Expect"]) do 7.860 + if string.lower(value) ~= "100-continue" then 7.861 + return error_response("417 Expectation Failed", "Unexpected Expect header") 7.862 + end 7.863 + end 7.864 + -- parse GET params: 7.865 + request.path, request.query = string.match(request.url, "^([^?]*)%??(.*)$") 7.866 + read_urlencoded_form(request.get_params_list, request.query) 7.867 + -- parse cookies: 7.868 + for i, line in ipairs(request.headers["Cookie"]) do 7.869 + for rawkey, rawvalue in 7.870 + string.gmatch(line, "([^=; ]*)=([^=; ]*)") 7.871 + do 7.872 + request.cookies[decode_uri(rawkey)] = decode_uri(rawvalue) 7.873 + end 7.874 + end 7.875 + -- call underlying handler and remember boolean result: 7.876 + if handler(request) ~= true then survive = false end 7.877 + -- finish request (unless already done by underlying handler): 7.878 + request:finish() 7.879 + end 7.880 + end 7.881 + return survive 7.882 + end 7.883 +end 7.884 + 7.885 +return _M 7.886 +
8.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 8.2 +++ b/moonbridge.c Sun Jan 04 19:30:28 2015 +0100 8.3 @@ -0,0 +1,2715 @@ 8.4 + 8.5 +/*** Compile-time configuration ***/ 8.6 + 8.7 +#define MOONBR_LUA_PANIC_BUG_WORKAROUND 1 8.8 + 8.9 + 8.10 +/*** C preprocessor macros for portability support ***/ 8.11 + 8.12 +#ifndef __has_include 8.13 +#define __has_include(x) 0 8.14 +#endif 8.15 + 8.16 + 8.17 +/*** Include directives for used system libraries ***/ 8.18 + 8.19 +#if defined(__linux__) 8.20 +#define _GNU_SOURCE 8.21 +#endif 8.22 +#include <stdlib.h> 8.23 +#include <unistd.h> 8.24 +#include <stdint.h> 8.25 +#include <errno.h> 8.26 +#include <getopt.h> 8.27 +#include <syslog.h> 8.28 +#include <string.h> 8.29 +#include <stdio.h> 8.30 +#include <time.h> 8.31 +#include <sys/time.h> 8.32 +#include <sys/socket.h> 8.33 +#include <sys/un.h> 8.34 +#include <netinet/in.h> 8.35 +#include <poll.h> 8.36 +#include <signal.h> 8.37 +#include <sys/wait.h> 8.38 +#include <sys/resource.h> 8.39 +#include <sys/file.h> 8.40 +#if defined(__FreeBSD__) || __has_include(<libutil.h>) 8.41 +#include <libutil.h> 8.42 +#endif 8.43 +#if defined(__linux__) || __has_include(<bsd/libutil.h>) 8.44 +#include <bsd/libutil.h> 8.45 +#endif 8.46 +#if defined(__linux__) || __has_include(<bsd/unistd.h>) 8.47 +#include <bsd/unistd.h> 8.48 +#endif 8.49 + 8.50 + 8.51 +/*** Fallback definitions for missing constants on some platforms ***/ 8.52 + 8.53 +/* INFTIM is used as timeout parameter for poll() */ 8.54 +#ifndef INFTIM 8.55 +#define INFTIM -1 8.56 +#endif 8.57 + 8.58 + 8.59 +/*** Include directives for Lua ***/ 8.60 + 8.61 +#include <lua.h> 8.62 +#include <lauxlib.h> 8.63 +#include <lualib.h> 8.64 + 8.65 + 8.66 +/*** Constants ***/ 8.67 + 8.68 +/* Backlog option for listen() call */ 8.69 +#define MOONBR_LISTEN_BACKLOG 1024 8.70 + 8.71 +/* Maximum length of a timestamp used for strftime() */ 8.72 +#define MOONBR_LOG_MAXTIMELEN 40 8.73 + 8.74 +/* Maximum length of a log message */ 8.75 +#define MOONBR_LOG_MAXMSGLEN 4095 8.76 + 8.77 +/* Exitcodes passed to exit() call */ 8.78 +#define MOONBR_EXITCODE_GRACEFUL 0 8.79 +#define MOONBR_EXITCODE_CMDLINEERROR 1 8.80 +#define MOONBR_EXITCODE_ALREADYRUNNING 2 8.81 +#define MOONBR_EXITCODE_STARTUPERROR 3 8.82 +#define MOONBR_EXITCODE_RUNTIMEERROR 4 8.83 + 8.84 +/* Maximum length of a line sent to stderr by child processes */ 8.85 +#define MOONBR_MAXERRORLINELEN 1024 8.86 + 8.87 +/* Maximum length of an error string returned by strerror() */ 8.88 +#define MOONBR_MAXSTRERRORLEN 80 8.89 + 8.90 +/* Status bytes exchanged between master and child processes */ 8.91 +#define MOONBR_SOCKETTYPE_INTERVAL 'I' 8.92 +#define MOONBR_SOCKETTYPE_LOCAL 'L' 8.93 +#define MOONBR_SOCKETTYPE_NETWORK 'N' 8.94 +#define MOONBR_STATUS_IDLE '1' 8.95 +#define MOONBR_COMMAND_TERMINATE '2' 8.96 +#define MOONBR_STATUS_GOODBYE '3' 8.97 + 8.98 +/* Constant file descriptors */ 8.99 +#define MOONBR_FD_STDERR 2 8.100 +#define MOONBR_FD_CONTROL 3 8.101 +#define MOONBR_FD_END 4 8.102 + 8.103 +/* Return values of moonbr_try_destroy_worker() */ 8.104 +#define MOONBR_DESTROY_NONE 0 8.105 +#define MOONBR_DESTROY_PREPARE 1 8.106 +#define MOONBR_DESTROY_IDLE_OR_ASSIGNED 2 8.107 + 8.108 + 8.109 +/*** Types ***/ 8.110 + 8.111 +/* Enum for 'moonbr_pstate' */ 8.112 +#define MOONBR_PSTATE_STARTUP 0 8.113 +#define MOONBR_PSTATE_RUNNING 1 8.114 +#define MOONBR_PSTATE_FORKED 2 8.115 + 8.116 +/* Enum for 'proto' field of struct moonbr_listener */ 8.117 +#define MOONBR_PROTO_INTERVAL 1 8.118 +#define MOONBR_PROTO_LOCAL 2 8.119 +#define MOONBR_PROTO_TCP6 3 8.120 +#define MOONBR_PROTO_TCP4 4 8.121 + 8.122 +/* Data structure for a pool's listener that can accept incoming connections */ 8.123 +struct moonbr_listener { 8.124 + struct moonbr_pool *pool; 8.125 + struct moonbr_listener *prev_listener; /* previous idle or(!) connected listener */ 8.126 + struct moonbr_listener *next_listener; /* next idle or(!) connected listener */ 8.127 + int proto; 8.128 + union { 8.129 + struct { 8.130 + char *name; /* name of interval passed to 'connect' function as 'interval' field in table */ 8.131 + int strict; /* nonzero = runtime of 'connect' function does not delay interval */ 8.132 + struct timeval delay; /* interval between invocations of 'connect' function */ 8.133 + struct timeval wakeup; /* point in time of next invocation */ 8.134 + } interval; 8.135 + struct { 8.136 + char *path; /* full path name (i.e. filename with path) of UNIX domain socket */ 8.137 + } local; 8.138 + struct { 8.139 + int port; /* port number to listen on (in host endianess) */ 8.140 + int localhost_only; /* nonzero = listen on localhost only */ 8.141 + } tcp; 8.142 + } proto_specific; 8.143 + int listenfd; /* -1 = none */ 8.144 + int pollidx; /* -1 = none */ 8.145 +}; 8.146 + 8.147 +/* Data structure for a child process that is handling incoming connections */ 8.148 +struct moonbr_worker { 8.149 + struct moonbr_pool *pool; 8.150 + struct moonbr_worker *prev_worker; 8.151 + struct moonbr_worker *next_worker; 8.152 + struct moonbr_worker *prev_idle_worker; 8.153 + struct moonbr_worker *next_idle_worker; 8.154 + int idle; /* nonzero = waiting for command from parent process */ 8.155 + int assigned; /* nonzero = currently handling a connection */ 8.156 + pid_t pid; 8.157 + int controlfd; /* socket to send/receive control message to/from child process */ 8.158 + int errorfd; /* socket to receive error output from child process' stderr */ 8.159 + char *errorlinebuf; /* optional buffer for collecting stderr data from child process */ 8.160 + int errorlinelen; /* number of bytes stored in 'errorlinebuf' */ 8.161 + int errorlineovf; /* nonzero = line length overflow */ 8.162 + struct timeval idle_expiration; /* point in time until child process may stay in idle state */ 8.163 + struct moonbr_listener *restart_interval_listener; /* set while interval listener is assigned */ 8.164 +}; 8.165 + 8.166 +/* Data structure for a pool of workers and listeners */ 8.167 +struct moonbr_pool { 8.168 + int poolnum; /* number of pool for log output */ 8.169 + struct moonbr_pool *next_pool; /* next entry in linked list starting with 'moonbr_first_pool' */ 8.170 + struct moonbr_worker *first_worker; /* first worker of pool */ 8.171 + struct moonbr_worker *last_worker; /* last worker of pool */ 8.172 + struct moonbr_worker *first_idle_worker; /* first idle worker of pool */ 8.173 + struct moonbr_worker *last_idle_worker; /* last idle worker of pool */ 8.174 + int idle_worker_count; 8.175 + int unassigned_worker_count; 8.176 + int total_worker_count; 8.177 + int worker_count_stat; /* only needed for statistics */ 8.178 + int pre_fork; /* desired minimum number of unassigned workers */ 8.179 + int min_fork; /* desired minimum number of workers in total */ 8.180 + int max_fork; /* maximum number of workers */ 8.181 + struct timeval fork_delay; /* delay after each fork() until a fork may happen again */ 8.182 + struct timeval fork_wakeup; /* point in time when a fork may happen again (unless a worker terminates before) */ 8.183 + struct timeval fork_error_delay; /* delay between fork()s when an error during fork or preparation occurred */ 8.184 + struct timeval fork_error_wakeup; /* point in time when fork may happen again if an error in preparation occurred */ 8.185 + int use_fork_error_wakeup; /* nonzero = error in preparation occured; gets reset on next fork */ 8.186 + struct timeval exit_delay; /* delay for terminating excessive workers (unassigned_worker_count > pre_fork) */ 8.187 + struct timeval exit_wakeup; /* point in time when terminating an excessive worker */ 8.188 + struct timeval idle_timeout; /* delay before an idle worker is terminated */ 8.189 + size_t memory_limit; /* maximum bytes of memory that the Lua machine may allocate */ 8.190 + int listener_count; /* total number of listeners of pool (and size of 'listener' array at end of this struct) */ 8.191 + struct moonbr_listener *first_idle_listener; /* first listener that is idle (i.e. has no waiting connection) */ 8.192 + struct moonbr_listener *last_idle_listener; /* last listener that is idle (i.e. has no waiting connection) */ 8.193 + struct moonbr_listener *first_connected_listener; /* first listener that has a pending connection */ 8.194 + struct moonbr_listener *last_connected_listener; /* last listener that has a pending connection */ 8.195 + struct moonbr_listener listener[1]; /* static array of variable(!) size to contain 'listener' structures */ 8.196 +}; 8.197 + 8.198 +/* Enum for 'channel' field of struct moonbr_poll_worker */ 8.199 +#define MOONBR_POLL_WORKER_CONTROLCHANNEL 1 8.200 +#define MOONBR_POLL_WORKER_ERRORCHANNEL 2 8.201 + 8.202 +/* Structure to refer from 'moonbr_poll_worker_fds' entry to worker structure */ 8.203 +struct moonbr_poll_worker { 8.204 + struct moonbr_worker *worker; 8.205 + int channel; /* field indicating whether file descriptor is 'controlfd' or 'errorfd' */ 8.206 +}; 8.207 + 8.208 +/* Variable indicating that clean shutdown was requested */ 8.209 +static int moonbr_shutdown_in_progress = 0; 8.210 + 8.211 + 8.212 +/*** Macros for Lua registry ***/ 8.213 + 8.214 +/* Lightuserdata keys for Lua registry to store 'prepare', 'connect', and 'finish' functions */ 8.215 +#define moonbr_luakey_prepare_func(pool) ((void *)(intptr_t)(pool) + 0) 8.216 +#define moonbr_luakey_connect_func(pool) ((void *)(intptr_t)(pool) + 1) 8.217 +#define moonbr_luakey_finish_func(pool) ((void *)(intptr_t)(pool) + 2) 8.218 + 8.219 + 8.220 +/*** Global variables ***/ 8.221 + 8.222 +/* State of process execution */ 8.223 +static int moonbr_pstate = MOONBR_PSTATE_STARTUP; 8.224 + 8.225 +/* Process ID of the main process */ 8.226 +static pid_t moonbr_masterpid; 8.227 + 8.228 +/* Condition variables set by the signal handler */ 8.229 +static volatile sig_atomic_t moonbr_cond_poll = 0; 8.230 +static volatile sig_atomic_t moonbr_cond_terminate = 0; 8.231 +static volatile sig_atomic_t moonbr_cond_interrupt = 0; 8.232 +static volatile sig_atomic_t moonbr_cond_child = 0; 8.233 + 8.234 +/* Socket pair to denote signal delivery when signal handler was called just before poll() */ 8.235 +static int moonbr_poll_signalfds[2]; 8.236 +#define moonbr_poll_signalfd_read moonbr_poll_signalfds[0] 8.237 +#define moonbr_poll_signalfd_write moonbr_poll_signalfds[1] 8.238 + 8.239 +/* Global variables for pidfile and logging */ 8.240 +static struct pidfh *moonbr_pidfh = NULL; 8.241 +static FILE *moonbr_logfile = NULL; 8.242 +static int moonbr_use_syslog = 0; 8.243 + 8.244 +/* First and last entry of linked list of all created pools during initialization */ 8.245 +static struct moonbr_pool *moonbr_first_pool = NULL; 8.246 +static struct moonbr_pool *moonbr_last_pool = NULL; 8.247 + 8.248 +/* Total count of pools */ 8.249 +static int moonbr_pool_count = 0; 8.250 + 8.251 +/* Set to a nonzero value if dynamic part of 'moonbr_poll_fds' ('moonbr_poll_worker_fds') needs an update */ 8.252 +static int moonbr_poll_refresh_needed = 0; 8.253 + 8.254 +/* Array passed to poll(), consisting of static part and dynamic part ('moonbr_poll_worker_fds') */ 8.255 +static struct pollfd *moonbr_poll_fds = NULL; /* the array */ 8.256 +static int moonbr_poll_fds_bufsize = 0; /* memory allocated for this number of elements */ 8.257 +static int moonbr_poll_fds_count = 0; /* total number of elements */ 8.258 +static int moonbr_poll_fds_static_count; /* number of elements in static part */ 8.259 + 8.260 +/* Dynamic part of 'moonbr_poll_fds' array */ 8.261 +#define moonbr_poll_worker_fds (moonbr_poll_fds+moonbr_poll_fds_static_count) 8.262 + 8.263 +/* Additional information for dynamic part of 'moonbr_poll_fds' array */ 8.264 +struct moonbr_poll_worker *moonbr_poll_workers; /* the array */ 8.265 +static int moonbr_poll_workers_bufsize = 0; /* memory allocated for this number of elements */ 8.266 +static int moonbr_poll_worker_count = 0; /* number of elements in array */ 8.267 + 8.268 +/* Variable set to nonzero value to disallow further calls of 'listen' function */ 8.269 +static int moonbr_booted = 0; 8.270 + 8.271 +/* Global variables to store information on connection socket in child process */ 8.272 +static int moonbr_child_peersocket_type; /* type of socket by MOONBR_SOCKETTYPE constant */ 8.273 +static int moonbr_child_peersocket_fd; /* Original file descriptor of peer socket */ 8.274 +static luaL_Stream *moonbr_child_peersocket_inputstream; /* Lua input stream of socket */ 8.275 +static luaL_Stream *moonbr_child_peersocket_outputstream; /* Lua output stream of socket */ 8.276 + 8.277 +/* Verbosity settings */ 8.278 +static int moonbr_debug = 0; 8.279 +static int moonbr_stat = 0; 8.280 + 8.281 +/* Memory consumption by Lua machine */ 8.282 +static size_t moonbr_memory_usage = 0; 8.283 +static size_t moonbr_memory_limit = 0; 8.284 + 8.285 + 8.286 +/*** Functions for signal handling ***/ 8.287 + 8.288 +/* Signal handler for master and child processes */ 8.289 +static void moonbr_signal(int sig) { 8.290 + if (getpid() == moonbr_masterpid) { 8.291 + /* master process */ 8.292 + switch (sig) { 8.293 + case SIGHUP: 8.294 + case SIGINT: 8.295 + /* fast shutdown requested */ 8.296 + moonbr_cond_interrupt = 1; 8.297 + break; 8.298 + case SIGTERM: 8.299 + /* clean shutdown requested */ 8.300 + moonbr_cond_terminate = 1; 8.301 + break; 8.302 + case SIGCHLD: 8.303 + /* child process terminated */ 8.304 + moonbr_cond_child = 1; 8.305 + break; 8.306 + } 8.307 + if (moonbr_cond_poll) { 8.308 + /* avoid race condition if signal handler is invoked right before poll() */ 8.309 + char buf[1] = {0}; 8.310 + write(moonbr_poll_signalfd_write, buf, 1); 8.311 + } 8.312 + } else { 8.313 + /* child process forwards certain signals to parent process */ 8.314 + switch (sig) { 8.315 + case SIGHUP: 8.316 + case SIGINT: 8.317 + case SIGTERM: 8.318 + kill(moonbr_masterpid, sig); 8.319 + } 8.320 + } 8.321 +} 8.322 + 8.323 +/* Initialize signal handling */ 8.324 +static void moonbr_signal_init(){ 8.325 + moonbr_masterpid = getpid(); 8.326 + signal(SIGHUP, moonbr_signal); 8.327 + signal(SIGINT, moonbr_signal); 8.328 + signal(SIGTERM, moonbr_signal); 8.329 + signal(SIGCHLD, moonbr_signal); 8.330 +} 8.331 + 8.332 + 8.333 +/*** Functions for logging in master process ***/ 8.334 + 8.335 +/* Logs a pre-formatted message with given syslog() priority */ 8.336 +static void moonbr_log_msg(int priority, const char *msg) { 8.337 + if (moonbr_logfile) { 8.338 + /* logging to logfile desired (timestamp is prepended in that case) */ 8.339 + time_t now_time = 0; 8.340 + struct tm now_tmstruct; 8.341 + char timestr[MOONBR_LOG_MAXTIMELEN+1]; 8.342 + time(&now_time); 8.343 + localtime_r(&now_time, &now_tmstruct); 8.344 + if (!strftime( 8.345 + timestr, MOONBR_LOG_MAXTIMELEN+1, "%Y-%m-%d %H:%M:%S %Z: ", &now_tmstruct 8.346 + )) timestr[0] = 0; 8.347 + fprintf(moonbr_logfile, "%s%s\n", timestr, msg); 8.348 + } 8.349 + if (moonbr_use_syslog) { 8.350 + /* logging through syslog desired */ 8.351 + syslog(priority, "%s", msg); 8.352 + } 8.353 +} 8.354 + 8.355 +/* Formats a message via vsnprintf() and logs it with given syslog() priority */ 8.356 +static void moonbr_log(int priority, const char *message, ...) { 8.357 + char msgbuf[MOONBR_LOG_MAXMSGLEN+1]; /* buffer of static size to store formatted message */ 8.358 + int msglen; /* length of full message (may exceed MOONBR_LOG_MAXMSGLEN) */ 8.359 + { 8.360 + /* pass variable arguments to vsnprintf() to format message */ 8.361 + va_list ap; 8.362 + va_start(ap, message); 8.363 + msglen = vsnprintf(msgbuf, MOONBR_LOG_MAXMSGLEN+1, message, ap); 8.364 + va_end(ap); 8.365 + } 8.366 + { 8.367 + /* split and log message line by line */ 8.368 + char *line = msgbuf; 8.369 + while (1) { 8.370 + char *endptr = strchr(line, '\n'); 8.371 + if (endptr) { 8.372 + /* terminate string where newline character is found */ 8.373 + *endptr = 0; 8.374 + } else if (line != msgbuf && msglen > MOONBR_LOG_MAXMSGLEN) { 8.375 + /* break if line is incomplete and not the first line */ 8.376 + break; 8.377 + } 8.378 + moonbr_log_msg(priority, line); 8.379 + if (!endptr) break; /* break if end of formatted message is reached */ 8.380 + line = endptr+1; /* otherwise continue with remaining message */ 8.381 + } 8.382 + } 8.383 + if (msglen > MOONBR_LOG_MAXMSGLEN) { 8.384 + /* print warning if message was truncated */ 8.385 + moonbr_log_msg(priority, "Previous log message has been truncated due to excessive length"); 8.386 + } 8.387 +} 8.388 + 8.389 + 8.390 +/*** Termination function ***/ 8.391 + 8.392 +/* Kill all child processes, remove PID file (if existent), and exit master process with given exitcode */ 8.393 +static void moonbr_terminate(int exitcode) { 8.394 + { 8.395 + struct moonbr_pool *pool; 8.396 + for (pool=moonbr_first_pool; pool; pool=pool->next_pool) { 8.397 + { 8.398 + struct moonbr_worker *worker; 8.399 + for (worker=pool->first_worker; worker; worker=worker->next_worker) { 8.400 + moonbr_log(LOG_INFO, "Sending SIGKILL to child with PID %i", (int)worker->pid); 8.401 + if (kill(worker->pid, SIGKILL)) { 8.402 + moonbr_log(LOG_ERR, "Error while killing child process: %s", strerror(errno)); 8.403 + } 8.404 + } 8.405 + } 8.406 + { 8.407 + int i; 8.408 + for (i=0; i<pool->listener_count; i++) { 8.409 + struct moonbr_listener *listener = &pool->listener[i]; 8.410 + if (listener->proto == MOONBR_PROTO_LOCAL) { 8.411 + moonbr_log(LOG_INFO, "Unlinking local socket \"%s\"", listener->proto_specific.local.path); 8.412 + if (unlink(listener->proto_specific.local.path)) { 8.413 + moonbr_log(LOG_ERR, "Error while unlinking local socket: %s", strerror(errno)); 8.414 + } 8.415 + } 8.416 + } 8.417 + } 8.418 + } 8.419 + } 8.420 + moonbr_log(exitcode ? LOG_ERR : LOG_NOTICE, "Terminating with exit code %i", exitcode); 8.421 + if (moonbr_pidfh && pidfile_remove(moonbr_pidfh)) { 8.422 + moonbr_log(LOG_ERR, "Error while removing PID file: %s", strerror(errno)); 8.423 + } 8.424 + exit(exitcode); 8.425 +} 8.426 + 8.427 +/* Terminate with either MOONBR_EXITCODE_STARTUPERROR or MOONBR_EXITCODE_RUNTIMEERROR */ 8.428 +#define moonbr_terminate_error() \ 8.429 + moonbr_terminate( \ 8.430 + moonbr_pstate == MOONBR_PSTATE_STARTUP ? \ 8.431 + MOONBR_EXITCODE_STARTUPERROR : \ 8.432 + MOONBR_EXITCODE_RUNTIMEERROR \ 8.433 + ) 8.434 + 8.435 + 8.436 +/*** Helper functions ***/ 8.437 + 8.438 +/* Fills a 'struct timeval' structure with the current time (using CLOCK_MONOTONIC) */ 8.439 +static void moonbr_now(struct timeval *now) { 8.440 + struct timespec ts = {0, }; 8.441 + if (clock_gettime(CLOCK_MONOTONIC, &ts)) { 8.442 + moonbr_log(LOG_CRIT, "Error in clock_gettime() call: %s", strerror(errno)); 8.443 + moonbr_terminate_error(); 8.444 + } 8.445 + *now = (struct timeval){ .tv_sec = ts.tv_sec, .tv_usec = ts.tv_nsec / 1000 }; 8.446 +} 8.447 + 8.448 +/* Formats a 'struct timeval' value (not thread-safe) */ 8.449 +static char *moonbr_format_timeval(struct timeval *t) { 8.450 + static char buf[32]; 8.451 + snprintf(buf, 32, "%ji.%06ji seconds", (intmax_t)t->tv_sec, (intmax_t)t->tv_usec); 8.452 + return buf; 8.453 +} 8.454 + 8.455 + 8.456 +/*** Functions for pool creation and startup ***/ 8.457 + 8.458 +/* Creates a 'struct moonbr_pool' structure with a given number of listeners */ 8.459 +static struct moonbr_pool *moonbr_create_pool(int listener_count) { 8.460 + struct moonbr_pool *pool; 8.461 + pool = calloc(1, 8.462 + sizeof(struct moonbr_pool) + /* size of 'struct moonbr_pool' with one listener */ 8.463 + (listener_count-1) * sizeof(struct moonbr_listener) /* size of extra listeners */ 8.464 + ); 8.465 + if (!pool) { 8.466 + moonbr_log(LOG_CRIT, "Memory allocation error"); 8.467 + moonbr_terminate_error(); 8.468 + } 8.469 + pool->listener_count = listener_count; 8.470 + { 8.471 + /* initialization of listeners */ 8.472 + int i; 8.473 + for (i=0; i<listener_count; i++) { 8.474 + struct moonbr_listener *listener = &pool->listener[i]; 8.475 + listener->pool = pool; 8.476 + listener->listenfd = -1; 8.477 + listener->pollidx = -1; 8.478 + } 8.479 + } 8.480 + return pool; 8.481 +} 8.482 + 8.483 +/* Destroys a 'struct moonbr_pool' structure before it has been started */ 8.484 +static void moonbr_destroy_pool(struct moonbr_pool *pool) { 8.485 + int i; 8.486 + for (i=0; i<pool->listener_count; i++) { 8.487 + struct moonbr_listener *listener = &pool->listener[i]; 8.488 + if ( 8.489 + listener->proto == MOONBR_PROTO_INTERVAL && 8.490 + listener->proto_specific.interval.name 8.491 + ) { 8.492 + free(listener->proto_specific.interval.name); 8.493 + } 8.494 + if ( 8.495 + listener->proto == MOONBR_PROTO_LOCAL && 8.496 + listener->proto_specific.local.path 8.497 + ) { 8.498 + free(listener->proto_specific.local.path); 8.499 + } 8.500 + } 8.501 + free(pool); 8.502 +} 8.503 + 8.504 +/* Starts a all listeners in a pool */ 8.505 +static int moonbr_start_pool(struct moonbr_pool *pool) { 8.506 + moonbr_log(LOG_INFO, "Creating pool", pool->poolnum); 8.507 + { 8.508 + int i; 8.509 + for (i=0; i<pool->listener_count; i++) { 8.510 + struct moonbr_listener *listener = &pool->listener[i]; 8.511 + switch (listener->proto) { 8.512 + case MOONBR_PROTO_INTERVAL: 8.513 + // nothing to do here: starting intervals is performed in moonbr_run() function 8.514 + if (!listener->proto_specific.interval.name) { 8.515 + moonbr_log(LOG_INFO, "Adding unnamed interval listener"); 8.516 + } else { 8.517 + moonbr_log(LOG_INFO, "Adding interval listener \"%s\"", listener->proto_specific.interval.name); 8.518 + } 8.519 + break; 8.520 + case MOONBR_PROTO_LOCAL: 8.521 + moonbr_log(LOG_INFO, "Adding local socket listener for path \"%s\"", listener->proto_specific.local.path); 8.522 + { 8.523 + struct sockaddr_un servaddr = { .sun_family = AF_UNIX }; 8.524 + const int path_maxlen = sizeof(struct sockaddr_un) - ( 8.525 + (void *)&servaddr.sun_path - (void *)&servaddr 8.526 + ); 8.527 + if ( 8.528 + snprintf( 8.529 + servaddr.sun_path, 8.530 + path_maxlen, 8.531 + "%s", 8.532 + listener->proto_specific.local.path 8.533 + ) >= path_maxlen 8.534 + ) { 8.535 + errno = ENAMETOOLONG; 8.536 + }; 8.537 + listener->listenfd = socket(PF_LOCAL, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); 8.538 + if (listener->listenfd == -1) goto moonbr_start_pool_error; 8.539 + if (!unlink(listener->proto_specific.local.path)) { 8.540 + moonbr_log(LOG_WARNING, "Unlinked named socket \"%s\" prior to listening", listener->proto_specific.local.path); 8.541 + } else { 8.542 + if (errno != ENOENT) { 8.543 + moonbr_log(LOG_ERR, "Could not unlink named socket \"%s\" prior to listening: %s", listener->proto_specific.local.path, strerror(errno)); 8.544 + } 8.545 + } 8.546 + if ( 8.547 + bind(listener->listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) 8.548 + ) goto moonbr_start_pool_error; 8.549 + if (listen(listener->listenfd, MOONBR_LISTEN_BACKLOG)) goto moonbr_start_pool_error; 8.550 + } 8.551 + break; 8.552 + case MOONBR_PROTO_TCP6: 8.553 + if (listener->proto_specific.tcp.localhost_only) { 8.554 + moonbr_log(LOG_INFO, "Adding localhost TCP/IPv6 listener on port %i", listener->proto_specific.tcp.port); 8.555 + } else { 8.556 + moonbr_log(LOG_INFO, "Adding public TCP/IPv6 listener on port %i", listener->proto_specific.tcp.port); 8.557 + } 8.558 + { 8.559 + struct sockaddr_in6 servaddr = { 8.560 + .sin6_family = AF_INET6, 8.561 + .sin6_port = htons(listener->proto_specific.tcp.port) 8.562 + }; 8.563 + listener->listenfd = socket(PF_INET6, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); 8.564 + if (listener->listenfd == -1) goto moonbr_start_pool_error; 8.565 + { 8.566 + /* avoid "Address already in use" error when restarting service */ 8.567 + static const int reuseval = 1; 8.568 + if (setsockopt( 8.569 + listener->listenfd, SOL_SOCKET, SO_REUSEADDR, &reuseval, sizeof(reuseval) 8.570 + )) goto moonbr_start_pool_error; 8.571 + } 8.572 + { 8.573 + /* default to send TCP RST when process terminates unexpectedly */ 8.574 + static const struct linger lingerval = { 8.575 + .l_onoff = 1, 8.576 + .l_linger = 0 8.577 + }; 8.578 + if (setsockopt( 8.579 + listener->listenfd, SOL_SOCKET, SO_LINGER, &lingerval, sizeof(lingerval) 8.580 + )) goto moonbr_start_pool_error; 8.581 + } 8.582 + if (listener->proto_specific.tcp.localhost_only) { 8.583 + servaddr.sin6_addr.s6_addr[15] = 1; 8.584 + } 8.585 + if ( 8.586 + bind(listener->listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) 8.587 + ) goto moonbr_start_pool_error; 8.588 + if (listen(listener->listenfd, MOONBR_LISTEN_BACKLOG)) goto moonbr_start_pool_error; 8.589 + } 8.590 + break; 8.591 + case MOONBR_PROTO_TCP4: 8.592 + if (listener->proto_specific.tcp.localhost_only) { 8.593 + moonbr_log(LOG_INFO, "Adding localhost TCP/IPv4 listener on port %i", listener->proto_specific.tcp.port); 8.594 + } else { 8.595 + moonbr_log(LOG_INFO, "Adding public TCP/IPv4 listener on port %i", listener->proto_specific.tcp.port); 8.596 + } 8.597 + { 8.598 + struct sockaddr_in servaddr = { 8.599 + .sin_family = AF_INET, 8.600 + .sin_port = htons(listener->proto_specific.tcp.port) 8.601 + }; 8.602 + listener->listenfd = socket(PF_INET, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); 8.603 + if (listener->listenfd == -1) goto moonbr_start_pool_error; 8.604 + { 8.605 + /* avoid "Address already in use" error when restarting service */ 8.606 + static const int reuseval = 1; 8.607 + if (setsockopt( 8.608 + listener->listenfd, SOL_SOCKET, SO_REUSEADDR, &reuseval, sizeof(reuseval) 8.609 + )) goto moonbr_start_pool_error; 8.610 + } 8.611 + { 8.612 + /* default to send TCP RST when process terminates unexpectedly */ 8.613 + static const struct linger lingerval = { 8.614 + .l_onoff = 1, 8.615 + .l_linger = 0 8.616 + }; 8.617 + if (setsockopt( 8.618 + listener->listenfd, SOL_SOCKET, SO_LINGER, &lingerval, sizeof(lingerval) 8.619 + )) goto moonbr_start_pool_error; 8.620 + } 8.621 + if (listener->proto_specific.tcp.localhost_only) { 8.622 + ((uint8_t *)&servaddr.sin_addr.s_addr)[0] = 127; 8.623 + ((uint8_t *)&servaddr.sin_addr.s_addr)[3] = 1; 8.624 + } 8.625 + if ( 8.626 + bind(listener->listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) 8.627 + ) goto moonbr_start_pool_error; 8.628 + if (listen(listener->listenfd, MOONBR_LISTEN_BACKLOG)) goto moonbr_start_pool_error; 8.629 + } 8.630 + break; 8.631 + default: 8.632 + moonbr_log(LOG_CRIT, "Internal error (should not happen): Unexpected value in listener.proto field"); 8.633 + moonbr_terminate_error(); 8.634 + } 8.635 + } 8.636 + goto moonbr_start_pool_ok; 8.637 + moonbr_start_pool_error: 8.638 + { 8.639 + int j = i; 8.640 + int errno2 = errno; 8.641 + for (; i>=0; i--) { 8.642 + struct moonbr_listener *listener = &pool->listener[i]; 8.643 + if (listener->listenfd != -1) close(listener->listenfd); 8.644 + } 8.645 + errno = errno2; 8.646 + return j; 8.647 + } 8.648 + } 8.649 + moonbr_start_pool_ok: 8.650 + pool->poolnum = ++moonbr_pool_count; 8.651 + moonbr_log(LOG_INFO, "Pool #%i created", pool->poolnum); 8.652 + if (moonbr_last_pool) moonbr_last_pool->next_pool = pool; 8.653 + else moonbr_first_pool = pool; 8.654 + moonbr_last_pool = pool; 8.655 + return -1; 8.656 +} 8.657 + 8.658 + 8.659 +/*** Function to send data and a file descriptor to child process */ 8.660 + 8.661 +/* Sends control message of one bye plus optional file descriptor plus optional pointer to child process */ 8.662 +static void moonbr_send_control_message(struct moonbr_worker *worker, char status, int fd, void *ptr) { 8.663 + { 8.664 + struct iovec iovector = { .iov_base = &status, .iov_len = 1 }; /* carrying status byte */ 8.665 + char control_message_buffer[CMSG_SPACE(sizeof(int))] = {0, }; /* used to transfer file descriptor */ 8.666 + struct msghdr message = { .msg_iov = &iovector, .msg_iovlen = 1 }; /* data structure passed to sendmsg() call */ 8.667 + if (moonbr_debug) { 8.668 + if (fd == -1) { 8.669 + moonbr_log(LOG_DEBUG, "Sending control message \"%c\" to child process in pool #%i (PID %i)", (int)status, worker->pool->poolnum, (int)worker->pid); 8.670 + } else { 8.671 + moonbr_log(LOG_DEBUG, "Sending control message \"%c\" with file descriptor #%i to child process in pool #%i (PID %i)", (int)status, fd, worker->pool->poolnum, (int)worker->pid); 8.672 + } 8.673 + } 8.674 + if (fd != -1) { 8.675 + /* attach control message with file descriptor */ 8.676 + message.msg_control = control_message_buffer; 8.677 + message.msg_controllen = CMSG_SPACE(sizeof(int)); 8.678 + { 8.679 + struct cmsghdr *control_message = CMSG_FIRSTHDR(&message); 8.680 + control_message->cmsg_level = SOL_SOCKET; 8.681 + control_message->cmsg_type = SCM_RIGHTS; 8.682 + control_message->cmsg_len = CMSG_LEN(sizeof(int)); 8.683 + *((int *)CMSG_DATA(control_message)) = fd; 8.684 + } 8.685 + } 8.686 + while (sendmsg(worker->controlfd, &message, MSG_NOSIGNAL) < 0) { 8.687 + if (errno == EPIPE) { 8.688 + moonbr_log(LOG_ERR, "Error while communicating with idle child process in pool #%i (PID %i): %s", worker->pool->poolnum, (int)worker->pid, strerror(errno)); 8.689 + return; /* do not close socket; socket is closed when reading from it */ 8.690 + } 8.691 + if (errno != EINTR) { 8.692 + moonbr_log(LOG_CRIT, "Unexpected error while communicating with idle child process in pool #%i (PID %i): %s", worker->pool->poolnum, (int)worker->pid, strerror(errno)); 8.693 + moonbr_terminate_error(); 8.694 + } 8.695 + } 8.696 + } 8.697 + if (ptr) { 8.698 + char buf[sizeof(void *)]; 8.699 + char *pos = buf; 8.700 + int len = sizeof(void *); 8.701 + ssize_t written; 8.702 + if (moonbr_debug) { 8.703 + moonbr_log(LOG_DEBUG, "Sending memory pointer to child process in pool #%i (PID %i)", (int)status, worker->pool->poolnum, (int)worker->pid); 8.704 + } 8.705 + *((intptr_t *)buf) = (intptr_t)ptr; 8.706 + while (len) { 8.707 + written = send(worker->controlfd, pos, len, MSG_NOSIGNAL); 8.708 + if (written > 0) { 8.709 + pos += written; 8.710 + len -= written; 8.711 + } else if (errno == EPIPE) { 8.712 + moonbr_log(LOG_ERR, "Error while communicating with idle child process in pool #%i (PID %i): %s", worker->pool->poolnum, (int)worker->pid, strerror(errno)); 8.713 + return; /* do not close socket; socket is closed when reading from it */ 8.714 + } else if (errno != EINTR) { 8.715 + moonbr_log(LOG_CRIT, "Unexpected error while communicating with idle child process in pool #%i (PID %i): %s", worker->pool->poolnum, (int)worker->pid, strerror(errno)); 8.716 + moonbr_terminate_error(); 8.717 + } 8.718 + } 8.719 + } 8.720 +} 8.721 + 8.722 + 8.723 +/*** Functions running in child process ***/ 8.724 + 8.725 +/* Logs an error in child process */ 8.726 +static void moonbr_child_log(const char *message) { 8.727 + fprintf(stderr, "%s\n", message); 8.728 +} 8.729 + 8.730 +/* Logs a fatal error in child process and terminates process with error status */ 8.731 +static void moonbr_child_log_fatal(const char *message) { 8.732 + moonbr_child_log(message); 8.733 + exit(1); 8.734 +} 8.735 + 8.736 +/* Logs an error in child process while appending error string for global errno variable */ 8.737 +static void moonbr_child_log_errno(const char *message) { 8.738 + char errmsg[MOONBR_MAXSTRERRORLEN]; 8.739 + strerror_r(errno, errmsg, MOONBR_MAXSTRERRORLEN); /* use thread-safe call in case child created threads */ 8.740 + fprintf(stderr, "%s: %s\n", message, errmsg); 8.741 +} 8.742 + 8.743 +/* Logs a fatal error in child process while appending error string for errno and terminating process */ 8.744 +static void moonbr_child_log_errno_fatal(const char *message) { 8.745 + moonbr_child_log_errno(message); 8.746 + exit(1); 8.747 +} 8.748 + 8.749 +/* Receives a control message consisting of one character plus an optional file descriptor from parent process */ 8.750 +static void moonbr_child_receive_control_message(int socketfd, char *status, int *fd) { 8.751 + struct iovec iovector = { .iov_base = status, .iov_len = 1 }; /* reference to status byte variable */ 8.752 + char control_message_buffer[CMSG_SPACE(sizeof(int))] = {0, }; /* used to receive file descriptor */ 8.753 + struct msghdr message = { /* data structure passed to recvmsg() call */ 8.754 + .msg_iov = &iovector, 8.755 + .msg_iovlen = 1, 8.756 + .msg_control = control_message_buffer, 8.757 + .msg_controllen = CMSG_SPACE(sizeof(int)) 8.758 + }; 8.759 + { 8.760 + int received; 8.761 + while ((received = recvmsg(socketfd, &message, MSG_CMSG_CLOEXEC)) < 0) { 8.762 + if (errno != EINTR) { 8.763 + moonbr_child_log_errno_fatal("Error while trying to receive connection socket from parent process"); 8.764 + } 8.765 + } 8.766 + if (!received) { 8.767 + moonbr_child_log_fatal("Unexpected EOF while trying to receive connection socket from parent process"); 8.768 + } 8.769 + } 8.770 + { 8.771 + struct cmsghdr *control_message = CMSG_FIRSTHDR(&message); 8.772 + if (control_message) { 8.773 + if (control_message->cmsg_level != SOL_SOCKET) { 8.774 + moonbr_child_log_fatal("Received control message with cmsg_level not equal to SOL_SOCKET"); 8.775 + } 8.776 + if (control_message->cmsg_type != SCM_RIGHTS) { 8.777 + moonbr_child_log_fatal("Received control message with cmsg_type not equal to SCM_RIGHTS"); 8.778 + } 8.779 + *fd = *((int *)CMSG_DATA(control_message)); 8.780 + } else { 8.781 + *fd = -1; 8.782 + } 8.783 + } 8.784 +} 8.785 + 8.786 +/* Receives a pointer from parent process */ 8.787 +static void *moonbr_child_receive_pointer(int socketfd) { 8.788 + char buf[sizeof(void *)]; 8.789 + char *pos = buf; 8.790 + int len = sizeof(void *); 8.791 + ssize_t bytes_read; 8.792 + while (len) { 8.793 + bytes_read = recv(socketfd, pos, len, 0); 8.794 + if (bytes_read > 0) { 8.795 + pos += bytes_read; 8.796 + len -= bytes_read; 8.797 + } else if (!bytes_read) { 8.798 + moonbr_child_log_fatal("Unexpected EOF while trying to receive memory pointer from parent process"); 8.799 + } else if (errno != EINTR) { 8.800 + moonbr_child_log_errno_fatal("Error while trying to receive memory pointer from parent process"); 8.801 + } 8.802 + } 8.803 + return (void *)*(intptr_t *)buf; 8.804 +} 8.805 + 8.806 +/* Throws a Lua error message with an error string for errno appended to it */ 8.807 +static void moonbr_child_lua_errno_error(lua_State *L, char *message) { 8.808 + char errmsg[MOONBR_MAXSTRERRORLEN]; 8.809 + strerror_r(errno, errmsg, MOONBR_MAXSTRERRORLEN); /* use thread-safe call in case child created threads */ 8.810 + luaL_error(L, "%s: %s", message, errmsg); 8.811 +} 8.812 + 8.813 +/* Closes the input stream from peer unless it has already been closed */ 8.814 +static int moonbr_child_close_peersocket_inputstream( 8.815 + int cleanshut, /* nonzero = use shutdown() if applicable */ 8.816 + int mark /* nonzero = mark the stream as closed for Lua */ 8.817 +) { 8.818 + int err = 0; /* nonzero = error occurred */ 8.819 + int errno2; /* stores previous errno values that take precedence */ 8.820 + if (moonbr_child_peersocket_inputstream->f) { 8.821 + if (cleanshut && moonbr_child_peersocket_type == MOONBR_SOCKETTYPE_NETWORK) { 8.822 + if (shutdown(moonbr_child_peersocket_fd, SHUT_RD)) { 8.823 + errno2 = errno; 8.824 + err = -1; 8.825 + } 8.826 + } 8.827 + if (fclose(moonbr_child_peersocket_inputstream->f)) { 8.828 + if (!err) errno2 = errno; 8.829 + err = -1; 8.830 + } 8.831 + moonbr_child_peersocket_inputstream->f = NULL; 8.832 + } 8.833 + if (mark) moonbr_child_peersocket_inputstream->closef = NULL; 8.834 + if (err) errno = errno2; 8.835 + return err; 8.836 +} 8.837 + 8.838 +/* Closes the output stream to peer unless it has already been closed */ 8.839 +static int moonbr_child_close_peersocket_outputstream( 8.840 + int cleanshut, /* nonzero = use fflush() and shutdown() if applicable */ 8.841 + int mark /* nonzero = mark the stream as closed for Lua */ 8.842 +) { 8.843 + int err = 0; /* nonzero = error occurred */ 8.844 + int errno2; /* stores previous errno values that take precedence */ 8.845 + if (moonbr_child_peersocket_outputstream->f) { 8.846 + if (moonbr_child_peersocket_type == MOONBR_SOCKETTYPE_NETWORK) { 8.847 + if (cleanshut) { 8.848 + if (fflush(moonbr_child_peersocket_outputstream->f)) { 8.849 + errno2 = errno; 8.850 + err = -1; 8.851 + } else { 8.852 + if (shutdown(moonbr_child_peersocket_fd, SHUT_WR)) { 8.853 + errno2 = errno; 8.854 + err = -1; 8.855 + } 8.856 + } 8.857 + } else { 8.858 + fpurge(moonbr_child_peersocket_outputstream->f); 8.859 + } 8.860 + } 8.861 + if (fclose(moonbr_child_peersocket_outputstream->f)) { 8.862 + if (!err) errno2 = errno; 8.863 + err = -1; 8.864 + } 8.865 + moonbr_child_peersocket_outputstream->f = NULL; 8.866 + } 8.867 + if (mark) moonbr_child_peersocket_outputstream->closef = NULL; 8.868 + if (err) errno = errno2; 8.869 + return err; 8.870 +} 8.871 + 8.872 +/* Perform a clean shutdown of input and output stream (may be called multiple times) */ 8.873 +static int moonbr_child_close_peersocket(int timeout) { 8.874 + int errprio = 0; 8.875 + int errno2; 8.876 + if (moonbr_child_peersocket_fd == -1) return 0; 8.877 + if (moonbr_child_close_peersocket_inputstream(1, 1)) { 8.878 + errprio = 1; 8.879 + errno2 = errno; 8.880 + } 8.881 + if (moonbr_child_close_peersocket_outputstream(1, 1)) { 8.882 + errprio = 4; 8.883 + errno2 = errno; 8.884 + } 8.885 + if (moonbr_child_peersocket_type == MOONBR_SOCKETTYPE_NETWORK) { 8.886 + struct linger lingerval = { 0, }; 8.887 + if (timeout && !errprio) { 8.888 + lingerval.l_onoff = 1; 8.889 + lingerval.l_linger = timeout; 8.890 + } 8.891 + if (setsockopt(moonbr_child_peersocket_fd, SOL_SOCKET, SO_LINGER, &lingerval, sizeof(lingerval))) { 8.892 + if (errprio < 2) { 8.893 + errprio = 2; 8.894 + errno2 = errno; 8.895 + } 8.896 + } 8.897 + } 8.898 + if (close(moonbr_child_peersocket_fd)) { 8.899 + if (errprio < 3) { 8.900 + errprio = 3; 8.901 + errno2 = errno; 8.902 + } 8.903 + } 8.904 + moonbr_child_peersocket_fd = -1; 8.905 + if (errprio) { 8.906 + errno = errno2; 8.907 + return -1; 8.908 + } 8.909 + return 0; 8.910 +} 8.911 + 8.912 +/* Close socket and cause reset of TCP connection (TCP RST aka "Connection reset by peer") if possible */ 8.913 +static int moonbr_child_cancel_peersocket() { 8.914 + int err = 0; 8.915 + if (moonbr_child_close_peersocket_inputstream(0, 1)) err = -1; 8.916 + if (moonbr_child_close_peersocket_outputstream(0, 1)) err = -1; 8.917 + if (close(moonbr_child_peersocket_fd)) err = -1; 8.918 + moonbr_child_peersocket_fd = -1; 8.919 + return err; 8.920 +} 8.921 + 8.922 +/* Lua method for socket object to read from input stream */ 8.923 +static int moonbr_child_lua_read_stream(lua_State *L) { 8.924 + lua_getfield(L, 1, "input"); 8.925 + lua_getfield(L, -1, "read"); 8.926 + lua_insert(L, 1); 8.927 + lua_replace(L, 2); 8.928 + lua_call(L, lua_gettop(L) - 1, LUA_MULTRET); 8.929 + return lua_gettop(L); 8.930 +} 8.931 + 8.932 +/* Lua method for socket object to read from input stream until terminator */ 8.933 +static int moonbr_child_lua_readuntil_stream(lua_State *L) { 8.934 + lua_getfield(L, 1, "input"); 8.935 + lua_getfield(L, -1, "readuntil"); 8.936 + lua_insert(L, 1); 8.937 + lua_replace(L, 2); 8.938 + lua_call(L, lua_gettop(L) - 1, LUA_MULTRET); 8.939 + return lua_gettop(L); 8.940 +} 8.941 + 8.942 +/* Lua method for socket object to iterate over input stream */ 8.943 +static int moonbr_child_lua_lines_stream(lua_State *L) { 8.944 + lua_getfield(L, 1, "input"); 8.945 + lua_getfield(L, -1, "lines"); 8.946 + lua_insert(L, 1); 8.947 + lua_replace(L, 2); 8.948 + lua_call(L, lua_gettop(L) - 1, LUA_MULTRET); 8.949 + return lua_gettop(L); 8.950 +} 8.951 + 8.952 +/* Lua method for socket object to write to output stream */ 8.953 +static int moonbr_child_lua_write_stream(lua_State *L) { 8.954 + lua_getfield(L, 1, "output"); 8.955 + lua_getfield(L, -1, "write"); 8.956 + lua_insert(L, 1); 8.957 + lua_replace(L, 2); 8.958 + lua_call(L, lua_gettop(L) - 1, LUA_MULTRET); 8.959 + return lua_gettop(L); 8.960 +} 8.961 + 8.962 +/* Lua method for socket object to flush the output stream */ 8.963 +static int moonbr_child_lua_flush_stream(lua_State *L) { 8.964 + lua_getfield(L, 1, "output"); 8.965 + lua_getfield(L, -1, "flush"); 8.966 + lua_insert(L, 1); 8.967 + lua_replace(L, 2); 8.968 + lua_call(L, lua_gettop(L) - 1, LUA_MULTRET); 8.969 + return lua_gettop(L); 8.970 +} 8.971 + 8.972 +/* Lua function to close a single stream (input or output) from/to peer */ 8.973 +static int moonbr_child_lua_close_stream(lua_State *L) { 8.974 + luaL_Stream *stream = lua_touserdata(L, 1); 8.975 + if (stream == moonbr_child_peersocket_inputstream) { 8.976 + if (moonbr_child_close_peersocket_inputstream(1, 0)) { /* don't mark as closed as it's done by Lua */ 8.977 + moonbr_child_lua_errno_error(L, "Could not close input stream"); 8.978 + } 8.979 + } else if (stream == moonbr_child_peersocket_outputstream) { 8.980 + if (moonbr_child_close_peersocket_outputstream(1, 0)) { /* don't mark as closed as it's done by Lua */ 8.981 + moonbr_child_lua_errno_error(L, "Could not close output stream"); 8.982 + } 8.983 + } else { 8.984 + luaL_argerror(L, 1, "Not a connection socket"); 8.985 + } 8.986 + return 0; 8.987 +} 8.988 + 8.989 +/* Lua function to close both input and output stream from/to peer */ 8.990 +static int moonbr_child_lua_close_both_streams(lua_State *L) { 8.991 + int timeout = 0; 8.992 + if (!lua_isnoneornil(L, 2)) timeout = luaL_checkint(L, 2); 8.993 + if (moonbr_child_peersocket_fd == -1) { 8.994 + luaL_error(L, "Connection with peer has already been explicitly closed"); 8.995 + } 8.996 + if (moonbr_child_close_peersocket(timeout)) { 8.997 + moonbr_child_lua_errno_error(L, "Could not close socket connection with peer"); 8.998 + } 8.999 + return 0; 8.1000 +} 8.1001 + 8.1002 +/* Lua function to close both input and output stream from/to peer */ 8.1003 +static int moonbr_child_lua_cancel_both_streams(lua_State *L) { 8.1004 + if (moonbr_child_peersocket_fd == -1) { 8.1005 + luaL_error(L, "Connection with peer has already been explicitly closed"); 8.1006 + } 8.1007 + if (moonbr_child_cancel_peersocket()) { 8.1008 + moonbr_child_lua_errno_error(L, "Could not cancel socket connection with peer"); 8.1009 + } 8.1010 + return 0; 8.1011 +} 8.1012 + 8.1013 +/* Methods of (bidirectional) socket object passed to handler */ 8.1014 +static luaL_Reg moonbr_child_lua_socket_functions[] = { 8.1015 + {"read", moonbr_child_lua_read_stream}, 8.1016 + {"readuntil", moonbr_child_lua_readuntil_stream}, 8.1017 + {"lines", moonbr_child_lua_lines_stream}, 8.1018 + {"write", moonbr_child_lua_write_stream}, 8.1019 + {"flush", moonbr_child_lua_flush_stream}, 8.1020 + {"close", moonbr_child_lua_close_both_streams}, 8.1021 + {"cancel", moonbr_child_lua_cancel_both_streams}, 8.1022 + {NULL, NULL} 8.1023 +}; 8.1024 + 8.1025 +/* Main function of child process to be called after fork() and file descriptor rearrangement */ 8.1026 +void moonbr_child_run(struct moonbr_pool *pool, lua_State *L) { 8.1027 + char controlmsg; 8.1028 + struct itimerval notimer = { { 0, }, { 0, } }; 8.1029 + lua_rawgetp(L, LUA_REGISTRYINDEX, moonbr_luakey_prepare_func(pool)); 8.1030 + if (lua_isnil(L, -1)) lua_pop(L, 1); 8.1031 + else if (lua_pcall(L, 0, 0, 1)) { 8.1032 + fprintf(stderr, "Error in \"prepare\" function: %s\n", lua_tostring(L, -1)); 8.1033 + exit(1); 8.1034 + } 8.1035 + while (1) { 8.1036 + struct moonbr_listener *listener; 8.1037 + if (setitimer(ITIMER_REAL, ¬imer, NULL)) { 8.1038 + moonbr_child_log_errno_fatal("Could not reset ITIMER_REAL via setitimer()"); 8.1039 + } 8.1040 + controlmsg = MOONBR_STATUS_IDLE; 8.1041 + if (write(MOONBR_FD_CONTROL, &controlmsg, 1) <= 0) { 8.1042 + moonbr_child_log_errno_fatal("Error while sending ready message to parent process"); 8.1043 + } 8.1044 + moonbr_child_receive_control_message( 8.1045 + MOONBR_FD_CONTROL, 8.1046 + &controlmsg, 8.1047 + &moonbr_child_peersocket_fd 8.1048 + ); 8.1049 + if (!( 8.1050 + (controlmsg == MOONBR_COMMAND_TERMINATE && moonbr_child_peersocket_fd == -1) || 8.1051 + (controlmsg == MOONBR_SOCKETTYPE_INTERVAL && moonbr_child_peersocket_fd == -1) || 8.1052 + (controlmsg == MOONBR_SOCKETTYPE_LOCAL && moonbr_child_peersocket_fd != -1) || 8.1053 + (controlmsg == MOONBR_SOCKETTYPE_NETWORK && moonbr_child_peersocket_fd != -1) 8.1054 + )) { 8.1055 + moonbr_child_log_fatal("Received illegal control message from parent process"); 8.1056 + } 8.1057 + if (controlmsg == MOONBR_COMMAND_TERMINATE) break; 8.1058 + listener = moonbr_child_receive_pointer(MOONBR_FD_CONTROL); 8.1059 + moonbr_child_peersocket_type = controlmsg; 8.1060 + if (moonbr_child_peersocket_fd != -1) { 8.1061 + { 8.1062 + int clonedfd; 8.1063 + clonedfd = dup(moonbr_child_peersocket_fd); 8.1064 + if (!clonedfd) { 8.1065 + moonbr_child_log_errno_fatal("Could not duplicate file descriptor for input stream"); 8.1066 + } 8.1067 + moonbr_child_peersocket_inputstream = lua_newuserdata(L, sizeof(luaL_Stream)); 8.1068 + if (!moonbr_child_peersocket_inputstream) { 8.1069 + moonbr_child_log_fatal("Memory allocation error"); 8.1070 + } 8.1071 + moonbr_child_peersocket_inputstream->f = fdopen(clonedfd, "rb"); 8.1072 + if (!moonbr_child_peersocket_inputstream->f) { 8.1073 + moonbr_child_log_errno_fatal("Could not open input stream for remote connection"); 8.1074 + } 8.1075 + moonbr_child_peersocket_inputstream->closef = moonbr_child_lua_close_stream; 8.1076 + if (luaL_newmetatable(L, LUA_FILEHANDLE)) { 8.1077 + moonbr_child_log_fatal("Lua metatable LUA_FILEHANDLE does not exist"); 8.1078 + } 8.1079 + lua_setmetatable(L, -2); 8.1080 + } 8.1081 + { 8.1082 + int clonedfd; 8.1083 + clonedfd = dup(moonbr_child_peersocket_fd); 8.1084 + if (!clonedfd) { 8.1085 + moonbr_child_log_errno_fatal("Could not duplicate file descriptor for output stream"); 8.1086 + } 8.1087 + moonbr_child_peersocket_outputstream = lua_newuserdata(L, sizeof(luaL_Stream)); 8.1088 + if (!moonbr_child_peersocket_outputstream) { 8.1089 + moonbr_child_log_fatal("Memory allocation error"); 8.1090 + } 8.1091 + moonbr_child_peersocket_outputstream->f = fdopen(clonedfd, "wb"); 8.1092 + if (!moonbr_child_peersocket_outputstream->f) { 8.1093 + moonbr_child_log_errno_fatal("Could not open output stream for remote connection"); 8.1094 + } 8.1095 + moonbr_child_peersocket_outputstream->closef = moonbr_child_lua_close_stream; 8.1096 + if (luaL_newmetatable(L, LUA_FILEHANDLE)) { 8.1097 + moonbr_child_log_fatal("Lua metatable LUA_FILEHANDLE does not exist"); 8.1098 + } 8.1099 + lua_setmetatable(L, -2); 8.1100 + } 8.1101 + } 8.1102 + lua_rawgetp(L, LUA_REGISTRYINDEX, moonbr_luakey_connect_func(pool)); 8.1103 + if (listener->proto == MOONBR_PROTO_INTERVAL) { 8.1104 + lua_newtable(L); 8.1105 + lua_pushstring(L, 8.1106 + listener->proto_specific.interval.name ? 8.1107 + listener->proto_specific.interval.name : "" 8.1108 + ); 8.1109 + lua_setfield(L, -2, "interval"); 8.1110 + } else { 8.1111 + lua_newtable(L); 8.1112 + lua_pushvalue(L, -4); 8.1113 + lua_setfield(L, -2, "input"); 8.1114 + lua_pushvalue(L, -3); 8.1115 + lua_setfield(L, -2, "output"); 8.1116 + luaL_setfuncs(L, moonbr_child_lua_socket_functions, 0); 8.1117 + if (listener->proto == MOONBR_PROTO_TCP6) { 8.1118 + struct sockaddr_in6 addr; 8.1119 + socklen_t addr_len = sizeof(struct sockaddr_in6); 8.1120 + if (getsockname(moonbr_child_peersocket_fd, (struct sockaddr *)&addr, &addr_len)) { 8.1121 + moonbr_child_log_errno("Could not get local IP address/port"); 8.1122 + } else { 8.1123 + lua_pushlstring(L, (char *)addr.sin6_addr.s6_addr, 16); 8.1124 + lua_setfield(L, -2, "local_ip6"); 8.1125 + lua_pushinteger(L, ntohs(addr.sin6_port)); 8.1126 + lua_setfield(L, -2, "local_tcpport"); 8.1127 + } 8.1128 + if (getpeername(moonbr_child_peersocket_fd, (struct sockaddr *)&addr, &addr_len)) { 8.1129 + moonbr_child_log_errno("Could not get remote IP address/port"); 8.1130 + } else { 8.1131 + lua_pushlstring(L, (char *)addr.sin6_addr.s6_addr, 16); 8.1132 + lua_setfield(L, -2, "remote_ip6"); 8.1133 + lua_pushinteger(L, ntohs(addr.sin6_port)); 8.1134 + lua_setfield(L, -2, "remote_tcpport"); 8.1135 + } 8.1136 + } else if (listener->proto == MOONBR_PROTO_TCP4) { 8.1137 + struct sockaddr_in addr; 8.1138 + socklen_t addr_len = sizeof(struct sockaddr_in); 8.1139 + if (getsockname(moonbr_child_peersocket_fd, (struct sockaddr *)&addr, &addr_len)) { 8.1140 + moonbr_child_log_errno("Could not get local IP address/port"); 8.1141 + } else { 8.1142 + lua_pushlstring(L, (char *)&addr.sin_addr.s_addr, 4); 8.1143 + lua_setfield(L, -2, "local_ip4"); 8.1144 + lua_pushinteger(L, ntohs(addr.sin_port)); 8.1145 + lua_setfield(L, -2, "local_tcpport"); 8.1146 + } 8.1147 + if (getpeername(moonbr_child_peersocket_fd, (struct sockaddr *)&addr, &addr_len)) { 8.1148 + moonbr_child_log_errno("Could not get remote IP address/port"); 8.1149 + } else { 8.1150 + lua_pushlstring(L, (char *)&addr.sin_addr.s_addr, 4); 8.1151 + lua_setfield(L, -2, "remote_ip4"); 8.1152 + lua_pushinteger(L, ntohs(addr.sin_port)); 8.1153 + lua_setfield(L, -2, "remote_tcpport"); 8.1154 + } 8.1155 + } 8.1156 + } 8.1157 + if (lua_pcall(L, 1, 1, 1)) { 8.1158 + fprintf(stderr, "Error in \"connect\" function: %s\n", lua_tostring(L, -1)); 8.1159 + exit(1); 8.1160 + } 8.1161 + if (moonbr_child_close_peersocket(0)) { 8.1162 + moonbr_child_log_errno("Could not close socket connection with peer"); 8.1163 + } 8.1164 + if (lua_type(L, -1) != LUA_TBOOLEAN || !lua_toboolean(L, -1)) break; 8.1165 +#ifdef MOONBR_LUA_PANIC_BUG_WORKAROUND 8.1166 + lua_settop(L, 2); 8.1167 +#else 8.1168 + lua_settop(L, 1); 8.1169 +#endif 8.1170 + } 8.1171 + controlmsg = MOONBR_STATUS_GOODBYE; 8.1172 + if (write(MOONBR_FD_CONTROL, &controlmsg, 1) <= 0) { 8.1173 + moonbr_child_log_errno_fatal("Error while sending goodbye message to parent process"); 8.1174 + } 8.1175 + if (close(MOONBR_FD_CONTROL) && errno != EINTR) { 8.1176 + moonbr_child_log_errno("Error while closing control socket"); 8.1177 + } 8.1178 + lua_rawgetp(L, LUA_REGISTRYINDEX, moonbr_luakey_finish_func(pool)); 8.1179 + if (lua_isnil(L, -1)) lua_pop(L, 1); 8.1180 + else if (lua_pcall(L, 0, 0, 1)) { 8.1181 + fprintf(stderr, "Error in \"finish\" function: %s\n", lua_tostring(L, -1)); 8.1182 + exit(1); 8.1183 + } 8.1184 + lua_close(L); 8.1185 + exit(0); 8.1186 +} 8.1187 + 8.1188 + 8.1189 +/*** Functions to spawn child process ***/ 8.1190 + 8.1191 +/* Helper function to send an error message to a file descriptor (not needing a file stream) */ 8.1192 +static void moonbr_child_emergency_print(int fd, char *message) { 8.1193 + size_t len = strlen(message); 8.1194 + ssize_t written; 8.1195 + while (len) { 8.1196 + written = write(fd, message, len); 8.1197 + if (written > 0) { 8.1198 + message += written; 8.1199 + len -= written; 8.1200 + } else { 8.1201 + if (written != -1 || errno != EINTR) break; 8.1202 + } 8.1203 + } 8.1204 +} 8.1205 + 8.1206 +/* Helper function to send an error message plus a text for errno to a file descriptor and terminate the process */ 8.1207 +static void moonbr_child_emergency_error(int fd, char *message) { 8.1208 + int errno2 = errno; 8.1209 + moonbr_child_emergency_print(fd, message); 8.1210 + moonbr_child_emergency_print(fd, ": "); 8.1211 + moonbr_child_emergency_print(fd, strerror(errno2)); 8.1212 + moonbr_child_emergency_print(fd, "\n"); 8.1213 + exit(1); 8.1214 +} 8.1215 + 8.1216 +/* Creates a child process and (in case of success) registers it in the 'struct moonbr_pool' structure */ 8.1217 +static int moonbr_create_worker(struct moonbr_pool *pool, lua_State *L) { 8.1218 + struct moonbr_worker *worker; 8.1219 + worker = calloc(1, sizeof(struct moonbr_worker)); 8.1220 + if (!worker) { 8.1221 + moonbr_log(LOG_CRIT, "Memory allocation error"); 8.1222 + return -1; 8.1223 + } 8.1224 + worker->pool = pool; 8.1225 + { 8.1226 + int controlfds[2]; 8.1227 + int errorfds[2]; 8.1228 + if (socketpair(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, controlfds)) { 8.1229 + moonbr_log(LOG_ERR, "Could not create control socket pair for communcation with child process: %s", strerror(errno)); 8.1230 + free(worker); 8.1231 + return -1; 8.1232 + } 8.1233 + if (socketpair(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, errorfds)) { 8.1234 + moonbr_log(LOG_ERR, "Could not create socket pair to redirect stderr of child process: %s", strerror(errno)); 8.1235 + close(controlfds[0]); 8.1236 + close(controlfds[1]); 8.1237 + free(worker); 8.1238 + return -1; 8.1239 + } 8.1240 + if (moonbr_logfile && fflush(moonbr_logfile)) { 8.1241 + moonbr_log(LOG_CRIT, "Could not flush log file prior to forking: %s", strerror(errno)); 8.1242 + moonbr_terminate_error(); 8.1243 + } 8.1244 + worker->pid = fork(); 8.1245 + if (worker->pid == -1) { 8.1246 + moonbr_log(LOG_ERR, "Could not fork: %s", strerror(errno)); 8.1247 + close(controlfds[0]); 8.1248 + close(controlfds[1]); 8.1249 + close(errorfds[0]); 8.1250 + close(errorfds[1]); 8.1251 + free(worker); 8.1252 + return -1; 8.1253 + } else if (!worker->pid) { 8.1254 + moonbr_pstate = MOONBR_PSTATE_FORKED; 8.1255 +#ifdef MOONBR_LUA_PANIC_BUG_WORKAROUND 8.1256 + lua_pushliteral(L, "Failed to pass error message due to bug in Lua panic handler (hint: not enough memory?)"); 8.1257 +#endif 8.1258 + moonbr_memory_limit = pool->memory_limit; 8.1259 + if (moonbr_pidfh && pidfile_close(moonbr_pidfh)) { 8.1260 + moonbr_child_emergency_error(errorfds[1], "Could not close PID file in forked child process"); 8.1261 + } 8.1262 + if (moonbr_logfile && moonbr_logfile != stderr && fclose(moonbr_logfile)) { 8.1263 + moonbr_child_emergency_error(errorfds[1], "Could not close log file in forked child process"); 8.1264 + } 8.1265 + if (dup2(errorfds[1], MOONBR_FD_STDERR) == -1) { 8.1266 + moonbr_child_emergency_error(errorfds[1], "Could not duplicate socket to stderr file descriptor"); 8.1267 + } 8.1268 + if (dup2(controlfds[1], MOONBR_FD_CONTROL) == -1) { 8.1269 + moonbr_child_emergency_error(errorfds[1], "Could not duplicate control socket"); 8.1270 + } 8.1271 + closefrom(MOONBR_FD_END); 8.1272 + moonbr_child_run(pool, L); 8.1273 + } 8.1274 + if (moonbr_stat) { 8.1275 + moonbr_log(LOG_INFO, "Created new worker in pool #%i with PID %i", worker->pool->poolnum, (int)worker->pid); 8.1276 + } 8.1277 + worker->controlfd = controlfds[0]; 8.1278 + worker->errorfd = errorfds[0]; 8.1279 + if (close(controlfds[1]) && errno != EINTR) { 8.1280 + moonbr_log(LOG_CRIT, "Could not close opposite end of control file descriptor after forking"); 8.1281 + moonbr_terminate_error(); 8.1282 + } 8.1283 + if (close(errorfds[1]) && errno != EINTR) { 8.1284 + moonbr_log(LOG_CRIT, "Could not close opposite end of control file descriptor after forking"); 8.1285 + moonbr_terminate_error(); 8.1286 + } 8.1287 + } 8.1288 + worker->prev_worker = pool->last_worker; 8.1289 + if (worker->prev_worker) worker->prev_worker->next_worker = worker; 8.1290 + else pool->first_worker = worker; 8.1291 + pool->last_worker = worker; 8.1292 + pool->unassigned_worker_count++; 8.1293 + pool->total_worker_count++; 8.1294 + pool->worker_count_stat = 1; 8.1295 + moonbr_poll_refresh_needed = 1; 8.1296 + return 0; /* return zero only in case of success */ 8.1297 +} 8.1298 + 8.1299 + 8.1300 +/*** Functions to handle previously created 'struct moonbr_worker' structures ***/ 8.1301 + 8.1302 +#define moonbr_try_destroy_worker_stat(str, field) \ 8.1303 + moonbr_log(LOG_INFO, "Resource usage in pool #%i for PID %i: " str " %li", worker->pool->poolnum, (int)worker->pid, (long)childusage.field); 8.1304 + 8.1305 +/* Destroys a worker structure if socket connections have been closed and child process has terminated */ 8.1306 +static int moonbr_try_destroy_worker(struct moonbr_worker *worker) { 8.1307 + if (worker->controlfd != -1 || worker->errorfd != -1) return MOONBR_DESTROY_NONE; 8.1308 + { 8.1309 + int childstatus; 8.1310 + struct rusage childusage; 8.1311 + { 8.1312 + pid_t waitedpid; 8.1313 + while ( 8.1314 + (waitedpid = wait4(worker->pid, &childstatus, WNOHANG, &childusage)) == -1 8.1315 + ) { 8.1316 + if (errno != EINTR) { 8.1317 + moonbr_log(LOG_CRIT, "Error in wait4() call: %s", strerror(errno)); 8.1318 + moonbr_terminate_error(); 8.1319 + } 8.1320 + } 8.1321 + if (!waitedpid) return 0; /* return 0 if worker couldn't be destroyed */ 8.1322 + if (waitedpid != worker->pid) { 8.1323 + moonbr_log(LOG_CRIT, "Wrong PID returned by wait4() call"); 8.1324 + moonbr_terminate_error(); 8.1325 + } 8.1326 + } 8.1327 + if (WIFEXITED(childstatus)) { 8.1328 + if (WEXITSTATUS(childstatus) || moonbr_stat) { 8.1329 + moonbr_log( 8.1330 + WEXITSTATUS(childstatus) ? LOG_WARNING : LOG_INFO, 8.1331 + "Child process in pool #%i with PID %i returned with exit code %i", worker->pool->poolnum, (int)worker->pid, WEXITSTATUS(childstatus) 8.1332 + ); 8.1333 + } 8.1334 + } else if (WIFSIGNALED(childstatus)) { 8.1335 + if (WCOREDUMP(childstatus)) { 8.1336 + moonbr_log(LOG_ERR, "Child process in pool #%i with PID %i died from signal %i (core dump was created)", worker->pool->poolnum, (int)worker->pid, WTERMSIG(childstatus)); 8.1337 + } else if (WTERMSIG(childstatus) == SIGALRM) { 8.1338 + moonbr_log(LOG_WARNING, "Child process in pool #%i with PID %i exited prematurely due to timeout", worker->pool->poolnum, (int)worker->pid); 8.1339 + } else { 8.1340 + moonbr_log(LOG_ERR, "Child process in pool #%i with PID %i died from signal %i", worker->pool->poolnum, (int)worker->pid, WTERMSIG(childstatus)); 8.1341 + } 8.1342 + } else { 8.1343 + moonbr_log(LOG_CRIT, "Illegal exit status from child process in pool #%i with PID %i", worker->pool->poolnum, (int)worker->pid); 8.1344 + moonbr_terminate_error(); 8.1345 + } 8.1346 + if (moonbr_stat) { 8.1347 + moonbr_log(LOG_INFO, "Resource usage in pool #%i for PID %i: user time %s", worker->pool->poolnum, (int)worker->pid, moonbr_format_timeval(&childusage.ru_utime)); 8.1348 + moonbr_log(LOG_INFO, "Resource usage in pool #%i for PID %i: system time %s", worker->pool->poolnum, (int)worker->pid, moonbr_format_timeval(&childusage.ru_stime)); 8.1349 + moonbr_try_destroy_worker_stat("max resident set size", ru_maxrss); 8.1350 + moonbr_try_destroy_worker_stat("integral shared memory size", ru_ixrss); 8.1351 + moonbr_try_destroy_worker_stat("integral unshared data", ru_idrss); 8.1352 + moonbr_try_destroy_worker_stat("integral unshared stack", ru_isrss); 8.1353 + moonbr_try_destroy_worker_stat("page replaims", ru_minflt); 8.1354 + moonbr_try_destroy_worker_stat("page faults", ru_majflt); 8.1355 + moonbr_try_destroy_worker_stat("swaps", ru_nswap); 8.1356 + moonbr_try_destroy_worker_stat("block input operations", ru_inblock); 8.1357 + moonbr_try_destroy_worker_stat("block output operations", ru_oublock); 8.1358 + moonbr_try_destroy_worker_stat("messages sent", ru_msgsnd); 8.1359 + moonbr_try_destroy_worker_stat("messages received", ru_msgrcv); 8.1360 + moonbr_try_destroy_worker_stat("signals received", ru_nsignals); 8.1361 + moonbr_try_destroy_worker_stat("voluntary context switches", ru_nvcsw); 8.1362 + moonbr_try_destroy_worker_stat("involuntary context switches", ru_nivcsw); 8.1363 + } 8.1364 + } 8.1365 + { 8.1366 + int retval = ( 8.1367 + (worker->idle || worker->assigned) ? 8.1368 + MOONBR_DESTROY_IDLE_OR_ASSIGNED : 8.1369 + MOONBR_DESTROY_PREPARE 8.1370 + ); 8.1371 + if (worker->prev_worker) worker->prev_worker->next_worker = worker->next_worker; 8.1372 + else worker->pool->first_worker = worker->next_worker; 8.1373 + if (worker->next_worker) worker->next_worker->prev_worker = worker->prev_worker; 8.1374 + else worker->pool->last_worker = worker->prev_worker; 8.1375 + if (worker->idle) { 8.1376 + if (worker->prev_idle_worker) worker->prev_idle_worker->next_idle_worker = worker->next_idle_worker; 8.1377 + else worker->pool->first_idle_worker = worker->next_idle_worker; 8.1378 + if (worker->next_idle_worker) worker->next_idle_worker->prev_idle_worker = worker->prev_idle_worker; 8.1379 + else worker->pool->last_idle_worker = worker->prev_idle_worker; 8.1380 + worker->pool->idle_worker_count--; 8.1381 + } 8.1382 + if (!worker->assigned) worker->pool->unassigned_worker_count--; 8.1383 + worker->pool->total_worker_count--; 8.1384 + worker->pool->worker_count_stat = 1; 8.1385 + if (worker->errorlinebuf) free(worker->errorlinebuf); 8.1386 + free(worker); 8.1387 + return retval; 8.1388 + } 8.1389 +} 8.1390 + 8.1391 +/* Marks a worker as idle and stores it in a queue, optionally setting 'idle_expiration' value */ 8.1392 +static void moonbr_add_idle_worker(struct moonbr_worker *worker) { 8.1393 + worker->prev_idle_worker = worker->pool->last_idle_worker; 8.1394 + if (worker->prev_idle_worker) worker->prev_idle_worker->next_idle_worker = worker; 8.1395 + else worker->pool->first_idle_worker = worker; 8.1396 + worker->pool->last_idle_worker = worker; 8.1397 + worker->idle = 1; 8.1398 + worker->pool->idle_worker_count++; 8.1399 + if (worker->assigned) { 8.1400 + worker->assigned = 0; 8.1401 + worker->pool->unassigned_worker_count++; 8.1402 + } 8.1403 + worker->pool->worker_count_stat = 1; 8.1404 + if (timerisset(&worker->pool->idle_timeout)) { 8.1405 + struct timeval now; 8.1406 + moonbr_now(&now); 8.1407 + timeradd(&now, &worker->pool->idle_timeout, &worker->idle_expiration); 8.1408 + } 8.1409 +} 8.1410 + 8.1411 +/* Pops a worker from the queue of idle workers (idle queue must not be empty) */ 8.1412 +static struct moonbr_worker *moonbr_pop_idle_worker(struct moonbr_pool *pool) { 8.1413 + struct moonbr_worker *worker; 8.1414 + worker = pool->first_idle_worker; 8.1415 + pool->first_idle_worker = worker->next_idle_worker; 8.1416 + if (pool->first_idle_worker) pool->first_idle_worker->prev_idle_worker = NULL; 8.1417 + else pool->last_idle_worker = NULL; 8.1418 + worker->next_idle_worker = NULL; 8.1419 + worker->idle = 0; 8.1420 + worker->pool->idle_worker_count--; 8.1421 + worker->assigned = 1; 8.1422 + worker->pool->unassigned_worker_count--; 8.1423 + worker->pool->worker_count_stat = 1; 8.1424 + return worker; 8.1425 +} 8.1426 + 8.1427 + 8.1428 +/*** Functions for queues of 'struct moonbr_listener' ***/ 8.1429 + 8.1430 +/* Appends a 'struct moonbr_listener' to the queue of idle listeners and registers it for poll() */ 8.1431 +static void moonbr_add_idle_listener(struct moonbr_listener *listener) { 8.1432 + listener->prev_listener = listener->pool->last_idle_listener; 8.1433 + if (listener->prev_listener) listener->prev_listener->next_listener = listener; 8.1434 + else listener->pool->first_idle_listener = listener; 8.1435 + listener->pool->last_idle_listener = listener; 8.1436 + if (listener->pollidx != -1) moonbr_poll_fds[listener->pollidx].events |= POLLIN; 8.1437 +} 8.1438 + 8.1439 +/* Removes a 'struct moonbr_listener' from the queue of idle listeners and unregisters it from poll() */ 8.1440 +static void moonbr_remove_idle_listener(struct moonbr_listener *listener) { 8.1441 + if (listener->prev_listener) listener->prev_listener->next_listener = listener->next_listener; 8.1442 + else listener->pool->first_idle_listener = listener->next_listener; 8.1443 + if (listener->next_listener) listener->next_listener->prev_listener = listener->prev_listener; 8.1444 + else listener->pool->last_idle_listener = listener->prev_listener; 8.1445 + listener->prev_listener = NULL; 8.1446 + listener->next_listener = NULL; 8.1447 + if (listener->pollidx != -1) moonbr_poll_fds[listener->pollidx].events &= ~POLLIN; 8.1448 +} 8.1449 + 8.1450 +/* Adds a listener to the queue of connected listeners (i.e. waiting to have their incoming connection accepted) */ 8.1451 +static void moonbr_add_connected_listener(struct moonbr_listener *listener) { 8.1452 + listener->prev_listener = listener->pool->last_connected_listener; 8.1453 + if (listener->prev_listener) listener->prev_listener->next_listener = listener; 8.1454 + else listener->pool->first_connected_listener = listener; 8.1455 + listener->pool->last_connected_listener = listener; 8.1456 +} 8.1457 + 8.1458 +/* Removes and returns the first connected listener in the queue */ 8.1459 +static struct moonbr_listener *moonbr_pop_connected_listener(struct moonbr_pool *pool) { 8.1460 + struct moonbr_listener *listener = pool->first_connected_listener; 8.1461 + listener->pool->first_connected_listener = listener->next_listener; 8.1462 + if (listener->pool->first_connected_listener) listener->pool->first_connected_listener->prev_listener = NULL; 8.1463 + else listener->pool->last_connected_listener = NULL; 8.1464 + listener->next_listener = NULL; 8.1465 + return listener; 8.1466 +} 8.1467 + 8.1468 + 8.1469 +/*** Functions to handle polling ***/ 8.1470 + 8.1471 +/* Returns an index to a new initialized entry in moonbr_poll_fds[] */ 8.1472 +int moonbr_poll_fds_nextindex() { 8.1473 + if (moonbr_poll_fds_count >= moonbr_poll_fds_bufsize) { 8.1474 + if (moonbr_poll_fds_bufsize) moonbr_poll_fds_bufsize *= 2; 8.1475 + else moonbr_poll_fds_bufsize = 1; 8.1476 + moonbr_poll_fds = realloc( 8.1477 + moonbr_poll_fds, moonbr_poll_fds_bufsize * sizeof(struct pollfd) 8.1478 + ); 8.1479 + if (!moonbr_poll_fds) { 8.1480 + moonbr_log(LOG_CRIT, "Memory allocation error"); 8.1481 + moonbr_terminate_error(); 8.1482 + } 8.1483 + } 8.1484 + moonbr_poll_fds[moonbr_poll_fds_count] = (struct pollfd){0, }; 8.1485 + return moonbr_poll_fds_count++; 8.1486 +} 8.1487 + 8.1488 +/* Returns an index to a new initialized entry in moonbr_poll_workers[] */ 8.1489 +int moonbr_poll_workers_nextindex() { 8.1490 + if (moonbr_poll_worker_count >= moonbr_poll_workers_bufsize) { 8.1491 + if (moonbr_poll_workers_bufsize) moonbr_poll_workers_bufsize *= 2; 8.1492 + else moonbr_poll_workers_bufsize = 1; 8.1493 + moonbr_poll_workers = realloc( 8.1494 + moonbr_poll_workers, moonbr_poll_workers_bufsize * sizeof(struct moonbr_poll_worker) 8.1495 + ); 8.1496 + if (!moonbr_poll_workers) { 8.1497 + moonbr_log(LOG_CRIT, "Memory allocation error"); 8.1498 + moonbr_terminate_error(); 8.1499 + } 8.1500 + } 8.1501 + moonbr_poll_workers[moonbr_poll_worker_count] = (struct moonbr_poll_worker){0, }; 8.1502 + return moonbr_poll_worker_count++; 8.1503 +} 8.1504 + 8.1505 +/* Queues all listeners as idle, and initializes static part of moonbr_poll_fds[], which is passed to poll() */ 8.1506 +static void moonbr_poll_init() { 8.1507 + if (socketpair( 8.1508 + PF_LOCAL, 8.1509 + SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 8.1510 + 0, 8.1511 + moonbr_poll_signalfds 8.1512 + )) { 8.1513 + moonbr_log(LOG_CRIT, "Could not create socket pair for signal delivery during polling: %s", strerror(errno)); 8.1514 + moonbr_terminate_error(); 8.1515 + } 8.1516 + { 8.1517 + int j = moonbr_poll_fds_nextindex(); 8.1518 + struct pollfd *pollfd = &moonbr_poll_fds[j]; 8.1519 + pollfd->fd = moonbr_poll_signalfd_read; 8.1520 + pollfd->events = POLLIN; 8.1521 + } 8.1522 + { 8.1523 + struct moonbr_pool *pool; 8.1524 + for (pool=moonbr_first_pool; pool; pool=pool->next_pool) { 8.1525 + int i; 8.1526 + for (i=0; i<pool->listener_count; i++) { 8.1527 + struct moonbr_listener *listener = &pool->listener[i]; 8.1528 + if (listener->listenfd != -1) { 8.1529 + int j = moonbr_poll_fds_nextindex(); 8.1530 + listener->pollidx = j; 8.1531 + moonbr_poll_fds[j].fd = listener->listenfd; 8.1532 + } 8.1533 + moonbr_add_idle_listener(listener); 8.1534 + } 8.1535 + } 8.1536 + } 8.1537 + moonbr_poll_fds_static_count = moonbr_poll_fds_count; /* remember size of static part of array */ 8.1538 +} 8.1539 + 8.1540 +/* Disables polling of all listeners (required for clean shutdown) */ 8.1541 +static void moonbr_poll_shutdown() { 8.1542 + int i; 8.1543 + for (i=1; i<moonbr_poll_fds_static_count; i++) { 8.1544 + moonbr_poll_fds[i].fd = -1; 8.1545 + } 8.1546 +} 8.1547 + 8.1548 +/* (Re)builds dynamic part of moonbr_poll_fds[] array, and (re)builds moonbr_poll_workers[] array */ 8.1549 +static void moonbr_poll_refresh() { 8.1550 + moonbr_poll_refresh_needed = 0; 8.1551 + moonbr_poll_fds_count = moonbr_poll_fds_static_count; 8.1552 + moonbr_poll_worker_count = 0; 8.1553 + { 8.1554 + struct moonbr_pool *pool; 8.1555 + for (pool=moonbr_first_pool; pool; pool=pool->next_pool) { 8.1556 + struct moonbr_worker *worker; 8.1557 + for (worker=pool->first_worker; worker; worker=worker->next_worker) { 8.1558 + if (worker->controlfd != -1) { 8.1559 + int j = moonbr_poll_fds_nextindex(); 8.1560 + int k = moonbr_poll_workers_nextindex(); 8.1561 + struct pollfd *pollfd = &moonbr_poll_fds[j]; 8.1562 + struct moonbr_poll_worker *poll_worker = &moonbr_poll_workers[k]; 8.1563 + pollfd->fd = worker->controlfd; 8.1564 + pollfd->events = POLLIN; 8.1565 + poll_worker->channel = MOONBR_POLL_WORKER_CONTROLCHANNEL; 8.1566 + poll_worker->worker = worker; 8.1567 + } 8.1568 + if (worker->errorfd != -1) { 8.1569 + int j = moonbr_poll_fds_nextindex(); 8.1570 + int k = moonbr_poll_workers_nextindex(); 8.1571 + struct pollfd *pollfd = &moonbr_poll_fds[j]; 8.1572 + struct moonbr_poll_worker *poll_worker = &moonbr_poll_workers[k]; 8.1573 + pollfd->fd = worker->errorfd; 8.1574 + pollfd->events = POLLIN; 8.1575 + poll_worker->channel = MOONBR_POLL_WORKER_ERRORCHANNEL; 8.1576 + poll_worker->worker = worker; 8.1577 + } 8.1578 + } 8.1579 + } 8.1580 + } 8.1581 +} 8.1582 + 8.1583 +/* resets socket and 'revents' field of moonbr_poll_fds[] for signal delivery just before poll() is called */ 8.1584 +static void moonbr_poll_reset_signal() { 8.1585 + ssize_t readcount; 8.1586 + char buf[1]; 8.1587 + moonbr_poll_fds[0].revents = 0; 8.1588 + while ((readcount = read(moonbr_poll_signalfd_read, buf, 1)) < 0) { 8.1589 + if (errno == EAGAIN) break; 8.1590 + if (errno != EINTR) { 8.1591 + moonbr_log(LOG_CRIT, "Error while reading from signal delivery socket: %s", strerror(errno)); 8.1592 + moonbr_terminate_error(); 8.1593 + } 8.1594 + } 8.1595 + if (!readcount) { 8.1596 + moonbr_log(LOG_CRIT, "Unexpected EOF when reading from signal delivery socket: %s", strerror(errno)); 8.1597 + moonbr_terminate_error(); 8.1598 + } 8.1599 +} 8.1600 + 8.1601 + 8.1602 +/*** Shutdown initiation ***/ 8.1603 + 8.1604 +/* Sets global variable 'moonbr_shutdown_in_progress', closes listeners, and demands worker termination */ 8.1605 +static void moonbr_initiate_shutdown() { 8.1606 + struct moonbr_pool *pool; 8.1607 + int i; 8.1608 + if (moonbr_shutdown_in_progress) { 8.1609 + moonbr_log(LOG_NOTICE, "Shutdown already in progress"); 8.1610 + return; 8.1611 + } 8.1612 + moonbr_shutdown_in_progress = 1; 8.1613 + moonbr_log(LOG_NOTICE, "Initiate shutdown"); 8.1614 + for (pool = moonbr_first_pool; pool; pool = pool->next_pool) { 8.1615 + for (i=0; i<pool->listener_count; i++) { 8.1616 + struct moonbr_listener *listener = &pool->listener[i]; 8.1617 + if (listener->listenfd != -1) { 8.1618 + if (close(listener->listenfd) && errno != EINTR) { 8.1619 + moonbr_log(LOG_CRIT, "Could not close listening socket: %s", strerror(errno)); 8.1620 + moonbr_terminate_error(); 8.1621 + } 8.1622 + } 8.1623 + } 8.1624 + pool->pre_fork = 0; 8.1625 + pool->min_fork = 0; 8.1626 + pool->max_fork = 0; 8.1627 + timerclear(&pool->exit_delay); 8.1628 + } 8.1629 + moonbr_poll_shutdown(); /* avoids loops due to error condition when polling closed listeners */ 8.1630 +} 8.1631 + 8.1632 + 8.1633 +/*** Functions to communicate with child processes ***/ 8.1634 + 8.1635 +/* Tells child process to terminate */ 8.1636 +static void moonbr_terminate_idle_worker(struct moonbr_worker *worker) { 8.1637 + moonbr_send_control_message(worker, MOONBR_COMMAND_TERMINATE, -1, NULL); 8.1638 +} 8.1639 + 8.1640 +/* Handles status messages from child process */ 8.1641 +static void moonbr_read_controlchannel(struct moonbr_worker *worker) { 8.1642 + char controlmsg; 8.1643 + { 8.1644 + ssize_t bytes_read; 8.1645 + while ((bytes_read = read(worker->controlfd, &controlmsg, 1)) <= 0) { 8.1646 + if (bytes_read == 0 || errno == ECONNRESET) { 8.1647 + moonbr_log(LOG_WARNING, "Child process in pool #%i with PID %i unexpectedly closed control socket", worker->pool->poolnum, (int)worker->pid); 8.1648 + if (close(worker->controlfd) && errno != EINTR) { 8.1649 + moonbr_log(LOG_CRIT, "Error while closing control socket to child process in pool #%i with PID %i: %s", worker->pool->poolnum, (int)worker->pid, strerror(errno)); 8.1650 + moonbr_terminate_error(); 8.1651 + } 8.1652 + worker->controlfd = -1; 8.1653 + moonbr_poll_refresh_needed = 1; 8.1654 + return; 8.1655 + } 8.1656 + if (errno != EINTR) { 8.1657 + moonbr_log(LOG_CRIT, "Unexpected error while reading control socket from child process in pool #%i with PID %i: %s", worker->pool->poolnum, (int)worker->pid, strerror(errno)); 8.1658 + moonbr_terminate_error(); 8.1659 + } 8.1660 + } 8.1661 + } 8.1662 + if (worker->idle) { 8.1663 + moonbr_log(LOG_CRIT, "Unexpected data from supposedly idle child process in pool #%i with PID %i", worker->pool->poolnum, (int)worker->pid); 8.1664 + moonbr_terminate_error(); 8.1665 + } 8.1666 + if (moonbr_debug) { 8.1667 + moonbr_log(LOG_DEBUG, "Received control message from child in pool #%i with PID %i: \"%c\"", worker->pool->poolnum, (int)worker->pid, (int)controlmsg); 8.1668 + } 8.1669 + switch (controlmsg) { 8.1670 + case MOONBR_STATUS_IDLE: 8.1671 + if (moonbr_stat) { 8.1672 + moonbr_log(LOG_INFO, "Child process in pool #%i with PID %i reports as idle", worker->pool->poolnum, (int)worker->pid); 8.1673 + } 8.1674 + moonbr_add_idle_worker(worker); 8.1675 + break; 8.1676 + case MOONBR_STATUS_GOODBYE: 8.1677 + if (moonbr_stat) { 8.1678 + moonbr_log(LOG_INFO, "Child process in pool #%i with PID %i announced termination", worker->pool->poolnum, (int)worker->pid); 8.1679 + } 8.1680 + if (close(worker->controlfd) && errno != EINTR) { 8.1681 + moonbr_log(LOG_CRIT, "Error while closing control socket to child process in pool #%i with PID %i: %s", worker->pool->poolnum, (int)worker->pid, strerror(errno)); 8.1682 + moonbr_terminate_error(); 8.1683 + } 8.1684 + worker->controlfd = -1; 8.1685 + moonbr_poll_refresh_needed = 1; 8.1686 + break; 8.1687 + default: 8.1688 + moonbr_log(LOG_CRIT, "Received illegal data (\"%c\") while reading control socket from child process in pool #%i with PID %i", (int)controlmsg, worker->pool->poolnum, (int)worker->pid); 8.1689 + moonbr_terminate_error(); 8.1690 + } 8.1691 +} 8.1692 + 8.1693 +/* Handles stderr stream from child process */ 8.1694 +static void moonbr_read_errorchannel(struct moonbr_worker *worker) { 8.1695 + char staticbuf[MOONBR_MAXERRORLINELEN+1]; 8.1696 + char *buf = worker->errorlinebuf; 8.1697 + if (!buf) buf = staticbuf; 8.1698 + { 8.1699 + ssize_t bytes_read; 8.1700 + while ( 8.1701 + (bytes_read = read( 8.1702 + worker->errorfd, 8.1703 + buf + worker->errorlinelen, 8.1704 + MOONBR_MAXERRORLINELEN+1 - worker->errorlinelen 8.1705 + )) <= 0 8.1706 + ) { 8.1707 + if (bytes_read == 0 || errno == ECONNRESET) { 8.1708 + if (moonbr_debug) { 8.1709 + moonbr_log(LOG_DEBUG, "Child process in pool #%i with PID %i closed stderr socket", worker->pool->poolnum, (int)worker->pid); 8.1710 + } 8.1711 + if (close(worker->errorfd) && errno != EINTR) { 8.1712 + moonbr_log(LOG_CRIT, "Error while closing stderr socket to child process in pool #%i with PID %i: %s", worker->pool->poolnum, (int)worker->pid, strerror(errno)); 8.1713 + moonbr_terminate_error(); 8.1714 + } 8.1715 + worker->errorfd = -1; 8.1716 + moonbr_poll_refresh_needed = 1; 8.1717 + break; 8.1718 + } 8.1719 + if (errno != EINTR) { 8.1720 + moonbr_log(LOG_CRIT, "Unexpected error while reading stderr from child process in pool #%i with PID %i: %s", worker->pool->poolnum, (int)worker->pid, strerror(errno)); 8.1721 + moonbr_terminate_error(); 8.1722 + } 8.1723 + } 8.1724 + worker->errorlinelen += bytes_read; 8.1725 + } 8.1726 + { 8.1727 + int i; 8.1728 + for (i=0; i<worker->errorlinelen; i++) { 8.1729 + if (buf[i] == '\n') buf[i] = 0; 8.1730 + if (!buf[i]) { 8.1731 + if (worker->errorlineovf) { 8.1732 + worker->errorlineovf = 0; 8.1733 + } else { 8.1734 + moonbr_log(LOG_WARNING, "Error log from process in pool #%i with PID %i: %s", worker->pool->poolnum, (int)worker->pid, buf); 8.1735 + } 8.1736 + worker->errorlinelen -= i+1; 8.1737 + memmove(buf, buf+i+1, worker->errorlinelen); 8.1738 + i = -1; 8.1739 + } 8.1740 + } 8.1741 + if (i > MOONBR_MAXERRORLINELEN) { 8.1742 + buf[MOONBR_MAXERRORLINELEN] = 0; 8.1743 + if (!worker->errorlineovf) { 8.1744 + moonbr_log(LOG_WARNING, "Error log from process in pool #%i with PID %i (line has been truncated): %s", worker->pool->poolnum, (int)worker->pid, buf); 8.1745 + } 8.1746 + worker->errorlinelen = 0; 8.1747 + worker->errorlineovf = 1; 8.1748 + } 8.1749 + } 8.1750 + if (!worker->errorlinebuf && worker->errorlinelen) { /* allocate buffer on heap only if necessary */ 8.1751 + worker->errorlinebuf = malloc((MOONBR_MAXERRORLINELEN+1) * sizeof(char)); 8.1752 + if (!worker->errorlinebuf) { 8.1753 + moonbr_log(LOG_CRIT, "Memory allocation error"); 8.1754 + moonbr_terminate_error(); 8.1755 + } 8.1756 + memcpy(worker->errorlinebuf, staticbuf, worker->errorlinelen); 8.1757 + } 8.1758 +} 8.1759 + 8.1760 + 8.1761 +/*** Handler for incoming connections ***/ 8.1762 + 8.1763 +/* Accepts one or more incoming connections on listener socket and passes it to worker(s) popped from idle queue */ 8.1764 +static void moonbr_connect(struct moonbr_pool *pool) { 8.1765 + struct moonbr_listener *listener = moonbr_pop_connected_listener(pool); 8.1766 + struct moonbr_worker *worker; 8.1767 + switch (listener->proto) { 8.1768 + case MOONBR_PROTO_INTERVAL: 8.1769 + worker = moonbr_pop_idle_worker(pool); 8.1770 + if (moonbr_stat) { 8.1771 + moonbr_log(LOG_INFO, "Dispatching interval timer \"%s\" of pool #%i to PID %i", listener->proto_specific.interval.name, listener->pool->poolnum, (int)worker->pid); 8.1772 + } 8.1773 + worker->restart_interval_listener = listener; 8.1774 + moonbr_send_control_message(worker, MOONBR_SOCKETTYPE_INTERVAL, -1, listener); 8.1775 + /* do not push listener to queue of idle listeners yet */ 8.1776 + break; 8.1777 + case MOONBR_PROTO_LOCAL: 8.1778 + do { 8.1779 + int peerfd; 8.1780 + struct sockaddr_un peeraddr; 8.1781 + socklen_t peeraddr_len = sizeof(struct sockaddr_un); 8.1782 + peerfd = accept4( 8.1783 + listener->listenfd, 8.1784 + (struct sockaddr *)&peeraddr, 8.1785 + &peeraddr_len, 8.1786 + SOCK_CLOEXEC 8.1787 + ); 8.1788 + if (peerfd == -1) { 8.1789 + if (errno == EWOULDBLOCK) { 8.1790 + break; 8.1791 + } else if (errno == ECONNABORTED) { 8.1792 + moonbr_log(LOG_WARNING, "Connection aborted before accepting it (proto=\"local\", path=\"%s\")", listener->proto_specific.local.path); 8.1793 + break; 8.1794 + } else if (errno != EINTR) { 8.1795 + moonbr_log(LOG_ERR, "Could not accept socket connection: %s", strerror(errno)); 8.1796 + moonbr_terminate_error(); 8.1797 + } 8.1798 + } else { 8.1799 + worker = moonbr_pop_idle_worker(pool); 8.1800 + if (moonbr_stat) { 8.1801 + moonbr_log(LOG_INFO, "Dispatching local socket connection on path \"%s\" for pool #%i to PID %i", listener->proto_specific.local.path, listener->pool->poolnum, (int)worker->pid); 8.1802 + } 8.1803 + moonbr_send_control_message(worker, MOONBR_SOCKETTYPE_LOCAL, peerfd, listener); 8.1804 + if (close(peerfd) && errno != EINTR) { 8.1805 + moonbr_log(LOG_ERR, "Could not close incoming socket connection in parent process: %s", strerror(errno)); 8.1806 + moonbr_terminate_error(); 8.1807 + } 8.1808 + } 8.1809 + } while (pool->first_idle_worker); 8.1810 + moonbr_add_idle_listener(listener); 8.1811 + break; 8.1812 + case MOONBR_PROTO_TCP6: 8.1813 + do { 8.1814 + int peerfd; 8.1815 + struct sockaddr_in6 peeraddr; 8.1816 + socklen_t peeraddr_len = sizeof(struct sockaddr_in6); 8.1817 + peerfd = accept4( 8.1818 + listener->listenfd, 8.1819 + (struct sockaddr *)&peeraddr, 8.1820 + &peeraddr_len, 8.1821 + SOCK_CLOEXEC 8.1822 + ); 8.1823 + if (peerfd == -1) { 8.1824 + if (errno == EWOULDBLOCK) { 8.1825 + break; 8.1826 + } else if (errno == ECONNABORTED) { 8.1827 + moonbr_log(LOG_WARNING, "Connection aborted before accepting it (proto=\"tcp6\", port=%i)", listener->proto_specific.tcp.port); 8.1828 + break; 8.1829 + } else if (errno != EINTR) { 8.1830 + moonbr_log(LOG_ERR, "Could not accept socket connection: %s", strerror(errno)); 8.1831 + moonbr_terminate_error(); 8.1832 + } 8.1833 + } else { 8.1834 + worker = moonbr_pop_idle_worker(pool); 8.1835 + if (moonbr_stat) { 8.1836 + moonbr_log(LOG_INFO, "Dispatching TCP/IPv6 connection for pool #%i on port %i to PID %i", listener->pool->poolnum, listener->proto_specific.tcp.port, (int)worker->pid); 8.1837 + } 8.1838 + moonbr_send_control_message(worker, MOONBR_SOCKETTYPE_NETWORK, peerfd, listener); 8.1839 + if (close(peerfd) && errno != EINTR) { 8.1840 + moonbr_log(LOG_ERR, "Could not close incoming socket connection in parent process: %s", strerror(errno)); 8.1841 + moonbr_terminate_error(); 8.1842 + } 8.1843 + } 8.1844 + } while (pool->first_idle_worker); 8.1845 + moonbr_add_idle_listener(listener); 8.1846 + break; 8.1847 + case MOONBR_PROTO_TCP4: 8.1848 + do { 8.1849 + int peerfd; 8.1850 + struct sockaddr_in peeraddr; 8.1851 + socklen_t peeraddr_len = sizeof(struct sockaddr_in); 8.1852 + peerfd = accept4( 8.1853 + listener->listenfd, 8.1854 + (struct sockaddr *)&peeraddr, 8.1855 + &peeraddr_len, 8.1856 + SOCK_CLOEXEC 8.1857 + ); 8.1858 + if (peerfd == -1) { 8.1859 + if (errno == EWOULDBLOCK) { 8.1860 + break; 8.1861 + } else if (errno == ECONNABORTED) { 8.1862 + moonbr_log(LOG_WARNING, "Connection aborted before accepting it (proto=\"tcp4\", port=%i)", listener->proto_specific.tcp.port); 8.1863 + break; 8.1864 + } else if (errno != EINTR) { 8.1865 + moonbr_log(LOG_ERR, "Could not accept socket connection: %s", strerror(errno)); 8.1866 + moonbr_terminate_error(); 8.1867 + } 8.1868 + } else { 8.1869 + worker = moonbr_pop_idle_worker(pool); 8.1870 + if (moonbr_stat) { 8.1871 + moonbr_log(LOG_INFO, "Dispatching TCP/IPv4 connection for pool #%i on port %i to PID %i", listener->pool->poolnum, listener->proto_specific.tcp.port, (int)worker->pid); 8.1872 + } 8.1873 + moonbr_send_control_message(worker, MOONBR_SOCKETTYPE_NETWORK, peerfd, listener); 8.1874 + if (close(peerfd) && errno != EINTR) { 8.1875 + moonbr_log(LOG_ERR, "Could not close incoming socket connection in parent process: %s", strerror(errno)); 8.1876 + moonbr_terminate_error(); 8.1877 + } 8.1878 + } 8.1879 + } while (pool->first_idle_worker); 8.1880 + moonbr_add_idle_listener(listener); 8.1881 + break; 8.1882 + default: 8.1883 + moonbr_log(LOG_ERR, "Internal error (should not happen): Unexpected value in listener.proto field"); 8.1884 + moonbr_terminate_error(); 8.1885 + } 8.1886 +} 8.1887 + 8.1888 + 8.1889 +/*** Functions to initialize and restart interval timers ***/ 8.1890 + 8.1891 +/* Initializes all interval timers */ 8.1892 +static void moonbr_interval_initialize() { 8.1893 + struct timeval now; 8.1894 + struct moonbr_pool *pool; 8.1895 + moonbr_now(&now); 8.1896 + for (pool=moonbr_first_pool; pool; pool=pool->next_pool) { 8.1897 + int i; 8.1898 + for (i=0; i<pool->listener_count; i++) { 8.1899 + struct moonbr_listener *listener = &pool->listener[i]; 8.1900 + if (listener->proto == MOONBR_PROTO_INTERVAL) { 8.1901 + timeradd( 8.1902 + &now, 8.1903 + &listener->proto_specific.interval.delay, 8.1904 + &listener->proto_specific.interval.wakeup 8.1905 + ); 8.1906 + } 8.1907 + } 8.1908 + } 8.1909 +} 8.1910 + 8.1911 +/* If necessary, restarts interval timers and queues interval listener as idle after a worker changed status */ 8.1912 +static void moonbr_interval_restart( 8.1913 + struct moonbr_worker *worker, 8.1914 + struct timeval *now /* passed to synchronize with moonbr_run() function */ 8.1915 +) { 8.1916 + struct moonbr_listener *listener = worker->restart_interval_listener; 8.1917 + if (listener) { 8.1918 + moonbr_add_idle_listener(listener); 8.1919 + worker->restart_interval_listener = NULL; 8.1920 + if (listener->proto_specific.interval.strict) { 8.1921 + timeradd( 8.1922 + &listener->proto_specific.interval.wakeup, 8.1923 + &listener->proto_specific.interval.delay, 8.1924 + &listener->proto_specific.interval.wakeup 8.1925 + ); 8.1926 + if (timercmp(&listener->proto_specific.interval.wakeup, now, <)) { 8.1927 + listener->proto_specific.interval.wakeup = *now; 8.1928 + } 8.1929 + } else { 8.1930 + timeradd( 8.1931 + now, 8.1932 + &listener->proto_specific.interval.delay, 8.1933 + &listener->proto_specific.interval.wakeup 8.1934 + ); 8.1935 + } 8.1936 + } 8.1937 +} 8.1938 + 8.1939 + 8.1940 +/*** Main loop and helper functions ***/ 8.1941 + 8.1942 +/* Stores the earliest required wakeup time in 'wait' variable */ 8.1943 +static void moonbr_calc_wait(struct timeval *wait, struct timeval *wakeup) { 8.1944 + if (!timerisset(wait) || timercmp(wakeup, wait, <)) *wait = *wakeup; 8.1945 +} 8.1946 + 8.1947 +/* Main loop of Moonbridge system (including initialization of signal handlers and polling structures) */ 8.1948 +static void moonbr_run(lua_State *L) { 8.1949 + struct timeval now; 8.1950 + struct moonbr_pool *pool; 8.1951 + struct moonbr_worker *worker; 8.1952 + struct moonbr_worker *next_worker; /* needed when worker is removed during iteration of workers */ 8.1953 + struct moonbr_listener *listener; 8.1954 + struct moonbr_listener *next_listener; /* needed when listener is removed during iteration of listeners */ 8.1955 + int i; 8.1956 + moonbr_poll_init(); /* must be executed before moonbr_signal_init() */ 8.1957 + moonbr_signal_init(); 8.1958 + moonbr_interval_initialize(); 8.1959 + moonbr_pstate = MOONBR_PSTATE_RUNNING; 8.1960 + while (1) { 8.1961 + struct timeval wait = {0, }; /* point in time when premature wakeup of poll() is required */ 8.1962 + if (moonbr_cond_interrupt) { 8.1963 + moonbr_log(LOG_WARNING, "Fast shutdown requested"); 8.1964 + moonbr_terminate(MOONBR_EXITCODE_GRACEFUL); 8.1965 + } 8.1966 + if (moonbr_cond_terminate) { 8.1967 + moonbr_initiate_shutdown(); 8.1968 + moonbr_cond_terminate = 0; 8.1969 + } 8.1970 + moonbr_cond_child = 0; /* must not be reset between moonbr_try_destroy_worker() and poll() */ 8.1971 + moonbr_now(&now); 8.1972 + for (pool=moonbr_first_pool; pool; pool=pool->next_pool) { 8.1973 + int terminated_worker_count = 0; /* allows shortcut for new worker creation */ 8.1974 + /* terminate idle workers when expired */ 8.1975 + if (timerisset(&pool->idle_timeout)) { 8.1976 + while ((worker = pool->first_idle_worker) != NULL) { 8.1977 + if (timercmp(&worker->idle_expiration, &now, >)) break; 8.1978 + moonbr_pop_idle_worker(pool); 8.1979 + moonbr_terminate_idle_worker(worker); 8.1980 + } 8.1981 + } 8.1982 + /* mark listeners as connected when incoming connection is pending */ 8.1983 + for (listener=pool->first_idle_listener; listener; listener=next_listener) { 8.1984 + next_listener = listener->next_listener; /* extra variable necessary due to changing list */ 8.1985 + if (listener->pollidx != -1) { 8.1986 + if (moonbr_poll_fds[listener->pollidx].revents) { 8.1987 + moonbr_poll_fds[listener->pollidx].revents = 0; 8.1988 + moonbr_remove_idle_listener(listener); 8.1989 + moonbr_add_connected_listener(listener); 8.1990 + } 8.1991 + } else if (listener->proto == MOONBR_PROTO_INTERVAL) { 8.1992 + if (!timercmp(&listener->proto_specific.interval.wakeup, &now, >)) { 8.1993 + moonbr_remove_idle_listener(listener); 8.1994 + moonbr_add_connected_listener(listener); 8.1995 + } 8.1996 + } else { 8.1997 + moonbr_log(LOG_CRIT, "Internal error (should not happen): Listener is neither an interval timer nor has the 'pollidx' value set"); 8.1998 + moonbr_terminate_error(); 8.1999 + } 8.2000 + } 8.2001 + /* process input from child processes */ 8.2002 + for (i=0; i<moonbr_poll_worker_count; i++) { 8.2003 + if (moonbr_poll_worker_fds[i].revents) { 8.2004 + moonbr_poll_worker_fds[i].revents = 0; 8.2005 + struct moonbr_poll_worker *poll_worker = &moonbr_poll_workers[i]; 8.2006 + switch (poll_worker->channel) { 8.2007 + case MOONBR_POLL_WORKER_CONTROLCHANNEL: 8.2008 + moonbr_read_controlchannel(poll_worker->worker); 8.2009 + moonbr_interval_restart(poll_worker->worker, &now); 8.2010 + break; 8.2011 + case MOONBR_POLL_WORKER_ERRORCHANNEL: 8.2012 + moonbr_read_errorchannel(poll_worker->worker); 8.2013 + break; 8.2014 + } 8.2015 + } 8.2016 + } 8.2017 + /* collect dead child processes */ 8.2018 + for (worker=pool->first_worker; worker; worker=next_worker) { 8.2019 + next_worker = worker->next_worker; /* extra variable necessary due to changing list */ 8.2020 + switch (moonbr_try_destroy_worker(worker)) { 8.2021 + case MOONBR_DESTROY_PREPARE: 8.2022 + pool->use_fork_error_wakeup = 1; 8.2023 + break; 8.2024 + case MOONBR_DESTROY_IDLE_OR_ASSIGNED: 8.2025 + terminated_worker_count++; 8.2026 + break; 8.2027 + } 8.2028 + } 8.2029 + /* connect listeners with idle workers */ 8.2030 + if (!moonbr_shutdown_in_progress) { 8.2031 + while (pool->first_connected_listener && pool->first_idle_worker) { 8.2032 + moonbr_connect(pool); 8.2033 + } 8.2034 + } 8.2035 + /* create new worker processes */ 8.2036 + while ( 8.2037 + pool->total_worker_count < pool->max_fork && ( 8.2038 + pool->unassigned_worker_count < pool->pre_fork || 8.2039 + pool->total_worker_count < pool->min_fork 8.2040 + ) 8.2041 + ) { 8.2042 + if (pool->use_fork_error_wakeup) { 8.2043 + if (timercmp(&pool->fork_error_wakeup, &now, >)) { 8.2044 + moonbr_calc_wait(&wait, &pool->fork_error_wakeup); 8.2045 + break; 8.2046 + } 8.2047 + } else { 8.2048 + if (terminated_worker_count) { 8.2049 + terminated_worker_count--; 8.2050 + } else if (timercmp(&pool->fork_wakeup, &now, >)) { 8.2051 + moonbr_calc_wait(&wait, &pool->fork_wakeup); 8.2052 + break; 8.2053 + } 8.2054 + } 8.2055 + if (moonbr_create_worker(pool, L)) { 8.2056 + /* on error, enforce error delay */ 8.2057 + timeradd(&now, &pool->fork_error_delay, &pool->fork_error_wakeup); 8.2058 + pool->use_fork_error_wakeup = 1; 8.2059 + moonbr_calc_wait(&wait, &pool->fork_error_wakeup); 8.2060 + break; 8.2061 + } else { 8.2062 + /* normal fork delay on success */ 8.2063 + timeradd(&now, &pool->fork_delay, &pool->fork_wakeup); 8.2064 + timeradd(&now, &pool->fork_error_delay, &pool->fork_error_wakeup); 8.2065 + pool->use_fork_error_wakeup = 0; /* gets set later if error occures during preparation */ 8.2066 + } 8.2067 + } 8.2068 + /* terminate excessive worker processes */ 8.2069 + while ( 8.2070 + pool->total_worker_count > pool->min_fork && 8.2071 + pool->idle_worker_count > pool->pre_fork 8.2072 + ) { 8.2073 + if (timerisset(&pool->exit_wakeup)) { 8.2074 + if (timercmp(&pool->exit_wakeup, &now, >)) { 8.2075 + moonbr_calc_wait(&wait, &pool->exit_wakeup); 8.2076 + break; 8.2077 + } 8.2078 + moonbr_terminate_idle_worker(moonbr_pop_idle_worker(pool)); 8.2079 + timeradd(&now, &pool->exit_delay, &pool->exit_wakeup); 8.2080 + } else { 8.2081 + timeradd(&now, &pool->exit_delay, &pool->exit_wakeup); 8.2082 + break; 8.2083 + } 8.2084 + } 8.2085 + if (!( 8.2086 + pool->total_worker_count > pool->min_fork && 8.2087 + pool->idle_worker_count > pool->pre_fork 8.2088 + )) { 8.2089 + timerclear(&pool->exit_wakeup); /* timer gets restarted later when there are excessive workers */ 8.2090 + } 8.2091 + /* optionally output worker count stats */ 8.2092 + if (moonbr_stat && pool->worker_count_stat) { 8.2093 + pool->worker_count_stat = 0; 8.2094 + moonbr_log( 8.2095 + LOG_INFO, 8.2096 + "Worker count for pool #%i: %i idle, %i assigned, %i total", 8.2097 + pool->poolnum, pool->idle_worker_count, 8.2098 + pool->total_worker_count - pool->unassigned_worker_count, 8.2099 + pool->total_worker_count); 8.2100 + } 8.2101 + /* calculate wakeup time for interval listeners */ 8.2102 + for (listener=pool->first_idle_listener; listener; listener=listener->next_listener) { 8.2103 + if (listener->proto == MOONBR_PROTO_INTERVAL) { 8.2104 + moonbr_calc_wait(&wait, &listener->proto_specific.interval.wakeup); 8.2105 + } 8.2106 + } 8.2107 + /* calculate wakeup time for idle workers (only first idle worker is significant) */ 8.2108 + if (timerisset(&pool->idle_timeout) && pool->first_idle_worker) { 8.2109 + moonbr_calc_wait(&wait, &pool->first_idle_worker->idle_expiration); 8.2110 + } 8.2111 + } 8.2112 + /* check if shutdown is complete */ 8.2113 + if (moonbr_shutdown_in_progress) { 8.2114 + for (pool=moonbr_first_pool; pool; pool=pool->next_pool) { 8.2115 + if (pool->first_worker) break; 8.2116 + } 8.2117 + if (!pool) { 8.2118 + moonbr_log(LOG_INFO, "All worker threads have terminated"); 8.2119 + moonbr_terminate(MOONBR_EXITCODE_GRACEFUL); 8.2120 + } 8.2121 + } 8.2122 + if (moonbr_poll_refresh_needed) moonbr_poll_refresh(); 8.2123 + moonbr_cond_poll = 1; 8.2124 + if (!moonbr_cond_child && !moonbr_cond_terminate && !moonbr_cond_interrupt) { 8.2125 + int timeout; 8.2126 + if (timerisset(&wait)) { 8.2127 + if (timercmp(&wait, &now, <)) { 8.2128 + moonbr_log(LOG_CRIT, "Internal error (should not happen): Future is in the past"); 8.2129 + moonbr_terminate_error(); 8.2130 + } 8.2131 + timersub(&wait, &now, &wait); 8.2132 + timeout = wait.tv_sec * 1000 + wait.tv_usec / 1000; 8.2133 + } else { 8.2134 + timeout = INFTIM; 8.2135 + } 8.2136 + if (moonbr_debug) { 8.2137 + moonbr_log(LOG_DEBUG, "Waiting for I/O"); 8.2138 + } 8.2139 + poll(moonbr_poll_fds, moonbr_poll_fds_count, timeout); 8.2140 + } else { 8.2141 + if (moonbr_debug) { 8.2142 + moonbr_log(LOG_DEBUG, "Do not wait for I/O"); 8.2143 + } 8.2144 + } 8.2145 + moonbr_cond_poll = 0; 8.2146 + moonbr_poll_reset_signal(); 8.2147 + } 8.2148 +} 8.2149 + 8.2150 + 8.2151 +/*** Lua interface ***/ 8.2152 + 8.2153 +static int moonbr_lua_panic(lua_State *L) { 8.2154 + const char *errmsg; 8.2155 + errmsg = lua_tostring(L, -1); 8.2156 + if (!errmsg) { 8.2157 + if (lua_isnoneornil(L, -1)) errmsg = "(error message is nil)"; 8.2158 + else errmsg = "(error message is not a string)"; 8.2159 + } 8.2160 + if (moonbr_pstate == MOONBR_PSTATE_FORKED) { 8.2161 + fprintf(stderr, "Uncaught Lua error: %s\n", errmsg); 8.2162 + exit(1); 8.2163 + } else { 8.2164 + moonbr_log(LOG_CRIT, "Uncaught Lua error: %s", errmsg); 8.2165 + moonbr_terminate_error(); 8.2166 + } 8.2167 + return 0; 8.2168 +} 8.2169 + 8.2170 +static int moonbr_addtraceback(lua_State *L) { 8.2171 + luaL_traceback(L, L, luaL_tolstring(L, 1, NULL), 1); 8.2172 + return 1; 8.2173 +} 8.2174 + 8.2175 +/* Memory allocator that allows limiting memory consumption */ 8.2176 +static void *moonbr_alloc (void *ud, void *ptr, size_t osize, size_t nsize) { 8.2177 + (void)ud; /* not used */ 8.2178 + if (nsize == 0) { 8.2179 + if (ptr) { 8.2180 + moonbr_memory_usage -= osize; 8.2181 + free(ptr); 8.2182 + } 8.2183 + return NULL; 8.2184 + } else if (ptr) { 8.2185 + if ( 8.2186 + moonbr_memory_limit && 8.2187 + nsize > osize && 8.2188 + moonbr_memory_usage + (nsize - osize) > moonbr_memory_limit 8.2189 + ) { 8.2190 + return NULL; 8.2191 + } else { 8.2192 + ptr = realloc(ptr, nsize); 8.2193 + if (ptr) moonbr_memory_usage += nsize - osize; 8.2194 + } 8.2195 + } else { 8.2196 + if ( 8.2197 + moonbr_memory_limit && 8.2198 + moonbr_memory_usage + nsize > moonbr_memory_limit 8.2199 + ) { 8.2200 + return NULL; 8.2201 + } else { 8.2202 + ptr = realloc(ptr, nsize); 8.2203 + if (ptr) moonbr_memory_usage += nsize; 8.2204 + } 8.2205 + } 8.2206 + return ptr; 8.2207 +} 8.2208 + 8.2209 +/* New method for Lua file objects: read until terminator or length exceeded */ 8.2210 +static int moonbr_readuntil(lua_State *L) { 8.2211 + luaL_Stream *stream; 8.2212 + FILE *file; 8.2213 + const char *terminatorstr; 8.2214 + size_t terminatorlen; 8.2215 + luaL_Buffer buf; 8.2216 + lua_Integer maxlen; 8.2217 + char terminator; 8.2218 + int byte; 8.2219 + stream = luaL_checkudata(L, 1, LUA_FILEHANDLE); 8.2220 + terminatorstr = luaL_checklstring(L, 2, &terminatorlen); 8.2221 + luaL_argcheck(L, terminatorlen == 1, 2, "single byte expected"); 8.2222 + maxlen = luaL_optinteger(L, 3, 0); 8.2223 + if (!stream->closef) luaL_error(L, "attempt to use a closed file"); 8.2224 + file = stream->f; 8.2225 + luaL_buffinit(L, &buf); 8.2226 + if (!maxlen) maxlen = -1; 8.2227 + terminator = terminatorstr[0]; 8.2228 + while (maxlen > 0 ? maxlen-- : maxlen) { 8.2229 + byte = fgetc(file); 8.2230 + if (byte == EOF) { 8.2231 + if (ferror(file)) { 8.2232 + char errmsg[MOONBR_MAXSTRERRORLEN]; 8.2233 + strerror_r(errno, errmsg, MOONBR_MAXSTRERRORLEN); /* use thread-safe call in case child created threads */ 8.2234 + luaL_error(L, "%s", errmsg); 8.2235 + } else { 8.2236 + break; 8.2237 + } 8.2238 + } 8.2239 + luaL_addchar(&buf, byte); 8.2240 + if (byte == terminator) break; 8.2241 + } 8.2242 + luaL_pushresult(&buf); 8.2243 + if (!lua_rawlen(L, -1)) lua_pushnil(L); 8.2244 + return 1; 8.2245 +} 8.2246 + 8.2247 +static int moonbr_lua_tonatural(lua_State *L, int idx) { 8.2248 + int isnum; 8.2249 + lua_Number n; 8.2250 + n = lua_tonumberx(L, idx, &isnum); 8.2251 + if (isnum && n>=0 && n<INT_MAX && (lua_Number)(int)n == n) return n; 8.2252 + else return -1; 8.2253 +} 8.2254 + 8.2255 +static int moonbr_lua_totimeval(lua_State *L, int idx, struct timeval *value) { 8.2256 + int isnum; 8.2257 + lua_Number n; 8.2258 + n = lua_tonumberx(L, idx, &isnum); 8.2259 + if (isnum && n>=0 && n<=100000000) { 8.2260 + value->tv_sec = n; 8.2261 + value->tv_usec = 1e6 * (n - value->tv_sec); 8.2262 + return 1; 8.2263 + } else { 8.2264 + return 0; 8.2265 + } 8.2266 +} 8.2267 + 8.2268 +static int moonbr_timeout(lua_State *L) { 8.2269 + struct itimerval oldval; 8.2270 + if (lua_isnoneornil(L, 1) && lua_isnoneornil(L, 2)) { 8.2271 + getitimer(ITIMER_REAL, &oldval); 8.2272 + } else { 8.2273 + struct itimerval newval = {}; 8.2274 + if (lua_toboolean(L, 1)) { 8.2275 + luaL_argcheck( 8.2276 + L, moonbr_lua_totimeval(L, 1, &newval.it_value), 1, 8.2277 + "interval in seconds expected" 8.2278 + ); 8.2279 + } 8.2280 + if (lua_isnoneornil(L, 2)) { 8.2281 + if (setitimer(ITIMER_REAL, &newval, &oldval)) { 8.2282 + moonbr_log(LOG_CRIT, "Could not set ITIMER_REAL via setitimer()"); 8.2283 + moonbr_terminate_error(); 8.2284 + } 8.2285 + } else { 8.2286 + getitimer(ITIMER_REAL, &oldval); 8.2287 + if (timercmp(&newval.it_value, &oldval.it_value, <)) { 8.2288 + struct itimerval remval; 8.2289 + if (setitimer(ITIMER_REAL, &newval, NULL)) { 8.2290 + moonbr_log(LOG_CRIT, "Could not set ITIMER_REAL via setitimer()"); 8.2291 + moonbr_terminate_error(); 8.2292 + } 8.2293 + lua_call(L, lua_gettop(L) - 2, LUA_MULTRET); 8.2294 + getitimer(ITIMER_REAL, &remval); 8.2295 + timersub(&oldval.it_value, &newval.it_value, &newval.it_value); 8.2296 + timeradd(&newval.it_value, &remval.it_value, &newval.it_value); 8.2297 + if (setitimer(ITIMER_REAL, &newval, NULL)) { 8.2298 + moonbr_log(LOG_CRIT, "Could not set ITIMER_REAL via setitimer()"); 8.2299 + moonbr_terminate_error(); 8.2300 + } 8.2301 + } else { 8.2302 + lua_call(L, lua_gettop(L) - 2, LUA_MULTRET); 8.2303 + } 8.2304 + return lua_gettop(L) - 1; 8.2305 + } 8.2306 + } 8.2307 + lua_pushnumber(L, oldval.it_value.tv_sec + 1e-6 * oldval.it_value.tv_usec); 8.2308 + return 1; 8.2309 +} 8.2310 + 8.2311 +#define moonbr_listen_init_pool_forkoption(luaname, cname, defval) { \ 8.2312 + lua_getfield(L, 2, luaname); \ 8.2313 + pool->cname = lua_isnil(L, -1) ? (defval) : moonbr_lua_tonatural(L, -1); \ 8.2314 +} while(0) 8.2315 + 8.2316 +#define moonbr_listen_init_pool_timeoption(luaname, cname, defval, defvalu) ( \ 8.2317 + lua_getfield(L, 2, luaname), \ 8.2318 + lua_isnil(L, -1) ? ( \ 8.2319 + pool->cname.tv_sec = (defval), pool->cname.tv_usec = (defvalu), \ 8.2320 + 1 \ 8.2321 + ) : ( \ 8.2322 + (lua_isboolean(L, -1) && !lua_toboolean(L, -1)) ? ( \ 8.2323 + pool->cname.tv_sec = 0, pool->cname.tv_usec = 0, \ 8.2324 + 1 \ 8.2325 + ) : ( \ 8.2326 + moonbr_lua_totimeval(L, -1, &pool->cname) \ 8.2327 + ) \ 8.2328 + ) \ 8.2329 +) 8.2330 + 8.2331 +static int moonbr_listen_init_pool(lua_State *L) { 8.2332 + struct moonbr_pool *pool; 8.2333 + const char *proto; 8.2334 + int i; 8.2335 + pool = lua_touserdata(L, 1); 8.2336 + for (i=0; i<pool->listener_count; i++) { 8.2337 + struct moonbr_listener *listener = &pool->listener[i]; 8.2338 + lua_settop(L, 2); 8.2339 + lua_pushinteger(L, i+1); 8.2340 + lua_gettable(L, 2); 8.2341 + lua_getfield(L, 3, "proto"); 8.2342 + proto = lua_tostring(L, -1); 8.2343 + if (proto && !strcmp(proto, "interval")) { 8.2344 + listener->proto = MOONBR_PROTO_INTERVAL; 8.2345 + lua_getfield(L, 3, "name"); 8.2346 + { 8.2347 + const char *name = lua_tostring(L, -1); 8.2348 + if (name) { 8.2349 + if (asprintf(&listener->proto_specific.interval.name, "%s", name) < 0) { 8.2350 + moonbr_log(LOG_CRIT, "Memory allocation_error"); 8.2351 + moonbr_terminate_error(); 8.2352 + } 8.2353 + } 8.2354 + } 8.2355 + lua_getfield(L, 3, "delay"); 8.2356 + if ( 8.2357 + !moonbr_lua_totimeval(L, -1, &listener->proto_specific.interval.delay) || 8.2358 + !timerisset(&listener->proto_specific.interval.delay) 8.2359 + ) { 8.2360 + luaL_error(L, "No valid interval delay specified; use listen{{proto=\"interval\", delay=...}, ...}"); 8.2361 + } 8.2362 + lua_getfield(L, 3, "strict"); 8.2363 + if (!lua_isnil(L, -1)) { 8.2364 + if (lua_isboolean(L, -1)) { 8.2365 + if (lua_toboolean(L, -1)) listener->proto_specific.interval.strict = 1; 8.2366 + } else { 8.2367 + luaL_error(L, "Option \"strict\" must be a boolean if set; use listen{{proto=\"interval\", strict=true, ...}, ...}"); 8.2368 + } 8.2369 + } 8.2370 + } else if (proto && !strcmp(proto, "local")) { 8.2371 + listener->proto = MOONBR_PROTO_LOCAL; 8.2372 + lua_getfield(L, 3, "path"); 8.2373 + { 8.2374 + const char *path = lua_tostring(L, -1); 8.2375 + if (!path) { 8.2376 + luaL_error(L, "No valid path specified for local socket; use listen{{proto=\"local\", path=...}, ...}"); 8.2377 + } 8.2378 + if (asprintf(&listener->proto_specific.local.path, "%s", path) < 0) { 8.2379 + moonbr_log(LOG_CRIT, "Memory allocation_error"); 8.2380 + moonbr_terminate_error(); 8.2381 + } 8.2382 + } 8.2383 + } else if (proto && !strcmp(proto, "tcp6")) { 8.2384 + listener->proto = MOONBR_PROTO_TCP6; 8.2385 + lua_getfield(L, 3, "port"); 8.2386 + listener->proto_specific.tcp.port = lua_tointeger(L, -1); 8.2387 + if ( 8.2388 + listener->proto_specific.tcp.port < 1 || 8.2389 + listener->proto_specific.tcp.port > 65535 8.2390 + ) { 8.2391 + luaL_error(L, "No valid port number specified; use listen{{proto=\"tcp6\", port=...}, ...}"); 8.2392 + } 8.2393 + lua_getfield(L, 3, "localhost"); 8.2394 + if (!lua_isnil(L, -1)) { 8.2395 + if (lua_isboolean(L, -1)) { 8.2396 + if (lua_toboolean(L, -1)) listener->proto_specific.tcp.localhost_only = 1; 8.2397 + } else { 8.2398 + luaL_error(L, "Option \"localhost\" must be a boolean if set; use listen{{proto=\"tcp6\", localhost=true, ...}, ...}"); 8.2399 + } 8.2400 + } 8.2401 + } else if (proto && !strcmp(proto, "tcp4")) { 8.2402 + listener->proto = MOONBR_PROTO_TCP4; 8.2403 + lua_getfield(L, 3, "port"); 8.2404 + listener->proto_specific.tcp.port = lua_tointeger(L, -1); 8.2405 + if ( 8.2406 + listener->proto_specific.tcp.port < 1 || 8.2407 + listener->proto_specific.tcp.port > 65535 8.2408 + ) { 8.2409 + luaL_error(L, "No valid port number specified; use listen{{proto=\"tcp4\", port=...}, ...}"); 8.2410 + } 8.2411 + lua_getfield(L, 3, "localhost"); 8.2412 + if (!lua_isnil(L, -1)) { 8.2413 + if (lua_isboolean(L, -1)) { 8.2414 + if (lua_toboolean(L, -1)) listener->proto_specific.tcp.localhost_only = 1; 8.2415 + } else { 8.2416 + luaL_error(L, "Option \"localhost\" must be a boolean if set; use listen{{proto=\"tcp4\", localhost=true, ...}, ...}"); 8.2417 + } 8.2418 + } 8.2419 + } 8.2420 + } 8.2421 + lua_settop(L, 2); 8.2422 + moonbr_listen_init_pool_forkoption("pre_fork", pre_fork, 1); 8.2423 + moonbr_listen_init_pool_forkoption("min_fork", min_fork, pool->pre_fork > 2 ? pool->pre_fork : 2); 8.2424 + moonbr_listen_init_pool_forkoption("max_fork", max_fork, pool->min_fork > 16 ? pool->min_fork : 16); 8.2425 + if (!moonbr_listen_init_pool_timeoption("fork_delay", fork_delay, 1, 0)) { 8.2426 + luaL_error(L, "Option \"fork_delay\" is expected to be a non-negative number"); 8.2427 + } 8.2428 + if (!moonbr_listen_init_pool_timeoption("fork_error_delay", fork_error_delay, 2, 0)) { 8.2429 + luaL_error(L, "Option \"fork_error_delay\" is expected to be a non-negative number"); 8.2430 + } 8.2431 + if (!moonbr_listen_init_pool_timeoption("exit_delay", exit_delay, 60, 0)) { 8.2432 + luaL_error(L, "Option \"exit_delay\" is expected to be a non-negative number"); 8.2433 + } 8.2434 + if (timercmp(&pool->fork_error_delay, &pool->fork_delay, <)) { 8.2435 + pool->fork_error_delay = pool->fork_delay; 8.2436 + } 8.2437 + if (!moonbr_listen_init_pool_timeoption("idle_timeout", idle_timeout, 0, 0)) { 8.2438 + luaL_error(L, "Option \"idle_timeout\" is expected to be a non-negative number"); 8.2439 + } 8.2440 + lua_getfield(L, 2, "memory_limit"); 8.2441 + if (!lua_isnil(L, -1)) { 8.2442 + int isnum; 8.2443 + lua_Number n; 8.2444 + n = lua_tonumberx(L, -1, &isnum); 8.2445 + if (n < 0 || !isnum) { 8.2446 + luaL_error(L, "Option \"memory_limit\" is expected to be a non-negative number"); 8.2447 + } 8.2448 + pool->memory_limit = n; 8.2449 + } 8.2450 + lua_settop(L, 2); 8.2451 + lua_getfield(L, 2, "prepare"); 8.2452 + if (!lua_isnil(L, -1) && !lua_isfunction(L, -1)) { 8.2453 + luaL_error(L, "Option \"prepare\" must be nil or a function"); 8.2454 + } 8.2455 + lua_rawsetp(L, LUA_REGISTRYINDEX, moonbr_luakey_prepare_func(pool)); 8.2456 + lua_getfield(L, 2, "connect"); 8.2457 + if (!lua_isfunction(L, -1)) { 8.2458 + luaL_error(L, "Option \"connect\" must be a function; use listen{{...}, {...}, connect=function(socket) ... end, ...}"); 8.2459 + } 8.2460 + lua_rawsetp(L, LUA_REGISTRYINDEX, moonbr_luakey_connect_func(pool)); 8.2461 + lua_getfield(L, 2, "finish"); 8.2462 + if (!lua_isnil(L, -1) && !lua_isfunction(L, -1)) { 8.2463 + luaL_error(L, "Option \"finish\" must be nil or a function"); 8.2464 + } 8.2465 + lua_rawsetp(L, LUA_REGISTRYINDEX, moonbr_luakey_finish_func(pool)); 8.2466 + return 0; 8.2467 +} 8.2468 + 8.2469 +static int moonbr_listen(lua_State *L) { 8.2470 + struct moonbr_pool *pool; 8.2471 + lua_Integer listener_count; 8.2472 + if (moonbr_booted) luaL_error(L, "Moonbridge bootup is already complete"); 8.2473 + luaL_checktype(L, 1, LUA_TTABLE); 8.2474 + listener_count = luaL_len(L, 1); 8.2475 + if (!listener_count) luaL_error(L, "No listen ports specified; use listen{{proto=..., port=...},...}"); 8.2476 + if (listener_count > 100) luaL_error(L, "Too many listeners"); 8.2477 + pool = moonbr_create_pool(listener_count); 8.2478 + lua_pushcfunction(L, moonbr_listen_init_pool); 8.2479 + lua_pushlightuserdata(L, pool); 8.2480 + lua_pushvalue(L, 1); 8.2481 + if (lua_pcall(L, 2, 0, 0)) goto moonbr_listen_error; 8.2482 + { 8.2483 + int i; 8.2484 + i = moonbr_start_pool(pool); 8.2485 + if (i >= 0) { 8.2486 + struct moonbr_listener *listener = &pool->listener[i]; 8.2487 + switch (listener->proto) { 8.2488 + case MOONBR_PROTO_INTERVAL: 8.2489 + lua_pushfstring(L, "Could not initialize listener #%d (proto=\"interval\"): %s", i+1, strerror(errno)); 8.2490 + break; 8.2491 + case MOONBR_PROTO_LOCAL: 8.2492 + lua_pushfstring(L, "Could not initialize listener #%d (proto=\"local\", path=\"%s\"): %s", i+1, listener->proto_specific.local.path, strerror(errno)); 8.2493 + break; 8.2494 + case MOONBR_PROTO_TCP6: 8.2495 + lua_pushfstring(L, "Could not initialize listener #%d (proto=\"tcp6\", port=%d): %s", i+1, listener->proto_specific.tcp.port, strerror(errno)); 8.2496 + break; 8.2497 + case MOONBR_PROTO_TCP4: 8.2498 + lua_pushfstring(L, "Could not initialize listener #%d (proto=\"tcp4\", port=%d): %s", i+1, listener->proto_specific.tcp.port, strerror(errno)); 8.2499 + break; 8.2500 + default: 8.2501 + moonbr_log(LOG_ERR, "Internal error (should not happen): Unexpected value in listener.proto field"); 8.2502 + moonbr_terminate_error(); 8.2503 + } 8.2504 + goto moonbr_listen_error; 8.2505 + } 8.2506 + } 8.2507 + return 0; 8.2508 + moonbr_listen_error: 8.2509 + moonbr_destroy_pool(pool); 8.2510 + lua_pushnil(L); 8.2511 + lua_rawsetp(L, LUA_REGISTRYINDEX, moonbr_luakey_prepare_func(pool)); 8.2512 + lua_pushnil(L); 8.2513 + lua_rawsetp(L, LUA_REGISTRYINDEX, moonbr_luakey_connect_func(pool)); 8.2514 + lua_pushnil(L); 8.2515 + lua_rawsetp(L, LUA_REGISTRYINDEX, moonbr_luakey_finish_func(pool)); 8.2516 + lua_error(L); 8.2517 + return 0; // avoid compiler warning 8.2518 +} 8.2519 + 8.2520 + 8.2521 +/*** Main function and command line invokation ***/ 8.2522 + 8.2523 +static void moonbr_usage(int err, const char *cmd) { 8.2524 + FILE *out; 8.2525 + out = err ? stderr : stdout; 8.2526 + if (!cmd) cmd = "moonbridge"; 8.2527 + fprintf(out, "Get this help message: %s {-h|--help}\n", cmd); 8.2528 + fprintf(out, "Usage: %s \\\n", cmd); 8.2529 + fprintf(out, " [-b|--background] \\\n"); 8.2530 + fprintf(out, " [-d|--debug] \\\n"); 8.2531 + fprintf(out, " [-f|--logfacility {DAEMON|USER|0|1|...|7}] \\\n"); 8.2532 + fprintf(out, " [-i|--logident <syslog ident> \\\n"); 8.2533 + fprintf(out, " [-l|--logfile <logfile>] \\\n"); 8.2534 + fprintf(out, " [-p|--pidfile <pidfile>] \\\n"); 8.2535 + fprintf(out, " [-s|--stats] \\\n"); 8.2536 + fprintf(out, " <Lua file> [<Lua file> ...]\n"); 8.2537 + exit(err); 8.2538 +} 8.2539 + 8.2540 +#define moonbr_usage_error() moonbr_usage(MOONBR_EXITCODE_CMDLINEERROR, argc ? argv[0] : NULL) 8.2541 + 8.2542 +int main(int argc, char **argv) { 8.2543 + { 8.2544 + int daemonize = 0; 8.2545 + int log_facility = LOG_USER; 8.2546 + const char *log_ident = "moonbridge"; 8.2547 + const char *log_filename = NULL; 8.2548 + const char *pid_filename = NULL; 8.2549 + int option; 8.2550 + struct option longopts[] = { 8.2551 + { "background", no_argument, NULL, 'b' }, 8.2552 + { "debug", no_argument, NULL, 'd' }, 8.2553 + { "logfacility", required_argument, NULL, 'f' }, 8.2554 + { "help", no_argument, NULL, 'h' }, 8.2555 + { "logident", required_argument, NULL, 'i' }, 8.2556 + { "logfile", required_argument, NULL, 'l' }, 8.2557 + { "pidfile", required_argument, NULL, 'p' }, 8.2558 + { "stats", no_argument, NULL, 's' } 8.2559 + }; 8.2560 + while ((option = getopt_long(argc, argv, "bdf:hi:l:p:s", longopts, NULL)) != -1) { 8.2561 + switch (option) { 8.2562 + case 'b': 8.2563 + daemonize = 1; 8.2564 + break; 8.2565 + case 'd': 8.2566 + moonbr_debug = 1; 8.2567 + moonbr_stat = 1; 8.2568 + break; 8.2569 + case 'f': 8.2570 + if (!strcmp(optarg, "DAEMON")) { 8.2571 + log_facility = LOG_DAEMON; 8.2572 + } else if (!strcmp(optarg, "USER")) { 8.2573 + log_facility = LOG_USER; 8.2574 + } else if (!strcmp(optarg, "0")) { 8.2575 + log_facility = LOG_LOCAL0; 8.2576 + } else if (!strcmp(optarg, "1")) { 8.2577 + log_facility = LOG_LOCAL1; 8.2578 + } else if (!strcmp(optarg, "2")) { 8.2579 + log_facility = LOG_LOCAL2; 8.2580 + } else if (!strcmp(optarg, "3")) { 8.2581 + log_facility = LOG_LOCAL3; 8.2582 + } else if (!strcmp(optarg, "4")) { 8.2583 + log_facility = LOG_LOCAL4; 8.2584 + } else if (!strcmp(optarg, "5")) { 8.2585 + log_facility = LOG_LOCAL5; 8.2586 + } else if (!strcmp(optarg, "6")) { 8.2587 + log_facility = LOG_LOCAL6; 8.2588 + } else if (!strcmp(optarg, "7")) { 8.2589 + log_facility = LOG_LOCAL7; 8.2590 + } else { 8.2591 + moonbr_usage_error(); 8.2592 + } 8.2593 + moonbr_use_syslog = 1; 8.2594 + break; 8.2595 + case 'h': 8.2596 + moonbr_usage(MOONBR_EXITCODE_GRACEFUL, argv[0]); 8.2597 + break; 8.2598 + case 'i': 8.2599 + log_ident = optarg; 8.2600 + moonbr_use_syslog = 1; 8.2601 + break; 8.2602 + case 'l': 8.2603 + log_filename = optarg; 8.2604 + break; 8.2605 + case 'p': 8.2606 + pid_filename = optarg; 8.2607 + break; 8.2608 + case 's': 8.2609 + moonbr_stat = 1; 8.2610 + break; 8.2611 + default: 8.2612 + moonbr_usage_error(); 8.2613 + } 8.2614 + } 8.2615 + if (argc - optind <= 0) moonbr_usage_error(); 8.2616 + if (pid_filename) { 8.2617 + pid_t otherpid; 8.2618 + while ((moonbr_pidfh = pidfile_open(pid_filename, 0644, &otherpid)) == NULL) { 8.2619 + if (errno == EEXIST) { 8.2620 + if (otherpid == -1) { 8.2621 + fprintf(stderr, "PID file \"%s\" is already locked\n", pid_filename); 8.2622 + } else { 8.2623 + fprintf(stderr, "PID file \"%s\" is already locked by process with PID: %i\n", pid_filename, (int)otherpid); 8.2624 + } 8.2625 + exit(MOONBR_EXITCODE_ALREADYRUNNING); 8.2626 + } else if (errno != EINTR) { 8.2627 + fprintf(stderr, "Could not write PID file \"%s\": %s\n", pid_filename, strerror(errno)); 8.2628 + exit(MOONBR_EXITCODE_STARTUPERROR); 8.2629 + } 8.2630 + } 8.2631 + } 8.2632 + if (log_filename) { 8.2633 + int logfd; 8.2634 + while ( 8.2635 + ( logfd = flopen( 8.2636 + log_filename, 8.2637 + O_WRONLY|O_NONBLOCK|O_CREAT|O_APPEND|O_CLOEXEC, 8.2638 + 0640 8.2639 + ) 8.2640 + ) < 0 8.2641 + ) { 8.2642 + if (errno == EWOULDBLOCK) { 8.2643 + fprintf(stderr, "Logfile \"%s\" is locked\n", log_filename); 8.2644 + exit(MOONBR_EXITCODE_ALREADYRUNNING); 8.2645 + } else if (errno != EINTR) { 8.2646 + fprintf(stderr, "Could not open logfile \"%s\": %s\n", log_filename, strerror(errno)); 8.2647 + exit(MOONBR_EXITCODE_STARTUPERROR); 8.2648 + } 8.2649 + } 8.2650 + moonbr_logfile = fdopen(logfd, "a"); 8.2651 + if (!moonbr_logfile) { 8.2652 + fprintf(stderr, "Could not open write stream to logfile \"%s\": %s\n", log_filename, strerror(errno)); 8.2653 + exit(MOONBR_EXITCODE_STARTUPERROR); 8.2654 + } 8.2655 + } 8.2656 + if (daemonize == 0 && !moonbr_logfile) moonbr_logfile = stderr; 8.2657 + if (moonbr_logfile) setlinebuf(moonbr_logfile); 8.2658 + else moonbr_use_syslog = 1; 8.2659 + if (moonbr_use_syslog) openlog(log_ident, LOG_NDELAY | LOG_PID, log_facility); 8.2660 + if (daemonize) { 8.2661 + if (daemon(1, 0)) { 8.2662 + moonbr_log(LOG_ERR, "Could not daemonize moonbridge process"); 8.2663 + moonbr_terminate_error(); 8.2664 + } 8.2665 + } 8.2666 + } 8.2667 + moonbr_log(LOG_NOTICE, "Starting moonbridge server"); 8.2668 + if (moonbr_pidfh && pidfile_write(moonbr_pidfh)) { 8.2669 + moonbr_log(LOG_ERR, "Could not write pidfile (after locking)"); 8.2670 + } 8.2671 + { 8.2672 + lua_State *L; 8.2673 + L = lua_newstate(moonbr_alloc, NULL); 8.2674 + if (!L) { 8.2675 + moonbr_log(LOG_CRIT, "Could not initialize Lua state"); 8.2676 + moonbr_terminate_error(); 8.2677 + } 8.2678 + lua_atpanic(L, moonbr_lua_panic); 8.2679 + luaL_openlibs(L); 8.2680 + if (luaL_newmetatable(L, LUA_FILEHANDLE)) { 8.2681 + moonbr_log(LOG_CRIT, "Lua metatable LUA_FILEHANDLE does not exist"); 8.2682 + moonbr_terminate_error(); 8.2683 + } 8.2684 + lua_getfield(L, -1, "__index"); 8.2685 + lua_pushcfunction(L, moonbr_readuntil); 8.2686 + lua_setfield(L, -2, "readuntil"); 8.2687 + lua_pop(L, 2); 8.2688 + lua_pushcfunction(L, moonbr_timeout); 8.2689 + lua_setglobal(L, "timeout"); 8.2690 + lua_pushcfunction(L, moonbr_listen); 8.2691 + lua_setglobal(L, "listen"); 8.2692 + lua_pushcfunction(L, moonbr_addtraceback); // on stack position 1 8.2693 + { 8.2694 + int i; 8.2695 + for (i=optind; i<argc; i++) { 8.2696 + moonbr_log(LOG_INFO, "Loading \"%s\"", argv[i]); 8.2697 + if (luaL_loadfile(L, argv[i])) { 8.2698 + moonbr_log(LOG_ERR, "Error while loading \"%s\": %s", argv[i], lua_tostring(L, -1)); 8.2699 + moonbr_terminate_error(); 8.2700 + } 8.2701 + if (lua_pcall(L, 0, 0, 1)) { 8.2702 + moonbr_log(LOG_ERR, "Error while executing \"%s\": %s", argv[i], lua_tostring(L, -1)); 8.2703 + moonbr_terminate_error(); 8.2704 + } 8.2705 + } 8.2706 + } 8.2707 + lua_getglobal(L, "listen"); 8.2708 + lua_pushcfunction(L, moonbr_listen); 8.2709 + if (lua_compare(L, -2, -1, LUA_OPEQ)) { 8.2710 + lua_pushnil(L); 8.2711 + lua_setglobal(L, "listen"); 8.2712 + } 8.2713 + lua_settop(L, 1); 8.2714 + moonbr_run(L); 8.2715 + } 8.2716 + return 0; 8.2717 +} 8.2718 +
9.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 9.2 +++ b/reference.txt Sun Jan 04 19:30:28 2015 +0100 9.3 @@ -0,0 +1,401 @@ 9.4 + 9.5 +Moonbridge reference 9.6 +==================== 9.7 + 9.8 + 9.9 + 9.10 +Global function listen(...) 9.11 +--------------------------- 9.12 + 9.13 +This function initializes the Moonbridge Network Server. It may be called 9.14 +multiple times. However, it is not allowed to register additional listeners by 9.15 +calling listen(...) from a "prepare", "connect", or "finish" handler. 9.16 + 9.17 +See file "example.lua" for parametrization of the listen(...) function. 9.18 + 9.19 +Warning: Moonbridge will fork the Lua environment to handle parallel requests. 9.20 +Functions provided as "prepare", "connect", and "finish" handlers may access 9.21 +global variables, but for every child process these global variables will not 9.22 +be shared! If you require a global state, a DBMS, cache server, or similar is 9.23 +necessary. 9.24 + 9.25 + 9.26 + 9.27 +Socket object passed to "connect" handler 9.28 +----------------------------------------- 9.29 + 9.30 +For every incoming connection, the registered "connect" handler is called with 9.31 +a single socket object as argument, which is described below: 9.32 + 9.33 + 9.34 +### socket:cancel() 9.35 + 9.36 +Closes the socket connection by sending a TCP RST package if possible to 9.37 +indicate error condition. 9.38 + 9.39 +Warning: Previously sent (and flushed) data may be lost during transmission. 9.40 + 9.41 + 9.42 +### socket:close(timeout) 9.43 + 9.44 +Closes the socket connection (input and output stream) by flushing all data and 9.45 +sending a TCP FIN package. Performs no operation if stream has already been 9.46 +closed. 9.47 + 9.48 +Warning: Pending data on the input stream may cause connection aborts (TCP RST) 9.49 +depending on the particular operating system used. All pending input data 9.50 +should have been read before calling socket:close(). 9.51 + 9.52 +The optional timeout parameter may be used to wait until all data has been sent 9.53 +out, or until the timeout elapses (in which case a TCP RST is sent) whichever 9.54 +happens first. A timeout value of 0 or nil causes immediate return and sending 9.55 +of pending data in background (recommended). 9.56 + 9.57 + 9.58 +### socket:flush() 9.59 + 9.60 +Alias for socket.output:flush() 9.61 + 9.62 + 9.63 +### socket.input 9.64 + 9.65 +Lua file handle representing the input stream of the socket connection. 9.66 +Supports the same methods as io.open()'s return values. 9.67 + 9.68 + 9.69 +### socket.interval 9.70 + 9.71 +Set to the name of an interval timer if the "connect" handler was called due to 9.72 +an elapsed interval timer. Otherwise nil. 9.73 + 9.74 + 9.75 +### socket:lines() 9.76 + 9.77 +Alias for socket.input:lines() 9.78 + 9.79 + 9.80 +### socket.local_ip4 9.81 + 9.82 +Local IPv4 address used for the connection. Encoded as 4 raw bytes in form of a 9.83 +string. 9.84 + 9.85 + 9.86 +### socket.local_ip6 9.87 + 9.88 +Local IPv6 address used for the connection. Encoded as 16 raw bytes in form of 9.89 +a string. 9.90 + 9.91 + 9.92 +### socket.local_tcpport 9.93 + 9.94 +Local TCP port used for the connection. 9.95 + 9.96 + 9.97 +### socket.output 9.98 + 9.99 +Lua file handle representing the output stream of the socket connection. 9.100 +Supports the same methods as io.open()'s return values. 9.101 + 9.102 + 9.103 +### socket:read(...) 9.104 + 9.105 +Alias for socket.input:read() 9.106 + 9.107 + 9.108 +### socket:readuntil(terminator, maxlen) 9.109 + 9.110 +Reads as many bytes until a byte equal to the terminator value occurs. An 9.111 +optional maximum length may be specified. The terminating byte is included in 9.112 +the return value (unless the maximum length would be exceeded). 9.113 + 9.114 +Also available as :readuntil(...) method for any other Lua file handle 9.115 +(including socket.input) 9.116 + 9.117 + 9.118 +### socket.remote_ip4 9.119 + 9.120 +Remote IPv4 address used for the connection. Encoded as 4 raw bytes in form of 9.121 +a string. 9.122 + 9.123 + 9.124 +### socket.remote_ip6 9.125 + 9.126 +Remote IPv6 address used for the connection. Encoded as 16 raw bytes in form of 9.127 +a string. 9.128 + 9.129 + 9.130 +### socket.remote_tcpport 9.131 + 9.132 +Remote TCP port used for the connection. 9.133 + 9.134 + 9.135 +### socket:write(...) 9.136 + 9.137 +Alias for socket.output:write(...) 9.138 + 9.139 + 9.140 + 9.141 +HTTP module 9.142 +----------- 9.143 + 9.144 +The http module exports the function http.generate_handler(callback) that 9.145 +converts an HTTP handler to a "connect" handler. See file "example.lua" for an 9.146 +example of invocation. A table with options may be passed either as a second 9.147 +argument, or as a first argument preceeding the callback function (whichever is 9.148 +more convenient). 9.149 + 9.150 +The following options are supported: 9.151 + 9.152 +- request_body_size_limit: maximum size of payload of HTTP request body 9.153 + (transfer encoding is allowed to add a limited amount of extra data) 9.154 +- chunk_size: optional default value for maximum_input_chunk_size and 9.155 + minimum_output_chunk_size 9.156 +- request_header_size_limit: maximum size of HTTP request headers 9.157 +- maximum_input_chunk_size: maximum chunk size when streaming a request body or 9.158 + certain POST fields (bigger chunks will be fragmented automatically) 9.159 +- minimum_output_chunk_size: minimum size for a chunk when sending a response 9.160 + body (smaller chunks will be buffered and concatenated with future data; 9.161 + ignored when request:flush() is called) 9.162 +- static_headers: a set of headers to be included in every HTTP response 9.163 + (may be a string, a table or strings, or a table of key-value pairs) 9.164 + 9.165 +The callback function receives a single request object as argument, which is 9.166 +described below. 9.167 + 9.168 + 9.169 +### request.body 9.170 + 9.171 +The request body (without headers) as a string. Accessing this value makes 9.172 +further access to request.post_params and request.post_params_list, or 9.173 +invocation of request:stream_request_body(...) impossible. 9.174 + 9.175 + 9.176 +### request.cookies 9.177 + 9.178 +A table with all cookies sent by the client. 9.179 + 9.180 + 9.181 +### request.defer_reading() 9.182 + 9.183 +Disables automatic request body processing on write. Can be called before 9.184 +sending a HTTP status code to send a response before the request has been fully 9.185 +received. 9.186 + 9.187 +CAUTION: Responding to a request before the request body has been processed may 9.188 +lead to a deadlock if the browser does not process the response while trying to 9.189 +send the request. Therefore, this function should only be used if: 9.190 + 9.191 +- the TCP stack has enough buffer space for the response (i.e. if the response 9.192 + is small enough), and if 9.193 +- a timer is used to cancel the response in case of a deadlock. 9.194 + 9.195 +It is recommended to not use this function unless certain performance tweaks 9.196 +are desired. 9.197 + 9.198 + 9.199 +### request:finish() 9.200 + 9.201 +Finishes and flushes a HTTP response. May be called multiple times. An 9.202 +HTTP status, all headers, and the response body (if applicable) must have been 9.203 +previously sent. After calling this method, no further data may be written. 9.204 + 9.205 + 9.206 +### request:finish_headers() 9.207 + 9.208 +Finishes and flushes the HTTP response header section. May be called multiple 9.209 +times, as long as the request is not finished completely. This method is 9.210 +automatically invoked if the application is beginning to send a response body. 9.211 +After calling this method, no further headers may be sent. 9.212 + 9.213 + 9.214 +### request:flush() 9.215 + 9.216 +Flushes any pending output data. Note: In order to mark the end of a response 9.217 +body, it is required to call request:finish(). 9.218 + 9.219 + 9.220 +### request.get_params 9.221 + 9.222 +A table that maps field names to their corresponding GET value. If there are 9.223 +several GET values with the given field name, then the first value is used. 9.224 + 9.225 + 9.226 +### request.get_params_list 9.227 + 9.228 +A table that maps field names to a sequence of their corresponding GET values. 9.229 + 9.230 + 9.231 +### request.headers 9.232 + 9.233 +A table that maps (case-insensitively) a HTTP header field name to a sequence 9.234 +of values. One entry is created for every occurrence of a header line with the 9.235 +given field name). 9.236 + 9.237 + 9.238 +### request.headers_csv_string 9.239 + 9.240 +A table that maps (case-insensitively) a HTTP header field name to a comma 9.241 +separated string. Multiple occurrences of the header with the given field name 9.242 +are automatically merged into the comma separated string. 9.243 + 9.244 + 9.245 +### request.headers_csv_table 9.246 + 9.247 +A table that maps (case-insensitively) a HTTP header field name to a sequence 9.248 +of values. One entry is created for every comma separated value of each header 9.249 +with the given field name. 9.250 + 9.251 + 9.252 +### request.headers_flags 9.253 + 9.254 +A table that maps (case-insensitively) a HTTP header field name to another 9.255 +table which (again case-insensitively) maps a string to a boolean, depending on 9.256 +whether this string occurred in the list of comma separated values of one 9.257 +header line with the given field name that was the key in the first table. 9.258 + 9.259 + 9.260 +### request.headers_value 9.261 + 9.262 +A table that maps (case-insensitively) a HTTP header field name to a value. If 9.263 +multiple header lines with the given field name have been received, false is 9.264 +used as value. 9.265 + 9.266 + 9.267 +### request.method 9.268 + 9.269 +The HTTP request method, e.g. "HEAD", "GET", or "POST". 9.270 + 9.271 + 9.272 +### request.path 9.273 + 9.274 +The requested path, e.g. "/index.html", without the query part (that starts 9.275 +with a question mark, see request.query and request.url). 9.276 + 9.277 + 9.278 +### request.post_metadata 9.279 + 9.280 +Only set for multipart/form-data POST requests. A table that maps field names 9.281 +to their corresponding POST metadata table which contains two entries: 9.282 +"file_name" and "content_type". If there are several POST values with the given 9.283 +field name, then the first value/file is used. 9.284 + 9.285 + 9.286 +### request.post_metadata_list 9.287 + 9.288 +Only set for multipart/form-data POST requests. A table that maps field names 9.289 +to a sequence with their corresponding POST metadata tables. Needed if multiple 9.290 +files are uploaded with the same field name. 9.291 + 9.292 + 9.293 +### request.post_params 9.294 + 9.295 +A table that maps field names to their corresponding POST value. If there are 9.296 +several POST values with the given field name, then the first value is used. 9.297 + 9.298 + 9.299 +### request.post_params_list 9.300 + 9.301 +A table that maps field names to a sequence of their corresponding POST values. 9.302 + 9.303 + 9.304 +### request.query 9.305 + 9.306 +Query part of request path without the leading question mark, e.g. "a=b&c=d" if 9.307 +request.path is "index.html?a=b&c=d". The data is automatically parsed and made 9.308 +available through request.get_params and request.get_params_list. 9.309 + 9.310 + 9.311 +### request:process_request_body() 9.312 + 9.313 +Starts processing the request body (if existent) to set the values 9.314 +request.post_params, request.post_params_list, request.post_metadata, and 9.315 +and request.post_metadata_list and/or to call POST field stream handlers that 9.316 +have been previously registered with request:stream_post_param(...) or 9.317 +request:stream_post_params(...). 9.318 + 9.319 +This method gets invoked automatically when the POST param tables 9.320 +(request.post_params, etc.) are accessed, or if a response is sent (to avoid 9.321 +deadlocks with the webbrowser). (Note: Automatic request body processing on 9.322 +write may be disabled by calling request:defer_reading().) 9.323 + 9.324 +After this method returned, all registered POST field stream handlers have 9.325 +received all data. Registration of other POST field stream handlers is not 9.326 +possible after this method has been called (or after request.post_params_list 9.327 +or request.post_params have been accessed). 9.328 + 9.329 + 9.330 +### request:send_data(...) 9.331 + 9.332 +Sends data as response body. All arguments are converted via tostring(...) and 9.333 +concatenated. May be called multiple times until the request has been finished 9.334 +by calling request:finish(). 9.335 + 9.336 +If the request method (see request.method) is "HEAD", then calls to 9.337 +request:send_data(...) are automatically ignored. 9.338 + 9.339 + 9.340 +### request:send_header(key, value) 9.341 + 9.342 +Sends a HTTP response header that consists of the given key and the given 9.343 +value. Note: Key and value must be provided as separate arguments. Before any 9.344 +headers can be sent, a HTTP status must have been set with 9.345 +request:send_status(status_string). 9.346 + 9.347 + 9.348 +### request:send_status(status_string) 9.349 + 9.350 +Sends a HTTP response status that is given as a string consisting of a 3-digit 9.351 +number and an explanatory string, e.g. "200 OK" or "404 Not Found". This 9.352 +function must be called once before any headers or response body data may be 9.353 +sent. 9.354 + 9.355 + 9.356 +### request.socket 9.357 + 9.358 +The underlaying socket. Can be used to force a TCP RST, etc. 9.359 + 9.360 + 9.361 +### request:stream_post_param(field_name, callback) 9.362 + 9.363 +Registers a stream handler for the given POST parameter. The callback function 9.364 +will be called in the following manner: 9.365 + 9.366 +- For the initial chunk, the first chunk gets passed as first argument while a 9.367 + table with metadata ("field_name" and "content_type") gets passed as second 9.368 + argument. In case of an immediate EOF (i.e. an empty file), the passed 9.369 + chunk is the empty string. In all other cases the chunk has a length greater 9.370 + than zero. 9.371 +- For any remaining chunks, the respective chunk gets passed as first and only 9.372 + argument (no metadata). Here, the chunk has always a length greater than 9.373 + zero. 9.374 +- To indicate the end of the stream, the callback function is called without 9.375 + arguments. This also happens in case of an immediate EOF (see above). 9.376 + 9.377 +In case of an immediate EOF (i.e. an empty file), the callback function is thus 9.378 +called as follows: 9.379 + 9.380 +- The first time with an empty string as first argument, and with the metadata 9.381 + as second argument. 9.382 +- The second time without any arguments. 9.383 + 9.384 + 9.385 +### request:stream_post_params(pattern, callback) 9.386 + 9.387 +Same as request:stream_post_param(...) but providing a string pattern to match 9.388 +multiple field names (e.g. "^file_[0-9]+$"). 9.389 + 9.390 + 9.391 +### request:stream_request_body(callback) 9.392 + 9.393 +Start streaming of request body. For each chunk of the request body, the 9.394 +callback function is called with the corresponding chunk. End of data is 9.395 +indicated through return of request:stream_request_body(...) (not by calling 9.396 +the callback without arguments). 9.397 + 9.398 + 9.399 +### request.url 9.400 + 9.401 +The requested URL. This value is automatically split up into request.path and 9.402 +request.query using the question mark as delimiter. The 9.403 + 9.404 +