Mercurial > prosody-hg
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, { |
