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"
296 package.loaded['core.componentmanager'] = setmetatable({},{__index=function()
297 log("warn", "componentmanager is deprecated: %s", debug.traceback():match("\n[^\n]*\n[\s\t]*([^\n]*)"));
298 return function() end
304 require "util.datetime"
305 require "util.iterators"
307 require "util.helpers"
309 pcall(require, "util.signal") -- Not on Windows
311 -- Commented to protect us from
312 -- the second kind of people
314 pcall(require, "remdebug.engine");
315 if remdebug then remdebug.engine.start() end
318 require "net.connlisteners";
320 require "util.stanza"
324 function init_data_store()
325 local data_path = config.get("*", "core", "data_path") or CFG_DATADIR or "data";
326 require "util.datamanager".set_data_path(data_path);
327 require "util.datamanager".add_callback(function(username, host, datastore, data)
328 if config.get(host, "core", "anonymous_login") then
331 return username, host, datastore, data;
333 require "core.storagemanager";
336 function prepare_to_start()
337 log("info", "Prosody is using the %s backend for connection handling", server.get_backend());
338 -- Signal to modules that we are ready to start
339 prosody.events.fire_event("server-starting");
341 -- start listening on sockets
342 if config.get("*", "core", "ports") then
343 prosody.net_activate_ports(nil, "multiplex", {5222, 5269});
344 if config.get("*", "core", "ssl_ports") then
345 prosody.net_activate_ports("ssl", "multiplex", {5223}, "ssl");
348 prosody.net_activate_ports("c2s", "xmppclient", {5222});
349 prosody.net_activate_ports("s2s", "xmppserver", {5269});
350 prosody.net_activate_ports("component", "xmppcomponent", {5347}, "tcp");
351 prosody.net_activate_ports("legacy_ssl", "xmppclient", {}, "ssl");
354 prosody.start_time = os.time();
357 function init_global_protection()
358 -- Catch global accesses
359 local locked_globals_mt = {
360 __index = function (t, k) log("warn", "%s", debug.traceback("Attempt to read a non-existent global '"..tostring(k).."'", 2)); end;
361 __newindex = function (t, k, v) error("Attempt to set a global: "..tostring(k).." = "..tostring(v), 2); end;
364 function prosody.unlock_globals()
365 setmetatable(_G, nil);
368 function prosody.lock_globals()
369 setmetatable(_G, locked_globals_mt);
373 prosody.lock_globals();
377 -- Error handler for errors that make it this far
378 local function catch_uncaught_error(err)
379 if type(err) == "string" and err:match("interrupted!$") then
383 log("error", "Top-level error, please report:\n%s", tostring(err));
384 local traceback = debug.traceback("", 2);
386 log("error", "%s", traceback);
389 prosody.events.fire_event("very-bad-error", {error = err, traceback = traceback});
392 while select(2, xpcall(server.loop, catch_uncaught_error)) ~= "quitting" do
398 log("info", "Shutdown status: Cleaning up");
399 prosody.events.fire_event("server-cleanup");
401 -- Ok, we're quitting I know, but we
402 -- need to do some tidying before we go :)
403 server.setquitting(false);
405 log("info", "Shutdown status: Closing all active sessions");
406 for hostname, host in pairs(hosts) do
407 log("debug", "Shutdown status: Closing client connections for %s", hostname)
408 if host.sessions then
409 local reason = { condition = "system-shutdown", text = "Server is shutting down" };
410 if prosody.shutdown_reason then
411 reason.text = reason.text..": "..prosody.shutdown_reason;
413 for username, user in pairs(host.sessions) do
414 for resource, session in pairs(user.sessions) do
415 log("debug", "Closing connection for %s@%s/%s", username, hostname, resource);
416 session:close(reason);
421 log("debug", "Shutdown status: Closing outgoing s2s connections from %s", hostname);
423 for remotehost, session in pairs(host.s2sout) do
424 if session.close then
425 session:close("system-shutdown");
427 log("warn", "Unable to close outgoing s2s session to %s, no session:close()?!", remotehost);
433 log("info", "Shutdown status: Closing all server connections");
436 server.setquitting(true);
440 -- These actions are in a strict order, as many depend on
441 -- previous steps to have already been performed
444 check_dependencies();
446 set_function_metatable();
450 log("info", "Hello and welcome to Prosody version %s", prosody.version);
451 load_secondary_libraries();
453 init_global_protection();
456 prosody.events.fire_event("server-started");
460 log("info", "Shutting down...");
462 prosody.events.fire_event("server-stopped");
463 log("info", "Shutdown complete");