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"
155 require "util.helpers"
157 -- Commented to protect us from
158 -- the second kind of people
160 pcall(require, "remdebug.engine");
161 if remdebug then remdebug.engine.start() end
164 require "net.connlisteners";
166 require "util.stanza"
170 function init_data_store()
171 local data_path = config.get("*", "core", "data_path") or CFG_DATADIR or "data";
172 require "util.datamanager".set_data_path(data_path);
173 require "util.datamanager".add_callback(function(username, host, datastore, data)
174 if config.get(host, "core", "anonymous_login") then
177 return username, host, datastore, data;
181 function prepare_to_start()
182 -- Signal to modules that we are ready to start
183 eventmanager.fire_event("server-starting");
184 prosody.events.fire_event("server-starting");
186 -- Load SSL settings from config, and create a ctx table
187 local global_ssl_ctx = ssl and config.get("*", "core", "ssl");
188 if global_ssl_ctx then
189 local default_ssl_ctx = { mode = "server", protocol = "sslv23", capath = "/etc/ssl/certs", verify = "none"; };
190 setmetatable(global_ssl_ctx, { __index = default_ssl_ctx });
193 local cl = require "net.connlisteners";
194 -- start listening on sockets
195 function net_activate_ports(option, listener, default, conntype)
196 local ports = config.get("*", "core", option.."_ports") or default;
197 if type(ports) == "number" then ports = {ports} end;
199 if type(ports) ~= "table" then
200 log("error", "core."..option.." is not a table");
202 for _, port in ipairs(ports) do
203 if type(port) ~= "number" then
204 log("error", "Non-numeric "..option.."_ports: "..tostring(port));
207 ssl = conntype ~= "tcp" and global_ssl_ctx,
209 interface = config.get("*", "core", option.."_interface")
210 or cl.get(listener).default_interface
211 or config.get("*", "core", "interface"),
219 net_activate_ports("c2s", "xmppclient", {5222}, (global_ssl_ctx and "tls") or "tcp");
220 net_activate_ports("s2s", "xmppserver", {5269}, "tcp");
221 net_activate_ports("component", "xmppcomponent", {}, "tcp");
222 net_activate_ports("legacy_ssl", "xmppclient", {}, "ssl");
224 if cl.get("console") then
225 cl.start("console", { interface = config.get("*", "core", "console_interface") or "127.0.0.1" })
228 prosody.start_time = os.time();
231 function init_global_protection()
232 -- Catch global accesses --
233 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 }
235 function prosody.unlock_globals()
236 setmetatable(_G, nil);
239 function prosody.lock_globals()
240 setmetatable(_G, locked_globals_mt);
244 prosody.lock_globals();
248 -- Error handler for errors that make it this far
249 local function catch_uncaught_error(err)
250 if err:match("%d*: interrupted!$") then
254 log("error", "Top-level error, please report:\n%s", tostring(err));
255 local traceback = debug.traceback("", 2);
257 log("error", "%s", traceback);
260 prosody.events.fire_event("very-bad-error", {error = err, traceback = traceback});
263 while select(2, xpcall(server.loop, catch_uncaught_error)) ~= "quitting" do
269 log("info", "Shutdown status: Cleaning up");
270 prosody.events.fire_event("server-cleanup");
272 -- Ok, we're quitting I know, but we
273 -- need to do some tidying before we go :)
274 server.setquitting(false);
276 log("info", "Shutdown status: Closing all active sessions");
277 for hostname, host in pairs(hosts) do
278 log("debug", "Shutdown status: Closing client connections for %s", hostname)
279 if host.sessions then
280 for username, user in pairs(host.sessions) do
281 for resource, session in pairs(user.sessions) do
282 log("debug", "Closing connection for %s@%s/%s", username, hostname, resource);
283 session:close("system-shutdown");
288 log("debug", "Shutdown status: Closing outgoing s2s connections from %s", hostname);
290 for remotehost, session in pairs(host.s2sout) do
291 if session.close then
292 session:close("system-shutdown");
294 log("warn", "Unable to close outgoing s2s session to %s, no session:close()?!", remotehost);
300 log("info", "Shutdown status: Closing all server connections");
303 server.setquitting(true);
310 log("info", "Hello and welcome to Prosody version %s", prosody.version);
311 load_secondary_libraries();
314 init_global_protection();
316 eventmanager.fire_event("server-started");
317 prosody.events.fire_event("server-started");
321 log("info", "Shutting down...");
323 eventmanager.fire_event("server-stopped");
324 prosody.events.fire_event("server-stopped");
325 log("info", "Shutdown complete");