3 -- Copyright (C) 2008-2010 Matthew Wild
4 -- Copyright (C) 2008-2010 Waqas Hussain
6 -- This project is MIT/X11 licensed. Please see the
7 -- COPYING file in the source package for more information.
10 -- Will be modified by configure script if run --
12 CFG_SOURCEDIR=os.getenv("PROSODY_SRCDIR");
13 CFG_CONFIGDIR=os.getenv("PROSODY_CFGDIR");
14 CFG_PLUGINDIR=os.getenv("PROSODY_PLUGINDIR");
15 CFG_DATADIR=os.getenv("PROSODY_DATADIR");
17 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
19 -- Tell Lua where to find our libraries
21 package.path = CFG_SOURCEDIR.."/?.lua;"..package.path;
22 package.cpath = CFG_SOURCEDIR.."/?.so;"..package.cpath;
25 package.path = package.path..";"..(CFG_SOURCEDIR or ".").."/fallbacks/?.lua";
26 package.cpath = package.cpath..";"..(CFG_SOURCEDIR or ".").."/fallbacks/?.so";
28 -- Substitute ~ with path to home directory in data path
30 if os.getenv("HOME") then
31 CFG_DATADIR = CFG_DATADIR:gsub("^~", os.getenv("HOME"));
35 -- Load the config-parsing module
36 config = require "core.configmanager"
39 -- Define the functions we call during startup, the
40 -- actual startup happens right at the end, where these
41 -- functions get called
43 function read_config()
47 if arg[1] == "--config" and arg[2] then
48 table.insert(filenames, arg[2]);
50 table.insert(filenames, CFG_CONFIGDIR.."/"..arg[2]);
53 for _, format in ipairs(config.parsers()) do
54 table.insert(filenames, (CFG_CONFIGDIR or ".").."/prosody.cfg."..format);
57 for _,_filename in ipairs(filenames) do
59 local file = io.open(filename);
62 CFG_CONFIGDIR = filename:match("^(.*)[\\/][^\\/]*$");
66 local ok, level, err = config.load(filename);
69 print("**************************");
70 if level == "parser" then
71 print("A problem occured while reading the config file "..(CFG_CONFIGDIR or ".").."/prosody.cfg.lua");
72 local err_line, err_message = tostring(err):match("%[string .-%]:(%d*): (.*)");
73 print("Error"..(err_line and (" on line "..err_line) or "")..": "..(err_message or tostring(err)));
75 elseif level == "file" then
76 print("Prosody was unable to find the configuration file.");
77 print("We looked for: "..(CFG_CONFIGDIR or ".").."/prosody.cfg.lua");
78 print("A sample config file is included in the Prosody download called prosody.cfg.lua.dist");
79 print("Copy or rename it to prosody.cfg.lua and edit as necessary.");
81 print("More help on configuring Prosody can be found at http://prosody.im/doc/configure");
83 print("**************************");
89 function load_libraries()
90 -- Load socket framework
91 server = require "net.server"
94 function init_logging()
96 require "core.loggingmanager"
99 function check_dependencies()
100 -- Check runtime dependencies
101 if not require "util.dependencies".check_dependencies() then
106 function sandbox_require()
107 -- Replace require() with one that doesn't pollute _G, required
108 -- for neat sandboxing of modules
110 local _real_require = require;
111 function require(...)
112 local curr_env = getfenv(2);
113 local curr_env_mt = getmetatable(getfenv(2));
114 local _realG_mt = getmetatable(_realG);
115 if curr_env_mt and curr_env_mt.__index and not curr_env_mt.__newindex and _realG_mt then
117 old_newindex, _realG_mt.__newindex = _realG_mt.__newindex, curr_env;
118 local ret = _real_require(...);
119 _realG_mt.__newindex = old_newindex;
122 return _real_require(...);
126 function set_function_metatable()
128 function mt.__index(f, upvalue)
129 local i, name, value = 0;
132 name, value = debug.getupvalue(f, i);
133 until name == upvalue or name == nil;
136 function mt.__newindex(f, upvalue, value)
140 name = debug.getupvalue(f, i);
141 until name == upvalue or name == nil;
143 debug.setupvalue(f, i, value);
146 function mt.__tostring(f)
147 local info = debug.getinfo(f);
148 return ("function(%s:%d)"):format(info.short_src:match("[^\\/]*$"), info.linedefined);
150 debug.setmetatable(function() end, mt);
153 function init_global_state()
158 -- Global 'prosody' object
160 local prosody = prosody;
162 prosody.bare_sessions = bare_sessions;
163 prosody.full_sessions = full_sessions;
164 prosody.hosts = hosts;
166 prosody.paths = { source = CFG_SOURCEDIR, config = CFG_CONFIGDIR,
167 plugins = CFG_PLUGINDIR, data = CFG_DATADIR };
169 prosody.arg = _G.arg;
171 prosody.events = require "util.events".new();
173 prosody.platform = "unknown";
174 if os.getenv("WINDIR") then
175 prosody.platform = "windows";
176 elseif package.config:sub(1,1) == "/" then
177 prosody.platform = "posix";
180 prosody.installed = nil;
181 if CFG_SOURCEDIR and (prosody.platform == "windows" or CFG_SOURCEDIR:match("^/")) then
182 prosody.installed = true;
185 -- Function to reload the config file
186 function prosody.reload_config()
187 log("info", "Reloading configuration file");
188 prosody.events.fire_event("reloading-config");
189 local ok, level, err = config.load((rawget(_G, "CFG_CONFIGDIR") or ".").."/prosody.cfg.lua");
191 if level == "parser" then
192 log("error", "There was an error parsing the configuration file: %s", tostring(err));
193 elseif level == "file" then
194 log("error", "Couldn't read the config file when trying to reload: %s", tostring(err));
197 return ok, (err and tostring(level)..": "..tostring(err)) or nil;
200 -- Function to reopen logfiles
201 function prosody.reopen_logfiles()
202 log("info", "Re-opening log files");
203 eventmanager.fire_event("reopen-log-files"); -- Handled by appropriate log sinks
204 prosody.events.fire_event("reopen-log-files");
207 -- Function to initiate prosody shutdown
208 function prosody.shutdown(reason)
209 log("info", "Shutting down: %s", reason or "unknown reason");
210 prosody.shutdown_reason = reason;
211 prosody.events.fire_event("server-stopping", {reason = reason});
212 server.setquitting(true);
215 -- Load SSL settings from config, and create a ctx table
216 local certmanager = require "core.certmanager";
217 local global_ssl_ctx = certmanager.create_context("*", "server");
218 prosody.global_ssl_ctx = global_ssl_ctx;
220 local cl = require "net.connlisteners";
221 function prosody.net_activate_ports(option, listener, default, conntype)
222 conntype = conntype or (global_ssl_ctx and "tls") or "tcp";
223 local ports_option = option and option.."_ports" or "ports";
224 if not cl.get(listener) then return; end
225 local ports = config.get("*", "core", ports_option) or default;
226 if type(ports) == "number" then ports = {ports} end;
228 if type(ports) ~= "table" then
229 log("error", "core."..ports_option.." is not a table");
231 for _, port in ipairs(ports) do
232 port = tonumber(port);
233 if type(port) ~= "number" then
234 log("error", "Non-numeric "..ports_option..": "..tostring(port));
236 local ok, err = cl.start(listener, {
237 ssl = conntype == "ssl" and global_ssl_ctx,
239 interface = (option and config.get("*", "core", option.."_interface"))
240 or cl.get(listener).default_interface
241 or config.get("*", "core", "interface"),
245 local friendly_message = err;
246 if err:match(" in use") then
247 if port == 5222 or port == 5223 or port == 5269 then
248 friendly_message = "check that Prosody or another XMPP server is "
249 .."not already running and using this port";
250 elseif port == 80 or port == 81 then
251 friendly_message = "check that a HTTP server is not already using "
253 elseif port == 5280 then
254 friendly_message = "check that Prosody or a BOSH connection manager "
255 .."is not already running";
257 friendly_message = "this port is in use by another application";
259 elseif err:match("permission") then
260 friendly_message = "Prosody does not have sufficient privileges to use this port";
261 elseif err == "no ssl context" then
262 if not config.get("*", "core", "ssl") then
263 friendly_message = "there is no 'ssl' config under Host \"*\" which is "
264 .."require for legacy SSL ports";
266 friendly_message = "initializing SSL support failed, see previous log entries";
269 log("error", "Failed to open server port %d, %s", port, friendly_message);
277 function read_version()
278 -- Try to determine version
279 local version_file = io.open((CFG_SOURCEDIR or ".").."/prosody.version");
281 prosody.version = version_file:read("*a"):gsub("%s*$", "");
282 version_file:close();
283 if #prosody.version == 12 and prosody.version:match("^[a-f0-9]+$") then
284 prosody.version = "hg:"..prosody.version;
287 prosody.version = "unknown";
291 function load_secondary_libraries()
292 --- Load and initialise core modules
293 require "util.import"
294 require "core.xmlhandlers"
295 require "core.rostermanager"
296 require "core.eventmanager"
297 require "core.hostmanager"
298 require "core.modulemanager"
299 require "core.usermanager"
300 require "core.sessionmanager"
301 require "core.stanza_router"
306 require "util.datetime"
307 require "util.iterators"
309 require "util.helpers"
311 pcall(require, "util.signal") -- Not on Windows
313 -- Commented to protect us from
314 -- the second kind of people
316 pcall(require, "remdebug.engine");
317 if remdebug then remdebug.engine.start() end
320 require "net.connlisteners";
322 require "util.stanza"
326 function init_data_store()
327 local data_path = config.get("*", "core", "data_path") or CFG_DATADIR or "data";
328 require "util.datamanager".set_data_path(data_path);
329 require "util.datamanager".add_callback(function(username, host, datastore, data)
330 if config.get(host, "core", "anonymous_login") then
333 return username, host, datastore, data;
337 function prepare_to_start()
338 log("debug", "Prosody is using the %s backend for connection handling", server.get_backend());
339 -- Signal to modules that we are ready to start
340 eventmanager.fire_event("server-starting");
341 prosody.events.fire_event("server-starting");
343 -- start listening on sockets
344 if config.get("*", "core", "ports") then
345 prosody.net_activate_ports(nil, "multiplex", {5222, 5269});
346 if config.get("*", "core", "ssl_ports") then
347 prosody.net_activate_ports("ssl", "multiplex", {5223}, "ssl");
350 prosody.net_activate_ports("c2s", "xmppclient", {5222});
351 prosody.net_activate_ports("s2s", "xmppserver", {5269});
352 prosody.net_activate_ports("component", "xmppcomponent", {5347}, "tcp");
353 prosody.net_activate_ports("legacy_ssl", "xmppclient", {}, "ssl");
356 prosody.start_time = os.time();
359 function init_global_protection()
360 -- Catch global accesses
361 local locked_globals_mt = {
362 __index = function (t, k) log("warn", "%s", debug.traceback("Attempt to read a non-existent global '"..tostring(k).."'", 2)); end;
363 __newindex = function (t, k, v) error("Attempt to set a global: "..tostring(k).." = "..tostring(v), 2); end;
366 function prosody.unlock_globals()
367 setmetatable(_G, nil);
370 function prosody.lock_globals()
371 setmetatable(_G, locked_globals_mt);
375 prosody.lock_globals();
379 -- Error handler for errors that make it this far
380 local function catch_uncaught_error(err)
381 if type(err) == "string" and err:match("interrupted!$") then
385 log("error", "Top-level error, please report:\n%s", tostring(err));
386 local traceback = debug.traceback("", 2);
388 log("error", "%s", traceback);
391 prosody.events.fire_event("very-bad-error", {error = err, traceback = traceback});
394 while select(2, xpcall(server.loop, catch_uncaught_error)) ~= "quitting" do
400 log("info", "Shutdown status: Cleaning up");
401 prosody.events.fire_event("server-cleanup");
403 -- Ok, we're quitting I know, but we
404 -- need to do some tidying before we go :)
405 server.setquitting(false);
407 log("info", "Shutdown status: Closing all active sessions");
408 for hostname, host in pairs(hosts) do
409 log("debug", "Shutdown status: Closing client connections for %s", hostname)
410 if host.sessions then
411 local reason = { condition = "system-shutdown", text = "Server is shutting down" };
412 if prosody.shutdown_reason then
413 reason.text = reason.text..": "..prosody.shutdown_reason;
415 for username, user in pairs(host.sessions) do
416 for resource, session in pairs(user.sessions) do
417 log("debug", "Closing connection for %s@%s/%s", username, hostname, resource);
418 session:close(reason);
423 log("debug", "Shutdown status: Closing outgoing s2s connections from %s", hostname);
425 for remotehost, session in pairs(host.s2sout) do
426 if session.close then
427 session:close("system-shutdown");
429 log("warn", "Unable to close outgoing s2s session to %s, no session:close()?!", remotehost);
435 log("info", "Shutdown status: Closing all server connections");
438 server.setquitting(true);
442 -- These actions are in a strict order, as many depend on
443 -- previous steps to have already been performed
446 check_dependencies();
448 set_function_metatable();
452 log("info", "Hello and welcome to Prosody version %s", prosody.version);
453 load_secondary_libraries();
455 init_global_protection();
458 eventmanager.fire_event("server-started");
459 prosody.events.fire_event("server-started");
463 log("info", "Shutting down...");
465 eventmanager.fire_event("server-stopped");
466 prosody.events.fire_event("server-stopped");
467 log("info", "Shutdown complete");