Mercurial > prosody-hg
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 |
| parents | d0d3e25b7300 |
| children | 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 }; |
