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 -- Global 'prosody' object
33 prosody = { events = require "util.events".new(); };
34 local prosody = prosody;
36 -- Load the config-parsing module
37 config = require "core.configmanager"
40 -- Define the functions we call during startup, the
41 -- actual startup happens right at the end, where these
42 -- functions get called
44 function read_config()
48 if arg[1] == "--config" and arg[2] then
49 table.insert(filenames, arg[2]);
51 table.insert(filenames, CFG_CONFIGDIR.."/"..arg[2]);
54 for _, format in ipairs(config.parsers()) do
55 table.insert(filenames, (CFG_CONFIGDIR or ".").."/prosody.cfg."..format);
58 for _,_filename in ipairs(filenames) do
60 local file = io.open(filename);
63 CFG_CONFIGDIR = filename:match("^(.*)[\\/][^\\/]*$");
67 local ok, level, err = config.load(filename);
70 print("**************************");
71 if level == "parser" then
72 print("A problem occured while reading the config file "..(CFG_CONFIGDIR or ".").."/prosody.cfg.lua");
73 local err_line, err_message = tostring(err):match("%[string .-%]:(%d*): (.*)");
74 print("Error"..(err_line and (" on line "..err_line) or "")..": "..(err_message or tostring(err)));
76 elseif level == "file" then
77 print("Prosody was unable to find the configuration file.");
78 print("We looked for: "..(CFG_CONFIGDIR or ".").."/prosody.cfg.lua");
79 print("A sample config file is included in the Prosody download called prosody.cfg.lua.dist");
80 print("Copy or rename it to prosody.cfg.lua and edit as necessary.");
82 print("More help on configuring Prosody can be found at http://prosody.im/doc/configure");
84 print("**************************");
90 function load_libraries()
91 -- Load socket framework
92 server = require "net.server"
95 function init_logging()
97 require "core.loggingmanager"
100 function check_dependencies()
101 -- Check runtime dependencies
102 if not require "util.dependencies".check_dependencies() then
107 function sandbox_require()
108 -- Replace require() with one that doesn't pollute _G, required
109 -- for neat sandboxing of modules
111 local _real_require = require;
112 function require(...)
113 local curr_env = getfenv(2);
114 local curr_env_mt = getmetatable(getfenv(2));
115 local _realG_mt = getmetatable(_realG);
116 if curr_env_mt and curr_env_mt.__index and not curr_env_mt.__newindex and _realG_mt then
118 old_newindex, _realG_mt.__newindex = _realG_mt.__newindex, curr_env;
119 local ret = _real_require(...);
120 _realG_mt.__newindex = old_newindex;
123 return _real_require(...);
127 function set_function_metatable()
129 function mt.__index(f, upvalue)
130 local i, name, value = 0;
133 name, value = debug.getupvalue(f, i);
134 until name == upvalue or name == nil;
137 function mt.__newindex(f, upvalue, value)
141 name = debug.getupvalue(f, i);
142 until name == upvalue or name == nil;
144 debug.setupvalue(f, i, value);
147 function mt.__tostring(f)
148 local info = debug.getinfo(f);
149 return ("function(%s:%d)"):format(info.short_src:match("[^\\/]*$"), info.linedefined);
151 debug.setmetatable(function() end, mt);
154 function init_global_state()
159 prosody.bare_sessions = bare_sessions;
160 prosody.full_sessions = full_sessions;
161 prosody.hosts = hosts;
163 prosody.paths = { source = CFG_SOURCEDIR, config = CFG_CONFIGDIR,
164 plugins = CFG_PLUGINDIR, data = CFG_DATADIR };
166 local path_sep = package.config:sub(1,1);
167 local rel_path_start = ".."..path_sep;
168 function prosody.resolve_relative_path(parent_path, path)
171 if path_sep == "/" and path:sub(1,1) ~= "/" then
173 elseif path_sep == "\\" and (path:sub(1,1) ~= "/" and path:sub(2,3) ~= ":\\") then
177 return parent_path..path_sep..path;
183 prosody.arg = _G.arg;
185 prosody.platform = "unknown";
186 if os.getenv("WINDIR") then
187 prosody.platform = "windows";
188 elseif package.config:sub(1,1) == "/" then
189 prosody.platform = "posix";
192 prosody.installed = nil;
193 if CFG_SOURCEDIR and (prosody.platform == "windows" or CFG_SOURCEDIR:match("^/")) then
194 prosody.installed = true;
197 -- Function to reload the config file
198 function prosody.reload_config()
199 log("info", "Reloading configuration file");
200 prosody.events.fire_event("reloading-config");
201 local ok, level, err = config.load((rawget(_G, "CFG_CONFIGDIR") or ".").."/prosody.cfg.lua");
203 if level == "parser" then
204 log("error", "There was an error parsing the configuration file: %s", tostring(err));
205 elseif level == "file" then
206 log("error", "Couldn't read the config file when trying to reload: %s", tostring(err));
209 return ok, (err and tostring(level)..": "..tostring(err)) or nil;
212 -- Function to reopen logfiles
213 function prosody.reopen_logfiles()
214 log("info", "Re-opening log files");
215 prosody.events.fire_event("reopen-log-files");
218 -- Function to initiate prosody shutdown
219 function prosody.shutdown(reason)
220 log("info", "Shutting down: %s", reason or "unknown reason");
221 prosody.shutdown_reason = reason;
222 prosody.events.fire_event("server-stopping", {reason = reason});
223 server.setquitting(true);
226 -- Load SSL settings from config, and create a ctx table
227 local certmanager = require "core.certmanager";
228 local global_ssl_ctx = certmanager.create_context("*", "server");
229 prosody.global_ssl_ctx = global_ssl_ctx;
231 local cl = require "net.connlisteners";
232 function prosody.net_activate_ports(option, listener, default, conntype)
233 conntype = conntype or (global_ssl_ctx and "tls") or "tcp";
234 local ports_option = option and option.."_ports" or "ports";
235 if not cl.get(listener) then return; end
236 local ports = config.get("*", "core", ports_option) or default;
237 if type(ports) == "number" then ports = {ports} end;
239 if type(ports) ~= "table" then
240 log("error", "core."..ports_option.." is not a table");
242 for _, port in ipairs(ports) do
243 port = tonumber(port);
244 if type(port) ~= "number" then
245 log("error", "Non-numeric "..ports_option..": "..tostring(port));
247 local ok, err = cl.start(listener, {
248 ssl = conntype == "ssl" and global_ssl_ctx,
250 interface = (option and config.get("*", "core", option.."_interface"))
251 or cl.get(listener).default_interface
252 or config.get("*", "core", "interface"),
256 local friendly_message = err;
257 if err:match(" in use") then
258 if port == 5222 or port == 5223 or port == 5269 then
259 friendly_message = "check that Prosody or another XMPP server is "
260 .."not already running and using this port";
261 elseif port == 80 or port == 81 then
262 friendly_message = "check that a HTTP server is not already using "
264 elseif port == 5280 then
265 friendly_message = "check that Prosody or a BOSH connection manager "
266 .."is not already running";
268 friendly_message = "this port is in use by another application";
270 elseif err:match("permission") then
271 friendly_message = "Prosody does not have sufficient privileges to use this port";
272 elseif err == "no ssl context" then
273 if not config.get("*", "core", "ssl") then
274 friendly_message = "there is no 'ssl' config under Host \"*\" which is "
275 .."require for legacy SSL ports";
277 friendly_message = "initializing SSL support failed, see previous log entries";
280 log("error", "Failed to open server port %d, %s", port, friendly_message);
288 function read_version()
289 -- Try to determine version
290 local version_file = io.open((CFG_SOURCEDIR or ".").."/prosody.version");
292 prosody.version = version_file:read("*a"):gsub("%s*$", "");
293 version_file:close();
294 if #prosody.version == 12 and prosody.version:match("^[a-f0-9]+$") then
295 prosody.version = "hg:"..prosody.version;
298 prosody.version = "unknown";
302 function load_secondary_libraries()
303 --- Load and initialise core modules
304 require "util.import"
305 require "util.xmppstream"
306 require "core.xmlhandlers"
307 require "core.rostermanager"
308 require "core.hostmanager"
309 require "core.modulemanager"
310 require "core.usermanager"
311 require "core.sessionmanager"
312 require "core.stanza_router"
317 require "util.datetime"
318 require "util.iterators"
320 require "util.helpers"
322 pcall(require, "util.signal") -- Not on Windows
324 -- Commented to protect us from
325 -- the second kind of people
327 pcall(require, "remdebug.engine");
328 if remdebug then remdebug.engine.start() end
331 require "net.connlisteners";
333 require "util.stanza"
337 function init_data_store()
338 local data_path = config.get("*", "core", "data_path") or CFG_DATADIR or "data";
339 require "util.datamanager".set_data_path(data_path);
340 require "util.datamanager".add_callback(function(username, host, datastore, data)
341 if config.get(host, "core", "anonymous_login") then
344 return username, host, datastore, data;
346 require "core.storagemanager";
349 function prepare_to_start()
350 log("info", "Prosody is using the %s backend for connection handling", server.get_backend());
351 -- Signal to modules that we are ready to start
352 prosody.events.fire_event("server-starting");
354 -- start listening on sockets
355 if config.get("*", "core", "ports") then
356 prosody.net_activate_ports(nil, "multiplex", {5222, 5269});
357 if config.get("*", "core", "ssl_ports") then
358 prosody.net_activate_ports("ssl", "multiplex", {5223}, "ssl");
361 prosody.net_activate_ports("c2s", "xmppclient", {5222});
362 prosody.net_activate_ports("s2s", "xmppserver", {5269});
363 prosody.net_activate_ports("component", "xmppcomponent", {5347}, "tcp");
364 prosody.net_activate_ports("legacy_ssl", "xmppclient", {}, "ssl");
367 prosody.start_time = os.time();
370 function init_global_protection()
371 -- Catch global accesses
372 local locked_globals_mt = {
373 __index = function (t, k) log("warn", "%s", debug.traceback("Attempt to read a non-existent global '"..tostring(k).."'", 2)); end;
374 __newindex = function (t, k, v) error("Attempt to set a global: "..tostring(k).." = "..tostring(v), 2); end;
377 function prosody.unlock_globals()
378 setmetatable(_G, nil);
381 function prosody.lock_globals()
382 setmetatable(_G, locked_globals_mt);
386 prosody.lock_globals();
390 -- Error handler for errors that make it this far
391 local function catch_uncaught_error(err)
392 if type(err) == "string" and err:match("interrupted!$") then
396 log("error", "Top-level error, please report:\n%s", tostring(err));
397 local traceback = debug.traceback("", 2);
399 log("error", "%s", traceback);
402 prosody.events.fire_event("very-bad-error", {error = err, traceback = traceback});
405 while select(2, xpcall(server.loop, catch_uncaught_error)) ~= "quitting" do
411 log("info", "Shutdown status: Cleaning up");
412 prosody.events.fire_event("server-cleanup");
414 -- Ok, we're quitting I know, but we
415 -- need to do some tidying before we go :)
416 server.setquitting(false);
418 log("info", "Shutdown status: Closing all active sessions");
419 for hostname, host in pairs(hosts) do
420 log("debug", "Shutdown status: Closing client connections for %s", hostname)
421 if host.sessions then
422 local reason = { condition = "system-shutdown", text = "Server is shutting down" };
423 if prosody.shutdown_reason then
424 reason.text = reason.text..": "..prosody.shutdown_reason;
426 for username, user in pairs(host.sessions) do
427 for resource, session in pairs(user.sessions) do
428 log("debug", "Closing connection for %s@%s/%s", username, hostname, resource);
429 session:close(reason);
434 log("debug", "Shutdown status: Closing outgoing s2s connections from %s", hostname);
436 for remotehost, session in pairs(host.s2sout) do
437 if session.close then
438 session:close("system-shutdown");
440 log("warn", "Unable to close outgoing s2s session to %s, no session:close()?!", remotehost);
446 log("info", "Shutdown status: Closing all server connections");
449 server.setquitting(true);
453 -- These actions are in a strict order, as many depend on
454 -- previous steps to have already been performed
457 check_dependencies();
459 set_function_metatable();
463 log("info", "Hello and welcome to Prosody version %s", prosody.version);
464 load_secondary_libraries();
466 init_global_protection();
469 prosody.events.fire_event("server-started");
473 log("info", "Shutting down...");
475 prosody.events.fire_event("server-stopped");
476 log("info", "Shutdown complete");