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?
61 local ok, level, err = config.load((CFG_CONFIGDIR or ".").."/prosody.cfg.lua");
64 print("**************************");
65 if level == "parser" then
66 print("A problem occured while reading the config file "..(CFG_CONFIGDIR or ".").."/prosody.cfg.lua");
67 local err_line, err_message = tostring(err):match("%[string .-%]:(%d*): (.*)");
68 print("Error"..(err_line and (" on line "..err_line) or "")..": "..(err_message or tostring(err)));
70 elseif level == "file" then
71 print("Prosody was unable to find the configuration file.");
72 print("We looked for: "..(CFG_CONFIGDIR or ".").."/prosody.cfg.lua");
73 print("A sample config file is included in the Prosody download called prosody.cfg.lua.dist");
74 print("Copy or rename it to prosody.cfg.lua and edit as necessary.");
76 print("More help on configuring Prosody can be found at http://prosody.im/doc/configure");
78 print("**************************");
84 function load_libraries()
85 --- Initialize logging
86 require "core.loggingmanager"
88 --- Check runtime dependencies
89 require "util.dependencies"
91 --- Load socket framework
92 server = require "net.server"
95 function init_global_state()
100 -- Global 'prosody' object
102 local prosody = prosody;
104 prosody.bare_sessions = bare_sessions;
105 prosody.full_sessions = full_sessions;
106 prosody.hosts = hosts;
108 prosody.paths = { source = CFG_SOURCEDIR, config = CFG_CONFIGDIR,
109 plugins = CFG_PLUGINDIR, data = CFG_DATADIR };
111 prosody.arg = _G.arg;
113 prosody.events = require "util.events".new();
115 prosody.platform = "unknown";
116 if os.getenv("WINDIR") then
117 prosody.platform = "windows";
118 elseif package.config:sub(1,1) == "/" then
119 prosody.platform = "posix";
122 prosody.installed = nil;
123 if CFG_SOURCEDIR and (prosody.platform == "windows" or CFG_SOURCEDIR:match("^/")) then
124 prosody.installed = true;
127 -- Function to reload the config file
128 function prosody.reload_config()
129 log("info", "Reloading configuration file");
130 prosody.events.fire_event("reloading-config");
131 local ok, level, err = config.load((rawget(_G, "CFG_CONFIGDIR") or ".").."/prosody.cfg.lua");
133 if level == "parser" then
134 log("error", "There was an error parsing the configuration file: %s", tostring(err));
135 elseif level == "file" then
136 log("error", "Couldn't read the config file when trying to reload: %s", tostring(err));
139 return ok, (err and tostring(level)..": "..tostring(err)) or nil;
142 -- Function to reopen logfiles
143 function prosody.reopen_logfiles()
144 log("info", "Re-opening log files");
145 eventmanager.fire_event("reopen-log-files"); -- Handled by appropriate log sinks
146 prosody.events.fire_event("reopen-log-files");
149 -- Function to initiate prosody shutdown
150 function prosody.shutdown(reason)
151 log("info", "Shutting down: %s", reason or "unknown reason");
152 prosody.shutdown_reason = reason;
153 prosody.events.fire_event("server-stopping", {reason = reason});
154 server.setquitting(true);
157 -- Load SSL settings from config, and create a ctx table
158 local global_ssl_ctx = rawget(_G, "ssl") and config.get("*", "core", "ssl");
159 if global_ssl_ctx then
160 local default_ssl_ctx = { mode = "server", protocol = "sslv23", capath = "/etc/ssl/certs", verify = "none"; };
161 setmetatable(global_ssl_ctx, { __index = default_ssl_ctx });
164 local cl = require "net.connlisteners";
165 function prosody.net_activate_ports(option, listener, default, conntype)
166 conntype = conntype or (global_ssl_ctx and "tls") or "tcp";
167 if not cl.get(listener) then return; end
168 local ports = config.get("*", "core", option.."_ports") or default;
169 if type(ports) == "number" then ports = {ports} end;
171 if type(ports) ~= "table" then
172 log("error", "core."..option.." is not a table");
174 for _, port in ipairs(ports) do
175 if type(port) ~= "number" then
176 log("error", "Non-numeric "..option.."_ports: "..tostring(port));
179 ssl = conntype ~= "tcp" and global_ssl_ctx,
181 interface = config.get("*", "core", option.."_interface")
182 or cl.get(listener).default_interface
183 or config.get("*", "core", "interface"),
192 function read_version()
193 -- Try to determine version
194 local version_file = io.open((CFG_SOURCEDIR or ".").."/prosody.version");
196 prosody.version = version_file:read("*a"):gsub("%s*$", "");
197 version_file:close();
198 if #prosody.version == 12 and prosody.version:match("^[a-f0-9]+$") then
199 prosody.version = "hg:"..prosody.version;
202 prosody.version = "unknown";
206 function load_secondary_libraries()
207 --- Load and initialise core modules
208 require "util.import"
209 require "core.xmlhandlers"
210 require "core.rostermanager"
211 require "core.eventmanager"
212 require "core.hostmanager"
213 require "core.modulemanager"
214 require "core.usermanager"
215 require "core.sessionmanager"
216 require "core.stanza_router"
221 require "util.datetime"
222 require "util.iterators"
224 require "util.helpers"
226 pcall(require, "util.signal") -- Not on Windows
228 -- Commented to protect us from
229 -- the second kind of people
231 pcall(require, "remdebug.engine");
232 if remdebug then remdebug.engine.start() end
235 require "net.connlisteners";
237 require "util.stanza"
241 function init_data_store()
242 local data_path = config.get("*", "core", "data_path") or CFG_DATADIR or "data";
243 require "util.datamanager".set_data_path(data_path);
244 require "util.datamanager".add_callback(function(username, host, datastore, data)
245 if config.get(host, "core", "anonymous_login") then
248 return username, host, datastore, data;
252 function prepare_to_start()
253 -- Signal to modules that we are ready to start
254 eventmanager.fire_event("server-starting");
255 prosody.events.fire_event("server-starting");
257 -- start listening on sockets
258 prosody.net_activate_ports("c2s", "xmppclient", {5222});
259 prosody.net_activate_ports("s2s", "xmppserver", {5269});
260 prosody.net_activate_ports("component", "xmppcomponent", {}, "tcp");
261 prosody.net_activate_ports("legacy_ssl", "xmppclient", {}, "ssl");
263 prosody.start_time = os.time();
266 function init_global_protection()
267 -- Catch global accesses --
268 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 }
270 function prosody.unlock_globals()
271 setmetatable(_G, nil);
274 function prosody.lock_globals()
275 setmetatable(_G, locked_globals_mt);
279 prosody.lock_globals();
283 -- Error handler for errors that make it this far
284 local function catch_uncaught_error(err)
285 if type(err) == "string" and err:match("%d*: interrupted!$") then
289 log("error", "Top-level error, please report:\n%s", tostring(err));
290 local traceback = debug.traceback("", 2);
292 log("error", "%s", traceback);
295 prosody.events.fire_event("very-bad-error", {error = err, traceback = traceback});
298 while select(2, xpcall(server.loop, catch_uncaught_error)) ~= "quitting" do
304 log("info", "Shutdown status: Cleaning up");
305 prosody.events.fire_event("server-cleanup");
307 -- Ok, we're quitting I know, but we
308 -- need to do some tidying before we go :)
309 server.setquitting(false);
311 log("info", "Shutdown status: Closing all active sessions");
312 for hostname, host in pairs(hosts) do
313 log("debug", "Shutdown status: Closing client connections for %s", hostname)
314 if host.sessions then
315 local reason = { condition = "system-shutdown", text = "Server is shutting down" };
316 if prosody.shutdown_reason then
317 reason.text = reason.text..": "..prosody.shutdown_reason;
319 for username, user in pairs(host.sessions) do
320 for resource, session in pairs(user.sessions) do
321 log("debug", "Closing connection for %s@%s/%s", username, hostname, resource);
322 session:close(reason);
327 log("debug", "Shutdown status: Closing outgoing s2s connections from %s", hostname);
329 for remotehost, session in pairs(host.s2sout) do
330 if session.close then
331 session:close("system-shutdown");
333 log("warn", "Unable to close outgoing s2s session to %s, no session:close()?!", remotehost);
339 log("info", "Shutdown status: Closing all server connections");
342 server.setquitting(true);
349 log("info", "Hello and welcome to Prosody version %s", prosody.version);
350 load_secondary_libraries();
352 init_global_protection();
355 eventmanager.fire_event("server-started");
356 prosody.events.fire_event("server-started");
360 log("info", "Shutting down...");
362 eventmanager.fire_event("server-stopped");
363 prosody.events.fire_event("server-stopped");
364 log("info", "Shutdown complete");