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 -- 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 -- Load the config-parsing module
33 config = require "core.configmanager"
36 -- Define the functions we call during startup, the
37 -- actual startup happens right at the end, where these
38 -- functions get called
40 function read_config()
44 if arg[1] == "--config" and arg[2] then
45 table.insert(filenames, arg[2]);
47 table.insert(filenames, CFG_CONFIGDIR.."/"..arg[2]);
50 for _, format in ipairs(config.parsers()) do
51 table.insert(filenames, (CFG_CONFIGDIR or ".").."/prosody.cfg."..format);
54 for _,_filename in ipairs(filenames) do
56 local file = io.open(filename);
59 CFG_CONFIGDIR = filename:match("^(.*)[\\/][^\\/]*$");
63 local ok, level, err = config.load(filename);
66 print("**************************");
67 if level == "parser" then
68 print("A problem occured while reading the config file "..(CFG_CONFIGDIR or ".").."/prosody.cfg.lua");
69 local err_line, err_message = tostring(err):match("%[string .-%]:(%d*): (.*)");
70 print("Error"..(err_line and (" on line "..err_line) or "")..": "..(err_message or tostring(err)));
72 elseif level == "file" then
73 print("Prosody was unable to find the configuration file.");
74 print("We looked for: "..(CFG_CONFIGDIR or ".").."/prosody.cfg.lua");
75 print("A sample config file is included in the Prosody download called prosody.cfg.lua.dist");
76 print("Copy or rename it to prosody.cfg.lua and edit as necessary.");
78 print("More help on configuring Prosody can be found at http://prosody.im/doc/configure");
80 print("**************************");
86 function load_libraries()
87 -- Load socket framework
88 server = require "net.server"
91 function init_logging()
93 require "core.loggingmanager"
96 function check_dependencies()
97 -- Check runtime dependencies
98 if not require "util.dependencies".check_dependencies() then
103 function sandbox_require()
104 -- Replace require() with one that doesn't pollute _G, required
105 -- for neat sandboxing of modules
107 local _real_require = require;
108 function require(...)
109 local curr_env = getfenv(2);
110 local curr_env_mt = getmetatable(getfenv(2));
111 local _realG_mt = getmetatable(_realG);
112 if curr_env_mt and curr_env_mt.__index and not curr_env_mt.__newindex and _realG_mt then
114 old_newindex, _realG_mt.__newindex = _realG_mt.__newindex, curr_env;
115 local ret = _real_require(...);
116 _realG_mt.__newindex = old_newindex;
119 return _real_require(...);
123 function init_global_state()
128 -- Global 'prosody' object
130 local prosody = prosody;
132 prosody.bare_sessions = bare_sessions;
133 prosody.full_sessions = full_sessions;
134 prosody.hosts = hosts;
136 prosody.paths = { source = CFG_SOURCEDIR, config = CFG_CONFIGDIR,
137 plugins = CFG_PLUGINDIR, data = CFG_DATADIR };
139 prosody.arg = _G.arg;
141 prosody.events = require "util.events".new();
143 prosody.platform = "unknown";
144 if os.getenv("WINDIR") then
145 prosody.platform = "windows";
146 elseif package.config:sub(1,1) == "/" then
147 prosody.platform = "posix";
150 prosody.installed = nil;
151 if CFG_SOURCEDIR and (prosody.platform == "windows" or CFG_SOURCEDIR:match("^/")) then
152 prosody.installed = true;
155 -- Function to reload the config file
156 function prosody.reload_config()
157 log("info", "Reloading configuration file");
158 prosody.events.fire_event("reloading-config");
159 local ok, level, err = config.load((rawget(_G, "CFG_CONFIGDIR") or ".").."/prosody.cfg.lua");
161 if level == "parser" then
162 log("error", "There was an error parsing the configuration file: %s", tostring(err));
163 elseif level == "file" then
164 log("error", "Couldn't read the config file when trying to reload: %s", tostring(err));
167 return ok, (err and tostring(level)..": "..tostring(err)) or nil;
170 -- Function to reopen logfiles
171 function prosody.reopen_logfiles()
172 log("info", "Re-opening log files");
173 eventmanager.fire_event("reopen-log-files"); -- Handled by appropriate log sinks
174 prosody.events.fire_event("reopen-log-files");
177 -- Function to initiate prosody shutdown
178 function prosody.shutdown(reason)
179 log("info", "Shutting down: %s", reason or "unknown reason");
180 prosody.shutdown_reason = reason;
181 prosody.events.fire_event("server-stopping", {reason = reason});
182 server.setquitting(true);
185 -- Load SSL settings from config, and create a ctx table
186 local certmanager = require "core.certmanager";
187 local global_ssl_ctx = certmanager.create_context("*", "server");
188 prosody.global_ssl_ctx = global_ssl_ctx;
190 local cl = require "net.connlisteners";
191 function prosody.net_activate_ports(option, listener, default, conntype)
192 conntype = conntype or (global_ssl_ctx and "tls") or "tcp";
193 local ports_option = option and option.."_ports" or "ports";
194 if not cl.get(listener) then return; end
195 local ports = config.get("*", "core", ports_option) or default;
196 if type(ports) == "number" then ports = {ports} end;
198 if type(ports) ~= "table" then
199 log("error", "core."..ports_option.." is not a table");
201 for _, port in ipairs(ports) do
202 port = tonumber(port);
203 if type(port) ~= "number" then
204 log("error", "Non-numeric "..ports_option..": "..tostring(port));
206 local ok, err = cl.start(listener, {
207 ssl = conntype == "ssl" and global_ssl_ctx,
209 interface = (option and config.get("*", "core", option.."_interface"))
210 or cl.get(listener).default_interface
211 or config.get("*", "core", "interface"),
215 local friendly_message = err;
216 if err:match(" in use") then
217 if port == 5222 or port == 5223 or port == 5269 then
218 friendly_message = "check that Prosody or another XMPP server is "
219 .."not already running and using this port";
220 elseif port == 80 or port == 81 then
221 friendly_message = "check that a HTTP server is not already using "
223 elseif port == 5280 then
224 friendly_message = "check that Prosody or a BOSH connection manager "
225 .."is not already running";
227 friendly_message = "this port is in use by another application";
229 elseif err:match("permission") then
230 friendly_message = "Prosody does not have sufficient privileges to use this port";
231 elseif err == "no ssl context" then
232 if not config.get("*", "core", "ssl") then
233 friendly_message = "there is no 'ssl' config under Host \"*\" which is "
234 .."require for legacy SSL ports";
236 friendly_message = "initializing SSL support failed, see previous log entries";
239 log("error", "Failed to open server port %d, %s", port, friendly_message);
247 function read_version()
248 -- Try to determine version
249 local version_file = io.open((CFG_SOURCEDIR or ".").."/prosody.version");
251 prosody.version = version_file:read("*a"):gsub("%s*$", "");
252 version_file:close();
253 if #prosody.version == 12 and prosody.version:match("^[a-f0-9]+$") then
254 prosody.version = "hg:"..prosody.version;
257 prosody.version = "unknown";
261 function load_secondary_libraries()
262 --- Load and initialise core modules
263 require "util.import"
264 require "core.xmlhandlers"
265 require "core.rostermanager"
266 require "core.eventmanager"
267 require "core.hostmanager"
268 require "core.modulemanager"
269 require "core.usermanager"
270 require "core.sessionmanager"
271 require "core.stanza_router"
276 require "util.datetime"
277 require "util.iterators"
279 require "util.helpers"
281 pcall(require, "util.signal") -- Not on Windows
283 -- Commented to protect us from
284 -- the second kind of people
286 pcall(require, "remdebug.engine");
287 if remdebug then remdebug.engine.start() end
290 require "net.connlisteners";
292 require "util.stanza"
296 function init_data_store()
297 local data_path = config.get("*", "core", "data_path") or CFG_DATADIR or "data";
298 require "util.datamanager".set_data_path(data_path);
299 require "util.datamanager".add_callback(function(username, host, datastore, data)
300 if config.get(host, "core", "anonymous_login") then
303 return username, host, datastore, data;
307 function prepare_to_start()
308 log("info", "Prosody is using the %s backend for connection handling", server.get_backend());
309 -- Signal to modules that we are ready to start
310 eventmanager.fire_event("server-starting");
311 prosody.events.fire_event("server-starting");
313 -- start listening on sockets
314 if config.get("*", "core", "ports") then
315 prosody.net_activate_ports(nil, "multiplex", {5222, 5269});
316 if config.get("*", "core", "ssl_ports") then
317 prosody.net_activate_ports("ssl", "multiplex", {5223}, "ssl");
320 prosody.net_activate_ports("c2s", "xmppclient", {5222});
321 prosody.net_activate_ports("s2s", "xmppserver", {5269});
322 prosody.net_activate_ports("component", "xmppcomponent", {5347}, "tcp");
323 prosody.net_activate_ports("legacy_ssl", "xmppclient", {}, "ssl");
326 prosody.start_time = os.time();
329 function init_global_protection()
330 -- Catch global accesses
331 local locked_globals_mt = {
332 __index = function (t, k) log("warn", "%s", debug.traceback("Attempt to read a non-existent global '"..tostring(k).."'", 2)); end;
333 __newindex = function (t, k, v) error("Attempt to set a global: "..tostring(k).." = "..tostring(v), 2); end;
336 function prosody.unlock_globals()
337 setmetatable(_G, nil);
340 function prosody.lock_globals()
341 setmetatable(_G, locked_globals_mt);
345 prosody.lock_globals();
349 -- Error handler for errors that make it this far
350 local function catch_uncaught_error(err)
351 if type(err) == "string" and err:match("interrupted!$") then
355 log("error", "Top-level error, please report:\n%s", tostring(err));
356 local traceback = debug.traceback("", 2);
358 log("error", "%s", traceback);
361 prosody.events.fire_event("very-bad-error", {error = err, traceback = traceback});
364 while select(2, xpcall(server.loop, catch_uncaught_error)) ~= "quitting" do
370 log("info", "Shutdown status: Cleaning up");
371 prosody.events.fire_event("server-cleanup");
373 -- Ok, we're quitting I know, but we
374 -- need to do some tidying before we go :)
375 server.setquitting(false);
377 log("info", "Shutdown status: Closing all active sessions");
378 for hostname, host in pairs(hosts) do
379 log("debug", "Shutdown status: Closing client connections for %s", hostname)
380 if host.sessions then
381 local reason = { condition = "system-shutdown", text = "Server is shutting down" };
382 if prosody.shutdown_reason then
383 reason.text = reason.text..": "..prosody.shutdown_reason;
385 for username, user in pairs(host.sessions) do
386 for resource, session in pairs(user.sessions) do
387 log("debug", "Closing connection for %s@%s/%s", username, hostname, resource);
388 session:close(reason);
393 log("debug", "Shutdown status: Closing outgoing s2s connections from %s", hostname);
395 for remotehost, session in pairs(host.s2sout) do
396 if session.close then
397 session:close("system-shutdown");
399 log("warn", "Unable to close outgoing s2s session to %s, no session:close()?!", remotehost);
405 log("info", "Shutdown status: Closing all server connections");
408 server.setquitting(true);
412 -- These actions are in a strict order, as many depend on
413 -- previous steps to have already been performed
416 check_dependencies();
421 log("info", "Hello and welcome to Prosody version %s", prosody.version);
422 load_secondary_libraries();
424 init_global_protection();
427 eventmanager.fire_event("server-started");
428 prosody.events.fire_event("server-started");
432 log("info", "Shutting down...");
434 eventmanager.fire_event("server-stopped");
435 prosody.events.fire_event("server-stopped");
436 log("info", "Shutdown complete");