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 prosody.arg = _G.arg;
168 prosody.platform = "unknown";
169 if os.getenv("WINDIR") then
170 prosody.platform = "windows";
171 elseif package.config:sub(1,1) == "/" then
172 prosody.platform = "posix";
175 prosody.installed = nil;
176 if CFG_SOURCEDIR and (prosody.platform == "windows" or CFG_SOURCEDIR:match("^/")) then
177 prosody.installed = true;
180 -- Function to reload the config file
181 function prosody.reload_config()
182 log("info", "Reloading configuration file");
183 prosody.events.fire_event("reloading-config");
184 local ok, level, err = config.load((rawget(_G, "CFG_CONFIGDIR") or ".").."/prosody.cfg.lua");
186 if level == "parser" then
187 log("error", "There was an error parsing the configuration file: %s", tostring(err));
188 elseif level == "file" then
189 log("error", "Couldn't read the config file when trying to reload: %s", tostring(err));
192 return ok, (err and tostring(level)..": "..tostring(err)) or nil;
195 -- Function to reopen logfiles
196 function prosody.reopen_logfiles()
197 log("info", "Re-opening log files");
198 prosody.events.fire_event("reopen-log-files");
201 -- Function to initiate prosody shutdown
202 function prosody.shutdown(reason)
203 log("info", "Shutting down: %s", reason or "unknown reason");
204 prosody.shutdown_reason = reason;
205 prosody.events.fire_event("server-stopping", {reason = reason});
206 server.setquitting(true);
209 -- Load SSL settings from config, and create a ctx table
210 local certmanager = require "core.certmanager";
211 local global_ssl_ctx = certmanager.create_context("*", "server");
212 prosody.global_ssl_ctx = global_ssl_ctx;
214 local cl = require "net.connlisteners";
215 function prosody.net_activate_ports(option, listener, default, conntype)
216 conntype = conntype or (global_ssl_ctx and "tls") or "tcp";
217 local ports_option = option and option.."_ports" or "ports";
218 if not cl.get(listener) then return; end
219 local ports = config.get("*", "core", ports_option) or default;
220 if type(ports) == "number" then ports = {ports} end;
222 if type(ports) ~= "table" then
223 log("error", "core."..ports_option.." is not a table");
225 for _, port in ipairs(ports) do
226 port = tonumber(port);
227 if type(port) ~= "number" then
228 log("error", "Non-numeric "..ports_option..": "..tostring(port));
230 local ok, err = cl.start(listener, {
231 ssl = conntype == "ssl" and global_ssl_ctx,
233 interface = (option and config.get("*", "core", option.."_interface"))
234 or cl.get(listener).default_interface
235 or config.get("*", "core", "interface"),
239 local friendly_message = err;
240 if err:match(" in use") then
241 if port == 5222 or port == 5223 or port == 5269 then
242 friendly_message = "check that Prosody or another XMPP server is "
243 .."not already running and using this port";
244 elseif port == 80 or port == 81 then
245 friendly_message = "check that a HTTP server is not already using "
247 elseif port == 5280 then
248 friendly_message = "check that Prosody or a BOSH connection manager "
249 .."is not already running";
251 friendly_message = "this port is in use by another application";
253 elseif err:match("permission") then
254 friendly_message = "Prosody does not have sufficient privileges to use this port";
255 elseif err == "no ssl context" then
256 if not config.get("*", "core", "ssl") then
257 friendly_message = "there is no 'ssl' config under Host \"*\" which is "
258 .."require for legacy SSL ports";
260 friendly_message = "initializing SSL support failed, see previous log entries";
263 log("error", "Failed to open server port %d, %s", port, friendly_message);
271 function read_version()
272 -- Try to determine version
273 local version_file = io.open((CFG_SOURCEDIR or ".").."/prosody.version");
275 prosody.version = version_file:read("*a"):gsub("%s*$", "");
276 version_file:close();
277 if #prosody.version == 12 and prosody.version:match("^[a-f0-9]+$") then
278 prosody.version = "hg:"..prosody.version;
281 prosody.version = "unknown";
285 function load_secondary_libraries()
286 --- Load and initialise core modules
287 require "util.import"
288 require "util.xmppstream"
289 require "core.xmlhandlers"
290 require "core.rostermanager"
291 require "core.hostmanager"
292 require "core.modulemanager"
293 require "core.usermanager"
294 require "core.sessionmanager"
295 require "core.stanza_router"
300 require "util.datetime"
301 require "util.iterators"
303 require "util.helpers"
305 pcall(require, "util.signal") -- Not on Windows
307 -- Commented to protect us from
308 -- the second kind of people
310 pcall(require, "remdebug.engine");
311 if remdebug then remdebug.engine.start() end
314 require "net.connlisteners";
316 require "util.stanza"
320 function init_data_store()
321 local data_path = config.get("*", "core", "data_path") or CFG_DATADIR or "data";
322 require "util.datamanager".set_data_path(data_path);
323 require "util.datamanager".add_callback(function(username, host, datastore, data)
324 if config.get(host, "core", "anonymous_login") then
327 return username, host, datastore, data;
331 function prepare_to_start()
332 log("info", "Prosody is using the %s backend for connection handling", server.get_backend());
333 -- Signal to modules that we are ready to start
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 prosody.events.fire_event("server-started");
455 log("info", "Shutting down...");
457 prosody.events.fire_event("server-stopped");
458 log("info", "Shutdown complete");