Software /
code /
prosody
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 |