Listing zones and services is what firewall-cmd already does. The question it does not answer needs both halves at once: a port is reachable only when something is LISTENING on a network address AND the firewall permits it. On this machine that crossing is the whole story. The rules look unremarkable -- one zone, three services, a port range -- and what they mean is that PostgreSQL and Redis, published by rootless containers on every interface, are reachable by anyone on the network. Neither half says that alone, which is exactly how a tidy rules list coexists with an open database. Nothing was misconfigured: Fedora's default zone met podman's default publish behaviour. Ephemeral client sockets are excluded. A browser's outbound UDP port is indistinguishable from a service in ss, and listing twenty of them buried the two rows that mattered. Closing the port range names what it would cut off, by service, before doing it, and removing ssh says so when someone is connected over it. Rich rules are shown and never edited: a syntax is not a setting, but hiding it would misrepresent the configuration. The contract needed a recorded firewall, and the reason is worth keeping. The rule this page exists for cannot be tested against this machine -- its zone permits everything above 1024, so "listening" and "listening and permitted" give identical answers, and a blocked listener needs a port below 1024, which needs root. With the crossing deleted, the contract passed. It now runs against a fixture where two listeners are blocked, and catches it. Also here: polkit response files are written 0600 rather than at the default mask, the agent sweeps requests left by an instance that did not exit cleanly, and the write sweep waits for its harness to be ready instead of reporting the startup race as settings that failed. Claude-Session: https://claude.ai/code/session_01BRvzt4H8XXLPVH5MyYdk9L
117 lines
4.8 KiB
QML
117 lines
4.8 KiB
QML
pragma Singleton
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
// Shared UI state.
|
|
//
|
|
// Every overlay in the shell is mutually exclusive with the others -- opening
|
|
// the overview should close the quick settings, and so on. Centralising that
|
|
// here means no module needs a reference to any other module, and the IPC
|
|
// handlers in shell.qml have exactly one thing to talk to.
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
import Quickshell
|
|
import QtQuick
|
|
import qs.config
|
|
|
|
Singleton {
|
|
id: root
|
|
|
|
// Exactly one of these may be non-empty at a time.
|
|
// "" | "overview" | "quicksettings" | "notifications" | "clipboard" | "capture" | "activity" | "powermenu"
|
|
property string activeOverlay: ""
|
|
|
|
readonly property bool overviewOpen: activeOverlay === "overview"
|
|
readonly property bool quickSettingsOpen: activeOverlay === "quicksettings"
|
|
readonly property bool notificationsOpen: activeOverlay === "notifications"
|
|
readonly property bool clipboardOpen: activeOverlay === "clipboard"
|
|
readonly property bool captureOpen: activeOverlay === "capture"
|
|
readonly property bool activityOpen: activeOverlay === "activity"
|
|
readonly property bool powerMenuOpen: activeOverlay === "powermenu"
|
|
|
|
// Settings is a normal application window rather than a transient overlay.
|
|
// It can stay open while Quick Settings or the notification center appears.
|
|
property bool settingsOpen: false
|
|
property string settingsPage: "home"
|
|
|
|
readonly property bool anyOverlayOpen: activeOverlay !== ""
|
|
|
|
// The date menu keeps calendar context fixed while its right pane switches
|
|
// between the schedule, persistent activity and message history.
|
|
property string dateMenuPage: "agenda"
|
|
|
|
// 0 means "use the currently focused workspace". A positive value lets a
|
|
// contextual surface, such as Focus, open Mission Control at its target.
|
|
property int overviewWorkspaceId: 0
|
|
property string overviewQuery: ""
|
|
|
|
function toggle(name: string): void {
|
|
root.activeOverlay = (root.activeOverlay === name) ? "" : name;
|
|
}
|
|
|
|
function open(name: string): void {
|
|
root.activeOverlay = name;
|
|
}
|
|
|
|
function openDateMenu(page: string): void {
|
|
root.dateMenuPage = root.normalizedDateMenuPage(page);
|
|
root.activeOverlay = "notifications";
|
|
}
|
|
|
|
function toggleDateMenu(page: string): void {
|
|
const target = root.normalizedDateMenuPage(page);
|
|
if (root.activeOverlay === "notifications" && root.dateMenuPage === target) {
|
|
root.activeOverlay = "";
|
|
return;
|
|
}
|
|
root.dateMenuPage = target;
|
|
root.activeOverlay = "notifications";
|
|
}
|
|
|
|
function normalizedDateMenuPage(page: string): string {
|
|
return ["agenda", "ongoing", "notifications"].indexOf(page) >= 0 ? page : "agenda";
|
|
}
|
|
|
|
function openOverview(workspaceId: int): void {
|
|
root.overviewWorkspaceId = Math.max(0, workspaceId);
|
|
root.overviewQuery = "";
|
|
root.activeOverlay = "overview";
|
|
}
|
|
|
|
function searchOverview(query: string): void {
|
|
root.overviewWorkspaceId = 0;
|
|
root.overviewQuery = query;
|
|
root.activeOverlay = "overview";
|
|
}
|
|
|
|
function close(): void {
|
|
if (root.activeOverlay === "overview") {
|
|
root.overviewWorkspaceId = 0;
|
|
root.overviewQuery = "";
|
|
}
|
|
root.activeOverlay = "";
|
|
}
|
|
|
|
function openSettings(page: string): void {
|
|
const allowed = ["home", "appearance", "displays", "connectivity", "home-phone", "desktop", "sound", "gaming", "notifications", "screen-intelligence", "shortcuts", "mouse", "privacy", "region", "accounts", "accessibility", "power", "datetime", "applications", "updates", "storage", "snapshots", "users", "sharing", "firewall", "printers", "services", "about"];
|
|
root.settingsPage = allowed.indexOf(page) >= 0 ? page : "home";
|
|
DesktopPreferences.set("lastPage", root.settingsPage);
|
|
root.settingsOpen = true;
|
|
}
|
|
|
|
function toggleSettings(): void {
|
|
if (root.settingsOpen) {
|
|
root.closeSettings();
|
|
return;
|
|
}
|
|
root.openSettings(DesktopPreferences.get("lastPage") || "home");
|
|
}
|
|
|
|
function closeSettings(): void {
|
|
root.settingsOpen = false;
|
|
}
|
|
|
|
// Set by Dock.qml so the bar can avoid fighting it for pointer grabs, and
|
|
// read by the capture overlay so the dock isn't in the screenshot.
|
|
property bool dockRevealed: false
|
|
}
|