mod_console: Redirect print() to console session when executing commands in global...
[prosody.git] / prosody
1 #!/usr/bin/env lua
2 -- Prosody IM v0.4
3 -- Copyright (C) 2008-2009 Matthew Wild
4 -- Copyright (C) 2008-2009 Waqas Hussain
5 -- 
6 -- This project is MIT/X11 licensed. Please see the
7 -- COPYING file in the source package for more information.
8 --
9
10 -- Will be modified by configure script if run --
11
12 CFG_SOURCEDIR=nil;
13 CFG_CONFIGDIR=os.getenv("PROSODY_CFGDIR");
14 CFG_PLUGINDIR=nil;
15 CFG_DATADIR=os.getenv("PROSODY_DATADIR");
16
17 -- -- -- -- -- -- -- ---- -- -- -- -- -- -- -- --
18
19 if CFG_SOURCEDIR then
20         package.path = CFG_SOURCEDIR.."/?.lua;"..package.path
21         package.cpath = CFG_SOURCEDIR.."/?.so;"..package.cpath
22 end
23
24 if CFG_DATADIR then
25         if os.getenv("HOME") then
26                 CFG_DATADIR = CFG_DATADIR:gsub("^~", os.getenv("HOME"));
27         end
28 end
29
30 -- Required to be able to find packages installed with luarocks
31 pcall(require, "luarocks.require")
32
33
34 config = require "core.configmanager"
35
36 do
37         -- TODO: Check for other formats when we add support for them
38         -- Use lfs? Make a new conf/ dir?
39         local ok, level, err = config.load((CFG_CONFIGDIR or ".").."/prosody.cfg.lua");
40         if not ok then
41                 print("\n");
42                 print("**************************");
43                 if level == "parser" then
44                         print("A problem occured while reading the config file "..(CFG_CONFIGDIR or ".").."/prosody.cfg.lua");
45                         local err_line, err_message = tostring(err):match("%[string .-%]:(%d*): (.*)");
46                         print("Error"..(err_line and (" on line "..err_line) or "")..": "..(err_message or tostring(err)));
47                         print("");
48                 elseif level == "file" then
49                         print("Prosody was unable to find the configuration file.");
50                         print("We looked for: "..(CFG_CONFIGDIR or ".").."/prosody.cfg.lua");
51                         print("A sample config file is included in the Prosody download called prosody.cfg.lua.dist");
52                         print("Copy or rename it to prosody.cfg.lua and edit as necessary.");
53                 end
54                 print("More help on configuring Prosody can be found at http://prosody.im/doc/configure");
55                 print("Good luck!");
56                 print("**************************");
57                 print("");
58                 os.exit(1);
59         end
60 end
61
62 --- Initialize logging
63 require "core.loggingmanager"
64
65 --- Check runtime dependencies
66 require "util.dependencies"
67
68 --- Load socket framework
69 local server = require "net.server"
70
71 bare_sessions = {};
72 full_sessions = {};
73 hosts = {};
74
75 -- Global 'prosody' object
76 prosody = {};
77 local prosody = prosody;
78
79 prosody.bare_sessions = bare_sessions;
80 prosody.full_sessions = full_sessions;
81 prosody.hosts = hosts;
82
83 prosody.paths = { source = CFG_SOURCEDIR, config = CFG_CONFIGDIR, 
84                   plugins = CFG_PLUGINDIR, data = CFG_DATADIR };
85
86 prosody.arg = arg;
87
88 prosody.events = require "util.events".new();
89
90 -- Try to determine version
91 local version_file = io.open((CFG_SOURCEDIR or ".").."/prosody.version");
92 if version_file then
93         prosody.version = version_file:read("*a"):gsub("%s*$", "");
94         version_file:close();
95         if #prosody.version == 12 and prosody.version:match("^[a-f0-9]+$") then
96                 prosody.version = "hg:"..prosody.version;
97         end
98 else
99         prosody.version = "unknown";
100 end
101
102 log("info", "Hello and welcome to Prosody version %s", prosody.version);
103
104 --- Load and initialise core modules
105 require "util.import"
106 require "core.xmlhandlers"
107 require "core.rostermanager"
108 require "core.eventmanager"
109 require "core.hostmanager"
110 require "core.modulemanager"
111 require "core.usermanager"
112 require "core.sessionmanager"
113 require "core.stanza_router"
114
115 require "util.array"
116 require "util.iterators"
117 require "util.timer"
118
119 -- Commented to protect us from 
120 -- the second kind of people
121 --[[ 
122 pcall(require, "remdebug.engine");
123 if remdebug then remdebug.engine.start() end
124 ]]
125
126 local cl = require "net.connlisteners";
127
128 require "util.stanza"
129 require "util.jid"
130
131 ------------------------------------------------------------------------
132
133
134 ------------- Begin code without a home ---------------------
135
136 local data_path = config.get("*", "core", "data_path") or CFG_DATADIR or "data";
137 require "util.datamanager".set_data_path(data_path);
138 require "util.datamanager".set_callback(function(username, host, datastore)
139         return config.get(host, "core", "anonymous_login");
140 end);
141
142 ----------- End of out-of-place code --------------
143
144 -- Function to reload the config file
145 function prosody.reload_config()
146         log("info", "Reloading configuration file");
147         eventmanager.fire_event("reloading-config");
148         local ok, level, err = config.load((rawget(_G, "CFG_CONFIGDIR") or ".").."/prosody.cfg.lua");
149         if not ok then
150                 if level == "parser" then
151                         log("error", "There was an error parsing the configuration file: %s", tostring(err));
152                 elseif level == "file" then
153                         log("error", "Couldn't read the config file when trying to reload: %s", tostring(err));
154                 end
155         end
156 end
157
158 -- Function to reopen logfiles
159 function prosody.reopen_logfiles()
160         log("info", "Re-opening log files");
161         eventmanager.fire_event("reopen-log-files"); -- Handled by appropriate log sinks
162 end
163
164 -- Function to initiate prosody shutdown
165 function prosody.shutdown(reason)
166         log("info", "Shutting down: %s", reason or "unknown reason");
167         eventmanager.fire_event("server-stopping", { reason = reason });
168         server.setquitting(true);
169 end
170
171 -- Signal to modules that we are ready to start
172 eventmanager.fire_event("server-starting");
173
174 -- Load SSL settings from config, and create a ctx table
175 local global_ssl_ctx = ssl and config.get("*", "core", "ssl");
176 if global_ssl_ctx then
177         local default_ssl_ctx = { mode = "server", protocol = "sslv23", capath = "/etc/ssl/certs", verify = "none"; };
178         setmetatable(global_ssl_ctx, { __index = default_ssl_ctx });
179 end
180
181 -- start listening on sockets
182 function net_activate_ports(option, listener, default, conntype)
183         local ports = config.get("*", "core", option.."_ports") or default;
184         if type(ports) == "number" then ports = {ports} end;
185         
186         if type(ports) ~= "table" then
187                 log("error", "core."..option.." is not a table");
188         else
189                 for _, port in ipairs(ports) do
190                         if type(port) ~= "number" then
191                                 log("error", "Non-numeric "..option.."_ports: "..tostring(port));
192                         else
193                                 cl.start(listener, { 
194                                         ssl = conntype ~= "tcp" and global_ssl_ctx,
195                                         port = port,
196                                         interface = config.get("*", "core", option.."_interface") 
197                                                 or cl.get(listener).default_interface 
198                                                 or config.get("*", "core", "interface"),
199                                         type = conntype
200                                 });
201                         end
202                 end
203         end
204 end
205
206 net_activate_ports("c2s", "xmppclient", {5222}, (global_ssl_ctx and "tls") or "tcp");
207 net_activate_ports("s2s", "xmppserver", {5269}, "tcp");
208 net_activate_ports("component", "xmppcomponent", {}, "tcp");
209 net_activate_ports("legacy_ssl", "xmppclient", {}, "ssl");
210
211 if cl.get("console") then
212         cl.start("console", { interface = config.get("*", "core", "console_interface") or "127.0.0.1" })
213 end
214
215 -- Catch global accesses --
216 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 }
217
218 function prosody.unlock_globals()
219         setmetatable(_G, nil);
220 end
221
222 function prosody.lock_globals()
223         setmetatable(_G, locked_globals_mt);
224 end
225
226 -- And lock now...
227 prosody.lock_globals();
228
229 eventmanager.fire_event("server-started");
230
231 -- Error handler for errors that make it this far
232 local function catch_uncaught_error(err)
233         if err:match("%d*: interrupted!$") then
234                 return "quitting";
235         end
236         
237         log("error", "Top-level error, please report:\n%s", tostring(err));
238         local traceback = debug.traceback("", 2);
239         if traceback then
240                 log("error", "%s", traceback);
241         end
242         
243         eventmanager.fire_event("very-bad-error", "*", err, traceback);
244 end
245
246 while select(2, xpcall(server.loop, catch_uncaught_error)) ~= "quitting" do
247         socket.sleep(0.2);
248 end
249
250 log("info", "Shutdown status: Cleaning up");
251 eventmanager.fire_event("server-cleanup");
252
253 -- Ok, we're quitting I know, but we
254 -- need to do some tidying before we go :)
255 server.setquitting(false);
256
257 log("info", "Shutdown status: Closing all active sessions");
258 for hostname, host in pairs(hosts) do
259         log("debug", "Shutdown status: Closing client connections for %s", hostname)
260         if host.sessions then
261                 for username, user in pairs(host.sessions) do
262                         for resource, session in pairs(user.sessions) do
263                                 log("debug", "Closing connection for %s@%s/%s", username, hostname, resource);
264                                 session:close("system-shutdown");
265                         end
266                 end
267         end
268         
269         log("debug", "Shutdown status: Closing outgoing s2s connections from %s", hostname);
270         if host.s2sout then
271                 for remotehost, session in pairs(host.s2sout) do
272                         if session.close then
273                                 session:close("system-shutdown");
274                         else
275                                 log("warn", "Unable to close outgoing s2s session to %s, no session:close()?!", remotehost);
276                         end
277                 end
278         end
279 end
280
281 log("info", "Shutdown status: Closing all server connections");
282 server.closeall();
283
284 server.setquitting(true);
285
286 eventmanager.fire_event("server-stopped");
287 log("info", "Shutdown status: Complete!");