1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
|
local config = require "prosody.core.configmanager";
local human_io = require "prosody.util.human.io";
local server = require "prosody.net.server";
local st = require "prosody.util.stanza";
local path = require "prosody.util.paths";
local parse_args = require "prosody.util.argparse".parse;
local tc = require "prosody.util.termcolours";
local isatty = require "prosody.util.pposix".isatty;
local term_width = require"prosody.util.human.io".term_width;
local have_readline, readline = pcall(require, "readline");
local adminstream = require "prosody.util.adminstream";
if have_readline then
readline.set_readline_name("prosody");
readline.set_options({
histfile = path.join(prosody.paths.data, ".shell_history");
ignoredups = true;
});
end
local function read_line(prompt_string)
if have_readline then
return readline.readline(prompt_string);
else
io.write(prompt_string);
return io.read("*line");
end
end
local function send_line(client, line, interactive)
client.send(st.stanza("repl-input", { width = tostring(term_width()), repl = interactive == false and "0" or "1" }):text(line));
end
local function repl(client)
local line = read_line(client.prompt_string or "prosody> ");
if not line or line == "quit" or line == "exit" or line == "bye" then
if not line then
print("");
end
if have_readline then
readline.save_history();
end
os.exit(0, true);
end
send_line(client, line);
end
local function printbanner()
local banner = config.get("*", "console_banner");
if banner then return print(banner); end
print([[
____ \ / _
| _ \ _ __ ___ ___ _-_ __| |_ _
| |_) | '__/ _ \/ __|/ _ \ / _` | | | |
| __/| | | (_) \__ \ |_| | (_| | |_| |
|_| |_| \___/|___/\___/ \__,_|\__, |
A study in simplicity |___/
]]);
print("Welcome to the Prosody administration console. For a list of commands, type: help");
print("You may find more help on using this console in our online documentation at ");
print("https://prosody.im/doc/console\n");
end
local function check()
local lfs = require "lfs";
local socket_path = path.resolve_relative_path(prosody.paths.data, config.get("*", "admin_socket") or "prosody.sock");
local state = lfs.attributes(socket_path, "mode");
return state == "socket";
end
local function start(arg) --luacheck: ignore 212/arg
local client = adminstream.client();
local opts, err, where = parse_args(arg);
local ttyout = isatty(io.stdout);
if not opts then
if err == "param-not-found" then
print("Unknown command-line option: "..tostring(where));
elseif err == "missing-value" then
print("Expected a value to follow command-line option: "..where);
end
os.exit(1);
end
if arg[1] then
if arg[2] then
arg[1] = ("{"..string.rep("%q", #arg, ", ").."}"):format(table.unpack(arg, 1, #arg));
end
client.events.add_handler("connected", function()
send_line(client, arg[1], false);
return true;
end, 1);
local errors = 0; -- TODO This is weird, but works for now.
client.events.add_handler("received", function(stanza)
if stanza.name == "repl-output" or stanza.name == "repl-result" then
local dest = io.stdout;
if stanza.attr.type == "error" then
errors = errors + 1;
dest = io.stderr;
end
if stanza.attr.eol == "0" then
dest:write(stanza:get_text());
else
dest:write(stanza:get_text(), "\n");
end
end
if stanza.name == "repl-result" then
os.exit(errors);
end
return true;
end, 1);
end
client.events.add_handler("connected", function ()
if not opts.quiet then
printbanner();
end
repl(client);
end);
client.events.add_handler("disconnected", function ()
print("--- session closed ---");
os.exit(0, true);
end);
client.events.add_handler("received", function (stanza)
if stanza.name ~= "repl-request-input" then
return;
end
if stanza.attr.type == "password" then
local password = human_io.read_password();
client.send(st.stanza("repl-requested-input", {
type = stanza.attr.type;
id = stanza.attr.id;
status = password and "submit" or "cancel";
}):text(password or ""));
else
io.stderr:write("Internal error - unexpected input request type "..tostring(stanza.attr.type).."\n");
os.exit(1);
end
return true;
end, 2);
client.events.add_handler("received", function (stanza)
if stanza.name == "repl-output" or stanza.name == "repl-result" then
local result_prefix = stanza.attr.type == "error" and "!" or "|";
local out = result_prefix.." "..stanza:get_text();
if ttyout and stanza.attr.type == "error" then
out = tc.getstring(tc.getstyle("red"), out);
end
print(out);
end
if stanza.name == "repl-result" then
repl(client);
end
end);
client.prompt_string = config.get("*", "admin_shell_prompt");
local socket_path = path.resolve_relative_path(prosody.paths.data, opts.socket or config.get("*", "admin_socket") or "prosody.sock");
local conn = adminstream.connection(socket_path, client.listeners);
local ok, err = conn:connect();
if not ok then
if err == "no unix socket support" then
print("** LuaSocket unix socket support not available or incompatible, ensure your");
print("** version is up to date.");
else
print("** Unable to connect to server - is it running? Is mod_admin_shell enabled?");
print("** Connection error: "..err);
end
os.exit(1);
end
server.loop();
end
return {
shell = start;
available = check;
};
|