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 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
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 pcall(require, "luarocks.require")
36 -- Replace require with one that doesn't pollute _G
39 local _real_require = require;
41 local curr_env = getfenv(2);
42 local curr_env_mt = getmetatable(getfenv(2));
43 local _realG_mt = getmetatable(_realG);
44 if curr_env_mt and curr_env_mt.__index and not curr_env_mt.__newindex and _realG_mt then
46 old_newindex, _realG_mt.__newindex = _realG_mt.__newindex, curr_env;
47 local ret = _real_require(...);
48 _realG_mt.__newindex = old_newindex;
51 return _real_require(...);
56 config = require "core.configmanager"
58 function read_config()
59 -- TODO: Check for other formats when we add support for them
60 -- Use lfs? Make a new conf/ dir?
64 if arg[1] == "--config" and arg[2] then
65 table.insert(filenames, arg[2]);
67 table.insert(filenames, CFG_CONFIGDIR.."/"..arg[2]);
70 table.insert(filenames, (CFG_CONFIGDIR or ".").."/prosody.cfg.lua");
72 for _,_filename in ipairs(filenames) do
74 local file = io.open(filename);
77 CFG_CONFIGDIR = filename:match("^(.*)[\\/][^\\/]*$");
81 local ok, level, err = config.load(filename);
84 print("**************************");
85 if level == "parser" then
86 print("A problem occured while reading the config file "..(CFG_CONFIGDIR or ".").."/prosody.cfg.lua");
87 local err_line, err_message = tostring(err):match("%[string .-%]:(%d*): (.*)");
88 print("Error"..(err_line and (" on line "..err_line) or "")..": "..(err_message or tostring(err)));
90 elseif level == "file" then
91 print("Prosody was unable to find the configuration file.");
92 print("We looked for: "..(CFG_CONFIGDIR or ".").."/prosody.cfg.lua");
93 print("A sample config file is included in the Prosody download called prosody.cfg.lua.dist");
94 print("Copy or rename it to prosody.cfg.lua and edit as necessary.");
96 print("More help on configuring Prosody can be found at http://prosody.im/doc/configure");
98 print("**************************");
104 function load_libraries()
105 -- Initialize logging
106 require "core.loggingmanager"
108 -- Check runtime dependencies
109 require "util.dependencies"
111 -- Load socket framework
112 server = require "net.server"
115 function init_global_state()
120 -- Global 'prosody' object
122 local prosody = prosody;
124 prosody.bare_sessions = bare_sessions;
125 prosody.full_sessions = full_sessions;
126 prosody.hosts = hosts;
128 prosody.paths = { source = CFG_SOURCEDIR, config = CFG_CONFIGDIR,
129 plugins = CFG_PLUGINDIR, data = CFG_DATADIR };
131 prosody.arg = _G.arg;
133 prosody.events = require "util.events".new();
135 prosody.platform = "unknown";
136 if os.getenv("WINDIR") then
137 prosody.platform = "windows";
138 elseif package.config:sub(1,1) == "/" then
139 prosody.platform = "posix";
142 prosody.installed = nil;
143 if CFG_SOURCEDIR and (prosody.platform == "windows" or CFG_SOURCEDIR:match("^/")) then
144 prosody.installed = true;
147 -- Function to reload the config file
148 function prosody.reload_config()
149 log("info", "Reloading configuration file");
150 prosody.events.fire_event("reloading-config");
151 local ok, level, err = config.load((rawget(_G, "CFG_CONFIGDIR") or ".").."/prosody.cfg.lua");
153 if level == "parser" then
154 log("error", "There was an error parsing the configuration file: %s", tostring(err));
155 elseif level == "file" then
156 log("error", "Couldn't read the config file when trying to reload: %s", tostring(err));
159 return ok, (err and tostring(level)..": "..tostring(err)) or nil;
162 -- Function to reopen logfiles
163 function prosody.reopen_logfiles()
164 log("info", "Re-opening log files");
165 eventmanager.fire_event("reopen-log-files"); -- Handled by appropriate log sinks
166 prosody.events.fire_event("reopen-log-files");
169 -- Function to initiate prosody shutdown
170 function prosody.shutdown(reason)
171 log("info", "Shutting down: %s", reason or "unknown reason");
172 prosody.shutdown_reason = reason;
173 prosody.events.fire_event("server-stopping", {reason = reason});
174 server.setquitting(true);
177 -- Load SSL settings from config, and create a ctx table
178 local global_ssl_ctx = rawget(_G, "ssl") and config.get("*", "core", "ssl");
179 if global_ssl_ctx then
180 local default_ssl_ctx = { mode = "server", protocol = "sslv23", capath = "/etc/ssl/certs", verify = "none", options = "no_sslv2"; };
181 setmetatable(global_ssl_ctx, { __index = default_ssl_ctx });
182 prosody.global_ssl_ctx = global_ssl_ctx;
185 local cl = require "net.connlisteners";
186 function prosody.net_activate_ports(option, listener, default, conntype)
187 conntype = conntype or (global_ssl_ctx and "tls") or "tcp";
188 if not cl.get(listener) then return; end
189 local ports = config.get("*", "core", option.."_ports") or default;
190 if type(ports) == "number" then ports = {ports} end;
192 if type(ports) ~= "table" then
193 log("error", "core."..option.." is not a table");
195 for _, port in ipairs(ports) do
196 if type(port) ~= "number" then
197 log("error", "Non-numeric "..option.."_ports: "..tostring(port));
200 ssl = conntype ~= "tcp" and global_ssl_ctx,
202 interface = config.get("*", "core", option.."_interface")
203 or cl.get(listener).default_interface
204 or config.get("*", "core", "interface"),
213 function read_version()
214 -- Try to determine version
215 local version_file = io.open((CFG_SOURCEDIR or ".").."/prosody.version");
217 prosody.version = version_file:read("*a"):gsub("%s*$", "");
218 version_file:close();
219 if #prosody.version == 12 and prosody.version:match("^[a-f0-9]+$") then
220 prosody.version = "hg:"..prosody.version;
223 prosody.version = "unknown";
227 function load_secondary_libraries()
228 --- Load and initialise core modules
229 require "util.import"
230 require "core.xmlhandlers"
231 require "core.rostermanager"
232 require "core.eventmanager"
233 require "core.hostmanager"
234 require "core.modulemanager"
235 require "core.usermanager"
236 require "core.sessionmanager"
237 require "core.stanza_router"
242 require "util.datetime"
243 require "util.iterators"
245 require "util.helpers"
247 pcall(require, "util.signal") -- Not on Windows
249 -- Commented to protect us from
250 -- the second kind of people
252 pcall(require, "remdebug.engine");
253 if remdebug then remdebug.engine.start() end
256 require "net.connlisteners";
258 require "util.stanza"
262 function init_data_store()
263 local data_path = config.get("*", "core", "data_path") or CFG_DATADIR or "data";
264 require "util.datamanager".set_data_path(data_path);
265 require "util.datamanager".add_callback(function(username, host, datastore, data)
266 if config.get(host, "core", "anonymous_login") then
269 return username, host, datastore, data;
273 function prepare_to_start()
274 -- Signal to modules that we are ready to start
275 eventmanager.fire_event("server-starting");
276 prosody.events.fire_event("server-starting");
278 -- start listening on sockets
279 prosody.net_activate_ports("c2s", "xmppclient", {5222});
280 prosody.net_activate_ports("s2s", "xmppserver", {5269});
281 prosody.net_activate_ports("component", "xmppcomponent", {}, "tcp");
282 prosody.net_activate_ports("legacy_ssl", "xmppclient", {}, "ssl");
284 prosody.start_time = os.time();
287 function init_global_protection()
288 -- Catch global accesses --
289 local locked_globals_mt = { __index = function (t, k) error("Attempt to read a non-existent global '"..k.."'", 2); end, __newindex = function (t, k, v) error("Attempt to set a global: "..tostring(k).." = "..tostring(v), 2); end }
291 function prosody.unlock_globals()
292 setmetatable(_G, nil);
295 function prosody.lock_globals()
296 setmetatable(_G, locked_globals_mt);
300 prosody.lock_globals();
304 -- Error handler for errors that make it this far
305 local function catch_uncaught_error(err)
306 if type(err) == "string" and err:match("%d*: interrupted!$") then
310 log("error", "Top-level error, please report:\n%s", tostring(err));
311 local traceback = debug.traceback("", 2);
313 log("error", "%s", traceback);
316 prosody.events.fire_event("very-bad-error", {error = err, traceback = traceback});
319 while select(2, xpcall(server.loop, catch_uncaught_error)) ~= "quitting" do
325 log("info", "Shutdown status: Cleaning up");
326 prosody.events.fire_event("server-cleanup");
328 -- Ok, we're quitting I know, but we
329 -- need to do some tidying before we go :)
330 server.setquitting(false);
332 log("info", "Shutdown status: Closing all active sessions");
333 for hostname, host in pairs(hosts) do
334 log("debug", "Shutdown status: Closing client connections for %s", hostname)
335 if host.sessions then
336 local reason = { condition = "system-shutdown", text = "Server is shutting down" };
337 if prosody.shutdown_reason then
338 reason.text = reason.text..": "..prosody.shutdown_reason;
340 for username, user in pairs(host.sessions) do
341 for resource, session in pairs(user.sessions) do
342 log("debug", "Closing connection for %s@%s/%s", username, hostname, resource);
343 session:close(reason);
348 log("debug", "Shutdown status: Closing outgoing s2s connections from %s", hostname);
350 for remotehost, session in pairs(host.s2sout) do
351 if session.close then
352 session:close("system-shutdown");
354 log("warn", "Unable to close outgoing s2s session to %s, no session:close()?!", remotehost);
360 log("info", "Shutdown status: Closing all server connections");
363 server.setquitting(true);
370 log("info", "Hello and welcome to Prosody version %s", prosody.version);
371 load_secondary_libraries();
373 init_global_protection();
376 eventmanager.fire_event("server-started");
377 prosody.events.fire_event("server-started");
381 log("info", "Shutting down...");
383 eventmanager.fire_event("server-stopped");
384 prosody.events.fire_event("server-stopped");
385 log("info", "Shutdown complete");