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 &ndash; Example Application</title>\n")
   4.119 +          request:send_data("</head>\n<body>\n")
   4.120 +          request:send_data("<h1>Moonbridge Network Server for Lua &ndash; 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 &ndash; Example Application</title>
     6.9 +  </head>
    6.10 +  <body>
    6.11 +    <h1>Moonbridge Network Server for Lua &ndash; 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 "&lt;"
    7.25 +        elseif char == '>' then
    7.26 +          return "&gt;"
    7.27 +        elseif char == '&' then
    7.28 +          return "&amp;"
    7.29 +        elseif char == '"' then
    7.30 +          return "&quot;"
    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, &notimer, 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 +

Impressum / About Us