aboutsummaryrefslogtreecommitdiffstats
path: root/util/startup.lua
diff options
context:
space:
mode:
Diffstat (limited to 'util/startup.lua')
-rw-r--r--util/startup.lua336
1 files changed, 278 insertions, 58 deletions
diff --git a/util/startup.lua b/util/startup.lua
index 545b6ae7..caae895d 100644
--- a/util/startup.lua
+++ b/util/startup.lua
@@ -2,15 +2,15 @@
-- luacheck: ignore 113/CFG_CONFIGDIR 113/CFG_SOURCEDIR 113/CFG_DATADIR 113/CFG_PLUGINDIR
local startup = {};
-local prosody = { events = require "util.events".new() };
-local logger = require "util.logger";
+local prosody = { events = require "prosody.util.events".new() };
+local logger = require "prosody.util.logger";
local log = logger.init("startup");
-local parse_args = require "util.argparse".parse;
+local parse_args = require "prosody.util.argparse".parse;
-local config = require "core.configmanager";
+local config = require "prosody.core.configmanager";
local config_warnings;
-local dependencies = require "util.dependencies";
+local dependencies = require "prosody.util.dependencies";
local original_logging_config;
@@ -132,14 +132,14 @@ end
function startup.load_libraries()
-- Load socket framework
-- luacheck: ignore 111/server 111/socket
- require "util.import"
+ require "prosody.util.import"
socket = require "socket";
- server = require "net.server"
+ server = require "prosody.net.server"
end
function startup.init_logging()
-- Initialize logging
- local loggingmanager = require "core.loggingmanager"
+ local loggingmanager = require "prosody.core.loggingmanager"
loggingmanager.reload_logging();
prosody.events.add_handler("config-reloaded", function ()
prosody.events.fire_event("reopen-log-files");
@@ -233,7 +233,7 @@ function startup.set_function_metatable()
if info.isvararg then
info[n_params+1] = "...";
end
- return ("function<%s:%d>(%s)"):format(info.short_src:match("[^\\/]*$"), info.linedefined, table.concat(info, ", "));
+ return ("function @%s:%d(%s)"):format(info.short_src:match("[^\\/]*$"), info.linedefined, table.concat(info, ", "));
end
debug.setmetatable(function() end, mt);
end
@@ -277,6 +277,11 @@ function startup.init_global_state()
startup.detect_platform();
startup.detect_installed();
_G.prosody = prosody;
+
+ -- COMPAT Lua < 5.3
+ if not math.type then
+ require "prosody.util.mathcompat"
+ end
end
function startup.setup_datadir()
@@ -298,7 +303,7 @@ function startup.setup_plugin_install_path()
local installer_plugin_path = config.get("*", "installer_plugin_path") or "custom_plugins";
local path_sep = package.config:sub(3,3);
installer_plugin_path = config.resolve_relative_path(CFG_DATADIR or "data", installer_plugin_path);
- require"util.paths".complement_lua_path(installer_plugin_path);
+ require"prosody.util.paths".complement_lua_path(installer_plugin_path);
-- luacheck: ignore 111
CFG_PLUGINDIR = installer_plugin_path..path_sep..(CFG_PLUGINDIR or "plugins");
prosody.paths.installer = installer_plugin_path;
@@ -360,28 +365,23 @@ end
function startup.load_secondary_libraries()
--- Load and initialise core modules
- require "util.xmppstream"
- require "core.stanza_router"
- require "core.statsmanager"
- require "core.hostmanager"
- require "core.portmanager"
- require "core.modulemanager"
- require "core.usermanager"
- require "core.rostermanager"
- require "core.sessionmanager"
- package.loaded['core.componentmanager'] = setmetatable({},{__index=function()
- -- COMPAT which version?
- log("warn", "componentmanager is deprecated: %s", debug.traceback():match("\n[^\n]*\n[ \t]*([^\n]*)"));
- return function() end
- end});
-
- require "util.array"
- require "util.datetime"
- require "util.iterators"
- require "util.timer"
- require "util.helpers"
-
- pcall(require, "util.signal") -- Not on Windows
+ require "prosody.util.xmppstream"
+ require "prosody.core.stanza_router"
+ require "prosody.core.statsmanager".metric("gauge", "prosody_info", "", "Prosody version", { "version" }):with_labels(prosody.version):set(1);
+ require "prosody.core.hostmanager"
+ require "prosody.core.portmanager"
+ require "prosody.core.modulemanager"
+ require "prosody.core.usermanager"
+ require "prosody.core.rostermanager"
+ require "prosody.core.sessionmanager"
+
+ require "prosody.util.array"
+ require "prosody.util.datetime"
+ require "prosody.util.iterators"
+ require "prosody.util.timer"
+ require "prosody.util.helpers"
+
+ pcall(require, "prosody.util.signal") -- Not on Windows
-- Commented to protect us from
-- the second kind of people
@@ -390,43 +390,83 @@ function startup.load_secondary_libraries()
if remdebug then remdebug.engine.start() end
]]
- require "util.stanza"
- require "util.jid"
+ require "prosody.util.stanza"
+ require "prosody.util.jid"
+
+ prosody.features = require "prosody.core.features".available;
end
function startup.init_http_client()
- local http = require "net.http"
+ local http = require "prosody.net.http"
local config_ssl = config.get("*", "ssl") or {}
local https_client = config.get("*", "client_https_ssl")
- http.default.options.sslctx = require "core.certmanager".create_context("client_https port 0", "client",
+ http.default.options.sslctx = require "prosody.core.certmanager".create_context("client_https port 0", "client",
{ capath = config_ssl.capath, cafile = config_ssl.cafile, verify = "peer", }, https_client);
http.default.options.use_dane = config.get("*", "use_dane")
end
function startup.init_promise()
- local promise = require "util.promise";
+ local promise = require "prosody.util.promise";
- local timer = require "util.timer";
+ local timer = require "prosody.util.timer";
promise.set_nexttick(function(f) return timer.add_task(0, f); end);
end
function startup.init_async()
- local async = require "util.async";
+ local async = require "prosody.util.async";
- local timer = require "util.timer";
+ local timer = require "prosody.util.timer";
async.set_nexttick(function(f) return timer.add_task(0, f); end);
async.set_schedule_function(timer.add_task);
end
function startup.init_data_store()
- require "core.storagemanager";
+ require "prosody.core.storagemanager";
end
+local running_state = require "prosody.util.fsm".new({
+ default_state = "uninitialized";
+ transitions = {
+ { name = "begin_startup", from = "uninitialized", to = "starting" };
+ { name = "finish_startup", from = "starting", to = "running" };
+ { name = "begin_shutdown", from = { "running", "starting" }, to = "stopping" };
+ { name = "finish_shutdown", from = "stopping", to = "stopped" };
+ };
+ handlers = {
+ transitioned = function (transition)
+ prosody.state = transition.to;
+ end;
+ };
+ state_handlers = {
+ starting = function ()
+ prosody.log("debug", "Firing server-starting event");
+ prosody.events.fire_event("server-starting");
+ prosody.start_time = os.time();
+ end;
+ running = function ()
+ prosody.log("debug", "Startup complete, firing server-started");
+ prosody.events.fire_event("server-started");
+ end;
+ };
+}):init();
+
function startup.prepare_to_start()
log("info", "Prosody is using the %s backend for connection handling", server.get_backend());
+
-- Signal to modules that we are ready to start
- prosody.events.fire_event("server-starting");
- prosody.start_time = os.time();
+ prosody.started = require "prosody.util.promise".new(function (resolve)
+ if prosody.state == "running" then
+ resolve();
+ else
+ prosody.events.add_handler("server-started", function ()
+ resolve();
+ end);
+ end
+ end):catch(function (err)
+ prosody.log("error", "Prosody startup error: %s", err);
+ end);
+
+ running_state:begin_startup();
end
function startup.init_global_protection()
@@ -460,7 +500,7 @@ function startup.read_version()
prosody.version = "hg:"..prosody.version;
end
else
- local hg = require"util.mercurial";
+ local hg = require"prosody.util.mercurial";
local hgid = hg.check_id(CFG_SOURCEDIR or ".");
if hgid then prosody.version = "hg:" .. hgid; end
end
@@ -471,7 +511,7 @@ function startup.log_greeting()
end
function startup.notify_started()
- prosody.events.fire_event("server-started");
+ running_state:finish_startup();
end
-- Override logging config (used by prosodyctl)
@@ -491,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.1";
+ 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, "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
@@ -594,7 +643,7 @@ end
function startup.init_gc()
-- Apply garbage collector settings from the config file
- local gc = require "util.gc";
+ local gc = require "prosody.util.gc";
local gc_settings = config.get("*", "gc") or { mode = default_gc_params.mode };
local ok, err = gc.configure(gc_settings, default_gc_params);
@@ -606,7 +655,7 @@ function startup.init_gc()
end
function startup.init_errors()
- require "util.error".configure(config.get("*", "error_library") or {});
+ require "prosody.util.error".configure(config.get("*", "error_library") or {});
end
function startup.make_host(hostname)
@@ -615,7 +664,7 @@ function startup.make_host(hostname)
events = prosody.events,
modules = {},
sessions = {},
- users = require "core.usermanager".new_null_provider(hostname)
+ users = require "prosody.core.usermanager".new_null_provider(hostname)
};
end
@@ -631,17 +680,183 @@ 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");
end
function startup.shutdown()
+ running_state:begin_shutdown();
+
prosody.log("info", "Shutting down...");
startup.cleanup();
prosody.events.fire_event("server-stopped");
- prosody.log("info", "Shutdown complete");
+ running_state:finish_shutdown();
+
+ prosody.log("info", "Shutdown complete");
prosody.log("debug", "Shutdown reason was: %s", prosody.shutdown_reason or "not specified");
prosody.log("debug", "Exiting with status code: %d", prosody.shutdown_code or 0);
server.setquitting(true);
@@ -682,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();
@@ -704,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