3 -- Copyright (C) 2008-2009 Matthew Wild
4 -- Copyright (C) 2008-2009 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 init_global_state()
131 -- Global 'prosody' object
133 local prosody = prosody;
135 prosody.bare_sessions = bare_sessions;
136 prosody.full_sessions = full_sessions;
137 prosody.hosts = hosts;
139 prosody.paths = { source = CFG_SOURCEDIR, config = CFG_CONFIGDIR,
140 plugins = CFG_PLUGINDIR, data = CFG_DATADIR };
142 prosody.arg = _G.arg;
144 prosody.events = require "util.events".new();
146 prosody.platform = "unknown";
147 if os.getenv("WINDIR") then
148 prosody.platform = "windows";
149 elseif package.config:sub(1,1) == "/" then
150 prosody.platform = "posix";
153 prosody.installed = nil;
154 if CFG_SOURCEDIR and (prosody.platform == "windows" or CFG_SOURCEDIR:match("^/")) then
155 prosody.installed = true;
158 -- Function to reload the config file
159 function prosody.reload_config()
160 log("info", "Reloading configuration file");
161 prosody.events.fire_event("reloading-config");
162 local ok, level, err = config.load((rawget(_G, "CFG_CONFIGDIR") or ".").."/prosody.cfg.lua");
164 if level == "parser" then
165 log("error", "There was an error parsing the configuration file: %s", tostring(err));
166 elseif level == "file" then
167 log("error", "Couldn't read the config file when trying to reload: %s", tostring(err));
170 return ok, (err and tostring(level)..": "..tostring(err)) or nil;
173 -- Function to reopen logfiles
174 function prosody.reopen_logfiles()
175 log("info", "Re-opening log files");
176 eventmanager.fire_event("reopen-log-files"); -- Handled by appropriate log sinks
177 prosody.events.fire_event("reopen-log-files");
180 -- Function to initiate prosody shutdown
181 function prosody.shutdown(reason)
182 log("info", "Shutting down: %s", reason or "unknown reason");
183 prosody.shutdown_reason = reason;
184 prosody.events.fire_event("server-stopping", {reason = reason});
185 server.setquitting(true);
188 -- Load SSL settings from config, and create a ctx table
189 local global_ssl_ctx = rawget(_G, "ssl") and config.get("*", "core", "ssl");
190 if global_ssl_ctx then
191 local default_ssl_ctx = { mode = "server", protocol = "sslv23", capath = "/etc/ssl/certs", verify = "none", options = "no_sslv2" };
192 setmetatable(global_ssl_ctx, { __index = default_ssl_ctx });
193 prosody.global_ssl_ctx = global_ssl_ctx;
196 local cl = require "net.connlisteners";
197 function prosody.net_activate_ports(option, listener, default, conntype)
198 conntype = conntype or (global_ssl_ctx and "tls") or "tcp";
199 local ports_option = option and option.."_ports" or "ports";
200 if not cl.get(listener) then return; end
201 local ports = config.get("*", "core", ports_option) or default;
202 if type(ports) == "number" then ports = {ports} end;
204 if type(ports) ~= "table" then
205 log("error", "core."..ports_option.." is not a table");
207 for _, port in ipairs(ports) do
208 port = tonumber(port);
209 if type(port) ~= "number" then
210 log("error", "Non-numeric "..ports_option..": "..tostring(port));
212 local ok, err = cl.start(listener, {
213 ssl = conntype == "ssl" and global_ssl_ctx,
215 interface = (option and config.get("*", "core", option.."_interface"))
216 or cl.get(listener).default_interface
217 or config.get("*", "core", "interface"),
221 local friendly_message = err;
222 if err:match(" in use") then
223 if port == 5222 or port == 5223 or port == 5269 then
224 friendly_message = "check that Prosody or another XMPP server is "
225 .."not already running and using this port";
226 elseif port == 80 or port == 81 then
227 friendly_message = "check that a HTTP server is not already using "
229 elseif port == 5280 then
230 friendly_message = "check that Prosody or a BOSH connection manager "
231 .."is not already running";
233 friendly_message = "this port is in use by another application";
235 elseif err:match("permission") then
236 friendly_message = "Prosody does not have sufficient privileges to use this port";
237 elseif err == "no ssl context" then
238 friendly_message = "there is no 'ssl' config under Host \"*\" which is "
239 .."require for legacy SSL ports";
241 log("error", "Failed to open server port %d, %s", port, friendly_message);
249 function read_version()
250 -- Try to determine version
251 local version_file = io.open((CFG_SOURCEDIR or ".").."/prosody.version");
253 prosody.version = version_file:read("*a"):gsub("%s*$", "");
254 version_file:close();
255 if #prosody.version == 12 and prosody.version:match("^[a-f0-9]+$") then
256 prosody.version = "hg:"..prosody.version;
259 prosody.version = "unknown";
263 function load_secondary_libraries()
264 --- Load and initialise core modules
265 require "util.import"
266 require "core.xmlhandlers"
267 require "core.rostermanager"
268 require "core.eventmanager"
269 require "core.hostmanager"
270 require "core.modulemanager"
271 require "core.usermanager"
272 require "core.sessionmanager"
273 require "core.stanza_router"
278 require "util.datetime"
279 require "util.iterators"
281 require "util.helpers"
283 pcall(require, "util.signal") -- Not on Windows
285 -- Commented to protect us from
286 -- the second kind of people
288 pcall(require, "remdebug.engine");
289 if remdebug then remdebug.engine.start() end
292 require "net.connlisteners";
294 require "util.stanza"
298 function init_data_store()
299 local data_path = config.get("*", "core", "data_path") or CFG_DATADIR or "data";
300 require "util.datamanager".set_data_path(data_path);
301 require "util.datamanager".add_callback(function(username, host, datastore, data)
302 if config.get(host, "core", "anonymous_login") then
305 return username, host, datastore, data;
309 function prepare_to_start()
310 log("debug", "Prosody is using the %s backend for connection handling", server.get_backend());
311 -- Signal to modules that we are ready to start
312 eventmanager.fire_event("server-starting");
313 prosody.events.fire_event("server-starting");
315 -- start listening on sockets
316 if config.get("*", "core", "ports") then
317 prosody.net_activate_ports(nil, "multiplex", {5222, 5269});
318 if config.get("*", "core", "ssl_ports") then
319 prosody.net_activate_ports("ssl", "multiplex", {5223}, "ssl");
322 prosody.net_activate_ports("c2s", "xmppclient", {5222});
323 prosody.net_activate_ports("s2s", "xmppserver", {5269});
324 prosody.net_activate_ports("component", "xmppcomponent", {5347}, "tcp");
325 prosody.net_activate_ports("legacy_ssl", "xmppclient", {}, "ssl");
328 prosody.start_time = os.time();
331 function init_global_protection()
332 -- Catch global accesses
333 local locked_globals_mt = {
334 __index = function (t, k) log("warn", "%s", debug.traceback("Attempt to read a non-existent global '"..tostring(k).."'", 2)); end;
335 __newindex = function (t, k, v) error("Attempt to set a global: "..tostring(k).." = "..tostring(v), 2); end;
338 function prosody.unlock_globals()
339 setmetatable(_G, nil);
342 function prosody.lock_globals()
343 setmetatable(_G, locked_globals_mt);
347 prosody.lock_globals();
351 -- Error handler for errors that make it this far
352 local function catch_uncaught_error(err)
353 if type(err) == "string" and err:match("interrupted!$") then
357 log("error", "Top-level error, please report:\n%s", tostring(err));
358 local traceback = debug.traceback("", 2);
360 log("error", "%s", traceback);
363 prosody.events.fire_event("very-bad-error", {error = err, traceback = traceback});
366 while select(2, xpcall(server.loop, catch_uncaught_error)) ~= "quitting" do
372 log("info", "Shutdown status: Cleaning up");
373 prosody.events.fire_event("server-cleanup");
375 -- Ok, we're quitting I know, but we
376 -- need to do some tidying before we go :)
377 server.setquitting(false);
379 log("info", "Shutdown status: Closing all active sessions");
380 for hostname, host in pairs(hosts) do
381 log("debug", "Shutdown status: Closing client connections for %s", hostname)
382 if host.sessions then
383 local reason = { condition = "system-shutdown", text = "Server is shutting down" };
384 if prosody.shutdown_reason then
385 reason.text = reason.text..": "..prosody.shutdown_reason;
387 for username, user in pairs(host.sessions) do
388 for resource, session in pairs(user.sessions) do
389 log("debug", "Closing connection for %s@%s/%s", username, hostname, resource);
390 session:close(reason);
395 log("debug", "Shutdown status: Closing outgoing s2s connections from %s", hostname);
397 for remotehost, session in pairs(host.s2sout) do
398 if session.close then
399 session:close("system-shutdown");
401 log("warn", "Unable to close outgoing s2s session to %s, no session:close()?!", remotehost);
407 log("info", "Shutdown status: Closing all server connections");
410 server.setquitting(true);
414 -- These actions are in a strict order, as many depend on
415 -- previous steps to have already been performed
418 check_dependencies();
423 log("info", "Hello and welcome to Prosody version %s", prosody.version);
424 load_secondary_libraries();
426 init_global_protection();
429 eventmanager.fire_event("server-started");
430 prosody.events.fire_event("server-started");
434 log("info", "Shutting down...");
436 eventmanager.fire_event("server-stopped");
437 prosody.events.fire_event("server-stopped");
438 log("info", "Shutdown complete");