Software /
code /
prosody
Comparison
util/prosodyctl/check.lua @ 13707:6c59b9072871 13.0
prosodyctl: check features: Check for recommended feature availability
Inspired by mod_compliance_*, this command will help people (especially those
with older configs, upgrading from previous releases) learn what features
their Prosody configuration may be missing.
author | Matthew Wild <mwild1@gmail.com> |
---|---|
date | Sat, 15 Feb 2025 16:34:16 +0000 |
parent | 13706:a988867a5567 |
child | 13708:9f8e9aabc00b |
comparison
equal
deleted
inserted
replaced
13706:a988867a5567 | 13707:6c59b9072871 |
---|---|
323 local set = require "prosody.util.set"; | 323 local set = require "prosody.util.set"; |
324 local it = require "prosody.util.iterators"; | 324 local it = require "prosody.util.iterators"; |
325 local ok = true; | 325 local ok = true; |
326 local function contains_match(hayset, needle) for member in hayset do if member:find(needle) then return true end end end | 326 local function contains_match(hayset, needle) for member in hayset do if member:find(needle) then return true end end end |
327 local function disabled_hosts(host, conf) return host ~= "*" and conf.enabled ~= false; end | 327 local function disabled_hosts(host, conf) return host ~= "*" and conf.enabled ~= false; end |
328 local function is_user_host(host, conf) return host ~= "*" and conf.component_module == nil; end | |
329 local function is_component_host(host, conf) return host ~= "*" and conf.component_module ~= nil; end | |
328 local function enabled_hosts() return it.filter(disabled_hosts, it.sorted_pairs(configmanager.getconfig())); end | 330 local function enabled_hosts() return it.filter(disabled_hosts, it.sorted_pairs(configmanager.getconfig())); end |
331 local function enabled_user_hosts() return it.filter(is_user_host, it.sorted_pairs(configmanager.getconfig())); end | |
332 local function enabled_components() return it.filter(is_component_host, it.sorted_pairs(configmanager.getconfig())); end | |
333 | |
329 local checks = {}; | 334 local checks = {}; |
330 function checks.disabled() | 335 function checks.disabled() |
331 local disabled_hosts_set = set.new(); | 336 local disabled_hosts_set = set.new(); |
332 for host in it.filter("*", pairs(configmanager.getconfig())) do | 337 for host in it.filter("*", pairs(configmanager.getconfig())) do |
333 if api(host):get_option_boolean("enabled") == false then | 338 if api(host):get_option_boolean("enabled") == false then |
800 print(" WILL NOT migrate any existing data (user accounts, etc.) to the new name."); | 805 print(" WILL NOT migrate any existing data (user accounts, etc.) to the new name."); |
801 ok = false; | 806 ok = false; |
802 end | 807 end |
803 end | 808 end |
804 | 809 |
810 -- Check features | |
811 do | |
812 local missing_features = {}; | |
813 for host in enabled_user_hosts() do | |
814 local all_features = checks.features(host, true); | |
815 if not all_features then | |
816 table.insert(missing_features, host); | |
817 end | |
818 end | |
819 if #missing_features > 0 then | |
820 print(""); | |
821 print(" Some of your hosts may be missing features due to a lack of configuration."); | |
822 print(" For more details, use the 'prosodyctl check features' command."); | |
823 end | |
824 end | |
825 | |
805 print("Done.\n"); | 826 print("Done.\n"); |
806 end | 827 end |
807 function checks.dns() | 828 function checks.dns() |
808 local dns = require "prosody.net.dns"; | 829 local dns = require "prosody.net.dns"; |
809 pcall(function () | 830 pcall(function () |
1448 else | 1469 else |
1449 print("Success!\n"); | 1470 print("Success!\n"); |
1450 end | 1471 end |
1451 end | 1472 end |
1452 end | 1473 end |
1474 | |
1475 function checks.features(host, quiet) | |
1476 if not quiet then | |
1477 print("Feature report"); | |
1478 end | |
1479 | |
1480 local common_subdomains = { | |
1481 http_file_share = "share"; | |
1482 muc = "groups"; | |
1483 }; | |
1484 | |
1485 local function print_feature_status(feature, host) | |
1486 if quiet then return; end | |
1487 print("", feature.ok and "OK" or "(!)", feature.name); | |
1488 if not feature.ok then | |
1489 if feature.lacking_modules then | |
1490 table.sort(feature.lacking_modules); | |
1491 print("", "", "Suggested modules: "); | |
1492 for _, module in ipairs(feature.lacking_modules) do | |
1493 print("", "", (" - %s: https://prosody.im/doc/modules/mod_%s"):format(module, module)); | |
1494 end | |
1495 end | |
1496 if feature.lacking_components then | |
1497 table.sort(feature.lacking_components); | |
1498 for _, component_module in ipairs(feature.lacking_components) do | |
1499 local subdomain = common_subdomains[component_module]; | |
1500 if subdomain then | |
1501 print("", "", "Suggested component:"); | |
1502 print(""); | |
1503 print("", "", "", ("Component %q %q"):format(subdomain.."."..host, component_module)); | |
1504 print("", "", "", ("-- Documentation: https://prosody.im/doc/modules/mod_%s"):format(component_module)); | |
1505 else | |
1506 print("", "", ("Suggested component: %s"):format(component_module)); | |
1507 end | |
1508 end | |
1509 print(""); | |
1510 print("", "", "If you have already configured any these components, they may not be"); | |
1511 print("", "", "linked correctly to "..host..". For more info see https://prosody.im/doc/components"); | |
1512 end | |
1513 end | |
1514 print(""); | |
1515 end | |
1516 | |
1517 local all_ok = true; | |
1518 | |
1519 local config = configmanager.getconfig(); | |
1520 | |
1521 local f, s, v; | |
1522 if check_host then | |
1523 f, s, v = it.values({ check_host }); | |
1524 else | |
1525 f, s, v = enabled_user_hosts(); | |
1526 end | |
1527 | |
1528 for host in f, s, v do | |
1529 local modules_enabled = set.new(config["*"].modules_enabled); | |
1530 modules_enabled:include(set.new(config[host].modules_enabled)); | |
1531 | |
1532 -- { [component_module] = { hostname1, hostname2, ... } } | |
1533 local host_components = setmetatable({}, { __index = function (t, k) return rawset(t, k, {})[k]; end }); | |
1534 | |
1535 do | |
1536 local hostapi = api(host); | |
1537 | |
1538 -- Find implicitly linked components | |
1539 for other_host in enabled_components() do | |
1540 local parent_host = other_host:match("^[^.]+%.(.+)$"); | |
1541 if parent_host == host then | |
1542 local component_module = configmanager.get(other_host, "component_module"); | |
1543 if component_module then | |
1544 table.insert(host_components[component_module], other_host); | |
1545 end | |
1546 end | |
1547 end | |
1548 | |
1549 -- And components linked explicitly | |
1550 for _, disco_item in ipairs(hostapi:get_option_array("disco_items", {})) do | |
1551 local other_host = disco_item[1]; | |
1552 local component_module = configmanager.get(other_host, "component_module"); | |
1553 if component_module then | |
1554 table.insert(host_components[component_module], other_host); | |
1555 end | |
1556 end | |
1557 end | |
1558 | |
1559 local current_feature; | |
1560 | |
1561 local function check_module(suggested, alternate, ...) | |
1562 if set.intersection(modules_enabled, set.new({suggested, alternate, ...})):empty() then | |
1563 current_feature.lacking_modules = current_feature.lacking_modules or {}; | |
1564 table.insert(current_feature.lacking_modules, suggested); | |
1565 end | |
1566 end | |
1567 | |
1568 local function check_component(suggested, alternate, ...) | |
1569 local found; | |
1570 for _, component_module in ipairs({ suggested, alternate, ... }) do | |
1571 found = #host_components[component_module] > 0; | |
1572 if found then break; end | |
1573 end | |
1574 if not found then | |
1575 current_feature.lacking_components = current_feature.lacking_components or {}; | |
1576 table.insert(current_feature.lacking_components, suggested); | |
1577 end | |
1578 end | |
1579 | |
1580 local features = { | |
1581 { | |
1582 name = "Basic functionality"; | |
1583 check = function () | |
1584 check_module("disco"); | |
1585 check_module("roster"); | |
1586 check_module("saslauth"); | |
1587 check_module("tls"); | |
1588 check_module("pep"); | |
1589 end; | |
1590 }; | |
1591 { | |
1592 name = "Multi-device sync"; | |
1593 check = function () | |
1594 check_module("carbons"); | |
1595 check_module("mam"); | |
1596 check_module("bookmarks"); | |
1597 end; | |
1598 }; | |
1599 { | |
1600 name = "Mobile optimizations"; | |
1601 check = function () | |
1602 check_module("smacks"); | |
1603 check_module("csi_simple", "csi_battery_saver"); | |
1604 end; | |
1605 }; | |
1606 { | |
1607 name = "Web connections"; | |
1608 check = function () | |
1609 check_module("bosh"); | |
1610 check_module("websocket"); | |
1611 end; | |
1612 }; | |
1613 { | |
1614 name = "User profiles"; | |
1615 check = function () | |
1616 check_module("vcard_legacy", "vcard"); | |
1617 end; | |
1618 }; | |
1619 { | |
1620 name = "Blocking"; | |
1621 check = function () | |
1622 check_module("blocklist"); | |
1623 end; | |
1624 }; | |
1625 { | |
1626 name = "Push notifications"; | |
1627 check = function () | |
1628 check_module("cloud_notify"); | |
1629 end; | |
1630 }; | |
1631 { | |
1632 name = "Audio/video calls"; | |
1633 check = function () | |
1634 check_module( | |
1635 "turn_external", | |
1636 "external_services", | |
1637 "turncredentials", | |
1638 "extdisco" | |
1639 ); | |
1640 end; | |
1641 }; | |
1642 { | |
1643 name = "File sharing"; | |
1644 check = function () | |
1645 check_component("http_file_share", "http_upload"); | |
1646 end; | |
1647 }; | |
1648 { | |
1649 name = "Group chats"; | |
1650 check = function () | |
1651 check_component("muc"); | |
1652 end; | |
1653 }; | |
1654 }; | |
1655 | |
1656 if not quiet then | |
1657 print(host); | |
1658 end | |
1659 | |
1660 for _, feature in ipairs(features) do | |
1661 current_feature = feature; | |
1662 feature.check(); | |
1663 feature.ok = not feature.lacking_modules and not feature.lacking_components; | |
1664 -- For improved presentation, we group the (ok) and (not ok) features | |
1665 if feature.ok then | |
1666 print_feature_status(feature, host); | |
1667 end | |
1668 end | |
1669 | |
1670 for _, feature in ipairs(features) do | |
1671 if not feature.ok then | |
1672 all_ok = false; | |
1673 print_feature_status(feature, host); | |
1674 end | |
1675 end | |
1676 | |
1677 if not quiet then | |
1678 print(""); | |
1679 end | |
1680 end | |
1681 | |
1682 return all_ok; | |
1683 end | |
1684 | |
1453 if what == nil or what == "all" then | 1685 if what == nil or what == "all" then |
1454 local ret; | 1686 local ret; |
1455 ret = checks.disabled(); | 1687 ret = checks.disabled(); |
1456 if ret ~= nil then return ret; end | 1688 if ret ~= nil then return ret; end |
1457 ret = checks.config(); | 1689 ret = checks.config(); |