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 -- Required to be able to find packages installed with luarocks
36 pcall(require, "luarocks.require");
38 -- Replace require() with one that doesn't pollute _G, required
39 -- for neat sandboxing of modules
42 local _real_require = require;
44 local curr_env = getfenv(2);
45 local curr_env_mt = getmetatable(getfenv(2));
46 local _realG_mt = getmetatable(_realG);
47 if curr_env_mt and curr_env_mt.__index and not curr_env_mt.__newindex and _realG_mt then
49 old_newindex, _realG_mt.__newindex = _realG_mt.__newindex, curr_env;
50 local ret = _real_require(...);
51 _realG_mt.__newindex = old_newindex;
54 return _real_require(...);
58 -- Load the config-parsing module
59 config = require "core.configmanager"
62 -- Define the functions we call during startup, the
63 -- actual startup happens right at the end, where these
64 -- functions get called
66 function read_config()
70 if arg[1] == "--config" and arg[2] then
71 table.insert(filenames, arg[2]);
73 table.insert(filenames, CFG_CONFIGDIR.."/"..arg[2]);
76 for _, format in ipairs(config.parsers()) do
77 table.insert(filenames, (CFG_CONFIGDIR or ".").."/prosody.cfg."..format);
80 for _,_filename in ipairs(filenames) do
82 local file = io.open(filename);
85 CFG_CONFIGDIR = filename:match("^(.*)[\\/][^\\/]*$");
89 local ok, level, err = config.load(filename);
92 print("**************************");
93 if level == "parser" then
94 print("A problem occured while reading the config file "..(CFG_CONFIGDIR or ".").."/prosody.cfg.lua");
95 local err_line, err_message = tostring(err):match("%[string .-%]:(%d*): (.*)");
96 print("Error"..(err_line and (" on line "..err_line) or "")..": "..(err_message or tostring(err)));
98 elseif level == "file" then
99 print("Prosody was unable to find the configuration file.");
100 print("We looked for: "..(CFG_CONFIGDIR or ".").."/prosody.cfg.lua");
101 print("A sample config file is included in the Prosody download called prosody.cfg.lua.dist");
102 print("Copy or rename it to prosody.cfg.lua and edit as necessary.");
104 print("More help on configuring Prosody can be found at http://prosody.im/doc/configure");
106 print("**************************");
112 function load_libraries()
113 -- Initialize logging
114 require "core.loggingmanager"
116 -- Check runtime dependencies
117 if not require "util.dependencies".check_dependencies() then
121 -- Load socket framework
122 server = require "net.server"
125 function init_global_state()
130 -- Global 'prosody' object
132 local prosody = prosody;
134 prosody.bare_sessions = bare_sessions;
135 prosody.full_sessions = full_sessions;
136 prosody.hosts = hosts;
138 prosody.paths = { source = CFG_SOURCEDIR, config = CFG_CONFIGDIR,
139 plugins = CFG_PLUGINDIR, data = CFG_DATADIR };
141 prosody.arg = _G.arg;
143 prosody.events = require "util.events".new();
145 prosody.platform = "unknown";
146 if os.getenv("WINDIR") then
147 prosody.platform = "windows";
148 elseif package.config:sub(1,1) == "/" then
149 prosody.platform = "posix";
152 prosody.installed = nil;
153 if CFG_SOURCEDIR and (prosody.platform == "windows" or CFG_SOURCEDIR:match("^/")) then
154 prosody.installed = true;
157 -- Function to reload the config file
158 function prosody.reload_config()
159 log("info", "Reloading configuration file");
160 prosody.events.fire_event("reloading-config");
161 local ok, level, err = config.load((rawget(_G, "CFG_CONFIGDIR") or ".").."/prosody.cfg.lua");
163 if level == "parser" then
164 log("error", "There was an error parsing the configuration file: %s", tostring(err));
165 elseif level == "file" then
166 log("error", "Couldn't read the config file when trying to reload: %s", tostring(err));
169 return ok, (err and tostring(level)..": "..tostring(err)) or nil;
172 -- Function to reopen logfiles
173 function prosody.reopen_logfiles()
174 log("info", "Re-opening log files");
175 eventmanager.fire_event("reopen-log-files"); -- Handled by appropriate log sinks
176 prosody.events.fire_event("reopen-log-files");
179 -- Function to initiate prosody shutdown
180 function prosody.shutdown(reason)
181 log("info", "Shutting down: %s", reason or "unknown reason");
182 prosody.shutdown_reason = reason;
183 prosody.events.fire_event("server-stopping", {reason = reason});
184 server.setquitting(true);
187 -- Load SSL settings from config, and create a ctx table
188 local global_ssl_ctx = rawget(_G, "ssl") and config.get("*", "core", "ssl");
189 if global_ssl_ctx then
190 local default_ssl_ctx = { mode = "server", protocol = "sslv23", capath = "/etc/ssl/certs", verify = "none", options = "no_sslv2" };
191 setmetatable(global_ssl_ctx, { __index = default_ssl_ctx });
192 prosody.global_ssl_ctx = global_ssl_ctx;
195 local cl = require "net.connlisteners";
196 function prosody.net_activate_ports(option, listener, default, conntype)
197 conntype = conntype or (global_ssl_ctx and "tls") or "tcp";
198 local ports_option = option and option.."_ports" or "ports";
199 if not cl.get(listener) then return; end
200 local ports = config.get("*", "core", ports_option) or default;
201 if type(ports) == "number" then ports = {ports} end;
203 if type(ports) ~= "table" then
204 log("error", "core."..ports_option.." is not a table");
206 for _, port in ipairs(ports) do
207 port = tonumber(port);
208 if type(port) ~= "number" then
209 log("error", "Non-numeric "..ports_option..": "..tostring(port));
212 ssl = conntype ~= "tcp" and global_ssl_ctx,
214 interface = (option and config.get("*", "core", option.."_interface"))
215 or cl.get(listener).default_interface
216 or config.get("*", "core", "interface"),
225 function read_version()
226 -- Try to determine version
227 local version_file = io.open((CFG_SOURCEDIR or ".").."/prosody.version");
229 prosody.version = version_file:read("*a"):gsub("%s*$", "");
230 version_file:close();
231 if #prosody.version == 12 and prosody.version:match("^[a-f0-9]+$") then
232 prosody.version = "hg:"..prosody.version;
235 prosody.version = "unknown";
239 function load_secondary_libraries()
240 --- Load and initialise core modules
241 require "util.import"
242 require "core.xmlhandlers"
243 require "core.rostermanager"
244 require "core.eventmanager"
245 require "core.hostmanager"
246 require "core.modulemanager"
247 require "core.usermanager"
248 require "core.sessionmanager"
249 require "core.stanza_router"
254 require "util.datetime"
255 require "util.iterators"
257 require "util.helpers"
259 pcall(require, "util.signal") -- Not on Windows
261 -- Commented to protect us from
262 -- the second kind of people
264 pcall(require, "remdebug.engine");
265 if remdebug then remdebug.engine.start() end
268 require "net.connlisteners";
270 require "util.stanza"
274 function init_data_store()
275 local data_path = config.get("*", "core", "data_path") or CFG_DATADIR or "data";
276 require "util.datamanager".set_data_path(data_path);
277 require "util.datamanager".add_callback(function(username, host, datastore, data)
278 if config.get(host, "core", "anonymous_login") then
281 return username, host, datastore, data;
285 function prepare_to_start()
286 log("debug", "Prosody is using the %s backend for connection handling", server.get_backend());
287 -- Signal to modules that we are ready to start
288 eventmanager.fire_event("server-starting");
289 prosody.events.fire_event("server-starting");
291 -- start listening on sockets
292 if config.get("*", "core", "ports") then
293 prosody.net_activate_ports(nil, "multiplex", {5222, 5269});
294 if config.get("*", "core", "ssl_ports") then
295 prosody.net_activate_ports("ssl", "multiplex", {5223}, "ssl");
298 prosody.net_activate_ports("c2s", "xmppclient", {5222});
299 prosody.net_activate_ports("s2s", "xmppserver", {5269});
300 prosody.net_activate_ports("component", "xmppcomponent", {5347}, "tcp");
301 prosody.net_activate_ports("legacy_ssl", "xmppclient", {}, "ssl");
304 prosody.start_time = os.time();
307 function init_global_protection()
308 -- Catch global accesses
309 local locked_globals_mt = {
310 __index = function (t, k) log("warn", "%s", debug.traceback("Attempt to read a non-existent global '"..tostring(k).."'", 2)); end;
311 __newindex = function (t, k, v) error("Attempt to set a global: "..tostring(k).." = "..tostring(v), 2); end;
314 function prosody.unlock_globals()
315 setmetatable(_G, nil);
318 function prosody.lock_globals()
319 setmetatable(_G, locked_globals_mt);
323 prosody.lock_globals();
327 -- Error handler for errors that make it this far
328 local function catch_uncaught_error(err)
329 if type(err) == "string" and err:match("interrupted!$") then
333 log("error", "Top-level error, please report:\n%s", tostring(err));
334 local traceback = debug.traceback("", 2);
336 log("error", "%s", traceback);
339 prosody.events.fire_event("very-bad-error", {error = err, traceback = traceback});
342 while select(2, xpcall(server.loop, catch_uncaught_error)) ~= "quitting" do
348 log("info", "Shutdown status: Cleaning up");
349 prosody.events.fire_event("server-cleanup");
351 -- Ok, we're quitting I know, but we
352 -- need to do some tidying before we go :)
353 server.setquitting(false);
355 log("info", "Shutdown status: Closing all active sessions");
356 for hostname, host in pairs(hosts) do
357 log("debug", "Shutdown status: Closing client connections for %s", hostname)
358 if host.sessions then
359 local reason = { condition = "system-shutdown", text = "Server is shutting down" };
360 if prosody.shutdown_reason then
361 reason.text = reason.text..": "..prosody.shutdown_reason;
363 for username, user in pairs(host.sessions) do
364 for resource, session in pairs(user.sessions) do
365 log("debug", "Closing connection for %s@%s/%s", username, hostname, resource);
366 session:close(reason);
371 log("debug", "Shutdown status: Closing outgoing s2s connections from %s", hostname);
373 for remotehost, session in pairs(host.s2sout) do
374 if session.close then
375 session:close("system-shutdown");
377 log("warn", "Unable to close outgoing s2s session to %s, no session:close()?!", remotehost);
383 log("info", "Shutdown status: Closing all server connections");
386 server.setquitting(true);
394 log("info", "Hello and welcome to Prosody version %s", prosody.version);
395 load_secondary_libraries();
397 init_global_protection();
400 eventmanager.fire_event("server-started");
401 prosody.events.fire_event("server-started");
405 log("info", "Shutting down...");
407 eventmanager.fire_event("server-stopped");
408 prosody.events.fire_event("server-stopped");
409 log("info", "Shutdown complete");