3 -- Copyright (C) 2008-2010 Matthew Wild
4 -- Copyright (C) 2008-2010 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 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20 package.path = CFG_SOURCEDIR.."/?.lua;"..package.path;
21 package.cpath = CFG_SOURCEDIR.."/?.so;"..package.cpath;
24 package.path = package.path..";"..(CFG_SOURCEDIR or ".").."/fallbacks/?.lua";
25 package.cpath = package.cpath..";"..(CFG_SOURCEDIR or ".").."/fallbacks/?.so";
28 if os.getenv("HOME") then
29 CFG_DATADIR = CFG_DATADIR:gsub("^~", os.getenv("HOME"));
33 -- Required to be able to find packages installed with luarocks
34 if not pcall(require, "luarocks.loader") then -- Try LuaRocks 2.x
35 pcall(require, "luarocks.require") -- Try LuaRocks 1.x
38 -- Replace require with one that doesn't pollute _G
41 local _real_require = require;
43 local curr_env = getfenv(2);
44 local curr_env_mt = getmetatable(getfenv(2));
45 local _realG_mt = getmetatable(_realG);
46 if curr_env_mt and curr_env_mt.__index and not curr_env_mt.__newindex and _realG_mt then
48 old_newindex, _realG_mt.__newindex = _realG_mt.__newindex, curr_env;
49 local ret = _real_require(...);
50 _realG_mt.__newindex = old_newindex;
53 return _real_require(...);
58 config = require "core.configmanager"
60 function read_config()
61 -- TODO: Check for other formats when we add support for them
62 -- Use lfs? Make a new conf/ dir?
66 if arg[1] == "--config" and arg[2] then
67 table.insert(filenames, arg[2]);
69 table.insert(filenames, CFG_CONFIGDIR.."/"..arg[2]);
72 table.insert(filenames, (CFG_CONFIGDIR or ".").."/prosody.cfg.lua");
74 for _,_filename in ipairs(filenames) do
76 local file = io.open(filename);
79 CFG_CONFIGDIR = filename:match("^(.*)[\\/][^\\/]*$");
83 local ok, level, err = config.load(filename);
86 print("**************************");
87 if level == "parser" then
88 print("A problem occured while reading the config file "..(CFG_CONFIGDIR or ".").."/prosody.cfg.lua");
89 local err_line, err_message = tostring(err):match("%[string .-%]:(%d*): (.*)");
90 print("Error"..(err_line and (" on line "..err_line) or "")..": "..(err_message or tostring(err)));
92 elseif level == "file" then
93 print("Prosody was unable to find the configuration file.");
94 print("We looked for: "..(CFG_CONFIGDIR or ".").."/prosody.cfg.lua");
95 print("A sample config file is included in the Prosody download called prosody.cfg.lua.dist");
96 print("Copy or rename it to prosody.cfg.lua and edit as necessary.");
98 print("More help on configuring Prosody can be found at http://prosody.im/doc/configure");
100 print("**************************");
106 function load_libraries()
107 -- Initialize logging
108 require "core.loggingmanager"
110 -- Check runtime dependencies
111 require "util.dependencies"
113 -- Load socket framework
114 server = require "net.server"
117 function init_global_state()
122 -- Global 'prosody' object
124 local prosody = prosody;
126 prosody.bare_sessions = bare_sessions;
127 prosody.full_sessions = full_sessions;
128 prosody.hosts = hosts;
130 prosody.paths = { source = CFG_SOURCEDIR, config = CFG_CONFIGDIR,
131 plugins = CFG_PLUGINDIR, data = CFG_DATADIR };
133 prosody.arg = _G.arg;
135 prosody.events = require "util.events".new();
137 prosody.platform = "unknown";
138 if os.getenv("WINDIR") then
139 prosody.platform = "windows";
140 elseif package.config:sub(1,1) == "/" then
141 prosody.platform = "posix";
144 prosody.installed = nil;
145 if CFG_SOURCEDIR and (prosody.platform == "windows" or CFG_SOURCEDIR:match("^/")) then
146 prosody.installed = true;
149 -- Function to reload the config file
150 function prosody.reload_config()
151 log("info", "Reloading configuration file");
152 prosody.events.fire_event("reloading-config");
153 local ok, level, err = config.load((rawget(_G, "CFG_CONFIGDIR") or ".").."/prosody.cfg.lua");
155 if level == "parser" then
156 log("error", "There was an error parsing the configuration file: %s", tostring(err));
157 elseif level == "file" then
158 log("error", "Couldn't read the config file when trying to reload: %s", tostring(err));
161 return ok, (err and tostring(level)..": "..tostring(err)) or nil;
164 -- Function to reopen logfiles
165 function prosody.reopen_logfiles()
166 log("info", "Re-opening log files");
167 eventmanager.fire_event("reopen-log-files"); -- Handled by appropriate log sinks
168 prosody.events.fire_event("reopen-log-files");
171 -- Function to initiate prosody shutdown
172 function prosody.shutdown(reason)
173 log("info", "Shutting down: %s", reason or "unknown reason");
174 prosody.shutdown_reason = reason;
175 prosody.events.fire_event("server-stopping", {reason = reason});
176 server.setquitting(true);
179 -- Load SSL settings from config, and create a ctx table
180 local global_ssl_ctx = rawget(_G, "ssl") and config.get("*", "core", "ssl");
181 if global_ssl_ctx then
182 local default_ssl_ctx = { mode = "server", protocol = "sslv23", capath = "/etc/ssl/certs", verify = "none", options = "no_sslv2"; };
183 setmetatable(global_ssl_ctx, { __index = default_ssl_ctx });
186 local cl = require "net.connlisteners";
187 function prosody.net_activate_ports(option, listener, default, conntype)
188 conntype = conntype or (global_ssl_ctx and "tls") or "tcp";
189 if not cl.get(listener) then return; end
190 local ports = config.get("*", "core", option.."_ports") or default;
191 if type(ports) == "number" then ports = {ports} end;
193 if type(ports) ~= "table" then
194 log("error", "core."..option.." is not a table");
196 for _, port in ipairs(ports) do
197 port = tonumber(port);
198 if type(port) ~= "number" then
199 log("error", "Non-numeric "..option.."_ports: "..tostring(port));
201 local ok, err = cl.start(listener, {
202 ssl = conntype ~= "tcp" and global_ssl_ctx,
204 interface = (option and config.get("*", "core", option.."_interface"))
205 or cl.get(listener).default_interface
206 or config.get("*", "core", "interface"),
210 local friendly_message = err;
211 if err:match(" in use") then
212 if port == 5222 or port == 5223 or port == 5269 then
213 friendly_message = "check that Prosody or another XMPP server is "
214 .."not already running and using this port";
215 elseif port == 80 or port == 81 then
216 friendly_message = "check that a HTTP server is not already using "
218 elseif port == 5280 then
219 friendly_message = "check that Prosody or a BOSH connection manager "
220 .."is not already running";
222 friendly_message = "this port is in use by another application";
224 elseif err:match("permission") then
225 friendly_message = "Prosody does not have sufficient privileges to use this port";
226 elseif err == "no ssl context" then
227 if not config.get("*", "core", "ssl") then
228 friendly_message = "there is no 'ssl' config under Host \"*\" which is "
229 .."require for legacy SSL ports";
231 friendly_message = "initializing SSL support failed, see previous log entries";
234 log("error", "Failed to open server port %d, %s", port, friendly_message);
242 function read_version()
243 -- Try to determine version
244 local version_file = io.open((CFG_SOURCEDIR or ".").."/prosody.version");
246 prosody.version = version_file:read("*a"):gsub("%s*$", "");
247 version_file:close();
248 if #prosody.version == 12 and prosody.version:match("^[a-f0-9]+$") then
249 prosody.version = "hg:"..prosody.version;
252 prosody.version = "unknown";
256 function load_secondary_libraries()
257 --- Load and initialise core modules
258 require "util.import"
259 require "core.xmlhandlers"
260 require "core.rostermanager"
261 require "core.eventmanager"
262 require "core.hostmanager"
263 require "core.modulemanager"
264 require "core.usermanager"
265 require "core.sessionmanager"
266 require "core.stanza_router"
271 require "util.datetime"
272 require "util.iterators"
274 require "util.helpers"
276 pcall(require, "util.signal") -- Not on Windows
278 -- Commented to protect us from
279 -- the second kind of people
281 pcall(require, "remdebug.engine");
282 if remdebug then remdebug.engine.start() end
285 require "net.connlisteners";
287 require "util.stanza"
291 function init_data_store()
292 local data_path = config.get("*", "core", "data_path") or CFG_DATADIR or "data";
293 require "util.datamanager".set_data_path(data_path);
294 require "util.datamanager".add_callback(function(username, host, datastore, data)
295 if config.get(host, "core", "anonymous_login") then
298 return username, host, datastore, data;
302 function prepare_to_start()
303 -- Signal to modules that we are ready to start
304 eventmanager.fire_event("server-starting");
305 prosody.events.fire_event("server-starting");
307 -- start listening on sockets
308 prosody.net_activate_ports("c2s", "xmppclient", {5222});
309 prosody.net_activate_ports("s2s", "xmppserver", {5269});
310 prosody.net_activate_ports("component", "xmppcomponent", {5347}, "tcp");
311 prosody.net_activate_ports("legacy_ssl", "xmppclient", {}, "ssl");
313 prosody.start_time = os.time();
316 function init_global_protection()
317 -- Catch global accesses
318 local locked_globals_mt = {
319 __index = function (t, k) log("warn", "%s", debug.traceback("Attempt to read a non-existent global '"..tostring(k).."'", 2)); end;
320 __newindex = function (t, k, v) error("Attempt to set a global: "..tostring(k).." = "..tostring(v), 2); end;
323 function prosody.unlock_globals()
324 setmetatable(_G, nil);
327 function prosody.lock_globals()
328 setmetatable(_G, locked_globals_mt);
332 prosody.lock_globals();
336 -- Error handler for errors that make it this far
337 local function catch_uncaught_error(err)
338 if type(err) == "string" and err:match("interrupted!$") then
342 log("error", "Top-level error, please report:\n%s", tostring(err));
343 local traceback = debug.traceback("", 2);
345 log("error", "%s", traceback);
348 prosody.events.fire_event("very-bad-error", {error = err, traceback = traceback});
351 while select(2, xpcall(server.loop, catch_uncaught_error)) ~= "quitting" do
357 log("info", "Shutdown status: Cleaning up");
358 prosody.events.fire_event("server-cleanup");
360 -- Ok, we're quitting I know, but we
361 -- need to do some tidying before we go :)
362 server.setquitting(false);
364 log("info", "Shutdown status: Closing all active sessions");
365 for hostname, host in pairs(hosts) do
366 log("debug", "Shutdown status: Closing client connections for %s", hostname)
367 if host.sessions then
368 local reason = { condition = "system-shutdown", text = "Server is shutting down" };
369 if prosody.shutdown_reason then
370 reason.text = reason.text..": "..prosody.shutdown_reason;
372 for username, user in pairs(host.sessions) do
373 for resource, session in pairs(user.sessions) do
374 log("debug", "Closing connection for %s@%s/%s", username, hostname, resource);
375 session:close(reason);
380 log("debug", "Shutdown status: Closing outgoing s2s connections from %s", hostname);
382 for remotehost, session in pairs(host.s2sout) do
383 if session.close then
384 session:close("system-shutdown");
386 log("warn", "Unable to close outgoing s2s session to %s, no session:close()?!", remotehost);
392 log("info", "Shutdown status: Closing all server connections");
395 server.setquitting(true);
402 log("info", "Hello and welcome to Prosody version %s", prosody.version);
403 load_secondary_libraries();
405 init_global_protection();
408 eventmanager.fire_event("server-started");
409 prosody.events.fire_event("server-started");
413 log("info", "Shutting down...");
415 eventmanager.fire_event("server-stopped");
416 prosody.events.fire_event("server-stopped");
417 log("info", "Shutdown complete");