Mercurial > prosody-hg
comparison plugins/mod_admin_telnet.lua @ 10411:db2a06b9ff98
Merge 0.11->trunk
| author | Kim Alvefur <zash@zash.se> |
|---|---|
| date | Sat, 16 Nov 2019 16:52:31 +0100 |
| parents | 496248e48a1d |
| children | e7d3fa49495f |
comparison
equal
deleted
inserted
replaced
| 10410:659b577f280c | 10411:db2a06b9ff98 |
|---|---|
| 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 |
| 96 local chunk, err = envload("return "..line, chunkname, env); | 128 local chunk, err = envload("return "..line, chunkname, env); |
| 97 if not chunk then | 129 if not chunk then |
| 98 chunk, err = envload(line, chunkname, env); | 130 chunk, err = envload(line, chunkname, env); |
| 103 session.print("Sorry, I couldn't understand that... "..err); | 135 session.print("Sorry, I couldn't understand that... "..err); |
| 104 return; | 136 return; |
| 105 end | 137 end |
| 106 end | 138 end |
| 107 | 139 |
| 108 local ranok, taskok, message = pcall(chunk); | 140 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 | 141 |
| 121 if not message then | 142 if not message then |
| 122 session.print("Result: "..tostring(taskok)); | 143 session.print("Result: "..tostring(taskok)); |
| 123 return; | 144 return; |
| 124 elseif (not taskok) and message then | 145 elseif (not taskok) and message then |
| 148 data = partial..data; | 169 data = partial..data; |
| 149 end | 170 end |
| 150 | 171 |
| 151 for line in data:gmatch("[^\n]*[\n\004]") do | 172 for line in data:gmatch("[^\n]*[\n\004]") do |
| 152 if session.closed then return end | 173 if session.closed then return end |
| 153 console:process_line(session, line); | 174 session.thread:run(line); |
| 154 session.send(string.char(0)); | |
| 155 end | 175 end |
| 156 session.partial_data = data:match("[^\n]+$"); | 176 session.partial_data = data:match("[^\n]+$"); |
| 157 end | 177 end |
| 158 | 178 |
| 159 function console_listener.onreadtimeout(conn) | 179 function console_listener.onreadtimeout(conn) |
| 218 print [[host - Commands to activate, deactivate and list virtual hosts]] | 238 print [[host - Commands to activate, deactivate and list virtual hosts]] |
| 219 print [[user - Commands to create and delete users, and change their passwords]] | 239 print [[user - Commands to create and delete users, and change their passwords]] |
| 220 print [[server - Uptime, version, shutting down, etc.]] | 240 print [[server - Uptime, version, shutting down, etc.]] |
| 221 print [[port - Commands to manage ports the server is listening on]] | 241 print [[port - Commands to manage ports the server is listening on]] |
| 222 print [[dns - Commands to manage and inspect the internal DNS resolver]] | 242 print [[dns - Commands to manage and inspect the internal DNS resolver]] |
| 243 print [[xmpp - Commands for sending XMPP stanzas]] | |
| 223 print [[config - Reloading the configuration, etc.]] | 244 print [[config - Reloading the configuration, etc.]] |
| 224 print [[console - Help regarding the console itself]] | 245 print [[console - Help regarding the console itself]] |
| 225 elseif section == "c2s" then | 246 elseif section == "c2s" then |
| 226 print [[c2s:show(jid) - Show all client sessions with the specified JID (or all if no JID given)]] | 247 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]] | 248 print [[c2s:show_insecure() - Show all unencrypted client connections]] |
| 228 print [[c2s:show_secure() - Show all encrypted client connections]] | 249 print [[c2s:show_secure() - Show all encrypted client connections]] |
| 229 print [[c2s:show_tls() - Show TLS cipher info for encrypted sessions]] | 250 print [[c2s:show_tls() - Show TLS cipher info for encrypted sessions]] |
| 251 print [[c2s:count() - Count sessions without listing them]] | |
| 230 print [[c2s:close(jid) - Close all sessions for the specified JID]] | 252 print [[c2s:close(jid) - Close all sessions for the specified JID]] |
| 253 print [[c2s:closeall() - Close all active c2s connections ]] | |
| 231 elseif section == "s2s" then | 254 elseif section == "s2s" then |
| 232 print [[s2s:show(domain) - Show all s2s connections for the given domain (or all if no domain given)]] | 255 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]] | 256 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]] | 257 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]] | 258 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]] | 282 print [[dns:lookup(name, type, class) - Do a DNS lookup]] |
| 260 print [[dns:addnameserver(nameserver) - Add a nameserver to the list]] | 283 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]] | 284 print [[dns:setnameserver(nameserver) - Replace the list of name servers with the supplied one]] |
| 262 print [[dns:purge() - Clear the DNS cache]] | 285 print [[dns:purge() - Clear the DNS cache]] |
| 263 print [[dns:cache() - Show cached records]] | 286 print [[dns:cache() - Show cached records]] |
| 287 elseif section == "xmpp" then | |
| 288 print [[xmpp:ping(localhost, remotehost) -- Sends a ping to a remote XMPP server and reports the response]] | |
| 264 elseif section == "config" then | 289 elseif section == "config" then |
| 265 print [[config:reload() - Reload the server configuration. Modules may need to be reloaded for changes to take effect.]] | 290 print [[config:reload() - Reload the server configuration. Modules may need to be reloaded for changes to take effect.]] |
| 266 elseif section == "console" then | 291 elseif section == "console" then |
| 267 print [[Hey! Welcome to Prosody's admin console.]] | 292 print [[Hey! Welcome to Prosody's admin console.]] |
| 268 print [[First thing, if you're ever wondering how to get out, simply type 'quit'.]] | 293 print [[First thing, if you're ever wondering how to get out, simply type 'quit'.]] |
| 456 else | 481 else |
| 457 print(" Host not found"); | 482 print(" Host not found"); |
| 458 end | 483 end |
| 459 else | 484 else |
| 460 for _, name in ipairs(modules) do | 485 for _, name in ipairs(modules) do |
| 461 print(" "..name); | 486 local status, status_text = modulemanager.get_module(host, name).module:get_status(); |
| 487 local status_summary = ""; | |
| 488 if status == "warn" or status == "error" then | |
| 489 status_summary = (" (%s: %s)"):format(status, status_text); | |
| 490 end | |
| 491 print((" %s%s"):format(name, status_summary)); | |
| 462 end | 492 end |
| 463 end | 493 end |
| 464 end | 494 end |
| 465 end | 495 end |
| 466 | 496 |
| 472 return false, err or "Unknown error loading config"; | 502 return false, err or "Unknown error loading config"; |
| 473 end | 503 end |
| 474 return true, "Config loaded"; | 504 return true, "Config loaded"; |
| 475 end | 505 end |
| 476 | 506 |
| 477 function def_env.config:get(host, section, key) | 507 function def_env.config:get(host, key) |
| 508 if key == nil then | |
| 509 host, key = "*", host; | |
| 510 end | |
| 478 local config_get = require "core.configmanager".get | 511 local config_get = require "core.configmanager".get |
| 479 return true, tostring(config_get(host, section, key)); | 512 return true, serialize(config_get(host, key)); |
| 480 end | 513 end |
| 481 | 514 |
| 482 function def_env.config:reload() | 515 function def_env.config:reload() |
| 483 local ok, err = prosody.reload_config(); | 516 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); | 517 return ok, (ok and "Config reloaded (you may need to reload modules to take effect)") or tostring(err); |
| 503 line[#line+1] = status.."("..priority..")"; | 536 line[#line+1] = status.."("..priority..")"; |
| 504 end | 537 end |
| 505 if session.cert_identity_status == "valid" then | 538 if session.cert_identity_status == "valid" then |
| 506 line[#line+1] = "(authenticated)"; | 539 line[#line+1] = "(authenticated)"; |
| 507 end | 540 end |
| 541 if session.dialback_key then | |
| 542 line[#line+1] = "(dialback)"; | |
| 543 end | |
| 544 if session.external_auth then | |
| 545 line[#line+1] = "(SASL)"; | |
| 546 end | |
| 508 if session.secure then | 547 if session.secure then |
| 509 line[#line+1] = "(encrypted)"; | 548 line[#line+1] = "(encrypted)"; |
| 510 end | 549 end |
| 511 if session.compressed then | 550 if session.compressed then |
| 512 line[#line+1] = "(compressed)"; | 551 line[#line+1] = "(compressed)"; |
| 517 if session.ip and session.ip:match(":") then | 556 if session.ip and session.ip:match(":") then |
| 518 line[#line+1] = "(IPv6)"; | 557 line[#line+1] = "(IPv6)"; |
| 519 end | 558 end |
| 520 if session.remote then | 559 if session.remote then |
| 521 line[#line+1] = "(remote)"; | 560 line[#line+1] = "(remote)"; |
| 561 end | |
| 562 if session.incoming and session.outgoing then | |
| 563 line[#line+1] = "(bidi)"; | |
| 564 elseif session.is_bidi or session.bidi_session then | |
| 565 line[#line+1] = "(bidi)"; | |
| 566 end | |
| 567 if session.bosh_version then | |
| 568 line[#line+1] = "(bosh)"; | |
| 569 end | |
| 570 if session.websocket_request then | |
| 571 line[#line+1] = "(websocket)"; | |
| 522 end | 572 end |
| 523 return table.concat(line, " "); | 573 return table.concat(line, " "); |
| 524 end | 574 end |
| 525 | 575 |
| 526 local function tls_info(session, line) | 576 local function tls_info(session, line) |
| 553 local serverip = conn and conn.server and conn:server():ip() or "?"; | 603 local serverip = conn and conn.server and conn:server():ip() or "?"; |
| 554 local serverport = conn and conn:serverport() or "?" | 604 local serverport = conn and conn:serverport() or "?" |
| 555 return jid_join("["..ip.."]:"..clientport, session.host or "["..serverip.."]:"..serverport); | 605 return jid_join("["..ip.."]:"..clientport, session.host or "["..serverip.."]:"..serverport); |
| 556 end | 606 end |
| 557 | 607 |
| 608 local function get_c2s() | |
| 609 local c2s = array.collect(values(prosody.full_sessions)); | |
| 610 c2s:append(array.collect(values(module:shared"/*/c2s/sessions"))); | |
| 611 c2s:append(array.collect(values(module:shared"/*/bosh/sessions"))); | |
| 612 c2s:unique(); | |
| 613 return c2s; | |
| 614 end | |
| 615 | |
| 558 local function show_c2s(callback) | 616 local function show_c2s(callback) |
| 559 local c2s = array.collect(values(module:shared"/*/c2s/sessions")); | 617 get_c2s():sort(function(a, b) |
| 560 c2s:sort(function(a, b) | |
| 561 if a.host == b.host then | 618 if a.host == b.host then |
| 562 if a.username == b.username then | 619 if a.username == b.username then |
| 563 return (a.resource or "") > (b.resource or ""); | 620 return (a.resource or "") > (b.resource or ""); |
| 564 end | 621 end |
| 565 return (a.username or "") > (b.username or ""); | 622 return (a.username or "") > (b.username or ""); |
| 569 callback(get_jid(session), session) | 626 callback(get_jid(session), session) |
| 570 end); | 627 end); |
| 571 end | 628 end |
| 572 | 629 |
| 573 function def_env.c2s:count() | 630 function def_env.c2s:count() |
| 574 return true, "Total: ".. iterators.count(values(module:shared"/*/c2s/sessions")) .." clients"; | 631 local c2s = get_c2s(); |
| 632 return true, "Total: ".. #c2s .." clients"; | |
| 575 end | 633 end |
| 576 | 634 |
| 577 function def_env.c2s:show(match_jid, annotate) | 635 function def_env.c2s:show(match_jid, annotate) |
| 578 local print, count = self.session.print, 0; | 636 local print, count = self.session.print, 0; |
| 579 annotate = annotate or session_flags; | 637 annotate = annotate or session_flags; |
| 615 | 673 |
| 616 function def_env.c2s:show_tls(match_jid) | 674 function def_env.c2s:show_tls(match_jid) |
| 617 return self:show(match_jid, tls_info); | 675 return self:show(match_jid, tls_info); |
| 618 end | 676 end |
| 619 | 677 |
| 620 function def_env.c2s:close(match_jid) | 678 local function build_reason(text, condition) |
| 679 if text or condition then | |
| 680 return { | |
| 681 text = text, | |
| 682 condition = condition or "undefined-condition", | |
| 683 }; | |
| 684 end | |
| 685 end | |
| 686 | |
| 687 function def_env.c2s:close(match_jid, text, condition) | |
| 621 local count = 0; | 688 local count = 0; |
| 622 show_c2s(function (jid, session) | 689 show_c2s(function (jid, session) |
| 623 if jid == match_jid or jid_bare(jid) == match_jid then | 690 if jid == match_jid or jid_bare(jid) == match_jid then |
| 624 count = count + 1; | 691 count = count + 1; |
| 625 session:close(); | 692 session:close(build_reason(text, condition)); |
| 626 end | 693 end |
| 694 end); | |
| 695 return true, "Total: "..count.." sessions closed"; | |
| 696 end | |
| 697 | |
| 698 function def_env.c2s:closeall(text, condition) | |
| 699 local count = 0; | |
| 700 --luacheck: ignore 212/jid | |
| 701 show_c2s(function (jid, session) | |
| 702 count = count + 1; | |
| 703 session:close(build_reason(text, condition)); | |
| 627 end); | 704 end); |
| 628 return true, "Total: "..count.." sessions closed"; | 705 return true, "Total: "..count.." sessions closed"; |
| 629 end | 706 end |
| 630 | 707 |
| 631 | 708 |
| 826 return ("Showing "..n_certs.." certificate" | 903 return ("Showing "..n_certs.." certificate" |
| 827 ..(n_certs==1 and "" or "s") | 904 ..(n_certs==1 and "" or "s") |
| 828 .." presented by "..domain.."."); | 905 .." presented by "..domain.."."); |
| 829 end | 906 end |
| 830 | 907 |
| 831 function def_env.s2s:close(from, to) | 908 function def_env.s2s:close(from, to, text, condition) |
| 832 local print, count = self.session.print, 0; | 909 local print, count = self.session.print, 0; |
| 833 local s2s_sessions = module:shared"/*/s2s/sessions"; | 910 local s2s_sessions = module:shared"/*/s2s/sessions"; |
| 834 | 911 |
| 835 local match_id; | 912 local match_id; |
| 836 if from and not to then | 913 if from and not to then |
| 840 elseif from == to then | 917 elseif from == to then |
| 841 return false, "Both from and to are the same... you can't do that :)"; | 918 return false, "Both from and to are the same... you can't do that :)"; |
| 842 end | 919 end |
| 843 | 920 |
| 844 for _, session in pairs(s2s_sessions) do | 921 for _, session in pairs(s2s_sessions) do |
| 845 local id = session.type..tostring(session):match("[a-f0-9]+$"); | 922 local id = session.id or (session.type..tostring(session):match("[a-f0-9]+$")); |
| 846 if (match_id and match_id == id) | 923 if (match_id and match_id == id) |
| 847 or (session.from_host == from and session.to_host == to) then | 924 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)); | 925 print(("Closing connection from %s to %s [%s]"):format(session.from_host, session.to_host, id)); |
| 849 (session.close or s2smanager.destroy_session)(session); | 926 (session.close or s2smanager.destroy_session)(session, build_reason(text, condition)); |
| 850 count = count + 1 ; | 927 count = count + 1 ; |
| 851 end | 928 end |
| 852 end | 929 end |
| 853 return true, "Closed "..count.." s2s session"..((count == 1 and "") or "s"); | 930 return true, "Closed "..count.." s2s session"..((count == 1 and "") or "s"); |
| 854 end | 931 end |
| 855 | 932 |
| 856 function def_env.s2s:closeall(host) | 933 function def_env.s2s:closeall(host, text, condition) |
| 857 local count = 0; | 934 local count = 0; |
| 858 local s2s_sessions = module:shared"/*/s2s/sessions"; | 935 local s2s_sessions = module:shared"/*/s2s/sessions"; |
| 859 for _,session in pairs(s2s_sessions) do | 936 for _,session in pairs(s2s_sessions) do |
| 860 if not host or session.from_host == host or session.to_host == host then | 937 if not host or session.from_host == host or session.to_host == host then |
| 861 session:close(); | 938 session:close(build_reason(text, condition)); |
| 862 count = count + 1; | 939 count = count + 1; |
| 863 end | 940 end |
| 864 end | 941 end |
| 865 if count == 0 then return false, "No sessions to close."; | 942 if count == 0 then return false, "No sessions to close."; |
| 866 else return true, "Closed "..count.." s2s session"..((count == 1 and "") or "s"); end | 943 else return true, "Closed "..count.." s2s session"..((count == 1 and "") or "s"); end |
| 1060 end | 1137 end |
| 1061 | 1138 |
| 1062 def_env.xmpp = {}; | 1139 def_env.xmpp = {}; |
| 1063 | 1140 |
| 1064 local st = require "util.stanza"; | 1141 local st = require "util.stanza"; |
| 1065 function def_env.xmpp:ping(localhost, remotehost) | 1142 local new_id = require "util.id".medium; |
| 1066 if prosody.hosts[localhost] then | 1143 function def_env.xmpp:ping(localhost, remotehost, timeout) |
| 1067 module:send(st.iq{ from=localhost, to=remotehost, type="get", id="ping" } | 1144 localhost = select(2, jid_split(localhost)); |
| 1068 :tag("ping", {xmlns="urn:xmpp:ping"}), prosody.hosts[localhost]); | 1145 remotehost = select(2, jid_split(remotehost)); |
| 1069 return true, "Sent ping"; | 1146 if not localhost then |
| 1147 return nil, "Invalid sender hostname"; | |
| 1148 elseif not prosody.hosts[localhost] then | |
| 1149 return nil, "No such local host"; | |
| 1150 end | |
| 1151 if not remotehost then | |
| 1152 return nil, "Invalid destination hostname"; | |
| 1153 elseif prosody.hosts[remotehost] then | |
| 1154 return nil, "Both hosts are local"; | |
| 1155 end | |
| 1156 local iq = st.iq{ from=localhost, to=remotehost, type="get", id=new_id()} | |
| 1157 :tag("ping", {xmlns="urn:xmpp:ping"}); | |
| 1158 local time_start = time.now(); | |
| 1159 local ret, err = async.wait(module:context(localhost):send_iq(iq, nil, timeout)); | |
| 1160 if ret then | |
| 1161 return true, ("pong from %s in %gs"):format(ret.stanza.attr.from, time.now() - time_start); | |
| 1070 else | 1162 else |
| 1071 return nil, "No such host"; | 1163 return false, tostring(err); |
| 1072 end | 1164 end |
| 1073 end | 1165 end |
| 1074 | 1166 |
| 1075 def_env.dns = {}; | 1167 def_env.dns = {}; |
| 1076 local adns = require"net.adns"; | 1168 local adns = require"net.adns"; |
| 1205 local function format_stat(type, value, ref_value) | 1297 local function format_stat(type, value, ref_value) |
| 1206 ref_value = ref_value or value; | 1298 ref_value = ref_value or value; |
| 1207 --do return tostring(value) end | 1299 --do return tostring(value) end |
| 1208 if type == "duration" then | 1300 if type == "duration" then |
| 1209 if ref_value < 0.001 then | 1301 if ref_value < 0.001 then |
| 1210 return ("%d µs"):format(value*1000000); | 1302 return ("%g µs"):format(value*1000000); |
| 1211 elseif ref_value < 0.9 then | 1303 elseif ref_value < 0.9 then |
| 1212 return ("%0.2f ms"):format(value*1000); | 1304 return ("%0.2f ms"):format(value*1000); |
| 1213 end | 1305 end |
| 1214 return ("%0.2f"):format(value); | 1306 return ("%0.2f"):format(value); |
| 1215 elseif type == "size" then | 1307 elseif type == "size" then |
| 1493 | 1585 |
| 1494 function def_env.stats:show(filter) | 1586 function def_env.stats:show(filter) |
| 1495 local stats, changed, extra = require "core.statsmanager".get_stats(); | 1587 local stats, changed, extra = require "core.statsmanager".get_stats(); |
| 1496 local available, displayed = 0, 0; | 1588 local available, displayed = 0, 0; |
| 1497 local displayed_stats = new_stats_context(self); | 1589 local displayed_stats = new_stats_context(self); |
| 1498 for name, value in pairs(stats) do | 1590 for name, value in iterators.sorted_pairs(stats) do |
| 1499 available = available + 1; | 1591 available = available + 1; |
| 1500 if not filter or name:match(filter) then | 1592 if not filter or name:match(filter) then |
| 1501 displayed = displayed + 1; | 1593 displayed = displayed + 1; |
| 1502 local type = name:match(":(%a+)$"); | 1594 local type = name:match(":(%a+)$"); |
| 1503 table.insert(displayed_stats, { | 1595 table.insert(displayed_stats, { |
