From a355440c01d5ece456bda9c5a4f302ec82929dff Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 1 Jun 2020 15:44:44 +0100 Subject: prosodyctl, util.prosodyctl.shell: `prosodyctl shell` - a client to access the prosodyctl admin shell --- util/prosodyctl/shell.lua | 125 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 util/prosodyctl/shell.lua (limited to 'util/prosodyctl/shell.lua') diff --git a/util/prosodyctl/shell.lua b/util/prosodyctl/shell.lua new file mode 100644 index 00000000..0b84eaca --- /dev/null +++ b/util/prosodyctl/shell.lua @@ -0,0 +1,125 @@ +local have_unix, unix = pcall(require, "socket.unix"); + +if not have_unix or type(unix) ~= "table" then + print("** LuaSocket unix socket support not available or incompatible, ensure your"); + print("** version is up to date."); + os.exit(1); +end + +local server = require "net.server"; +local st = require "util.stanza"; + +local have_readline, readline = pcall(require, "readline"); + +local adminstream = require "util.adminstream"; + +if have_readline then + readline.set_readline_name("prosody"); +end + +local function read_line() + if have_readline then + return readline.readline("prosody> "); + else + io.write("prosody> "); + return io.read("*line"); + end +end + +local function send_line(client, line) + client.send(st.stanza("repl-line"):text(line)); +end + +local function repl(client) + local line = read_line(); + if not line or line == "quit" or line == "exit" or line == "bye" then + if not line then + print(""); + end + os.exit(); + end + send_line(client, line); +end + +local function connection(socket_path, listeners) + local conn, sock; + + return { + connect = function () + if sock or conn then + return nil, "already connected"; + end + sock = unix.stream(); + sock:settimeout(0); + local ok, err = sock:connect(socket_path); + if not ok then + return nil, err; + end + conn = server.wrapclient(sock, nil, nil, listeners, "*a"); + return true; + end; + disconnect = function () + if conn then + conn:close(); + conn = nil; + end + if sock then + sock:close(); + sock = nil; + end + return true; + end; + }; +end + +local function printbanner() + 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 start(arg) --luacheck: ignore 212/arg + local client = adminstream.client(); + + client.events.add_handler("connected", function () + if not arg.quiet then + printbanner(); + end + repl(client); + end); + + client.events.add_handler("disconnected", function () + print("--- session closed ---"); + os.exit(); + end); + + client.events.add_handler("received", function (stanza) + if stanza.name == "repl-result" then + local result_prefix = stanza.attr.type == "error" and "!" or "|"; + print(result_prefix.." "..stanza:get_text()); + repl(client); + end + end); + + local conn = connection("data/prosody.sock", client.listeners); + local ok, err = conn:connect(); + if not ok then + print("** Unable to connect to server - is it running? Is mod_admin_shell enabled?"); + print("** Connection error: "..err); + os.exit(1); + end + server.loop(); +end + +return { + start = start; +}; -- cgit v1.2.3 From e703759258ebed21e3a0aa5e55a163b22fd9d91b Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 1 Jun 2020 16:14:06 +0100 Subject: mod_admin_shell, mod_admin_telnet, util.prosodyctl.shell: Separate output from final result Fixes the client pausing for input after output from commands. --- util/prosodyctl/shell.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'util/prosodyctl/shell.lua') diff --git a/util/prosodyctl/shell.lua b/util/prosodyctl/shell.lua index 0b84eaca..3e70e8f1 100644 --- a/util/prosodyctl/shell.lua +++ b/util/prosodyctl/shell.lua @@ -27,7 +27,7 @@ local function read_line() end local function send_line(client, line) - client.send(st.stanza("repl-line"):text(line)); + client.send(st.stanza("repl-input"):text(line)); end local function repl(client) @@ -103,9 +103,11 @@ local function start(arg) --luacheck: ignore 212/arg end); client.events.add_handler("received", function (stanza) - if stanza.name == "repl-result" then + if stanza.name == "repl-output" or stanza.name == "repl-result" then local result_prefix = stanza.attr.type == "error" and "!" or "|"; print(result_prefix.." "..stanza:get_text()); + end + if stanza.name == "repl-result" then repl(client); end end); -- cgit v1.2.3 From aa24574b89d9a766d7789da3ecb09c600157ff95 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 1 Jun 2020 23:26:30 +0200 Subject: util.prosodyctl.shell: Join socket path with current data directory Don't hardcode socket path as it happens to be in a source checkout. Hold on, it should use the same config option as the module! --- util/prosodyctl/shell.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'util/prosodyctl/shell.lua') diff --git a/util/prosodyctl/shell.lua b/util/prosodyctl/shell.lua index 3e70e8f1..6d5ea116 100644 --- a/util/prosodyctl/shell.lua +++ b/util/prosodyctl/shell.lua @@ -8,6 +8,7 @@ end local server = require "net.server"; local st = require "util.stanza"; +local path = require "util.paths"; local have_readline, readline = pcall(require, "readline"); @@ -112,7 +113,8 @@ local function start(arg) --luacheck: ignore 212/arg end end); - local conn = connection("data/prosody.sock", client.listeners); + local socket_path = path.join(prosody.paths.data, "prosody.sock"); + local conn = connection(socket_path, client.listeners); local ok, err = conn:connect(); if not ok then print("** Unable to connect to server - is it running? Is mod_admin_shell enabled?"); -- cgit v1.2.3 From 05f5f794bf9afcf412f5f5a9b3935f5fb048ba0d Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 1 Jun 2020 23:53:03 +0200 Subject: util.prosodyctl.shell: Use same config option as module for socket path So now if you set it to a custom value, both the client and the server should use it. --- util/prosodyctl/shell.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'util/prosodyctl/shell.lua') diff --git a/util/prosodyctl/shell.lua b/util/prosodyctl/shell.lua index 6d5ea116..0e019152 100644 --- a/util/prosodyctl/shell.lua +++ b/util/prosodyctl/shell.lua @@ -6,6 +6,7 @@ if not have_unix or type(unix) ~= "table" then os.exit(1); end +local config = require "core.configmanager"; local server = require "net.server"; local st = require "util.stanza"; local path = require "util.paths"; @@ -113,7 +114,7 @@ local function start(arg) --luacheck: ignore 212/arg end end); - local socket_path = path.join(prosody.paths.data, "prosody.sock"); + local socket_path = path.resolve_relative_path(prosody.paths.data, config.get("*", "admin_socket") or "prosody.sock"); local conn = connection(socket_path, client.listeners); local ok, err = conn:connect(); if not ok then -- cgit v1.2.3 From f4436b0ad840c535dc5a8df513fb6e15401135d3 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 1 Jun 2020 23:57:50 +0200 Subject: util.prosodyctl.shell: Allow passing path to socket on command line E.g. `prosodyctl shell --socket /path/to/prosody.scok` --- util/prosodyctl/shell.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/prosodyctl/shell.lua') diff --git a/util/prosodyctl/shell.lua b/util/prosodyctl/shell.lua index 0e019152..cbcea927 100644 --- a/util/prosodyctl/shell.lua +++ b/util/prosodyctl/shell.lua @@ -114,7 +114,7 @@ local function start(arg) --luacheck: ignore 212/arg end end); - local socket_path = path.resolve_relative_path(prosody.paths.data, config.get("*", "admin_socket") or "prosody.sock"); + local socket_path = path.resolve_relative_path(prosody.paths.data, arg.socket or config.get("*", "admin_socket") or "prosody.sock"); local conn = connection(socket_path, client.listeners); local ok, err = conn:connect(); if not ok then -- cgit v1.2.3 From 46022b09886badc89425865ae1ac02651995f8c8 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 2 Jun 2020 08:01:21 +0100 Subject: prosodyctl+util.prosodyctl.*: Start breaking up the ever-growing prosodyctl --- util/prosodyctl/shell.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/prosodyctl/shell.lua') diff --git a/util/prosodyctl/shell.lua b/util/prosodyctl/shell.lua index cbcea927..1d07e6ec 100644 --- a/util/prosodyctl/shell.lua +++ b/util/prosodyctl/shell.lua @@ -126,5 +126,5 @@ local function start(arg) --luacheck: ignore 212/arg end return { - start = start; + shell = start; }; -- cgit v1.2.3 From 0fea1b599ea6d1c1f14d3566797ec148313d4ddb Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Tue, 2 Jun 2020 09:07:29 +0200 Subject: util.prosodyctl.shell: Correct check for --socket --- util/prosodyctl/shell.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/prosodyctl/shell.lua') diff --git a/util/prosodyctl/shell.lua b/util/prosodyctl/shell.lua index 1d07e6ec..1d312d3f 100644 --- a/util/prosodyctl/shell.lua +++ b/util/prosodyctl/shell.lua @@ -114,7 +114,7 @@ local function start(arg) --luacheck: ignore 212/arg end end); - local socket_path = path.resolve_relative_path(prosody.paths.data, arg.socket or config.get("*", "admin_socket") or "prosody.sock"); + local socket_path = path.resolve_relative_path(prosody.paths.data, prosody.opts.socket or config.get("*", "admin_socket") or "prosody.sock"); local conn = connection(socket_path, client.listeners); local ok, err = conn:connect(); if not ok then -- cgit v1.2.3 From 425c9cb979bfaae53b4ccf2a19ad1ec5fddac34c Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Tue, 2 Jun 2020 09:19:07 +0200 Subject: util.prosodyctl.shell: Really fix --socket option Forgot it stops parsing --foo options at the first argument, so subsequent commands need to parse their own options like this. --- util/prosodyctl/shell.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'util/prosodyctl/shell.lua') diff --git a/util/prosodyctl/shell.lua b/util/prosodyctl/shell.lua index 1d312d3f..8c8769e2 100644 --- a/util/prosodyctl/shell.lua +++ b/util/prosodyctl/shell.lua @@ -10,6 +10,7 @@ local config = require "core.configmanager"; local server = require "net.server"; local st = require "util.stanza"; local path = require "util.paths"; +local parse_args = require "util.argparse".parse; local have_readline, readline = pcall(require, "readline"); @@ -91,6 +92,7 @@ end local function start(arg) --luacheck: ignore 212/arg local client = adminstream.client(); + local opts = parse_args(arg); client.events.add_handler("connected", function () if not arg.quiet then @@ -114,7 +116,7 @@ local function start(arg) --luacheck: ignore 212/arg end end); - local socket_path = path.resolve_relative_path(prosody.paths.data, prosody.opts.socket or config.get("*", "admin_socket") or "prosody.sock"); + local socket_path = path.resolve_relative_path(prosody.paths.data, opts.socket or config.get("*", "admin_socket") or "prosody.sock"); local conn = connection(socket_path, client.listeners); local ok, err = conn:connect(); if not ok then -- cgit v1.2.3 From 78a8bfc31dbe40fb4e0d073b7eb86d7c78f6a183 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 2 Jun 2020 08:28:39 +0100 Subject: util.prosodyctl.shell, util.adminstream: Move connection logic into adminstream for easier reuse --- util/prosodyctl/shell.lua | 50 ++++++++--------------------------------------- 1 file changed, 8 insertions(+), 42 deletions(-) (limited to 'util/prosodyctl/shell.lua') diff --git a/util/prosodyctl/shell.lua b/util/prosodyctl/shell.lua index 8c8769e2..bbf0c83a 100644 --- a/util/prosodyctl/shell.lua +++ b/util/prosodyctl/shell.lua @@ -1,11 +1,3 @@ -local have_unix, unix = pcall(require, "socket.unix"); - -if not have_unix or type(unix) ~= "table" then - print("** LuaSocket unix socket support not available or incompatible, ensure your"); - print("** version is up to date."); - os.exit(1); -end - local config = require "core.configmanager"; local server = require "net.server"; local st = require "util.stanza"; @@ -44,37 +36,6 @@ local function repl(client) send_line(client, line); end -local function connection(socket_path, listeners) - local conn, sock; - - return { - connect = function () - if sock or conn then - return nil, "already connected"; - end - sock = unix.stream(); - sock:settimeout(0); - local ok, err = sock:connect(socket_path); - if not ok then - return nil, err; - end - conn = server.wrapclient(sock, nil, nil, listeners, "*a"); - return true; - end; - disconnect = function () - if conn then - conn:close(); - conn = nil; - end - if sock then - sock:close(); - sock = nil; - end - return true; - end; - }; -end - local function printbanner() print([[ ____ \ / _ @@ -117,11 +78,16 @@ local function start(arg) --luacheck: ignore 212/arg end); local socket_path = path.resolve_relative_path(prosody.paths.data, opts.socket or config.get("*", "admin_socket") or "prosody.sock"); - local conn = connection(socket_path, client.listeners); + local conn = adminstream.connection(socket_path, client.listeners); local ok, err = conn:connect(); if not ok then - print("** Unable to connect to server - is it running? Is mod_admin_shell enabled?"); - print("** Connection error: "..err); + 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(); -- cgit v1.2.3 From 6264bd48c14ae1d1ba5f22d90abf3c9842279f9c Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Tue, 2 Jun 2020 09:35:07 +0200 Subject: util.prosodyctl.shell: Save readline history --- util/prosodyctl/shell.lua | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'util/prosodyctl/shell.lua') diff --git a/util/prosodyctl/shell.lua b/util/prosodyctl/shell.lua index bbf0c83a..3e98540f 100644 --- a/util/prosodyctl/shell.lua +++ b/util/prosodyctl/shell.lua @@ -10,6 +10,10 @@ local adminstream = require "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() @@ -31,6 +35,9 @@ local function repl(client) if not line then print(""); end + if have_readline then + readline.save_history(); + end os.exit(); end send_line(client, line); -- cgit v1.2.3 From e2685cad2fbd4fbdecec1ceecab6ae3429a71070 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 17 Jun 2020 19:36:39 +0200 Subject: util.prosodyctl.shell: Handle argument parsing errors While almost identical to the handling in util.startup, this seems more appropriate. It would also simplify if shell-specific options need to be handled in the future. --- util/prosodyctl/shell.lua | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'util/prosodyctl/shell.lua') diff --git a/util/prosodyctl/shell.lua b/util/prosodyctl/shell.lua index 3e98540f..d0a6881e 100644 --- a/util/prosodyctl/shell.lua +++ b/util/prosodyctl/shell.lua @@ -62,6 +62,15 @@ local function start(arg) --luacheck: ignore 212/arg local client = adminstream.client(); local opts = parse_args(arg); + 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 + client.events.add_handler("connected", function () if not arg.quiet then printbanner(); -- cgit v1.2.3 From 3077309ce7d96550a8e536e75bdf93a7ebdda096 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 17 Jun 2020 19:39:14 +0200 Subject: util.prosodyctl.shell: Collect extra return values Forgot in previous commit --- util/prosodyctl/shell.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/prosodyctl/shell.lua') diff --git a/util/prosodyctl/shell.lua b/util/prosodyctl/shell.lua index d0a6881e..9ac982f7 100644 --- a/util/prosodyctl/shell.lua +++ b/util/prosodyctl/shell.lua @@ -60,7 +60,7 @@ end local function start(arg) --luacheck: ignore 212/arg local client = adminstream.client(); - local opts = parse_args(arg); + local opts, err, where = parse_args(arg); if not opts then if err == "param-not-found" then -- cgit v1.2.3