Serve unlink-dangling, so a leftover enable link goes through the seat

systemctl disable leaves a link whose unit file is gone, and the manager
keeps asking for the unit. The verb removes only such links in the
account's or the machine's own configuration, on the person's word, and
records each with its reason before removing it (hq issue 332).
This commit is contained in:
jochen
2026-10-08 17:53:53 +02:00
parent e740683ca4
commit 87c9d9f7e5
7 changed files with 395 additions and 9 deletions
+14 -3
View File
@@ -12,7 +12,8 @@ package main
// is the case a failed record of a unit whose load state is not-found comes from, and the answer says
// each link's target and whether it dangles.
//
// Nothing here changes anything. Clearing the record is `reset-failed`'s.
// Nothing here changes anything. Removing a dangling link is unlink.go's; clearing the record is
// `reset-failed`'s.
import (
"errors"
@@ -275,8 +276,9 @@ func findings(scope Scope, unit, loadState string, dependents int, links []Enabl
out = append(out, "the unit's file is not found, and "+plural(len(links), "enable link")+" still name it")
}
if removable > 0 {
out = append(out, "`disable` does not remove a link whose unit file is gone, and no verb of this seat removes one yet "+
"(novox/hq issue 332); once it is gone, `reset-failed` clears the unit's failed record")
out = append(out, "`disable` does not remove a link whose unit file is gone; `unlink-dangling` in this scope removes "+
"the dangling links named for the unit in the account's or the machine's own configuration, keeping a record of each; "+
"then `reset-failed` clears the unit's failed record")
} else if dangling > 0 {
out = append(out, "the dangling links are in a package's, a generator's or the runtime's directory, which puts them back: they are not the person's to remove")
}
@@ -326,6 +328,15 @@ func readDir(dir string) ([]Entry, error) {
return out, nil
}
// lstatEntry is one path as readDir would list it, read again just before it is acted on.
func lstatEntry(path string) (Entry, error) {
info, err := os.Lstat(path)
if err != nil {
return Entry{}, err
}
return entryOf(path, filepath.Base(path), info.Mode().Type()), nil
}
func entryOf(full, name string, mode fs.FileMode) Entry {
entry := Entry{Name: name, Dir: mode.IsDir()}
if mode&fs.ModeSymlink == 0 {