diff options
Diffstat (limited to 'util/startup.lua')
-rw-r--r-- | util/startup.lua | 196 |
1 files changed, 187 insertions, 9 deletions
diff --git a/util/startup.lua b/util/startup.lua index 0066fb8c..507c0528 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -392,6 +392,8 @@ function startup.load_secondary_libraries() require "prosody.util.stanza" require "prosody.util.jid" + + prosody.features = require "prosody.core.features".available; end function startup.init_http_client() @@ -529,21 +531,30 @@ function startup.force_console_logging() config.set("*", "log", { { levels = { min = log_level or "info" }, to = "console" } }); end +local function check_posix() + if prosody.platform ~= "posix" then return end + + local want_pposix_version = "0.4.0"; + local have_pposix, pposix = pcall(require, "prosody.util.pposix"); + + if pposix._VERSION ~= want_pposix_version then + print(string.format("Unknown version (%s) of binary pposix module, expected %s", + tostring(pposix._VERSION), want_pposix_version)); + os.exit(1); + end + if have_pposix and pposix then + return pposix; + end +end + function startup.switch_user() -- Switch away from root and into the prosody user -- -- NOTE: This function is only used by prosodyctl. -- The prosody process is built with the assumption that -- it is already started as the appropriate user. - local want_pposix_version = "0.4.0"; - local have_pposix, pposix = pcall(require, "prosody.util.pposix"); - - if have_pposix and pposix then - if pposix._VERSION ~= want_pposix_version then - print(string.format("Unknown version (%s) of binary pposix module, expected %s", - tostring(pposix._VERSION), want_pposix_version)); - os.exit(1); - end + local pposix = check_posix() + if pposix then prosody.current_uid = pposix.getuid(); local arg_root = prosody.opts.root; if prosody.current_uid == 0 and config.get("*", "run_as_root") ~= true and not arg_root then @@ -669,6 +680,168 @@ function startup.make_dummy_hosts() end end +function startup.posix_umask() + if prosody.platform ~= "posix" then return end + local pposix = require "prosody.util.pposix"; + local umask = config.get("*", "umask") or "027"; + pposix.umask(umask); +end + +function startup.check_user() + local pposix = check_posix(); + if not pposix then return end + -- Don't even think about it! + if pposix.getuid() == 0 and not config.get("*", "run_as_root") then + print("Danger, Will Robinson! Prosody doesn't need to be run as root, so don't do it!"); + print("For more information on running Prosody as root, see https://prosody.im/doc/root"); + os.exit(1); -- Refusing to run as root + end +end + +local function remove_pidfile() + local pidfile = prosody.pidfile; + if prosody.pidfile_handle then + prosody.pidfile_handle:close(); + os.remove(pidfile); + prosody.pidfile, prosody.pidfile_handle = nil, nil; + end +end + +function startup.write_pidfile() + local pposix = check_posix(); + if not pposix then return end + local lfs = require "lfs"; + local stat = lfs.attributes; + local pidfile = config.get("*", "pidfile") or nil; + if not pidfile then return end + pidfile = config.resolve_relative_path(prosody.paths.data, pidfile); + local mode = stat(pidfile) and "r+" or "w+"; + local pidfile_handle, err = io.open(pidfile, mode); + if not pidfile_handle then + log("error", "Couldn't write pidfile at %s; %s", pidfile, err); + os.exit(1); + else + prosody.pidfile = pidfile; + if not lfs.lock(pidfile_handle, "w") then -- Exclusive lock + local other_pid = pidfile_handle:read("*a"); + log("error", "Another Prosody instance seems to be running with PID %s, quitting", other_pid); + prosody.pidfile_handle = nil; + os.exit(1); + else + pidfile_handle:close(); + pidfile_handle, err = io.open(pidfile, "w+"); + if not pidfile_handle then + log("error", "Couldn't write pidfile at %s; %s", pidfile, err); + os.exit(1); + else + if lfs.lock(pidfile_handle, "w") then + pidfile_handle:write(tostring(pposix.getpid())); + pidfile_handle:flush(); + prosody.pidfile_handle = pidfile_handle; + end + end + end + end + prosody.events.add_handler("server-stopped", remove_pidfile); +end + +local function remove_log_sinks() + local lm = require "prosody.core.loggingmanager"; + lm.register_sink_type("console", nil); + lm.register_sink_type("stdout", nil); + lm.reload_logging(); +end + +function startup.posix_daemonize() + if not prosody.opts.daemonize then return end + local pposix = check_posix(); + log("info", "Prosody is about to detach from the console, disabling further console output"); + remove_log_sinks(); + local ok, ret = pposix.daemonize(); + if not ok then + log("error", "Failed to daemonize: %s", ret); + elseif ret and ret > 0 then + os.exit(0); + else + log("info", "Successfully daemonized to PID %d", pposix.getpid()); + end +end + +function startup.hook_posix_signals() + if prosody.platform ~= "posix" then return end + local have_signal, signal = pcall(require, "prosody.util.signal"); + if not have_signal then + log("warn", "Couldn't load signal library, won't respond to SIGTERM"); + return + end + signal.signal("SIGTERM", function() + log("warn", "Received SIGTERM"); + prosody.main_thread:run(function() + prosody.unlock_globals(); + prosody.shutdown("Received SIGTERM"); + prosody.lock_globals(); + end); + end); + + signal.signal("SIGHUP", function() + log("info", "Received SIGHUP"); + prosody.main_thread:run(function() prosody.reload_config(); end); + -- this also reloads logging + end); + + signal.signal("SIGINT", function() + log("info", "Received SIGINT"); + prosody.main_thread:run(function() + prosody.unlock_globals(); + prosody.shutdown("Received SIGINT"); + prosody.lock_globals(); + end); + end); + + signal.signal("SIGUSR1", function() + log("info", "Received SIGUSR1"); + prosody.events.fire_event("signal/SIGUSR1"); + end); + + signal.signal("SIGUSR2", function() + log("info", "Received SIGUSR2"); + prosody.events.fire_event("signal/SIGUSR2"); + end); +end + +function startup.systemd_notify() + local notify_socket_name = os.getenv("NOTIFY_SOCKET"); + if not notify_socket_name then return end + local have_unix, unix = pcall(require, "socket.unix"); + if not have_unix or type(unix) ~= "table" then + log("error", "LuaSocket without UNIX socket support, can't notify systemd.") + return os.exit(1); + end + log("debug", "Will notify on socket %q", notify_socket_name); + notify_socket_name = notify_socket_name:gsub("^@", "\0"); + local notify_socket = unix.dgram(); + local ok, err = notify_socket:setpeername(notify_socket_name); + if not ok then + log("error", "Could not connect to systemd notification socket %q: %q", notify_socket_name, err); + return os.exit(1); + end + local time = require "prosody.util.time"; + + prosody.notify_socket = notify_socket; + prosody.events.add_handler("server-started", function() + notify_socket:send("READY=1"); + end); + prosody.events.add_handler("reloading-config", function() + notify_socket:send(string.format("RELOADING=1\nMONOTONIC_USEC=%d", math.floor(time.monotonic() * 1000000))); + end); + prosody.events.add_handler("config-reloaded", function() + notify_socket:send("READY=1"); + end); + prosody.events.add_handler("server-stopping", function() + notify_socket:send("STOPPING=1"); + end); +end + function startup.cleanup() prosody.log("info", "Shutdown status: Cleaning up"); prosody.events.fire_event("server-cleanup"); @@ -724,6 +897,7 @@ function startup.prosody() startup.parse_args(); startup.init_global_state(); startup.read_config(); + startup.check_user(); startup.init_logging(); startup.init_gc(); startup.init_errors(); @@ -746,6 +920,10 @@ function startup.prosody() startup.init_http_client(); startup.init_data_store(); startup.init_global_protection(); + startup.posix_daemonize(); + startup.write_pidfile(); + startup.hook_posix_signals(); + startup.systemd_notify(); startup.prepare_to_start(); startup.notify_started(); end |