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"));
36 require "core.loggingmanager"
38 -- Check runtime dependencies
39 if not require "util.dependencies".check_dependencies() then
43 -- Replace require() with one that doesn't pollute _G, required
44 -- for neat sandboxing of modules
47 local _real_require = require;
49 local curr_env = getfenv(2);
50 local curr_env_mt = getmetatable(getfenv(2));
51 local _realG_mt = getmetatable(_realG);
52 if curr_env_mt and curr_env_mt.__index and not curr_env_mt.__newindex and _realG_mt then
54 old_newindex, _realG_mt.__newindex = _realG_mt.__newindex, curr_env;
55 local ret = _real_require(...);
56 _realG_mt.__newindex = old_newindex;
59 return _real_require(...);
63 -- Load the config-parsing module
64 config = require "core.configmanager"
67 -- Define the functions we call during startup, the
68 -- actual startup happens right at the end, where these
69 -- functions get called
71 function read_config()
75 if arg[1] == "--config" and arg[2] then
76 table.insert(filenames, arg[2]);
78 table.insert(filenames, CFG_CONFIGDIR.."/"..arg[2]);
81 for _, format in ipairs(config.parsers()) do
82 table.insert(filenames, (CFG_CONFIGDIR or ".").."/prosody.cfg."..format);
85 for _,_filename in ipairs(filenames) do
87 local file = io.open(filename);
90 CFG_CONFIGDIR = filename:match("^(.*)[\\/][^\\/]*$");
94 local ok, level, err = config.load(filename);
97 print("**************************");
98 if level == "parser" then
99 print("A problem occured while reading the config file "..(CFG_CONFIGDIR or ".").."/prosody.cfg.lua");
100 local err_line, err_message = tostring(err):match("%[string .-%]:(%d*): (.*)");
101 print("Error"..(err_line and (" on line "..err_line) or "")..": "..(err_message or tostring(err)));
103 elseif level == "file" then
104 print("Prosody was unable to find the configuration file.");
105 print("We looked for: "..(CFG_CONFIGDIR or ".").."/prosody.cfg.lua");
106 print("A sample config file is included in the Prosody download called prosody.cfg.lua.dist");
107 print("Copy or rename it to prosody.cfg.lua and edit as necessary.");
109 print("More help on configuring Prosody can be found at http://prosody.im/doc/configure");
111 print("**************************");
117 function load_libraries()
118 -- Load socket framework
119 server = require "net.server"
122 function init_global_state()
127 -- Global 'prosody' object
129 local prosody = prosody;
131 prosody.bare_sessions = bare_sessions;
132 prosody.full_sessions = full_sessions;
133 prosody.hosts = hosts;
135 prosody.paths = { source = CFG_SOURCEDIR, config = CFG_CONFIGDIR,
136 plugins = CFG_PLUGINDIR, data = CFG_DATADIR };
138 prosody.arg = _G.arg;
140 prosody.events = require "util.events".new();
142 prosody.platform = "unknown";
143 if os.getenv("WINDIR") then
144 prosody.platform = "windows";
145 elseif package.config:sub(1,1) == "/" then
146 prosody.platform = "posix";
149 prosody.installed = nil;
150 if CFG_SOURCEDIR and (prosody.platform == "windows" or CFG_SOURCEDIR:match("^/")) then
151 prosody.installed = true;
154 -- Function to reload the config file
155 function prosody.reload_config()
156 log("info", "Reloading configuration file");
157 prosody.events.fire_event("reloading-config");
158 local ok, level, err = config.load((rawget(_G, "CFG_CONFIGDIR") or ".").."/prosody.cfg.lua");
160 if level == "parser" then
161 log("error", "There was an error parsing the configuration file: %s", tostring(err));
162 elseif level == "file" then
163 log("error", "Couldn't read the config file when trying to reload: %s", tostring(err));
166 return ok, (err and tostring(level)..": "..tostring(err)) or nil;
169 -- Function to reopen logfiles
170 function prosody.reopen_logfiles()
171 log("info", "Re-opening log files");
172 eventmanager.fire_event("reopen-log-files"); -- Handled by appropriate log sinks
173 prosody.events.fire_event("reopen-log-files");
176 -- Function to initiate prosody shutdown
177 function prosody.shutdown(reason)
178 log("info", "Shutting down: %s", reason or "unknown reason");
179 prosody.shutdown_reason = reason;
180 prosody.events.fire_event("server-stopping", {reason = reason});
181 server.setquitting(true);
184 -- Load SSL settings from config, and create a ctx table
185 local global_ssl_ctx = rawget(_G, "ssl") and config.get("*", "core", "ssl");
186 if global_ssl_ctx then
187 local default_ssl_ctx = { mode = "server", protocol = "sslv23", capath = "/etc/ssl/certs", verify = "none", options = "no_sslv2" };
188 setmetatable(global_ssl_ctx, { __index = default_ssl_ctx });
189 prosody.global_ssl_ctx = global_ssl_ctx;
192 local cl = require "net.connlisteners";
193 function prosody.net_activate_ports(option, listener, default, conntype)
194 conntype = conntype or (global_ssl_ctx and "tls") or "tcp";
195 local ports_option = option and option.."_ports" or "ports";
196 if not cl.get(listener) then return; end
197 local ports = config.get("*", "core", ports_option) or default;
198 if type(ports) == "number" then ports = {ports} end;
200 if type(ports) ~= "table" then
201 log("error", "core."..ports_option.." is not a table");
203 for _, port in ipairs(ports) do
204 port = tonumber(port);
205 if type(port) ~= "number" then
206 log("error", "Non-numeric "..ports_option..": "..tostring(port));
208 local ok, err = cl.start(listener, {
209 ssl = conntype == "ssl" and global_ssl_ctx,
211 interface = (option and config.get("*", "core", option.."_interface"))
212 or cl.get(listener).default_interface
213 or config.get("*", "core", "interface"),
217 local friendly_message = err;
218 if err:match(" in use") then
219 if port == 5222 or port == 5223 or port == 5269 then
220 friendly_message = "check that Prosody or another XMPP server is "
221 .."not already running and using this port";
222 elseif port == 80 or port == 81 then
223 friendly_message = "check that a HTTP server is not already using "
225 elseif port == 5280 then
226 friendly_message = "check that Prosody or a BOSH connection manager "
227 .."is not already running";
229 friendly_message = "this port is in use by another application";
231 elseif err:match("permission") then
232 friendly_message = "Prosody does not have sufficient privileges to use this port";
233 elseif err == "no ssl context" then
234 friendly_message = "there is no 'ssl' config under Host \"*\" which is "
235 .."require for legacy SSL ports";
237 log("error", "Failed to open server port %d, %s", port, friendly_message);
245 function read_version()
246 -- Try to determine version
247 local version_file = io.open((CFG_SOURCEDIR or ".").."/prosody.version");
249 prosody.version = version_file:read("*a"):gsub("%s*$", "");
250 version_file:close();
251 if #prosody.version == 12 and prosody.version:match("^[a-f0-9]+$") then
252 prosody.version = "hg:"..prosody.version;
255 prosody.version = "unknown";
259 function load_secondary_libraries()
260 --- Load and initialise core modules
261 require "util.import"
262 require "core.xmlhandlers"
263 require "core.rostermanager"
264 require "core.eventmanager"
265 require "core.hostmanager"
266 require "core.modulemanager"
267 require "core.usermanager"
268 require "core.sessionmanager"
269 require "core.stanza_router"
274 require "util.datetime"
275 require "util.iterators"
277 require "util.helpers"
279 pcall(require, "util.signal") -- Not on Windows
281 -- Commented to protect us from
282 -- the second kind of people
284 pcall(require, "remdebug.engine");
285 if remdebug then remdebug.engine.start() end
288 require "net.connlisteners";
290 require "util.stanza"
294 function init_data_store()
295 local data_path = config.get("*", "core", "data_path") or CFG_DATADIR or "data";
296 require "util.datamanager".set_data_path(data_path);
297 require "util.datamanager".add_callback(function(username, host, datastore, data)
298 if config.get(host, "core", "anonymous_login") then
301 return username, host, datastore, data;
305 function prepare_to_start()
306 log("debug", "Prosody is using the %s backend for connection handling", server.get_backend());
307 -- Signal to modules that we are ready to start
308 eventmanager.fire_event("server-starting");
309 prosody.events.fire_event("server-starting");
311 -- start listening on sockets
312 if config.get("*", "core", "ports") then
313 prosody.net_activate_ports(nil, "multiplex", {5222, 5269});
314 if config.get("*", "core", "ssl_ports") then
315 prosody.net_activate_ports("ssl", "multiplex", {5223}, "ssl");
318 prosody.net_activate_ports("c2s", "xmppclient", {5222});
319 prosody.net_activate_ports("s2s", "xmppserver", {5269});
320 prosody.net_activate_ports("component", "xmppcomponent", {5347}, "tcp");
321 prosody.net_activate_ports("legacy_ssl", "xmppclient", {}, "ssl");
324 prosody.start_time = os.time();
327 function init_global_protection()
328 -- Catch global accesses
329 local locked_globals_mt = {
330 __index = function (t, k) log("warn", "%s", debug.traceback("Attempt to read a non-existent global '"..tostring(k).."'", 2)); end;
331 __newindex = function (t, k, v) error("Attempt to set a global: "..tostring(k).." = "..tostring(v), 2); end;
334 function prosody.unlock_globals()
335 setmetatable(_G, nil);
338 function prosody.lock_globals()
339 setmetatable(_G, locked_globals_mt);
343 prosody.lock_globals();
347 -- Error handler for errors that make it this far
348 local function catch_uncaught_error(err)
349 if type(err) == "string" and err:match("interrupted!$") then
353 log("error", "Top-level error, please report:\n%s", tostring(err));
354 local traceback = debug.traceback("", 2);
356 log("error", "%s", traceback);
359 prosody.events.fire_event("very-bad-error", {error = err, traceback = traceback});
362 while select(2, xpcall(server.loop, catch_uncaught_error)) ~= "quitting" do
368 log("info", "Shutdown status: Cleaning up");
369 prosody.events.fire_event("server-cleanup");
371 -- Ok, we're quitting I know, but we
372 -- need to do some tidying before we go :)
373 server.setquitting(false);
375 log("info", "Shutdown status: Closing all active sessions");
376 for hostname, host in pairs(hosts) do
377 log("debug", "Shutdown status: Closing client connections for %s", hostname)
378 if host.sessions then
379 local reason = { condition = "system-shutdown", text = "Server is shutting down" };
380 if prosody.shutdown_reason then
381 reason.text = reason.text..": "..prosody.shutdown_reason;
383 for username, user in pairs(host.sessions) do
384 for resource, session in pairs(user.sessions) do
385 log("debug", "Closing connection for %s@%s/%s", username, hostname, resource);
386 session:close(reason);
391 log("debug", "Shutdown status: Closing outgoing s2s connections from %s", hostname);
393 for remotehost, session in pairs(host.s2sout) do
394 if session.close then
395 session:close("system-shutdown");
397 log("warn", "Unable to close outgoing s2s session to %s, no session:close()?!", remotehost);
403 log("info", "Shutdown status: Closing all server connections");
406 server.setquitting(true);
414 log("info", "Hello and welcome to Prosody version %s", prosody.version);
415 load_secondary_libraries();
417 init_global_protection();
420 eventmanager.fire_event("server-started");
421 prosody.events.fire_event("server-started");
425 log("info", "Shutting down...");
427 eventmanager.fire_event("server-stopped");
428 prosody.events.fire_event("server-stopped");
429 log("info", "Shutdown complete");