Comparison

util/prosodyctl.lua @ 11200:bf8f2da84007

Merge 0.11->trunk
author Kim Alvefur <zash@zash.se>
date Thu, 05 Nov 2020 22:31:25 +0100
parent 11136:d0d3e25b7300
child 11290:7919ecdc4a72
comparison
equal deleted inserted replaced
11199:6c7c50a4de32 11200:bf8f2da84007
10 local config = require "core.configmanager"; 10 local config = require "core.configmanager";
11 local encodings = require "util.encodings"; 11 local encodings = require "util.encodings";
12 local stringprep = encodings.stringprep; 12 local stringprep = encodings.stringprep;
13 local storagemanager = require "core.storagemanager"; 13 local storagemanager = require "core.storagemanager";
14 local usermanager = require "core.usermanager"; 14 local usermanager = require "core.usermanager";
15 local interpolation = require "util.interpolation";
15 local signal = require "util.signal"; 16 local signal = require "util.signal";
16 local set = require "util.set"; 17 local set = require "util.set";
17 local lfs = require "lfs"; 18 local lfs = require "lfs";
18 local pcall = pcall;
19 local type = type; 19 local type = type;
20 20
21 local nodeprep, nameprep = stringprep.nodeprep, stringprep.nameprep; 21 local nodeprep, nameprep = stringprep.nodeprep, stringprep.nameprep;
22 22
23 local io, os = io, os; 23 local io, os = io, os;
25 local tonumber = tonumber; 25 local tonumber = tonumber;
26 26
27 local _G = _G; 27 local _G = _G;
28 local prosody = prosody; 28 local prosody = prosody;
29 29
30 local error_messages = setmetatable({
31 ["invalid-username"] = "The given username is invalid in a Jabber ID";
32 ["invalid-hostname"] = "The given hostname is invalid";
33 ["no-password"] = "No password was supplied";
34 ["no-such-user"] = "The given user does not exist on the server";
35 ["no-such-host"] = "The given hostname does not exist in the config";
36 ["unable-to-save-data"] = "Unable to store, perhaps you don't have permission?";
37 ["no-pidfile"] = "There is no 'pidfile' option in the configuration file, see https://prosody.im/doc/prosodyctl#pidfile for help";
38 ["invalid-pidfile"] = "The 'pidfile' option in the configuration file is not a string, see https://prosody.im/doc/prosodyctl#pidfile for help";
39 ["no-posix"] = "The mod_posix module is not enabled in the Prosody config file, see https://prosody.im/doc/prosodyctl for more info";
40 ["no-such-method"] = "This module has no commands";
41 ["not-running"] = "Prosody is not running";
42 }, { __index = function (_,k) return "Error: "..(tostring(k):gsub("%-", " "):gsub("^.", string.upper)); end });
43
30 -- UI helpers 44 -- UI helpers
31 local function show_message(msg, ...) 45 local show_message = require "util.human.io".printf;
32 print(msg:format(...));
33 end
34 46
35 local function show_usage(usage, desc) 47 local function show_usage(usage, desc)
36 print("Usage: ".._G.arg[0].." "..usage); 48 print("Usage: ".._G.arg[0].." "..usage);
37 if desc then 49 if desc then
38 print(" "..desc); 50 print(" "..desc);
39 end 51 end
40 end 52 end
41 53
42 local function getchar(n) 54 local function show_module_configuration_help(mod_name)
43 local stty_ret = os.execute("stty raw -echo 2>/dev/null"); 55 print("Done.")
44 local ok, char; 56 print("If you installed a prosody plugin, don't forget to add its name under the 'modules_enabled' section inside your configuration file.")
45 if stty_ret == true or stty_ret == 0 then 57 print("Depending on the module, there might be further configuration steps required.")
46 ok, char = pcall(io.read, n or 1); 58 print("")
47 os.execute("stty sane"); 59 print("More info about: ")
48 else 60 print(" modules_enabled: https://prosody.im/doc/modules_enabled")
49 ok, char = pcall(io.read, "*l"); 61 print(" "..mod_name..": https://modules.prosody.im/"..mod_name..".html")
50 if ok then
51 char = char:sub(1, n or 1);
52 end
53 end
54 if ok then
55 return char;
56 end
57 end
58
59 local function getline()
60 local ok, line = pcall(io.read, "*l");
61 if ok then
62 return line;
63 end
64 end
65
66 local function getpass()
67 local stty_ret, _, status_code = os.execute("stty -echo 2>/dev/null");
68 if status_code then -- COMPAT w/ Lua 5.1
69 stty_ret = status_code;
70 end
71 if stty_ret ~= 0 then
72 io.write("\027[08m"); -- ANSI 'hidden' text attribute
73 end
74 local ok, pass = pcall(io.read, "*l");
75 if stty_ret == 0 then
76 os.execute("stty sane");
77 else
78 io.write("\027[00m");
79 end
80 io.write("\n");
81 if ok then
82 return pass;
83 end
84 end
85
86 local function show_yesno(prompt)
87 io.write(prompt, " ");
88 local choice = getchar():lower();
89 io.write("\n");
90 if not choice:match("%a") then
91 choice = prompt:match("%[.-(%U).-%]$");
92 if not choice then return nil; end
93 end
94 return (choice == "y");
95 end
96
97 local function read_password()
98 local password;
99 while true do
100 io.write("Enter new password: ");
101 password = getpass();
102 if not password then
103 show_message("No password - cancelled");
104 return;
105 end
106 io.write("Retype new password: ");
107 if getpass() ~= password then
108 if not show_yesno [=[Passwords did not match, try again? [Y/n]]=] then
109 return;
110 end
111 else
112 break;
113 end
114 end
115 return password;
116 end
117
118 local function show_prompt(prompt)
119 io.write(prompt, " ");
120 local line = getline();
121 line = line and line:gsub("\n$","");
122 return (line and #line > 0) and line or nil;
123 end 62 end
124 63
125 -- Server control 64 -- Server control
126 local function adduser(params) 65 local function adduser(params)
127 local user, host, password = nodeprep(params.user), nameprep(params.host), params.password; 66 local user, host, password = nodeprep(params.user, true), nameprep(params.host), params.password;
128 if not user then 67 if not user then
129 return false, "invalid-username"; 68 return false, "invalid-username";
130 elseif not host then 69 elseif not host then
131 return false, "invalid-hostname"; 70 return false, "invalid-hostname";
132 end 71 end
198 local file, err = io.open(pidfile, "r+"); 137 local file, err = io.open(pidfile, "r+");
199 if not file then 138 if not file then
200 return false, "pidfile-read-failed", err; 139 return false, "pidfile-read-failed", err;
201 end 140 end
202 141
203 local locked, err = lfs.lock(file, "w"); 142 local locked, err = lfs.lock(file, "w"); -- luacheck: ignore 211/err
204 if locked then 143 if locked then
205 file:close(); 144 file:close();
206 return false, "pidfile-not-locked"; 145 return false, "pidfile-not-locked";
207 end 146 end
208 147
215 154
216 return true, pid; 155 return true, pid;
217 end 156 end
218 157
219 local function isrunning() 158 local function isrunning()
220 local ok, pid, err = getpid(); 159 local ok, pid, err = getpid(); -- luacheck: ignore 211/err
221 if not ok then 160 if not ok then
222 if pid == "pidfile-read-failed" or pid == "pidfile-not-locked" then 161 if pid == "pidfile-read-failed" or pid == "pidfile-not-locked" then
223 -- Report as not running, since we can't open the pidfile 162 -- Report as not running, since we can't open the pidfile
224 -- (it probably doesn't exist) 163 -- (it probably doesn't exist)
225 return true, false; 164 return true, false;
227 return ok, pid; 166 return ok, pid;
228 end 167 end
229 return true, signal.kill(pid, 0) == 0; 168 return true, signal.kill(pid, 0) == 0;
230 end 169 end
231 170
232 local function start(source_dir) 171 local function start(source_dir, lua)
172 lua = lua and lua .. " " or "";
233 local ok, ret = isrunning(); 173 local ok, ret = isrunning();
234 if not ok then 174 if not ok then
235 return ok, ret; 175 return ok, ret;
236 end 176 end
237 if ret then 177 if ret then
238 return false, "already-running"; 178 return false, "already-running";
239 end 179 end
240 if not source_dir then 180 if not source_dir then
241 os.execute("./prosody -D"); 181 os.execute(lua .. "./prosody -D");
242 else 182 else
243 os.execute(source_dir.."/../../bin/prosody -D"); 183 os.execute(lua .. source_dir.."/../../bin/prosody -D");
244 end 184 end
245 return true; 185 return true;
246 end 186 end
247 187
248 local function stop() 188 local function stop()
273 local ok, pid = getpid() 213 local ok, pid = getpid()
274 if not ok then return false, pid; end 214 if not ok then return false, pid; end
275 215
276 signal.kill(pid, signal.SIGHUP); 216 signal.kill(pid, signal.SIGHUP);
277 return true; 217 return true;
218 end
219
220 local function get_path_custom_plugins(plugin_paths)
221 -- I'm considering that the custom plugins' path is the first one at prosody.paths.plugins
222 -- luacheck: ignore 512
223 for path in plugin_paths:gmatch("[^;]+") do
224 return path;
225 end
226 end
227
228 local render_cli = interpolation.new("%b{}", function (s) return "'"..s:gsub("'","'\\''").."'" end)
229
230 local function call_luarocks(operation, mod, server)
231 local dir = get_path_custom_plugins(prosody.paths.plugins);
232 if operation == "install" then
233 show_message("Installing %s at %s", mod, dir);
234 elseif operation == "remove" then
235 show_message("Removing %s from %s", mod, dir);
236 end
237 os.execute(render_cli("luarocks {op} --tree={dir} {server&--server={server}} {mod?}", {
238 dir = dir; op = operation; mod = mod; server = server;
239 }));
240 if operation == "install" then
241 show_module_configuration_help(mod);
242 end
278 end 243 end
279 244
280 return { 245 return {
281 show_message = show_message; 246 show_message = show_message;
282 show_warning = show_message; 247 show_warning = show_message;
283 show_usage = show_usage; 248 show_usage = show_usage;
284 getchar = getchar; 249 show_module_configuration_help = show_module_configuration_help;
285 getline = getline;
286 getpass = getpass;
287 show_yesno = show_yesno;
288 read_password = read_password;
289 show_prompt = show_prompt;
290 adduser = adduser; 250 adduser = adduser;
291 user_exists = user_exists; 251 user_exists = user_exists;
292 passwd = passwd; 252 passwd = passwd;
293 deluser = deluser; 253 deluser = deluser;
294 getpid = getpid; 254 getpid = getpid;
295 isrunning = isrunning; 255 isrunning = isrunning;
296 start = start; 256 start = start;
297 stop = stop; 257 stop = stop;
298 reload = reload; 258 reload = reload;
259 get_path_custom_plugins = get_path_custom_plugins;
260 call_luarocks = call_luarocks;
261 error_messages = error_messages;
299 }; 262 };