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 debug.setmetatable(function() end, mt);
149 function init_global_state()
154 -- Global 'prosody' object
156 local prosody = prosody;
158 prosody.bare_sessions = bare_sessions;
159 prosody.full_sessions = full_sessions;
160 prosody.hosts = hosts;
162 prosody.paths = { source = CFG_SOURCEDIR, config = CFG_CONFIGDIR,
163 plugins = CFG_PLUGINDIR, data = CFG_DATADIR };
165 prosody.arg = _G.arg;
167 prosody.events = require "util.events".new();
169 prosody.platform = "unknown";
170 if os.getenv("WINDIR") then
171 prosody.platform = "windows";
172 elseif package.config:sub(1,1) == "/" then
173 prosody.platform = "posix";
176 prosody.installed = nil;
177 if CFG_SOURCEDIR and (prosody.platform == "windows" or CFG_SOURCEDIR:match("^/")) then
178 prosody.installed = true;
181 -- Function to reload the config file
182 function prosody.reload_config()
183 log("info", "Reloading configuration file");
184 prosody.events.fire_event("reloading-config");
185 local ok, level, err = config.load((rawget(_G, "CFG_CONFIGDIR") or ".").."/prosody.cfg.lua");
187 if level == "parser" then
188 log("error", "There was an error parsing the configuration file: %s", tostring(err));
189 elseif level == "file" then
190 log("error", "Couldn't read the config file when trying to reload: %s", tostring(err));
193 return ok, (err and tostring(level)..": "..tostring(err)) or nil;
196 -- Function to reopen logfiles
197 function prosody.reopen_logfiles()
198 log("info", "Re-opening log files");
199 eventmanager.fire_event("reopen-log-files"); -- Handled by appropriate log sinks
200 prosody.events.fire_event("reopen-log-files");
203 -- Function to initiate prosody shutdown
204 function prosody.shutdown(reason)
205 log("info", "Shutting down: %s", reason or "unknown reason");
206 prosody.shutdown_reason = reason;
207 prosody.events.fire_event("server-stopping", {reason = reason});
208 server.setquitting(true);
211 -- Load SSL settings from config, and create a ctx table
212 local certmanager = require "core.certmanager";
213 local global_ssl_ctx = certmanager.create_context("*", "server");
214 prosody.global_ssl_ctx = global_ssl_ctx;
216 local cl = require "net.connlisteners";
217 function prosody.net_activate_ports(option, listener, default, conntype)
218 conntype = conntype or (global_ssl_ctx and "tls") or "tcp";
219 local ports_option = option and option.."_ports" or "ports";
220 if not cl.get(listener) then return; end
221 local ports = config.get("*", "core", ports_option) or default;
222 if type(ports) == "number" then ports = {ports} end;
224 if type(ports) ~= "table" then
225 log("error", "core."..ports_option.." is not a table");
227 for _, port in ipairs(ports) do
228 port = tonumber(port);
229 if type(port) ~= "number" then
230 log("error", "Non-numeric "..ports_option..": "..tostring(port));
232 local ok, err = cl.start(listener, {
233 ssl = conntype == "ssl" and global_ssl_ctx,
235 interface = (option and config.get("*", "core", option.."_interface"))
236 or cl.get(listener).default_interface
237 or config.get("*", "core", "interface"),
241 local friendly_message = err;
242 if err:match(" in use") then
243 if port == 5222 or port == 5223 or port == 5269 then
244 friendly_message = "check that Prosody or another XMPP server is "
245 .."not already running and using this port";
246 elseif port == 80 or port == 81 then
247 friendly_message = "check that a HTTP server is not already using "
249 elseif port == 5280 then
250 friendly_message = "check that Prosody or a BOSH connection manager "
251 .."is not already running";
253 friendly_message = "this port is in use by another application";
255 elseif err:match("permission") then
256 friendly_message = "Prosody does not have sufficient privileges to use this port";
257 elseif err == "no ssl context" then
258 if not config.get("*", "core", "ssl") then
259 friendly_message = "there is no 'ssl' config under Host \"*\" which is "
260 .."require for legacy SSL ports";
262 friendly_message = "initializing SSL support failed, see previous log entries";
265 log("error", "Failed to open server port %d, %s", port, friendly_message);
273 function read_version()
274 -- Try to determine version
275 local version_file = io.open((CFG_SOURCEDIR or ".").."/prosody.version");
277 prosody.version = version_file:read("*a"):gsub("%s*$", "");
278 version_file:close();
279 if #prosody.version == 12 and prosody.version:match("^[a-f0-9]+$") then
280 prosody.version = "hg:"..prosody.version;
283 prosody.version = "unknown";
287 function load_secondary_libraries()
288 --- Load and initialise core modules
289 require "util.import"
290 require "core.xmlhandlers"
291 require "core.rostermanager"
292 require "core.eventmanager"
293 require "core.hostmanager"
294 require "core.modulemanager"
295 require "core.usermanager"
296 require "core.sessionmanager"
297 require "core.stanza_router"
302 require "util.datetime"
303 require "util.iterators"
305 require "util.helpers"
307 pcall(require, "util.signal") -- Not on Windows
309 -- Commented to protect us from
310 -- the second kind of people
312 pcall(require, "remdebug.engine");
313 if remdebug then remdebug.engine.start() end
316 require "net.connlisteners";
318 require "util.stanza"
322 function init_data_store()
323 local data_path = config.get("*", "core", "data_path") or CFG_DATADIR or "data";
324 require "util.datamanager".set_data_path(data_path);
325 require "util.datamanager".add_callback(function(username, host, datastore, data)
326 if config.get(host, "core", "anonymous_login") then
329 return username, host, datastore, data;
333 function prepare_to_start()
334 log("info", "Prosody is using the %s backend for connection handling", server.get_backend());
335 -- Signal to modules that we are ready to start
336 eventmanager.fire_event("server-starting");
337 prosody.events.fire_event("server-starting");
339 -- start listening on sockets
340 if config.get("*", "core", "ports") then
341 prosody.net_activate_ports(nil, "multiplex", {5222, 5269});
342 if config.get("*", "core", "ssl_ports") then
343 prosody.net_activate_ports("ssl", "multiplex", {5223}, "ssl");
346 prosody.net_activate_ports("c2s", "xmppclient", {5222});
347 prosody.net_activate_ports("s2s", "xmppserver", {5269});
348 prosody.net_activate_ports("component", "xmppcomponent", {5347}, "tcp");
349 prosody.net_activate_ports("legacy_ssl", "xmppclient", {}, "ssl");
352 prosody.start_time = os.time();
355 function init_global_protection()
356 -- Catch global accesses
357 local locked_globals_mt = {
358 __index = function (t, k) log("warn", "%s", debug.traceback("Attempt to read a non-existent global '"..tostring(k).."'", 2)); end;
359 __newindex = function (t, k, v) error("Attempt to set a global: "..tostring(k).." = "..tostring(v), 2); end;
362 function prosody.unlock_globals()
363 setmetatable(_G, nil);
366 function prosody.lock_globals()
367 setmetatable(_G, locked_globals_mt);
371 prosody.lock_globals();
375 -- Error handler for errors that make it this far
376 local function catch_uncaught_error(err)
377 if type(err) == "string" and err:match("interrupted!$") then
381 log("error", "Top-level error, please report:\n%s", tostring(err));
382 local traceback = debug.traceback("", 2);
384 log("error", "%s", traceback);
387 prosody.events.fire_event("very-bad-error", {error = err, traceback = traceback});
390 while select(2, xpcall(server.loop, catch_uncaught_error)) ~= "quitting" do
396 log("info", "Shutdown status: Cleaning up");
397 prosody.events.fire_event("server-cleanup");
399 -- Ok, we're quitting I know, but we
400 -- need to do some tidying before we go :)
401 server.setquitting(false);
403 log("info", "Shutdown status: Closing all active sessions");
404 for hostname, host in pairs(hosts) do
405 log("debug", "Shutdown status: Closing client connections for %s", hostname)
406 if host.sessions then
407 local reason = { condition = "system-shutdown", text = "Server is shutting down" };
408 if prosody.shutdown_reason then
409 reason.text = reason.text..": "..prosody.shutdown_reason;
411 for username, user in pairs(host.sessions) do
412 for resource, session in pairs(user.sessions) do
413 log("debug", "Closing connection for %s@%s/%s", username, hostname, resource);
414 session:close(reason);
419 log("debug", "Shutdown status: Closing outgoing s2s connections from %s", hostname);
421 for remotehost, session in pairs(host.s2sout) do
422 if session.close then
423 session:close("system-shutdown");
425 log("warn", "Unable to close outgoing s2s session to %s, no session:close()?!", remotehost);
431 log("info", "Shutdown status: Closing all server connections");
434 server.setquitting(true);
438 -- These actions are in a strict order, as many depend on
439 -- previous steps to have already been performed
442 check_dependencies();
444 set_function_metatable();
448 log("info", "Hello and welcome to Prosody version %s", prosody.version);
449 load_secondary_libraries();
451 init_global_protection();
454 eventmanager.fire_event("server-started");
455 prosody.events.fire_event("server-started");
459 log("info", "Shutting down...");
461 eventmanager.fire_event("server-stopped");
462 prosody.events.fire_event("server-stopped");
463 log("info", "Shutdown complete");