Comparison

prosodyctl @ 8635:47e3b8b6f17a

prosody, prosodyctl, util.startup: Finally factor out startup-related and common code into a separate module
author Matthew Wild <mwild1@gmail.com>
date Tue, 20 Mar 2018 16:10:37 +0000
parent 8561:7b9ffddc4276
child 8652:03bb534593cb
comparison
equal deleted inserted replaced
8634:f6f62c92b642 8635:47e3b8b6f17a
41 if os.getenv("HOME") then 41 if os.getenv("HOME") then
42 CFG_DATADIR = CFG_DATADIR:gsub("^~", os.getenv("HOME")); 42 CFG_DATADIR = CFG_DATADIR:gsub("^~", os.getenv("HOME"));
43 end 43 end
44 end 44 end
45 45
46 -- Global 'prosody' object 46 -----------
47 local prosody = { 47
48 hosts = {}; 48 require "util.startup".prosodyctl();
49 events = require "util.events".new(); 49
50 platform = "posix"; 50 -----------
51 lock_globals = function () end;
52 unlock_globals = function () end;
53 installed = CFG_SOURCEDIR ~= nil;
54 core_post_stanza = function () end; -- TODO: mod_router!
55 };
56 _G.prosody = prosody;
57
58 local dependencies = require "util.dependencies";
59 if not dependencies.check_dependencies() then
60 os.exit(1);
61 end
62
63 config = require "core.configmanager"
64
65 local ENV_CONFIG;
66 do
67 local filenames = {};
68
69 local filename;
70 if arg[1] == "--config" and arg[2] then
71 table.insert(filenames, arg[2]);
72 if CFG_CONFIGDIR then
73 table.insert(filenames, CFG_CONFIGDIR.."/"..arg[2]);
74 end
75 table.remove(arg, 1); table.remove(arg, 1);
76 else
77 table.insert(filenames, (CFG_CONFIGDIR or ".").."/prosody.cfg.lua");
78 end
79 for _,_filename in ipairs(filenames) do
80 filename = _filename;
81 local file = io.open(filename);
82 if file then
83 file:close();
84 ENV_CONFIG = filename;
85 CFG_CONFIGDIR = filename:match("^(.*)[\\/][^\\/]*$");
86 break;
87 end
88 end
89 local ok, level, err = config.load(filename);
90 if not ok then
91 print("\n");
92 print("**************************");
93 if level == "parser" then
94 print("A problem occured while reading the config file "..filename);
95 local err_line, err_message = tostring(err):match("%[string .-%]:(%d*): (.*)");
96 print("Error"..(err_line and (" on line "..err_line) or "")..": "..(err_message or tostring(err)));
97 print("");
98 elseif level == "file" then
99 print("Prosody was unable to find the configuration file.");
100 print("We looked for: "..filename);
101 print("A sample config file is included in the Prosody download called prosody.cfg.lua.dist");
102 print("Copy or rename it to prosody.cfg.lua and edit as necessary.");
103 end
104 print("More help on configuring Prosody can be found at https://prosody.im/doc/configure");
105 print("Good luck!");
106 print("**************************");
107 print("");
108 os.exit(1);
109 end
110 end
111 local original_logging_config = config.get("*", "log");
112 config.set("*", "log", { { levels = { min="info" }, to = "console" } });
113
114 local data_path = config.get("*", "data_path") or CFG_DATADIR or "data";
115 local custom_plugin_paths = config.get("*", "plugin_paths");
116 if custom_plugin_paths then
117 local path_sep = package.config:sub(3,3);
118 -- path1;path2;path3;defaultpath...
119 CFG_PLUGINDIR = table.concat(custom_plugin_paths, path_sep)..path_sep..(CFG_PLUGINDIR or "plugins");
120 end
121 prosody.paths = { source = CFG_SOURCEDIR, config = CFG_CONFIGDIR,
122 plugins = CFG_PLUGINDIR or "plugins", data = data_path };
123
124 if prosody.installed then
125 -- Change working directory to data path.
126 require "lfs".chdir(data_path);
127 end
128
129 require "core.loggingmanager"
130
131 dependencies.log_warnings();
132
133 -- Switch away from root and into the prosody user --
134 local switched_user, current_uid;
135
136 local want_pposix_version = "0.4.0";
137 local have_pposix, pposix = pcall(require, "util.pposix");
138
139 if have_pposix and pposix then
140 if pposix._VERSION ~= want_pposix_version then
141 print(string.format("Unknown version (%s) of binary pposix module, expected %s",
142 tostring(pposix._VERSION), want_pposix_version)); return;
143 end
144 current_uid = pposix.getuid();
145 local arg_root = arg[1] == "--root";
146 if arg_root then table.remove(arg, 1); end
147 if current_uid == 0 and config.get("*", "run_as_root") ~= true and not arg_root then
148 -- We haz root!
149 local desired_user = config.get("*", "prosody_user") or "prosody";
150 local desired_group = config.get("*", "prosody_group") or desired_user;
151 local ok, err = pposix.setgid(desired_group);
152 if ok then
153 ok, err = pposix.initgroups(desired_user);
154 end
155 if ok then
156 ok, err = pposix.setuid(desired_user);
157 if ok then
158 -- Yay!
159 switched_user = true;
160 end
161 end
162 if not switched_user then
163 -- Boo!
164 print("Warning: Couldn't switch to Prosody user/group '"..tostring(desired_user).."'/'"..tostring(desired_group).."': "..tostring(err));
165 else
166 -- Make sure the Prosody user can read the config
167 local conf, err, errno = io.open(ENV_CONFIG);
168 if conf then
169 conf:close();
170 else
171 print("The config file is not readable by the '"..desired_user.."' user.");
172 print("Prosody will not be able to read it.");
173 print("Error was "..err);
174 os.exit(1);
175 end
176 end
177 end
178
179 -- Set our umask to protect data files
180 pposix.umask(config.get("*", "umask") or "027");
181 pposix.setenv("HOME", data_path);
182 pposix.setenv("PROSODY_CONFIG", ENV_CONFIG);
183 else
184 print("Error: Unable to load pposix module. Check that Prosody is installed correctly.")
185 print("For more help send the below error to us through https://prosody.im/discuss");
186 print(tostring(pposix))
187 os.exit(1);
188 end
189
190 local function test_writeable(filename)
191 local f, err = io.open(filename, "a");
192 if not f then
193 return false, err;
194 end
195 f:close();
196 return true;
197 end
198
199 local unwriteable_files = {};
200 if type(original_logging_config) == "string" and original_logging_config:sub(1,1) ~= "*" then
201 local ok, err = test_writeable(original_logging_config);
202 if not ok then
203 table.insert(unwriteable_files, err);
204 end
205 elseif type(original_logging_config) == "table" then
206 for _, rule in ipairs(original_logging_config) do
207 if rule.filename then
208 local ok, err = test_writeable(rule.filename);
209 if not ok then
210 table.insert(unwriteable_files, err);
211 end
212 end
213 end
214 end
215
216 if #unwriteable_files > 0 then
217 print("One of more of the Prosody log files are not");
218 print("writeable, please correct the errors and try");
219 print("starting prosodyctl again.");
220 print("");
221 for _, err in ipairs(unwriteable_files) do
222 print(err);
223 end
224 print("");
225 os.exit(1);
226 end
227
228 51
229 local error_messages = setmetatable({ 52 local error_messages = setmetatable({
230 ["invalid-username"] = "The given username is invalid in a Jabber ID"; 53 ["invalid-username"] = "The given username is invalid in a Jabber ID";
231 ["invalid-hostname"] = "The given hostname is invalid"; 54 ["invalid-hostname"] = "The given hostname is invalid";
232 ["no-password"] = "No password was supplied"; 55 ["no-password"] = "No password was supplied";
238 ["no-posix"] = "The mod_posix module is not enabled in the Prosody config file, see https://prosody.im/doc/prosodyctl for more info"; 61 ["no-posix"] = "The mod_posix module is not enabled in the Prosody config file, see https://prosody.im/doc/prosodyctl for more info";
239 ["no-such-method"] = "This module has no commands"; 62 ["no-such-method"] = "This module has no commands";
240 ["not-running"] = "Prosody is not running"; 63 ["not-running"] = "Prosody is not running";
241 }, { __index = function (t,k) return "Error: "..(tostring(k):gsub("%-", " "):gsub("^.", string.upper)); end }); 64 }, { __index = function (t,k) return "Error: "..(tostring(k):gsub("%-", " "):gsub("^.", string.upper)); end });
242 65
243 hosts = prosody.hosts; 66 local config = require "core.configmanager";
244
245 local function make_host(hostname)
246 return {
247 type = "local",
248 events = prosody.events,
249 modules = {},
250 sessions = {},
251 users = require "core.usermanager".new_null_provider(hostname)
252 };
253 end
254
255 for hostname, config in pairs(config.getconfig()) do
256 hosts[hostname] = make_host(hostname);
257 end
258
259 local modulemanager = require "core.modulemanager" 67 local modulemanager = require "core.modulemanager"
260
261 local prosodyctl = require "util.prosodyctl" 68 local prosodyctl = require "util.prosodyctl"
262 local socket = require "socket" 69 local socket = require "socket"
263 70 local dependencies = require "util.dependencies";
264 local http = require "net.http"
265 local config_ssl = config.get("*", "ssl") or {}
266 local https_client = config.get("*", "client_https_ssl")
267 http.default.options.sslctx = require "core.certmanager".create_context("client_https port 0", "client",
268 { capath = config_ssl.capath, cafile = config_ssl.cafile, verify = "peer", }, https_client);
269 71
270 ----------------------- 72 -----------------------
271
272 -- FIXME: Duplicate code waiting for util.startup
273 function read_version()
274 -- Try to determine version
275 local version_file = io.open((CFG_SOURCEDIR or ".").."/prosody.version");
276 prosody.version = "unknown";
277 if version_file then
278 prosody.version = version_file:read("*a"):gsub("%s*$", "");
279 version_file:close();
280 if #prosody.version == 12 and prosody.version:match("^[a-f0-9]+$") then
281 prosody.version = "hg:"..prosody.version;
282 end
283 else
284 local hg = require"util.mercurial";
285 local hgid = hg.check_id(CFG_SOURCEDIR or ".");
286 if hgid then prosody.version = "hg:" .. hgid; end
287 end
288 end
289 73
290 local show_message, show_warning = prosodyctl.show_message, prosodyctl.show_warning; 74 local show_message, show_warning = prosodyctl.show_message, prosodyctl.show_warning;
291 local show_usage = prosodyctl.show_usage; 75 local show_usage = prosodyctl.show_usage;
292 local show_yesno = prosodyctl.show_yesno; 76 local show_yesno = prosodyctl.show_yesno;
293 local show_prompt = prosodyctl.show_prompt; 77 local show_prompt = prosodyctl.show_prompt;
544 commands.stop(arg); 328 commands.stop(arg);
545 return commands.start(arg); 329 return commands.start(arg);
546 end 330 end
547 331
548 function commands.about(arg) 332 function commands.about(arg)
549 read_version();
550 if arg[1] == "--help" then 333 if arg[1] == "--help" then
551 show_usage([[about]], [[Show information about this Prosody installation]]); 334 show_usage([[about]], [[Show information about this Prosody installation]]);
552 return 1; 335 return 1;
553 end 336 end
554 337
560 local relpath = config.resolve_relative_path; 343 local relpath = config.resolve_relative_path;
561 344
562 print("Prosody "..(prosody.version or "(unknown version)")); 345 print("Prosody "..(prosody.version or "(unknown version)"));
563 print(""); 346 print("");
564 print("# Prosody directories"); 347 print("# Prosody directories");
565 print("Data directory: "..relpath(pwd, data_path)); 348 print("Data directory: "..relpath(pwd, prosody.paths.data));
566 print("Config directory: "..relpath(pwd, CFG_CONFIGDIR or ".")); 349 print("Config directory: "..relpath(pwd, prosody.paths.config or "."));
567 print("Source directory: "..relpath(pwd, CFG_SOURCEDIR or ".")); 350 print("Source directory: "..relpath(pwd, prosody.paths.source or "."));
568 print("Plugin directories:") 351 print("Plugin directories:")
569 print(" "..(prosody.paths.plugins:gsub("([^;]+);?", function(path) 352 print(" "..(prosody.paths.plugins:gsub("([^;]+);?", function(path)
570 path = config.resolve_relative_path(pwd, path); 353 path = config.resolve_relative_path(pwd, path);
571 local hgid, hgrepo = hg.check_id(path); 354 local hgid, hgrepo = hg.check_id(path);
572 if not hgid and hgrepo then 355 if not hgid and hgrepo then
714 return true; 497 return true;
715 end 498 end
716 end 499 end
717 end 500 end
718 501
719 local cert_basedir = CFG_DATADIR or "./certs"; 502 local have_pposix, pposix = pcall(require, "util.pposix");
503 local cert_basedir = prosody.paths.data == "." and "./certs" or prosody.paths.data;
720 if have_pposix and pposix.getuid() == 0 then 504 if have_pposix and pposix.getuid() == 0 then
721 -- FIXME should be enough to check if this directory is writable 505 -- FIXME should be enough to check if this directory is writable
722 local cert_dir = config.get("*", "certificates") or "certs"; 506 local cert_dir = config.get("*", "certificates") or "certs";
723 cert_basedir = config.resolve_relative_path(prosody.paths.config, cert_dir); 507 cert_basedir = config.resolve_relative_path(prosody.paths.config, cert_dir);
724 end 508 end