util/prosodyctl/shell.lua
author Matthew Wild <mwild1@gmail.com>
Tue, 02 Jun 2020 08:01:21 +0100
changeset 10875 e5dee71d0ebb
parent 10873 c91697b81349
child 10877 813e632431e6
permissions -rw-r--r--
prosodyctl+util.prosodyctl.*: Start breaking up the ever-growing prosodyctl

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";
local path = require "util.paths";

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-input"):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-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);

	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
		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 {
	shell = start;
};