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 --
13 CFG_CONFIGDIR=os.getenv("PROSODY_CFGDIR");
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")
37 config = require "core.configmanager"
39 function read_config()
40 -- TODO: Check for other formats when we add support for them
41 -- Use lfs? Make a new conf/ dir?
42 local ok, level, err = config.load((CFG_CONFIGDIR or ".").."/prosody.cfg.lua");
45 print("**************************");
46 if level == "parser" then
47 print("A problem occured while reading the config file "..(CFG_CONFIGDIR or ".").."/prosody.cfg.lua");
48 local err_line, err_message = tostring(err):match("%[string .-%]:(%d*): (.*)");
49 print("Error"..(err_line and (" on line "..err_line) or "")..": "..(err_message or tostring(err)));
51 elseif level == "file" then
52 print("Prosody was unable to find the configuration file.");
53 print("We looked for: "..(CFG_CONFIGDIR or ".").."/prosody.cfg.lua");
54 print("A sample config file is included in the Prosody download called prosody.cfg.lua.dist");
55 print("Copy or rename it to prosody.cfg.lua and edit as necessary.");
57 print("More help on configuring Prosody can be found at http://prosody.im/doc/configure");
59 print("**************************");
65 function load_libraries()
66 --- Initialize logging
67 require "core.loggingmanager"
69 --- Check runtime dependencies
70 require "util.dependencies"
72 --- Load socket framework
73 server = require "net.server"
76 function init_global_state()
81 -- Global 'prosody' object
83 local prosody = prosody;
85 prosody.bare_sessions = bare_sessions;
86 prosody.full_sessions = full_sessions;
87 prosody.hosts = hosts;
89 prosody.paths = { source = CFG_SOURCEDIR, config = CFG_CONFIGDIR,
90 plugins = CFG_PLUGINDIR, data = CFG_DATADIR };
94 prosody.events = require "util.events".new();
97 -- Function to reload the config file
98 function prosody.reload_config()
99 log("info", "Reloading configuration file");
100 prosody.events.fire_event("reloading-config");
101 local ok, level, err = config.load((rawget(_G, "CFG_CONFIGDIR") or ".").."/prosody.cfg.lua");
103 if level == "parser" then
104 log("error", "There was an error parsing the configuration file: %s", tostring(err));
105 elseif level == "file" then
106 log("error", "Couldn't read the config file when trying to reload: %s", tostring(err));
111 -- Function to reopen logfiles
112 function prosody.reopen_logfiles()
113 log("info", "Re-opening log files");
114 eventmanager.fire_event("reopen-log-files"); -- Handled by appropriate log sinks
115 prosody.events.fire_event("reopen-log-files");
118 -- Function to initiate prosody shutdown
119 function prosody.shutdown(reason)
120 log("info", "Shutting down: %s", reason or "unknown reason");
121 prosody.events.fire_event("server-stopping", {reason = reason});
122 server.setquitting(true);
126 function read_version()
127 -- Try to determine version
128 local version_file = io.open((CFG_SOURCEDIR or ".").."/prosody.version");
130 prosody.version = version_file:read("*a"):gsub("%s*$", "");
131 version_file:close();
132 if #prosody.version == 12 and prosody.version:match("^[a-f0-9]+$") then
133 prosody.version = "hg:"..prosody.version;
136 prosody.version = "unknown";
140 function load_secondary_libraries()
141 --- Load and initialise core modules
142 require "util.import"
143 require "core.xmlhandlers"
144 require "core.rostermanager"
145 require "core.eventmanager"
146 require "core.hostmanager"
147 require "core.modulemanager"
148 require "core.usermanager"
149 require "core.sessionmanager"
150 require "core.stanza_router"
153 require "util.iterators"
156 -- Commented to protect us from
157 -- the second kind of people
159 pcall(require, "remdebug.engine");
160 if remdebug then remdebug.engine.start() end
163 require "net.connlisteners";
165 require "util.stanza"
169 function init_data_store()
170 local data_path = config.get("*", "core", "data_path") or CFG_DATADIR or "data";
171 require "util.datamanager".set_data_path(data_path);
172 require "util.datamanager".add_callback(function(username, host, datastore, data)
173 if config.get(host, "core", "anonymous_login") then
176 return username, host, datastore, data;
180 function prepare_to_start()
181 -- Signal to modules that we are ready to start
182 eventmanager.fire_event("server-starting");
183 prosody.events.fire_event("server-starting");
185 -- Load SSL settings from config, and create a ctx table
186 local global_ssl_ctx = 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"; };
189 setmetatable(global_ssl_ctx, { __index = default_ssl_ctx });
192 local cl = require "net.connlisteners";
193 -- start listening on sockets
194 function net_activate_ports(option, listener, default, conntype)
195 local ports = config.get("*", "core", option.."_ports") or default;
196 if type(ports) == "number" then ports = {ports} end;
198 if type(ports) ~= "table" then
199 log("error", "core."..option.." is not a table");
201 for _, port in ipairs(ports) do
202 if type(port) ~= "number" then
203 log("error", "Non-numeric "..option.."_ports: "..tostring(port));
206 ssl = conntype ~= "tcp" and global_ssl_ctx,
208 interface = config.get("*", "core", option.."_interface")
209 or cl.get(listener).default_interface
210 or config.get("*", "core", "interface"),
218 net_activate_ports("c2s", "xmppclient", {5222}, (global_ssl_ctx and "tls") or "tcp");
219 net_activate_ports("s2s", "xmppserver", {5269}, "tcp");
220 net_activate_ports("component", "xmppcomponent", {}, "tcp");
221 net_activate_ports("legacy_ssl", "xmppclient", {}, "ssl");
223 if cl.get("console") then
224 cl.start("console", { interface = config.get("*", "core", "console_interface") or "127.0.0.1" })
227 prosody.start_time = os.time();
230 function init_global_protection()
231 -- Catch global accesses --
232 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 }
234 function prosody.unlock_globals()
235 setmetatable(_G, nil);
238 function prosody.lock_globals()
239 setmetatable(_G, locked_globals_mt);
243 prosody.lock_globals();
247 -- Error handler for errors that make it this far
248 local function catch_uncaught_error(err)
249 if err:match("%d*: interrupted!$") then
253 log("error", "Top-level error, please report:\n%s", tostring(err));
254 local traceback = debug.traceback("", 2);
256 log("error", "%s", traceback);
259 prosody.events.fire_event("very-bad-error", {error = err, traceback = traceback});
262 while select(2, xpcall(server.loop, catch_uncaught_error)) ~= "quitting" do
268 log("info", "Shutdown status: Cleaning up");
269 prosody.events.fire_event("server-cleanup");
271 -- Ok, we're quitting I know, but we
272 -- need to do some tidying before we go :)
273 server.setquitting(false);
275 log("info", "Shutdown status: Closing all active sessions");
276 for hostname, host in pairs(hosts) do
277 log("debug", "Shutdown status: Closing client connections for %s", hostname)
278 if host.sessions then
279 for username, user in pairs(host.sessions) do
280 for resource, session in pairs(user.sessions) do
281 log("debug", "Closing connection for %s@%s/%s", username, hostname, resource);
282 session:close("system-shutdown");
287 log("debug", "Shutdown status: Closing outgoing s2s connections from %s", hostname);
289 for remotehost, session in pairs(host.s2sout) do
290 if session.close then
291 session:close("system-shutdown");
293 log("warn", "Unable to close outgoing s2s session to %s, no session:close()?!", remotehost);
299 log("info", "Shutdown status: Closing all server connections");
302 server.setquitting(true);
309 log("info", "Hello and welcome to Prosody version %s", prosody.version);
310 load_secondary_libraries();
313 init_global_protection();
315 eventmanager.fire_event("server-started");
316 prosody.events.fire_event("server-started");
320 log("info", "Shutting down...");
322 eventmanager.fire_event("server-stopped");
323 prosody.events.fire_event("server-stopped");
324 log("info", "Shutdown complete");