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 -- Check runtime dependencies
36 if not require "util.dependencies".check_dependencies() then
40 -- Replace require() with one that doesn't pollute _G, required
41 -- for neat sandboxing of modules
44 local _real_require = require;
46 local curr_env = getfenv(2);
47 local curr_env_mt = getmetatable(getfenv(2));
48 local _realG_mt = getmetatable(_realG);
49 if curr_env_mt and curr_env_mt.__index and not curr_env_mt.__newindex and _realG_mt then
51 old_newindex, _realG_mt.__newindex = _realG_mt.__newindex, curr_env;
52 local ret = _real_require(...);
53 _realG_mt.__newindex = old_newindex;
56 return _real_require(...);
60 -- Load the config-parsing module
61 config = require "core.configmanager"
64 -- Define the functions we call during startup, the
65 -- actual startup happens right at the end, where these
66 -- functions get called
68 function read_config()
72 if arg[1] == "--config" and arg[2] then
73 table.insert(filenames, arg[2]);
75 table.insert(filenames, CFG_CONFIGDIR.."/"..arg[2]);
78 for _, format in ipairs(config.parsers()) do
79 table.insert(filenames, (CFG_CONFIGDIR or ".").."/prosody.cfg."..format);
82 for _,_filename in ipairs(filenames) do
84 local file = io.open(filename);
87 CFG_CONFIGDIR = filename:match("^(.*)[\\/][^\\/]*$");
91 local ok, level, err = config.load(filename);
94 print("**************************");
95 if level == "parser" then
96 print("A problem occured while reading the config file "..(CFG_CONFIGDIR or ".").."/prosody.cfg.lua");
97 local err_line, err_message = tostring(err):match("%[string .-%]:(%d*): (.*)");
98 print("Error"..(err_line and (" on line "..err_line) or "")..": "..(err_message or tostring(err)));
100 elseif level == "file" then
101 print("Prosody was unable to find the configuration file.");
102 print("We looked for: "..(CFG_CONFIGDIR or ".").."/prosody.cfg.lua");
103 print("A sample config file is included in the Prosody download called prosody.cfg.lua.dist");
104 print("Copy or rename it to prosody.cfg.lua and edit as necessary.");
106 print("More help on configuring Prosody can be found at http://prosody.im/doc/configure");
108 print("**************************");
114 function load_libraries()
115 -- Initialize logging
116 require "core.loggingmanager"
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));
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"),
222 function read_version()
223 -- Try to determine version
224 local version_file = io.open((CFG_SOURCEDIR or ".").."/prosody.version");
226 prosody.version = version_file:read("*a"):gsub("%s*$", "");
227 version_file:close();
228 if #prosody.version == 12 and prosody.version:match("^[a-f0-9]+$") then
229 prosody.version = "hg:"..prosody.version;
232 prosody.version = "unknown";
236 function load_secondary_libraries()
237 --- Load and initialise core modules
238 require "util.import"
239 require "core.xmlhandlers"
240 require "core.rostermanager"
241 require "core.eventmanager"
242 require "core.hostmanager"
243 require "core.modulemanager"
244 require "core.usermanager"
245 require "core.sessionmanager"
246 require "core.stanza_router"
251 require "util.datetime"
252 require "util.iterators"
254 require "util.helpers"
256 pcall(require, "util.signal") -- Not on Windows
258 -- Commented to protect us from
259 -- the second kind of people
261 pcall(require, "remdebug.engine");
262 if remdebug then remdebug.engine.start() end
265 require "net.connlisteners";
267 require "util.stanza"
271 function init_data_store()
272 local data_path = config.get("*", "core", "data_path") or CFG_DATADIR or "data";
273 require "util.datamanager".set_data_path(data_path);
274 require "util.datamanager".add_callback(function(username, host, datastore, data)
275 if config.get(host, "core", "anonymous_login") then
278 return username, host, datastore, data;
282 function prepare_to_start()
283 log("debug", "Prosody is using the %s backend for connection handling", server.get_backend());
284 -- Signal to modules that we are ready to start
285 eventmanager.fire_event("server-starting");
286 prosody.events.fire_event("server-starting");
288 -- start listening on sockets
289 if config.get("*", "core", "ports") then
290 prosody.net_activate_ports(nil, "multiplex", {5222, 5269});
291 if config.get("*", "core", "ssl_ports") then
292 prosody.net_activate_ports("ssl", "multiplex", {5223}, "ssl");
295 prosody.net_activate_ports("c2s", "xmppclient", {5222});
296 prosody.net_activate_ports("s2s", "xmppserver", {5269});
297 prosody.net_activate_ports("component", "xmppcomponent", {5347}, "tcp");
298 prosody.net_activate_ports("legacy_ssl", "xmppclient", {}, "ssl");
301 prosody.start_time = os.time();
304 function init_global_protection()
305 -- Catch global accesses
306 local locked_globals_mt = {
307 __index = function (t, k) log("warn", "%s", debug.traceback("Attempt to read a non-existent global '"..tostring(k).."'", 2)); end;
308 __newindex = function (t, k, v) error("Attempt to set a global: "..tostring(k).." = "..tostring(v), 2); end;
311 function prosody.unlock_globals()
312 setmetatable(_G, nil);
315 function prosody.lock_globals()
316 setmetatable(_G, locked_globals_mt);
320 prosody.lock_globals();
324 -- Error handler for errors that make it this far
325 local function catch_uncaught_error(err)
326 if type(err) == "string" and err:match("interrupted!$") then
330 log("error", "Top-level error, please report:\n%s", tostring(err));
331 local traceback = debug.traceback("", 2);
333 log("error", "%s", traceback);
336 prosody.events.fire_event("very-bad-error", {error = err, traceback = traceback});
339 while select(2, xpcall(server.loop, catch_uncaught_error)) ~= "quitting" do
345 log("info", "Shutdown status: Cleaning up");
346 prosody.events.fire_event("server-cleanup");
348 -- Ok, we're quitting I know, but we
349 -- need to do some tidying before we go :)
350 server.setquitting(false);
352 log("info", "Shutdown status: Closing all active sessions");
353 for hostname, host in pairs(hosts) do
354 log("debug", "Shutdown status: Closing client connections for %s", hostname)
355 if host.sessions then
356 local reason = { condition = "system-shutdown", text = "Server is shutting down" };
357 if prosody.shutdown_reason then
358 reason.text = reason.text..": "..prosody.shutdown_reason;
360 for username, user in pairs(host.sessions) do
361 for resource, session in pairs(user.sessions) do
362 log("debug", "Closing connection for %s@%s/%s", username, hostname, resource);
363 session:close(reason);
368 log("debug", "Shutdown status: Closing outgoing s2s connections from %s", hostname);
370 for remotehost, session in pairs(host.s2sout) do
371 if session.close then
372 session:close("system-shutdown");
374 log("warn", "Unable to close outgoing s2s session to %s, no session:close()?!", remotehost);
380 log("info", "Shutdown status: Closing all server connections");
383 server.setquitting(true);
391 log("info", "Hello and welcome to Prosody version %s", prosody.version);
392 load_secondary_libraries();
394 init_global_protection();
397 eventmanager.fire_event("server-started");
398 prosody.events.fire_event("server-started");
402 log("info", "Shutting down...");
404 eventmanager.fire_event("server-stopped");
405 prosody.events.fire_event("server-stopped");
406 log("info", "Shutdown complete");