aboutsummaryrefslogtreecommitdiffstats
path: root/util/prosodyctl/shell.lua
blob: 1d312d3fa114dcedb29230ffe30d3d74a81112c8 (plain)
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
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, 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
		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;
};