comparison plugins/mod_admin_telnet.lua @ 10563:e8db377a2983

Merge 0.11->trunk
author Kim Alvefur <zash@zash.se>
date Tue, 24 Dec 2019 00:39:45 +0100
parents 0bd408d93f9a
children ef620906ab82
comparison
equal deleted inserted replaced
10562:670afc079f68 10563:e8db377a2983
20 20
21 local prosody = _G.prosody; 21 local prosody = _G.prosody;
22 22
23 local console_listener = { default_port = 5582; default_mode = "*a"; interface = "127.0.0.1" }; 23 local console_listener = { default_port = 5582; default_mode = "*a"; interface = "127.0.0.1" };
24 24
25 local unpack = table.unpack or unpack; -- luacheck: ignore 113
25 local iterators = require "util.iterators"; 26 local iterators = require "util.iterators";
26 local keys, values = iterators.keys, iterators.values; 27 local keys, values = iterators.keys, iterators.values;
27 local jid_bare, jid_split, jid_join = import("util.jid", "bare", "prepped_split", "join"); 28 local jid_bare, jid_split, jid_join = import("util.jid", "bare", "prepped_split", "join");
28 local set, array = require "util.set", require "util.array"; 29 local set, array = require "util.set", require "util.array";
29 local cert_verify_identity = require "util.x509".verify_identity; 30 local cert_verify_identity = require "util.x509".verify_identity;
30 local envload = require "util.envload".envload; 31 local envload = require "util.envload".envload;
31 local envloadfile = require "util.envload".envloadfile; 32 local envloadfile = require "util.envload".envloadfile;
32 local has_pposix, pposix = pcall(require, "util.pposix"); 33 local has_pposix, pposix = pcall(require, "util.pposix");
34 local async = require "util.async";
35 local serialize = require "util.serialization".new({ fatal = false, unquoted = true});
36 local time = require "util.time";
33 37
34 local commands = module:shared("commands") 38 local commands = module:shared("commands")
35 local def_env = module:shared("env"); 39 local def_env = module:shared("env");
36 local default_env_mt = { __index = def_env }; 40 local default_env_mt = { __index = def_env };
37 41
44 end; 48 end;
45 return env; 49 return env;
46 end 50 end
47 51
48 console = {}; 52 console = {};
53
54 local runner_callbacks = {};
55
56 function runner_callbacks:ready()
57 self.data.conn:resume();
58 end
59
60 function runner_callbacks:waiting()
61 self.data.conn:pause();
62 end
63
64 function runner_callbacks:error(err)
65 module:log("error", "Traceback[telnet]: %s", err);
66
67 self.data.print("Fatal error while running command, it did not complete");
68 self.data.print("Error: "..tostring(err));
69 end
70
49 71
50 function console:new_session(conn) 72 function console:new_session(conn)
51 local w = function(s) conn:write(s:gsub("\n", "\r\n")); end; 73 local w = function(s) conn:write(s:gsub("\n", "\r\n")); end;
52 local session = { conn = conn; 74 local session = { conn = conn;
53 send = function (t) w(tostring(t)); end; 75 send = function (t) w(tostring(t)); end;
60 end; 82 end;
61 disconnect = function () conn:close(); end; 83 disconnect = function () conn:close(); end;
62 }; 84 };
63 session.env = setmetatable({}, default_env_mt); 85 session.env = setmetatable({}, default_env_mt);
64 86
87 session.thread = async.runner(function (line)
88 console:process_line(session, line);
89 session.send(string.char(0));
90 end, runner_callbacks, session);
91
65 -- Load up environment with helper objects 92 -- Load up environment with helper objects
66 for name, t in pairs(def_env) do 93 for name, t in pairs(def_env) do
67 if type(t) == "table" then 94 if type(t) == "table" then
68 session.env[name] = setmetatable({ session = session }, { __index = t }); 95 session.env[name] = setmetatable({ session = session }, { __index = t });
69 end 96 end
89 end 116 end
90 end 117 end
91 118
92 session.env._ = line; 119 session.env._ = line;
93 120
121 if not useglobalenv and commands[line:lower()] then
122 commands[line:lower()](session, line);
123 return;
124 end
125
94 local chunkname = "=console"; 126 local chunkname = "=console";
95 local env = (useglobalenv and redirect_output(_G, session)) or session.env or nil 127 local env = (useglobalenv and redirect_output(_G, session)) or session.env or nil
128 -- luacheck: ignore 311/err
96 local chunk, err = envload("return "..line, chunkname, env); 129 local chunk, err = envload("return "..line, chunkname, env);
97 if not chunk then 130 if not chunk then
98 chunk, err = envload(line, chunkname, env); 131 chunk, err = envload(line, chunkname, env);
99 if not chunk then 132 if not chunk then
100 err = err:gsub("^%[string .-%]:%d+: ", ""); 133 err = err:gsub("^%[string .-%]:%d+: ", "");
103 session.print("Sorry, I couldn't understand that... "..err); 136 session.print("Sorry, I couldn't understand that... "..err);
104 return; 137 return;
105 end 138 end
106 end 139 end
107 140
108 local ranok, taskok, message = pcall(chunk); 141 local taskok, message = chunk();
109
110 if not (ranok or message or useglobalenv) and commands[line:lower()] then
111 commands[line:lower()](session, line);
112 return;
113 end
114
115 if not ranok then
116 session.print("Fatal error while running command, it did not complete");
117 session.print("Error: "..taskok);
118 return;
119 end
120 142
121 if not message then 143 if not message then
122 session.print("Result: "..tostring(taskok)); 144 session.print("Result: "..tostring(taskok));
123 return; 145 return;
124 elseif (not taskok) and message then 146 elseif (not taskok) and message then
148 data = partial..data; 170 data = partial..data;
149 end 171 end
150 172
151 for line in data:gmatch("[^\n]*[\n\004]") do 173 for line in data:gmatch("[^\n]*[\n\004]") do
152 if session.closed then return end 174 if session.closed then return end
153 console:process_line(session, line); 175 session.thread:run(line);
154 session.send(string.char(0));
155 end 176 end
156 session.partial_data = data:match("[^\n]+$"); 177 session.partial_data = data:match("[^\n]+$");
157 end 178 end
158 179
159 function console_listener.onreadtimeout(conn) 180 function console_listener.onreadtimeout(conn)
218 print [[host - Commands to activate, deactivate and list virtual hosts]] 239 print [[host - Commands to activate, deactivate and list virtual hosts]]
219 print [[user - Commands to create and delete users, and change their passwords]] 240 print [[user - Commands to create and delete users, and change their passwords]]
220 print [[server - Uptime, version, shutting down, etc.]] 241 print [[server - Uptime, version, shutting down, etc.]]
221 print [[port - Commands to manage ports the server is listening on]] 242 print [[port - Commands to manage ports the server is listening on]]
222 print [[dns - Commands to manage and inspect the internal DNS resolver]] 243 print [[dns - Commands to manage and inspect the internal DNS resolver]]
244 print [[xmpp - Commands for sending XMPP stanzas]]
223 print [[config - Reloading the configuration, etc.]] 245 print [[config - Reloading the configuration, etc.]]
224 print [[console - Help regarding the console itself]] 246 print [[console - Help regarding the console itself]]
225 elseif section == "c2s" then 247 elseif section == "c2s" then
226 print [[c2s:show(jid) - Show all client sessions with the specified JID (or all if no JID given)]] 248 print [[c2s:show(jid) - Show all client sessions with the specified JID (or all if no JID given)]]
227 print [[c2s:show_insecure() - Show all unencrypted client connections]] 249 print [[c2s:show_insecure() - Show all unencrypted client connections]]
228 print [[c2s:show_secure() - Show all encrypted client connections]] 250 print [[c2s:show_secure() - Show all encrypted client connections]]
229 print [[c2s:show_tls() - Show TLS cipher info for encrypted sessions]] 251 print [[c2s:show_tls() - Show TLS cipher info for encrypted sessions]]
252 print [[c2s:count() - Count sessions without listing them]]
230 print [[c2s:close(jid) - Close all sessions for the specified JID]] 253 print [[c2s:close(jid) - Close all sessions for the specified JID]]
254 print [[c2s:closeall() - Close all active c2s connections ]]
231 elseif section == "s2s" then 255 elseif section == "s2s" then
232 print [[s2s:show(domain) - Show all s2s connections for the given domain (or all if no domain given)]] 256 print [[s2s:show(domain) - Show all s2s connections for the given domain (or all if no domain given)]]
233 print [[s2s:show_tls(domain) - Show TLS cipher info for encrypted sessions]] 257 print [[s2s:show_tls(domain) - Show TLS cipher info for encrypted sessions]]
234 print [[s2s:close(from, to) - Close a connection from one domain to another]] 258 print [[s2s:close(from, to) - Close a connection from one domain to another]]
235 print [[s2s:closeall(host) - Close all the incoming/outgoing s2s sessions to specified host]] 259 print [[s2s:closeall(host) - Close all the incoming/outgoing s2s sessions to specified host]]
259 print [[dns:lookup(name, type, class) - Do a DNS lookup]] 283 print [[dns:lookup(name, type, class) - Do a DNS lookup]]
260 print [[dns:addnameserver(nameserver) - Add a nameserver to the list]] 284 print [[dns:addnameserver(nameserver) - Add a nameserver to the list]]
261 print [[dns:setnameserver(nameserver) - Replace the list of name servers with the supplied one]] 285 print [[dns:setnameserver(nameserver) - Replace the list of name servers with the supplied one]]
262 print [[dns:purge() - Clear the DNS cache]] 286 print [[dns:purge() - Clear the DNS cache]]
263 print [[dns:cache() - Show cached records]] 287 print [[dns:cache() - Show cached records]]
288 elseif section == "xmpp" then
289 print [[xmpp:ping(localhost, remotehost) -- Sends a ping to a remote XMPP server and reports the response]]
264 elseif section == "config" then 290 elseif section == "config" then
265 print [[config:reload() - Reload the server configuration. Modules may need to be reloaded for changes to take effect.]] 291 print [[config:reload() - Reload the server configuration. Modules may need to be reloaded for changes to take effect.]]
292 print [[config:get([host,] option) - Show the value of a config option.]]
266 elseif section == "console" then 293 elseif section == "console" then
267 print [[Hey! Welcome to Prosody's admin console.]] 294 print [[Hey! Welcome to Prosody's admin console.]]
268 print [[First thing, if you're ever wondering how to get out, simply type 'quit'.]] 295 print [[First thing, if you're ever wondering how to get out, simply type 'quit'.]]
269 print [[Secondly, note that we don't support the full telnet protocol yet (it's coming)]] 296 print [[Secondly, note that we don't support the full telnet protocol yet (it's coming)]]
270 print [[so you may have trouble using the arrow keys, etc. depending on your system.]] 297 print [[so you may have trouble using the arrow keys, etc. depending on your system.]]
337 return true, "OK"; 364 return true, "OK";
338 end 365 end
339 366
340 def_env.module = {}; 367 def_env.module = {};
341 368
342 local function get_hosts_set(hosts, module) 369 local function get_hosts_set(hosts)
343 if type(hosts) == "table" then 370 if type(hosts) == "table" then
344 if hosts[1] then 371 if hosts[1] then
345 return set.new(hosts); 372 return set.new(hosts);
346 elseif hosts._items then 373 elseif hosts._items then
347 return hosts; 374 return hosts;
348 end 375 end
349 elseif type(hosts) == "string" then 376 elseif type(hosts) == "string" then
350 return set.new { hosts }; 377 return set.new { hosts };
351 elseif hosts == nil then 378 elseif hosts == nil then
352 local hosts_set = set.new(array.collect(keys(prosody.hosts))) 379 return set.new(array.collect(keys(prosody.hosts)));
353 / function (host) return (prosody.hosts[host].type == "local" or module and modulemanager.is_loaded(host, module)) and host or nil; end; 380 end
354 if module and modulemanager.get_module("*", module) then 381 end
355 hosts_set:add("*"); 382
356 end 383 -- Hosts with a module or all virtualhosts if no module given
357 return hosts_set; 384 -- matching modules_enabled in the global section
358 end 385 local function get_hosts_with_module(hosts, module)
386 local hosts_set = get_hosts_set(hosts)
387 / function (host) return (prosody.hosts[host].type == "local" or module and modulemanager.is_loaded(host, module)) and host or nil; end;
388 if module and modulemanager.get_module("*", module) then
389 hosts_set:add("*");
390 end
391 return hosts_set;
359 end 392 end
360 393
361 function def_env.module:load(name, hosts, config) 394 function def_env.module:load(name, hosts, config)
362 hosts = get_hosts_set(hosts); 395 hosts = get_hosts_with_module(hosts);
363 396
364 -- Load the module for each host 397 -- Load the module for each host
365 local ok, err, count, mod = true, nil, 0; 398 local ok, err, count, mod = true, nil, 0;
366 for host in hosts do 399 for host in hosts do
367 if (not modulemanager.is_loaded(host, name)) then 400 if (not modulemanager.is_loaded(host, name)) then
384 417
385 return ok, (ok and "Module loaded onto "..count.." host"..(count ~= 1 and "s" or "")) or ("Last error: "..tostring(err)); 418 return ok, (ok and "Module loaded onto "..count.." host"..(count ~= 1 and "s" or "")) or ("Last error: "..tostring(err));
386 end 419 end
387 420
388 function def_env.module:unload(name, hosts) 421 function def_env.module:unload(name, hosts)
389 hosts = get_hosts_set(hosts, name); 422 hosts = get_hosts_with_module(hosts, name);
390 423
391 -- Unload the module for each host 424 -- Unload the module for each host
392 local ok, err, count = true, nil, 0; 425 local ok, err, count = true, nil, 0;
393 for host in hosts do 426 for host in hosts do
394 if modulemanager.is_loaded(host, name) then 427 if modulemanager.is_loaded(host, name) then
406 end 439 end
407 440
408 local function _sort_hosts(a, b) 441 local function _sort_hosts(a, b)
409 if a == "*" then return true 442 if a == "*" then return true
410 elseif b == "*" then return false 443 elseif b == "*" then return false
411 else return a < b; end 444 else return a:gsub("[^.]+", string.reverse):reverse() < b:gsub("[^.]+", string.reverse):reverse(); end
412 end 445 end
413 446
414 function def_env.module:reload(name, hosts) 447 function def_env.module:reload(name, hosts)
415 hosts = array.collect(get_hosts_set(hosts, name)):sort(_sort_hosts) 448 hosts = array.collect(get_hosts_with_module(hosts, name)):sort(_sort_hosts)
416 449
417 -- Reload the module for each host 450 -- Reload the module for each host
418 local ok, err, count = true, nil, 0; 451 local ok, err, count = true, nil, 0;
419 for _, host in ipairs(hosts) do 452 for _, host in ipairs(hosts) do
420 if modulemanager.is_loaded(host, name) then 453 if modulemanager.is_loaded(host, name) then
433 end 466 end
434 return ok, (ok and "Module reloaded on "..count.." host"..(count ~= 1 and "s" or "")) or ("Last error: "..tostring(err)); 467 return ok, (ok and "Module reloaded on "..count.." host"..(count ~= 1 and "s" or "")) or ("Last error: "..tostring(err));
435 end 468 end
436 469
437 function def_env.module:list(hosts) 470 function def_env.module:list(hosts)
438 if hosts == nil then 471 hosts = array.collect(set.new({ not hosts and "*" or nil }) + get_hosts_set(hosts)):sort(_sort_hosts);
439 hosts = array.collect(keys(prosody.hosts));
440 table.insert(hosts, 1, "*");
441 end
442 if type(hosts) == "string" then
443 hosts = { hosts };
444 end
445 if type(hosts) ~= "table" then
446 return false, "Please supply a host or a list of hosts you would like to see";
447 end
448 472
449 local print = self.session.print; 473 local print = self.session.print;
450 for _, host in ipairs(hosts) do 474 for _, host in ipairs(hosts) do
451 print((host == "*" and "Global" or host)..":"); 475 print((host == "*" and "Global" or host)..":");
452 local modules = array.collect(keys(modulemanager.get_modules(host) or {})):sort(); 476 local modules = array.collect(keys(modulemanager.get_modules(host) or {})):sort();
456 else 480 else
457 print(" Host not found"); 481 print(" Host not found");
458 end 482 end
459 else 483 else
460 for _, name in ipairs(modules) do 484 for _, name in ipairs(modules) do
461 print(" "..name); 485 local status, status_text = modulemanager.get_module(host, name).module:get_status();
486 local status_summary = "";
487 if status == "warn" or status == "error" then
488 status_summary = (" (%s: %s)"):format(status, status_text);
489 end
490 print((" %s%s"):format(name, status_summary));
462 end 491 end
463 end 492 end
464 end 493 end
465 end 494 end
466 495
472 return false, err or "Unknown error loading config"; 501 return false, err or "Unknown error loading config";
473 end 502 end
474 return true, "Config loaded"; 503 return true, "Config loaded";
475 end 504 end
476 505
477 function def_env.config:get(host, section, key) 506 function def_env.config:get(host, key)
507 if key == nil then
508 host, key = "*", host;
509 end
478 local config_get = require "core.configmanager".get 510 local config_get = require "core.configmanager".get
479 return true, tostring(config_get(host, section, key)); 511 return true, serialize(config_get(host, key));
480 end 512 end
481 513
482 function def_env.config:reload() 514 function def_env.config:reload()
483 local ok, err = prosody.reload_config(); 515 local ok, err = prosody.reload_config();
484 return ok, (ok and "Config reloaded (you may need to reload modules to take effect)") or tostring(err); 516 return ok, (ok and "Config reloaded (you may need to reload modules to take effect)") or tostring(err);
503 line[#line+1] = status.."("..priority..")"; 535 line[#line+1] = status.."("..priority..")";
504 end 536 end
505 if session.cert_identity_status == "valid" then 537 if session.cert_identity_status == "valid" then
506 line[#line+1] = "(authenticated)"; 538 line[#line+1] = "(authenticated)";
507 end 539 end
540 if session.dialback_key then
541 line[#line+1] = "(dialback)";
542 end
543 if session.external_auth then
544 line[#line+1] = "(SASL)";
545 end
508 if session.secure then 546 if session.secure then
509 line[#line+1] = "(encrypted)"; 547 line[#line+1] = "(encrypted)";
510 end 548 end
511 if session.compressed then 549 if session.compressed then
512 line[#line+1] = "(compressed)"; 550 line[#line+1] = "(compressed)";
517 if session.ip and session.ip:match(":") then 555 if session.ip and session.ip:match(":") then
518 line[#line+1] = "(IPv6)"; 556 line[#line+1] = "(IPv6)";
519 end 557 end
520 if session.remote then 558 if session.remote then
521 line[#line+1] = "(remote)"; 559 line[#line+1] = "(remote)";
560 end
561 if session.incoming and session.outgoing then
562 line[#line+1] = "(bidi)";
563 elseif session.is_bidi or session.bidi_session then
564 line[#line+1] = "(bidi)";
565 end
566 if session.bosh_version then
567 line[#line+1] = "(bosh)";
568 end
569 if session.websocket_request then
570 line[#line+1] = "(websocket)";
522 end 571 end
523 return table.concat(line, " "); 572 return table.concat(line, " ");
524 end 573 end
525 574
526 local function tls_info(session, line) 575 local function tls_info(session, line)
532 local info = sock:info(); 581 local info = sock:info();
533 line[#line+1] = ("(%s with %s)"):format(info.protocol, info.cipher); 582 line[#line+1] = ("(%s with %s)"):format(info.protocol, info.cipher);
534 else 583 else
535 line[#line+1] = "(cipher info unavailable)"; 584 line[#line+1] = "(cipher info unavailable)";
536 end 585 end
586 if sock.getsniname then
587 local name = sock:getsniname();
588 if name then
589 line[#line+1] = ("(SNI:%q)"):format(name);
590 end
591 end
592 if sock.getalpn then
593 local proto = sock:getalpn();
594 if proto then
595 line[#line+1] = ("(ALPN:%q)"):format(proto);
596 end
597 end
537 else 598 else
538 line[#line+1] = "(insecure)"; 599 line[#line+1] = "(insecure)";
539 end 600 end
540 return table.concat(line, " "); 601 return table.concat(line, " ");
541 end 602 end
553 local serverip = conn and conn.server and conn:server():ip() or "?"; 614 local serverip = conn and conn.server and conn:server():ip() or "?";
554 local serverport = conn and conn:serverport() or "?" 615 local serverport = conn and conn:serverport() or "?"
555 return jid_join("["..ip.."]:"..clientport, session.host or "["..serverip.."]:"..serverport); 616 return jid_join("["..ip.."]:"..clientport, session.host or "["..serverip.."]:"..serverport);
556 end 617 end
557 618
619 local function get_c2s()
620 local c2s = array.collect(values(prosody.full_sessions));
621 c2s:append(array.collect(values(module:shared"/*/c2s/sessions")));
622 c2s:append(array.collect(values(module:shared"/*/bosh/sessions")));
623 c2s:unique();
624 return c2s;
625 end
626
558 local function show_c2s(callback) 627 local function show_c2s(callback)
559 local c2s = array.collect(values(module:shared"/*/c2s/sessions")); 628 get_c2s():sort(function(a, b)
560 c2s:sort(function(a, b)
561 if a.host == b.host then 629 if a.host == b.host then
562 if a.username == b.username then 630 if a.username == b.username then
563 return (a.resource or "") > (b.resource or ""); 631 return (a.resource or "") > (b.resource or "");
564 end 632 end
565 return (a.username or "") > (b.username or ""); 633 return (a.username or "") > (b.username or "");
566 end 634 end
567 return (a.host or "") > (b.host or ""); 635 return _sort_hosts(a.host or "", b.host or "");
568 end):map(function (session) 636 end):map(function (session)
569 callback(get_jid(session), session) 637 callback(get_jid(session), session)
570 end); 638 end);
571 end 639 end
572 640
573 function def_env.c2s:count() 641 function def_env.c2s:count()
574 return true, "Total: ".. iterators.count(values(module:shared"/*/c2s/sessions")) .." clients"; 642 local c2s = get_c2s();
643 return true, "Total: ".. #c2s .." clients";
575 end 644 end
576 645
577 function def_env.c2s:show(match_jid, annotate) 646 function def_env.c2s:show(match_jid, annotate)
578 local print, count = self.session.print, 0; 647 local print, count = self.session.print, 0;
579 annotate = annotate or session_flags; 648 annotate = annotate or session_flags;
615 684
616 function def_env.c2s:show_tls(match_jid) 685 function def_env.c2s:show_tls(match_jid)
617 return self:show(match_jid, tls_info); 686 return self:show(match_jid, tls_info);
618 end 687 end
619 688
620 function def_env.c2s:close(match_jid) 689 local function build_reason(text, condition)
690 if text or condition then
691 return {
692 text = text,
693 condition = condition or "undefined-condition",
694 };
695 end
696 end
697
698 function def_env.c2s:close(match_jid, text, condition)
621 local count = 0; 699 local count = 0;
622 show_c2s(function (jid, session) 700 show_c2s(function (jid, session)
623 if jid == match_jid or jid_bare(jid) == match_jid then 701 if jid == match_jid or jid_bare(jid) == match_jid then
624 count = count + 1; 702 count = count + 1;
625 session:close(); 703 session:close(build_reason(text, condition));
626 end 704 end
705 end);
706 return true, "Total: "..count.." sessions closed";
707 end
708
709 function def_env.c2s:closeall(text, condition)
710 local count = 0;
711 --luacheck: ignore 212/jid
712 show_c2s(function (jid, session)
713 count = count + 1;
714 session:close(build_reason(text, condition));
627 end); 715 end);
628 return true, "Total: "..count.." sessions closed"; 716 return true, "Total: "..count.." sessions closed";
629 end 717 end
630 718
631 719
693 end 781 end
694 end 782 end
695 783
696 -- Sort by local host, then remote host 784 -- Sort by local host, then remote host
697 table.sort(s2s_list, function(a,b) 785 table.sort(s2s_list, function(a,b)
698 if a.l == b.l then return a.r < b.r; end 786 if a.l == b.l then return _sort_hosts(a.r, b.r); end
699 return a.l < b.l; 787 return _sort_hosts(a.l, b.l);
700 end); 788 end);
701 local lasthost; 789 local lasthost;
702 for _, sess_lines in ipairs(s2s_list) do 790 for _, sess_lines in ipairs(s2s_list) do
703 if sess_lines.l ~= lasthost then print(sess_lines.l); lasthost=sess_lines.l end 791 if sess_lines.l ~= lasthost then print(sess_lines.l); lasthost=sess_lines.l end
704 for _, line in ipairs(sess_lines) do print(line); end 792 for _, line in ipairs(sess_lines) do print(line); end
826 return ("Showing "..n_certs.." certificate" 914 return ("Showing "..n_certs.." certificate"
827 ..(n_certs==1 and "" or "s") 915 ..(n_certs==1 and "" or "s")
828 .." presented by "..domain.."."); 916 .." presented by "..domain..".");
829 end 917 end
830 918
831 function def_env.s2s:close(from, to) 919 function def_env.s2s:close(from, to, text, condition)
832 local print, count = self.session.print, 0; 920 local print, count = self.session.print, 0;
833 local s2s_sessions = module:shared"/*/s2s/sessions"; 921 local s2s_sessions = module:shared"/*/s2s/sessions";
834 922
835 local match_id; 923 local match_id;
836 if from and not to then 924 if from and not to then
840 elseif from == to then 928 elseif from == to then
841 return false, "Both from and to are the same... you can't do that :)"; 929 return false, "Both from and to are the same... you can't do that :)";
842 end 930 end
843 931
844 for _, session in pairs(s2s_sessions) do 932 for _, session in pairs(s2s_sessions) do
845 local id = session.type..tostring(session):match("[a-f0-9]+$"); 933 local id = session.id or (session.type..tostring(session):match("[a-f0-9]+$"));
846 if (match_id and match_id == id) 934 if (match_id and match_id == id)
847 or (session.from_host == from and session.to_host == to) then 935 or (session.from_host == from and session.to_host == to) then
848 print(("Closing connection from %s to %s [%s]"):format(session.from_host, session.to_host, id)); 936 print(("Closing connection from %s to %s [%s]"):format(session.from_host, session.to_host, id));
849 (session.close or s2smanager.destroy_session)(session); 937 (session.close or s2smanager.destroy_session)(session, build_reason(text, condition));
850 count = count + 1 ; 938 count = count + 1 ;
851 end 939 end
852 end 940 end
853 return true, "Closed "..count.." s2s session"..((count == 1 and "") or "s"); 941 return true, "Closed "..count.." s2s session"..((count == 1 and "") or "s");
854 end 942 end
855 943
856 function def_env.s2s:closeall(host) 944 function def_env.s2s:closeall(host, text, condition)
857 local count = 0; 945 local count = 0;
858 local s2s_sessions = module:shared"/*/s2s/sessions"; 946 local s2s_sessions = module:shared"/*/s2s/sessions";
859 for _,session in pairs(s2s_sessions) do 947 for _,session in pairs(s2s_sessions) do
860 if not host or session.from_host == host or session.to_host == host then 948 if not host or session.from_host == host or session.to_host == host then
861 session:close(); 949 session:close(build_reason(text, condition));
862 count = count + 1; 950 count = count + 1;
863 end 951 end
864 end 952 end
865 if count == 0 then return false, "No sessions to close."; 953 if count == 0 then return false, "No sessions to close.";
866 else return true, "Closed "..count.." s2s session"..((count == 1 and "") or "s"); end 954 else return true, "Closed "..count.." s2s session"..((count == 1 and "") or "s"); end
877 965
878 function def_env.host:list() 966 function def_env.host:list()
879 local print = self.session.print; 967 local print = self.session.print;
880 local i = 0; 968 local i = 0;
881 local type; 969 local type;
882 for host, host_session in iterators.sorted_pairs(prosody.hosts) do 970 for host, host_session in iterators.sorted_pairs(prosody.hosts, _sort_hosts) do
883 i = i + 1; 971 i = i + 1;
884 type = host_session.type; 972 type = host_session.type;
885 if type == "local" then 973 if type == "local" then
886 print(host); 974 print(host);
887 else 975 else
1060 end 1148 end
1061 1149
1062 def_env.xmpp = {}; 1150 def_env.xmpp = {};
1063 1151
1064 local st = require "util.stanza"; 1152 local st = require "util.stanza";
1065 function def_env.xmpp:ping(localhost, remotehost) 1153 local new_id = require "util.id".medium;
1066 if prosody.hosts[localhost] then 1154 function def_env.xmpp:ping(localhost, remotehost, timeout)
1067 module:send(st.iq{ from=localhost, to=remotehost, type="get", id="ping" } 1155 localhost = select(2, jid_split(localhost));
1068 :tag("ping", {xmlns="urn:xmpp:ping"}), prosody.hosts[localhost]); 1156 remotehost = select(2, jid_split(remotehost));
1069 return true, "Sent ping"; 1157 if not localhost then
1158 return nil, "Invalid sender hostname";
1159 elseif not prosody.hosts[localhost] then
1160 return nil, "No such local host";
1161 end
1162 if not remotehost then
1163 return nil, "Invalid destination hostname";
1164 elseif prosody.hosts[remotehost] then
1165 return nil, "Both hosts are local";
1166 end
1167 local iq = st.iq{ from=localhost, to=remotehost, type="get", id=new_id()}
1168 :tag("ping", {xmlns="urn:xmpp:ping"});
1169 local time_start = time.now();
1170 local ret, err = async.wait(module:context(localhost):send_iq(iq, nil, timeout));
1171 if ret then
1172 return true, ("pong from %s in %gs"):format(ret.stanza.attr.from, time.now() - time_start);
1070 else 1173 else
1071 return nil, "No such host"; 1174 return false, tostring(err);
1072 end 1175 end
1073 end 1176 end
1074 1177
1075 def_env.dns = {}; 1178 def_env.dns = {};
1076 local adns = require"net.adns"; 1179 local adns = require"net.adns";
1155 end 1258 end
1156 return true, helpers.show_events(events_obj, event); 1259 return true, helpers.show_events(events_obj, event);
1157 end 1260 end
1158 1261
1159 function def_env.debug:timers() 1262 function def_env.debug:timers()
1160 local socket = require "socket";
1161 local print = self.session.print; 1263 local print = self.session.print;
1162 local add_task = require"util.timer".add_task; 1264 local add_task = require"util.timer".add_task;
1163 local h, params = add_task.h, add_task.params; 1265 local h, params = add_task.h, add_task.params;
1164 if h then 1266 if h then
1165 print("-- util.timer"); 1267 print("-- util.timer");
1183 print(count .. " libevent callbacks"); 1285 print(count .. " libevent callbacks");
1184 end 1286 end
1185 if h then 1287 if h then
1186 local next_time = h:peek(); 1288 local next_time = h:peek();
1187 if next_time then 1289 if next_time then
1188 return true, os.date("Next event at %F %T (in %%.6fs)", next_time):format(next_time - socket.gettime()); 1290 return true, os.date("Next event at %F %T (in %%.6fs)", next_time):format(next_time - time.now());
1189 end 1291 end
1190 end 1292 end
1191 return true; 1293 return true;
1192 end 1294 end
1193 1295
1205 local function format_stat(type, value, ref_value) 1307 local function format_stat(type, value, ref_value)
1206 ref_value = ref_value or value; 1308 ref_value = ref_value or value;
1207 --do return tostring(value) end 1309 --do return tostring(value) end
1208 if type == "duration" then 1310 if type == "duration" then
1209 if ref_value < 0.001 then 1311 if ref_value < 0.001 then
1210 return ("%d µs"):format(value*1000000); 1312 return ("%g µs"):format(value*1000000);
1211 elseif ref_value < 0.9 then 1313 elseif ref_value < 0.9 then
1212 return ("%0.2f ms"):format(value*1000); 1314 return ("%0.2f ms"):format(value*1000);
1213 end 1315 end
1214 return ("%0.2f"):format(value); 1316 return ("%0.2f"):format(value);
1215 elseif type == "size" then 1317 elseif type == "size" then
1324 return self; 1426 return self;
1325 end 1427 end
1326 1428
1327 function stats_methods:cfgraph() 1429 function stats_methods:cfgraph()
1328 for _, stat_info in ipairs(self) do 1430 for _, stat_info in ipairs(self) do
1329 local name, type, value, data = unpack(stat_info, 1, 4); 1431 local name, type, value, data = unpack(stat_info, 1, 4); -- luacheck: ignore 211
1330 local function print(s) 1432 local function print(s)
1331 table.insert(stat_info.output, s); 1433 table.insert(stat_info.output, s);
1332 end 1434 end
1333 1435
1334 if data and data.sample_count and data.sample_count > 0 then 1436 if data and data.sample_count and data.sample_count > 0 then
1390 return self; 1492 return self;
1391 end 1493 end
1392 1494
1393 function stats_methods:histogram() 1495 function stats_methods:histogram()
1394 for _, stat_info in ipairs(self) do 1496 for _, stat_info in ipairs(self) do
1395 local name, type, value, data = unpack(stat_info, 1, 4); 1497 local name, type, value, data = unpack(stat_info, 1, 4); -- luacheck: ignore 211
1396 local function print(s) 1498 local function print(s)
1397 table.insert(stat_info.output, s); 1499 table.insert(stat_info.output, s);
1398 end 1500 end
1399 1501
1400 if not data then 1502 if not data then
1490 local function new_stats_context(self) 1592 local function new_stats_context(self)
1491 return setmetatable({ session = self.session, stats = true }, stats_mt); 1593 return setmetatable({ session = self.session, stats = true }, stats_mt);
1492 end 1594 end
1493 1595
1494 function def_env.stats:show(filter) 1596 function def_env.stats:show(filter)
1597 -- luacheck: ignore 211/changed
1495 local stats, changed, extra = require "core.statsmanager".get_stats(); 1598 local stats, changed, extra = require "core.statsmanager".get_stats();
1496 local available, displayed = 0, 0; 1599 local available, displayed = 0, 0;
1497 local displayed_stats = new_stats_context(self); 1600 local displayed_stats = new_stats_context(self);
1498 for name, value in pairs(stats) do 1601 for name, value in iterators.sorted_pairs(stats) do
1499 available = available + 1; 1602 available = available + 1;
1500 if not filter or name:match(filter) then 1603 if not filter or name:match(filter) then
1501 displayed = displayed + 1; 1604 displayed = displayed + 1;
1502 local type = name:match(":(%a+)$"); 1605 local type = name:match(":(%a+)$");
1503 table.insert(displayed_stats, { 1606 table.insert(displayed_stats, {