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"; };
181 setmetatable(global_ssl_ctx, { __index = default_ssl_ctx });
184 local cl = require "net.connlisteners";
185 function prosody.net_activate_ports(option, listener, default, conntype)
186 conntype = conntype or (global_ssl_ctx and "tls") or "tcp";
187 if not cl.get(listener) then return; end
188 local ports = config.get("*", "core", option.."_ports") or default;
189 if type(ports) == "number" then ports = {ports} end;
191 if type(ports) ~= "table" then
192 log("error", "core."..option.." is not a table");
194 for _, port in ipairs(ports) do
195 if type(port) ~= "number" then
196 log("error", "Non-numeric "..option.."_ports: "..tostring(port));
198 local ok, err = cl.start(listener, {
199 ssl = conntype ~= "tcp" and global_ssl_ctx,
201 interface = config.get("*", "core", option.."_interface")
202 or cl.get(listener).default_interface
203 or config.get("*", "core", "interface"),
207 local friendly_message = err;
208 if err:match(" in use") then
209 if port == 5222 or port == 5223 or port == 5269 then
210 friendly_message = "check that Prosody or another XMPP server is "
211 .."not already running and using this port";
212 elseif port == 80 or port == 81 then
213 friendly_message = "check that a HTTP server is not already using "
215 elseif port == 5280 then
216 friendly_message = "check that Prosody or a BOSH connection manager "
217 .."is not already running";
219 friendly_message = "this port is in use by another application";
221 elseif err:match("permission") then
222 friendly_message = "Prosody does not have sufficient privileges to use this port";
223 elseif err == "no ssl context" then
224 friendly_message = "there is no 'ssl' config under Host \"*\" which is "
225 .."require for legacy SSL ports";
227 log("error", "Failed to open server port %d, %s", port, friendly_message);
235 function read_version()
236 -- Try to determine version
237 local version_file = io.open((CFG_SOURCEDIR or ".").."/prosody.version");
239 prosody.version = version_file:read("*a"):gsub("%s*$", "");
240 version_file:close();
241 if #prosody.version == 12 and prosody.version:match("^[a-f0-9]+$") then
242 prosody.version = "hg:"..prosody.version;
245 prosody.version = "unknown";
249 function load_secondary_libraries()
250 --- Load and initialise core modules
251 require "util.import"
252 require "core.xmlhandlers"
253 require "core.rostermanager"
254 require "core.eventmanager"
255 require "core.hostmanager"
256 require "core.modulemanager"
257 require "core.usermanager"
258 require "core.sessionmanager"
259 require "core.stanza_router"
264 require "util.datetime"
265 require "util.iterators"
267 require "util.helpers"
269 pcall(require, "util.signal") -- Not on Windows
271 -- Commented to protect us from
272 -- the second kind of people
274 pcall(require, "remdebug.engine");
275 if remdebug then remdebug.engine.start() end
278 require "net.connlisteners";
280 require "util.stanza"
284 function init_data_store()
285 local data_path = config.get("*", "core", "data_path") or CFG_DATADIR or "data";
286 require "util.datamanager".set_data_path(data_path);
287 require "util.datamanager".add_callback(function(username, host, datastore, data)
288 if config.get(host, "core", "anonymous_login") then
291 return username, host, datastore, data;
295 function prepare_to_start()
296 -- Signal to modules that we are ready to start
297 eventmanager.fire_event("server-starting");
298 prosody.events.fire_event("server-starting");
300 -- start listening on sockets
301 prosody.net_activate_ports("c2s", "xmppclient", {5222});
302 prosody.net_activate_ports("s2s", "xmppserver", {5269});
303 prosody.net_activate_ports("component", "xmppcomponent", {5347}, "tcp");
304 prosody.net_activate_ports("legacy_ssl", "xmppclient", {}, "ssl");
306 prosody.start_time = os.time();
309 function init_global_protection()
310 -- Catch global accesses
311 local locked_globals_mt = {
312 __index = function (t, k) log("warn", "%s", debug.traceback("Attempt to read a non-existent global '"..tostring(k).."'", 2)); end;
313 __newindex = function (t, k, v) error("Attempt to set a global: "..tostring(k).." = "..tostring(v), 2); end;
316 function prosody.unlock_globals()
317 setmetatable(_G, nil);
320 function prosody.lock_globals()
321 setmetatable(_G, locked_globals_mt);
325 prosody.lock_globals();
329 -- Error handler for errors that make it this far
330 local function catch_uncaught_error(err)
331 if type(err) == "string" and err:match("%d*: interrupted!$") then
335 log("error", "Top-level error, please report:\n%s", tostring(err));
336 local traceback = debug.traceback("", 2);
338 log("error", "%s", traceback);
341 prosody.events.fire_event("very-bad-error", {error = err, traceback = traceback});
344 while select(2, xpcall(server.loop, catch_uncaught_error)) ~= "quitting" do
350 log("info", "Shutdown status: Cleaning up");
351 prosody.events.fire_event("server-cleanup");
353 -- Ok, we're quitting I know, but we
354 -- need to do some tidying before we go :)
355 server.setquitting(false);
357 log("info", "Shutdown status: Closing all active sessions");
358 for hostname, host in pairs(hosts) do
359 log("debug", "Shutdown status: Closing client connections for %s", hostname)
360 if host.sessions then
361 local reason = { condition = "system-shutdown", text = "Server is shutting down" };
362 if prosody.shutdown_reason then
363 reason.text = reason.text..": "..prosody.shutdown_reason;
365 for username, user in pairs(host.sessions) do
366 for resource, session in pairs(user.sessions) do
367 log("debug", "Closing connection for %s@%s/%s", username, hostname, resource);
368 session:close(reason);
373 log("debug", "Shutdown status: Closing outgoing s2s connections from %s", hostname);
375 for remotehost, session in pairs(host.s2sout) do
376 if session.close then
377 session:close("system-shutdown");
379 log("warn", "Unable to close outgoing s2s session to %s, no session:close()?!", remotehost);
385 log("info", "Shutdown status: Closing all server connections");
388 server.setquitting(true);
395 log("info", "Hello and welcome to Prosody version %s", prosody.version);
396 load_secondary_libraries();
398 init_global_protection();
401 eventmanager.fire_event("server-started");
402 prosody.events.fire_event("server-started");
406 log("info", "Shutting down...");
408 eventmanager.fire_event("server-stopped");
409 prosody.events.fire_event("server-stopped");
410 log("info", "Shutdown complete");