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 -- Substitute ~ with path to home directory in data path
27 if os.getenv("HOME") then
28 CFG_DATADIR = CFG_DATADIR:gsub("^~", os.getenv("HOME"));
32 -- Load the config-parsing module
33 config = require "core.configmanager"
36 -- Define the functions we call during startup, the
37 -- actual startup happens right at the end, where these
38 -- functions get called
40 function read_config()
44 if arg[1] == "--config" and arg[2] then
45 table.insert(filenames, arg[2]);
47 table.insert(filenames, CFG_CONFIGDIR.."/"..arg[2]);
50 for _, format in ipairs(config.parsers()) do
51 table.insert(filenames, (CFG_CONFIGDIR or ".").."/prosody.cfg."..format);
54 for _,_filename in ipairs(filenames) do
56 local file = io.open(filename);
59 CFG_CONFIGDIR = filename:match("^(.*)[\\/][^\\/]*$");
63 local ok, level, err = config.load(filename);
66 print("**************************");
67 if level == "parser" then
68 print("A problem occured while reading the config file "..(CFG_CONFIGDIR or ".").."/prosody.cfg.lua");
69 local err_line, err_message = tostring(err):match("%[string .-%]:(%d*): (.*)");
70 print("Error"..(err_line and (" on line "..err_line) or "")..": "..(err_message or tostring(err)));
72 elseif level == "file" then
73 print("Prosody was unable to find the configuration file.");
74 print("We looked for: "..(CFG_CONFIGDIR or ".").."/prosody.cfg.lua");
75 print("A sample config file is included in the Prosody download called prosody.cfg.lua.dist");
76 print("Copy or rename it to prosody.cfg.lua and edit as necessary.");
78 print("More help on configuring Prosody can be found at http://prosody.im/doc/configure");
80 print("**************************");
86 function load_libraries()
87 -- Load socket framework
88 server = require "net.server"
91 function init_logging()
93 require "core.loggingmanager"
96 function check_dependencies()
97 -- Check runtime dependencies
98 if not require "util.dependencies".check_dependencies() then
103 function sandbox_require()
104 -- Replace require() with one that doesn't pollute _G, required
105 -- for neat sandboxing of modules
107 local _real_require = require;
108 function require(...)
109 local curr_env = getfenv(2);
110 local curr_env_mt = getmetatable(getfenv(2));
111 local _realG_mt = getmetatable(_realG);
112 if curr_env_mt and curr_env_mt.__index and not curr_env_mt.__newindex and _realG_mt then
114 old_newindex, _realG_mt.__newindex = _realG_mt.__newindex, curr_env;
115 local ret = _real_require(...);
116 _realG_mt.__newindex = old_newindex;
119 return _real_require(...);
123 function set_function_metatable()
125 function mt.__index(f, upvalue)
126 local i, name, value = 0;
129 name, value = debug.getupvalue(f, i);
130 until name == upvalue or name == nil;
133 function mt.__newindex(f, upvalue, value)
137 name = debug.getupvalue(f, i);
138 until name == upvalue or name == nil;
140 debug.setupvalue(f, i, value);
143 debug.setmetatable(function() end, mt);
146 function init_global_state()
151 -- Global 'prosody' object
153 local prosody = prosody;
155 prosody.bare_sessions = bare_sessions;
156 prosody.full_sessions = full_sessions;
157 prosody.hosts = hosts;
159 prosody.paths = { source = CFG_SOURCEDIR, config = CFG_CONFIGDIR,
160 plugins = CFG_PLUGINDIR, data = CFG_DATADIR };
162 prosody.arg = _G.arg;
164 prosody.events = require "util.events".new();
166 prosody.platform = "unknown";
167 if os.getenv("WINDIR") then
168 prosody.platform = "windows";
169 elseif package.config:sub(1,1) == "/" then
170 prosody.platform = "posix";
173 prosody.installed = nil;
174 if CFG_SOURCEDIR and (prosody.platform == "windows" or CFG_SOURCEDIR:match("^/")) then
175 prosody.installed = true;
178 -- Function to reload the config file
179 function prosody.reload_config()
180 log("info", "Reloading configuration file");
181 prosody.events.fire_event("reloading-config");
182 local ok, level, err = config.load((rawget(_G, "CFG_CONFIGDIR") or ".").."/prosody.cfg.lua");
184 if level == "parser" then
185 log("error", "There was an error parsing the configuration file: %s", tostring(err));
186 elseif level == "file" then
187 log("error", "Couldn't read the config file when trying to reload: %s", tostring(err));
190 return ok, (err and tostring(level)..": "..tostring(err)) or nil;
193 -- Function to reopen logfiles
194 function prosody.reopen_logfiles()
195 log("info", "Re-opening log files");
196 eventmanager.fire_event("reopen-log-files"); -- Handled by appropriate log sinks
197 prosody.events.fire_event("reopen-log-files");
200 -- Function to initiate prosody shutdown
201 function prosody.shutdown(reason)
202 log("info", "Shutting down: %s", reason or "unknown reason");
203 prosody.shutdown_reason = reason;
204 prosody.events.fire_event("server-stopping", {reason = reason});
205 server.setquitting(true);
208 -- Load SSL settings from config, and create a ctx table
209 local certmanager = require "core.certmanager";
210 local global_ssl_ctx = certmanager.create_context("*", "server");
211 prosody.global_ssl_ctx = global_ssl_ctx;
213 local cl = require "net.connlisteners";
214 function prosody.net_activate_ports(option, listener, default, conntype)
215 conntype = conntype or (global_ssl_ctx and "tls") or "tcp";
216 local ports_option = option and option.."_ports" or "ports";
217 if not cl.get(listener) then return; end
218 local ports = config.get("*", "core", ports_option) or default;
219 if type(ports) == "number" then ports = {ports} end;
221 if type(ports) ~= "table" then
222 log("error", "core."..ports_option.." is not a table");
224 for _, port in ipairs(ports) do
225 port = tonumber(port);
226 if type(port) ~= "number" then
227 log("error", "Non-numeric "..ports_option..": "..tostring(port));
229 local ok, err = cl.start(listener, {
230 ssl = conntype == "ssl" and global_ssl_ctx,
232 interface = (option and config.get("*", "core", option.."_interface"))
233 or cl.get(listener).default_interface
234 or config.get("*", "core", "interface"),
238 local friendly_message = err;
239 if err:match(" in use") then
240 if port == 5222 or port == 5223 or port == 5269 then
241 friendly_message = "check that Prosody or another XMPP server is "
242 .."not already running and using this port";
243 elseif port == 80 or port == 81 then
244 friendly_message = "check that a HTTP server is not already using "
246 elseif port == 5280 then
247 friendly_message = "check that Prosody or a BOSH connection manager "
248 .."is not already running";
250 friendly_message = "this port is in use by another application";
252 elseif err:match("permission") then
253 friendly_message = "Prosody does not have sufficient privileges to use this port";
254 elseif err == "no ssl context" then
255 if not config.get("*", "core", "ssl") then
256 friendly_message = "there is no 'ssl' config under Host \"*\" which is "
257 .."require for legacy SSL ports";
259 friendly_message = "initializing SSL support failed, see previous log entries";
262 log("error", "Failed to open server port %d, %s", port, friendly_message);
270 function read_version()
271 -- Try to determine version
272 local version_file = io.open((CFG_SOURCEDIR or ".").."/prosody.version");
274 prosody.version = version_file:read("*a"):gsub("%s*$", "");
275 version_file:close();
276 if #prosody.version == 12 and prosody.version:match("^[a-f0-9]+$") then
277 prosody.version = "hg:"..prosody.version;
280 prosody.version = "unknown";
284 function load_secondary_libraries()
285 --- Load and initialise core modules
286 require "util.import"
287 require "core.xmlhandlers"
288 require "core.rostermanager"
289 require "core.eventmanager"
290 require "core.hostmanager"
291 require "core.modulemanager"
292 require "core.usermanager"
293 require "core.sessionmanager"
294 require "core.stanza_router"
299 require "util.datetime"
300 require "util.iterators"
302 require "util.helpers"
304 pcall(require, "util.signal") -- Not on Windows
306 -- Commented to protect us from
307 -- the second kind of people
309 pcall(require, "remdebug.engine");
310 if remdebug then remdebug.engine.start() end
313 require "net.connlisteners";
315 require "util.stanza"
319 function init_data_store()
320 local data_path = config.get("*", "core", "data_path") or CFG_DATADIR or "data";
321 require "util.datamanager".set_data_path(data_path);
322 require "util.datamanager".add_callback(function(username, host, datastore, data)
323 if config.get(host, "core", "anonymous_login") then
326 return username, host, datastore, data;
330 function prepare_to_start()
331 log("info", "Prosody is using the %s backend for connection handling", server.get_backend());
332 -- Signal to modules that we are ready to start
333 eventmanager.fire_event("server-starting");
334 prosody.events.fire_event("server-starting");
336 -- start listening on sockets
337 if config.get("*", "core", "ports") then
338 prosody.net_activate_ports(nil, "multiplex", {5222, 5269});
339 if config.get("*", "core", "ssl_ports") then
340 prosody.net_activate_ports("ssl", "multiplex", {5223}, "ssl");
343 prosody.net_activate_ports("c2s", "xmppclient", {5222});
344 prosody.net_activate_ports("s2s", "xmppserver", {5269});
345 prosody.net_activate_ports("component", "xmppcomponent", {5347}, "tcp");
346 prosody.net_activate_ports("legacy_ssl", "xmppclient", {}, "ssl");
349 prosody.start_time = os.time();
352 function init_global_protection()
353 -- Catch global accesses
354 local locked_globals_mt = {
355 __index = function (t, k) log("warn", "%s", debug.traceback("Attempt to read a non-existent global '"..tostring(k).."'", 2)); end;
356 __newindex = function (t, k, v) error("Attempt to set a global: "..tostring(k).." = "..tostring(v), 2); end;
359 function prosody.unlock_globals()
360 setmetatable(_G, nil);
363 function prosody.lock_globals()
364 setmetatable(_G, locked_globals_mt);
368 prosody.lock_globals();
372 -- Error handler for errors that make it this far
373 local function catch_uncaught_error(err)
374 if type(err) == "string" and err:match("interrupted!$") then
378 log("error", "Top-level error, please report:\n%s", tostring(err));
379 local traceback = debug.traceback("", 2);
381 log("error", "%s", traceback);
384 prosody.events.fire_event("very-bad-error", {error = err, traceback = traceback});
387 while select(2, xpcall(server.loop, catch_uncaught_error)) ~= "quitting" do
393 log("info", "Shutdown status: Cleaning up");
394 prosody.events.fire_event("server-cleanup");
396 -- Ok, we're quitting I know, but we
397 -- need to do some tidying before we go :)
398 server.setquitting(false);
400 log("info", "Shutdown status: Closing all active sessions");
401 for hostname, host in pairs(hosts) do
402 log("debug", "Shutdown status: Closing client connections for %s", hostname)
403 if host.sessions then
404 local reason = { condition = "system-shutdown", text = "Server is shutting down" };
405 if prosody.shutdown_reason then
406 reason.text = reason.text..": "..prosody.shutdown_reason;
408 for username, user in pairs(host.sessions) do
409 for resource, session in pairs(user.sessions) do
410 log("debug", "Closing connection for %s@%s/%s", username, hostname, resource);
411 session:close(reason);
416 log("debug", "Shutdown status: Closing outgoing s2s connections from %s", hostname);
418 for remotehost, session in pairs(host.s2sout) do
419 if session.close then
420 session:close("system-shutdown");
422 log("warn", "Unable to close outgoing s2s session to %s, no session:close()?!", remotehost);
428 log("info", "Shutdown status: Closing all server connections");
431 server.setquitting(true);
435 -- These actions are in a strict order, as many depend on
436 -- previous steps to have already been performed
439 check_dependencies();
441 set_function_metatable();
445 log("info", "Hello and welcome to Prosody version %s", prosody.version);
446 load_secondary_libraries();
448 init_global_protection();
451 eventmanager.fire_event("server-started");
452 prosody.events.fire_event("server-started");
456 log("info", "Shutting down...");
458 eventmanager.fire_event("server-stopped");
459 prosody.events.fire_event("server-stopped");
460 log("info", "Shutdown complete");