Changeset

10858:efa49d484560

prosodyctl, util.prosodyctl.shell: `prosodyctl shell` - a client to access the prosodyctl admin shell
author Matthew Wild <mwild1@gmail.com>
date Mon, 01 Jun 2020 15:44:44 +0100
parents 10857:826023c3322b
children 10859:8de0057b4279
files prosodyctl util/prosodyctl/shell.lua
diffstat 2 files changed, 129 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/prosodyctl	Mon Jun 01 15:43:47 2020 +0100
+++ b/prosodyctl	Mon Jun 01 15:44:44 2020 +0100
@@ -1356,6 +1356,10 @@
 	return ok and 0 or 2;
 end
 
+function commands.shell(arg)
+	require "util.prosodyctl.shell".start(arg);
+end
+
 ---------------------
 
 local async = require "util.async";
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/util/prosodyctl/shell.lua	Mon Jun 01 15:44:44 2020 +0100
@@ -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;
+};