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 require "util.dependencies"
119 -- Load socket framework
120 server = require "net.server"
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 global_ssl_ctx = rawget(_G, "ssl") and config.get("*", "core", "ssl");
187 if global_ssl_ctx then
188 local default_ssl_ctx = { mode = "server", protocol = "sslv23", capath = "/etc/ssl/certs", verify = "none", options = "no_sslv2" };
189 setmetatable(global_ssl_ctx, { __index = default_ssl_ctx });
190 prosody.global_ssl_ctx = global_ssl_ctx;
193 local cl = require "net.connlisteners";
194 function prosody.net_activate_ports(option, listener, default, conntype)
195 conntype = conntype or (global_ssl_ctx and "tls") or "tcp";
196 local ports_option = option and option.."_ports" or "ports";
197 if not cl.get(listener) then return; end
198 local ports = config.get("*", "core", ports_option) or default;
199 if type(ports) == "number" then ports = {ports} end;
201 if type(ports) ~= "table" then
202 log("error", "core."..ports_option.." is not a table");
204 for _, port in ipairs(ports) do
205 port = tonumber(port);
206 if type(port) ~= "number" then
207 log("error", "Non-numeric "..ports_option..": "..tostring(port));
210 ssl = conntype ~= "tcp" and global_ssl_ctx,
212 interface = (option and config.get("*", "core", option.."_interface"))
213 or cl.get(listener).default_interface
214 or config.get("*", "core", "interface"),
223 function read_version()
224 -- Try to determine version
225 local version_file = io.open((CFG_SOURCEDIR or ".").."/prosody.version");
227 prosody.version = version_file:read("*a"):gsub("%s*$", "");
228 version_file:close();
229 if #prosody.version == 12 and prosody.version:match("^[a-f0-9]+$") then
230 prosody.version = "hg:"..prosody.version;
233 prosody.version = "unknown";
237 function load_secondary_libraries()
238 --- Load and initialise core modules
239 require "util.import"
240 require "core.xmlhandlers"
241 require "core.rostermanager"
242 require "core.eventmanager"
243 require "core.hostmanager"
244 require "core.modulemanager"
245 require "core.usermanager"
246 require "core.sessionmanager"
247 require "core.stanza_router"
252 require "util.datetime"
253 require "util.iterators"
255 require "util.helpers"
257 pcall(require, "util.signal") -- Not on Windows
259 -- Commented to protect us from
260 -- the second kind of people
262 pcall(require, "remdebug.engine");
263 if remdebug then remdebug.engine.start() end
266 require "net.connlisteners";
268 require "util.stanza"
272 function init_data_store()
273 local data_path = config.get("*", "core", "data_path") or CFG_DATADIR or "data";
274 require "util.datamanager".set_data_path(data_path);
275 require "util.datamanager".add_callback(function(username, host, datastore, data)
276 if config.get(host, "core", "anonymous_login") then
279 return username, host, datastore, data;
283 function prepare_to_start()
284 log("debug", "Prosody is using the %s backend for connection handling", server.get_backend());
285 -- Signal to modules that we are ready to start
286 eventmanager.fire_event("server-starting");
287 prosody.events.fire_event("server-starting");
289 -- start listening on sockets
290 if config.get("*", "core", "ports") then
291 prosody.net_activate_ports(nil, "multiplex", {5222, 5269});
292 if config.get("*", "core", "ssl_ports") then
293 prosody.net_activate_ports("ssl", "multiplex", {5223}, "ssl");
296 prosody.net_activate_ports("c2s", "xmppclient", {5222});
297 prosody.net_activate_ports("s2s", "xmppserver", {5269});
298 prosody.net_activate_ports("component", "xmppcomponent", {}, "tcp");
299 prosody.net_activate_ports("legacy_ssl", "xmppclient", {}, "ssl");
302 prosody.start_time = os.time();
305 function init_global_protection()
306 -- Catch global accesses
307 local locked_globals_mt = {
308 __index = function (t, k) log("warn", "%s", debug.traceback("Attempt to read a non-existent global '"..tostring(k).."'", 2)); end;
309 __newindex = function (t, k, v) error("Attempt to set a global: "..tostring(k).." = "..tostring(v), 2); end;
312 function prosody.unlock_globals()
313 setmetatable(_G, nil);
316 function prosody.lock_globals()
317 setmetatable(_G, locked_globals_mt);
321 prosody.lock_globals();
325 -- Error handler for errors that make it this far
326 local function catch_uncaught_error(err)
327 if type(err) == "string" and err:match("interrupted!$") then
331 log("error", "Top-level error, please report:\n%s", tostring(err));
332 local traceback = debug.traceback("", 2);
334 log("error", "%s", traceback);
337 prosody.events.fire_event("very-bad-error", {error = err, traceback = traceback});
340 while select(2, xpcall(server.loop, catch_uncaught_error)) ~= "quitting" do
346 log("info", "Shutdown status: Cleaning up");
347 prosody.events.fire_event("server-cleanup");
349 -- Ok, we're quitting I know, but we
350 -- need to do some tidying before we go :)
351 server.setquitting(false);
353 log("info", "Shutdown status: Closing all active sessions");
354 for hostname, host in pairs(hosts) do
355 log("debug", "Shutdown status: Closing client connections for %s", hostname)
356 if host.sessions then
357 local reason = { condition = "system-shutdown", text = "Server is shutting down" };
358 if prosody.shutdown_reason then
359 reason.text = reason.text..": "..prosody.shutdown_reason;
361 for username, user in pairs(host.sessions) do
362 for resource, session in pairs(user.sessions) do
363 log("debug", "Closing connection for %s@%s/%s", username, hostname, resource);
364 session:close(reason);
369 log("debug", "Shutdown status: Closing outgoing s2s connections from %s", hostname);
371 for remotehost, session in pairs(host.s2sout) do
372 if session.close then
373 session:close("system-shutdown");
375 log("warn", "Unable to close outgoing s2s session to %s, no session:close()?!", remotehost);
381 log("info", "Shutdown status: Closing all server connections");
384 server.setquitting(true);
392 log("info", "Hello and welcome to Prosody version %s", prosody.version);
393 load_secondary_libraries();
395 init_global_protection();
398 eventmanager.fire_event("server-started");
399 prosody.events.fire_event("server-started");
403 log("info", "Shutting down...");
405 eventmanager.fire_event("server-stopped");
406 prosody.events.fire_event("server-stopped");
407 log("info", "Shutdown complete");