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));
109 return ok, (err and tostring(level)..": "..tostring(err)) or nil;
112 -- Function to reopen logfiles
113 function prosody.reopen_logfiles()
114 log("info", "Re-opening log files");
115 eventmanager.fire_event("reopen-log-files"); -- Handled by appropriate log sinks
116 prosody.events.fire_event("reopen-log-files");
119 -- Function to initiate prosody shutdown
120 function prosody.shutdown(reason)
121 log("info", "Shutting down: %s", reason or "unknown reason");
122 prosody.shutdown_reason = reason;
123 prosody.events.fire_event("server-stopping", {reason = reason});
124 server.setquitting(true);
128 function read_version()
129 -- Try to determine version
130 local version_file = io.open((CFG_SOURCEDIR or ".").."/prosody.version");
132 prosody.version = version_file:read("*a"):gsub("%s*$", "");
133 version_file:close();
134 if #prosody.version == 12 and prosody.version:match("^[a-f0-9]+$") then
135 prosody.version = "hg:"..prosody.version;
138 prosody.version = "unknown";
142 function load_secondary_libraries()
143 --- Load and initialise core modules
144 require "util.import"
145 require "core.xmlhandlers"
146 require "core.rostermanager"
147 require "core.eventmanager"
148 require "core.hostmanager"
149 require "core.modulemanager"
150 require "core.usermanager"
151 require "core.sessionmanager"
152 require "core.stanza_router"
155 require "util.iterators"
157 require "util.helpers"
159 -- Commented to protect us from
160 -- the second kind of people
162 pcall(require, "remdebug.engine");
163 if remdebug then remdebug.engine.start() end
166 require "net.connlisteners";
168 require "util.stanza"
172 function init_data_store()
173 local data_path = config.get("*", "core", "data_path") or CFG_DATADIR or "data";
174 require "util.datamanager".set_data_path(data_path);
175 require "util.datamanager".add_callback(function(username, host, datastore, data)
176 if config.get(host, "core", "anonymous_login") then
179 return username, host, datastore, data;
183 function prepare_to_start()
184 -- Signal to modules that we are ready to start
185 eventmanager.fire_event("server-starting");
186 prosody.events.fire_event("server-starting");
188 -- Load SSL settings from config, and create a ctx table
189 local global_ssl_ctx = ssl and config.get("*", "core", "ssl");
190 if global_ssl_ctx then
191 local default_ssl_ctx = { mode = "server", protocol = "sslv23", capath = "/etc/ssl/certs", verify = "none"; };
192 setmetatable(global_ssl_ctx, { __index = default_ssl_ctx });
195 local cl = require "net.connlisteners";
196 -- start listening on sockets
197 function net_activate_ports(option, listener, default, conntype)
198 local ports = config.get("*", "core", option.."_ports") or default;
199 if type(ports) == "number" then ports = {ports} end;
201 if type(ports) ~= "table" then
202 log("error", "core."..option.." is not a table");
204 for _, port in ipairs(ports) do
205 if type(port) ~= "number" then
206 log("error", "Non-numeric "..option.."_ports: "..tostring(port));
209 ssl = conntype ~= "tcp" and global_ssl_ctx,
211 interface = config.get("*", "core", option.."_interface")
212 or cl.get(listener).default_interface
213 or config.get("*", "core", "interface"),
221 net_activate_ports("c2s", "xmppclient", {5222}, (global_ssl_ctx and "tls") or "tcp");
222 net_activate_ports("s2s", "xmppserver", {5269}, "tcp");
223 net_activate_ports("component", "xmppcomponent", {}, "tcp");
224 net_activate_ports("legacy_ssl", "xmppclient", {}, "ssl");
226 if cl.get("console") then
227 cl.start("console", { interface = config.get("*", "core", "console_interface") or "127.0.0.1" })
230 prosody.start_time = os.time();
233 function init_global_protection()
234 -- Catch global accesses --
235 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 }
237 function prosody.unlock_globals()
238 setmetatable(_G, nil);
241 function prosody.lock_globals()
242 setmetatable(_G, locked_globals_mt);
246 prosody.lock_globals();
250 -- Error handler for errors that make it this far
251 local function catch_uncaught_error(err)
252 if err:match("%d*: interrupted!$") then
256 log("error", "Top-level error, please report:\n%s", tostring(err));
257 local traceback = debug.traceback("", 2);
259 log("error", "%s", traceback);
262 prosody.events.fire_event("very-bad-error", {error = err, traceback = traceback});
265 while select(2, xpcall(server.loop, catch_uncaught_error)) ~= "quitting" do
271 log("info", "Shutdown status: Cleaning up");
272 prosody.events.fire_event("server-cleanup");
274 -- Ok, we're quitting I know, but we
275 -- need to do some tidying before we go :)
276 server.setquitting(false);
278 log("info", "Shutdown status: Closing all active sessions");
279 for hostname, host in pairs(hosts) do
280 log("debug", "Shutdown status: Closing client connections for %s", hostname)
281 if host.sessions then
282 local reason = { condition = "system-shutdown", text = "Server is shutting down" };
283 if prosody.shutdown_reason then
284 reason.text = reason.text..": "..prosody.shutdown_reason;
286 for username, user in pairs(host.sessions) do
287 for resource, session in pairs(user.sessions) do
288 log("debug", "Closing connection for %s@%s/%s", username, hostname, resource);
289 session:close(reason);
294 log("debug", "Shutdown status: Closing outgoing s2s connections from %s", hostname);
296 for remotehost, session in pairs(host.s2sout) do
297 if session.close then
298 session:close("system-shutdown");
300 log("warn", "Unable to close outgoing s2s session to %s, no session:close()?!", remotehost);
306 log("info", "Shutdown status: Closing all server connections");
309 server.setquitting(true);
316 log("info", "Hello and welcome to Prosody version %s", prosody.version);
317 load_secondary_libraries();
320 init_global_protection();
322 eventmanager.fire_event("server-started");
323 prosody.events.fire_event("server-started");
327 log("info", "Shutting down...");
329 eventmanager.fire_event("server-stopped");
330 prosody.events.fire_event("server-stopped");
331 log("info", "Shutdown complete");