Read a one-shot that never ran as stopped, and one at rest as stopped to a stopped declaration (issue 1126)
The kernel module's boot-time cleanup, linux-modules-cleanup.service (Type=oneshot, declared stopped and enabled at boot), failed its delivery on ace with 'was asked to be stopped and is running': ServiceState read every inactive one-shot with ExecMainStatus=0 as running, and systemd says 0 for one that never ran too. Stopping an inactive unit changes nothing, so the read back said running again; after a boot that ran it, the same declaration would fail every apply. ServiceState now reads ExecMainStartTimestampMonotonic: 0 means never ran, read as stopped (a package's one-shot declared running is then started, and the hold no longer counts it as in use). The service apply reads a one-shot at rest as stopped to a declaration that it be stopped (ServiceAtRest), whatever its last run did. Quick fix weighed: declare the cleanup running in the catalogue. Thorough chosen: the reading was wrong for every one-shot, and this is its second fault of the kind (the firewall's was the first). Unit tests: three arch one-shot tests merged into one table (7 rows), one apply test (2 rows); 1263 to 1262.
This commit is contained in:
+31
-2
@@ -2002,6 +2002,35 @@ type unitReloader interface {
|
||||
ReloadUnits(ctx context.Context, run system.Runner) error
|
||||
}
|
||||
|
||||
// restReader is a service manager that can say whether a one-shot has nothing running now
|
||||
// (novox/hq issue 1126).
|
||||
type restReader interface {
|
||||
ServiceAtRest(ctx context.Context, run system.Runner, unit string) (bool, error)
|
||||
}
|
||||
|
||||
// declaredState is a unit's state as a declaration of want reads it. **A one-shot at rest is
|
||||
// stopped to a declaration that it be stopped** (novox/hq issue 1126): a one-shot that does not
|
||||
// linger reads "running" once it ran and worked — that is what a declaration that it run asks — but
|
||||
// it has no process between its runs, so it is also as stopped as it can be, and stopping it changes
|
||||
// nothing. Read only as "running", a boot-time cleanup declared stopped and enabled failed every
|
||||
// apply after the boot that ran it: "was asked to be stopped and is running".
|
||||
func declaredState(ctx context.Context, sys system.System, run Runner, unit, want string) (string, error) {
|
||||
state, err := sys.ServiceState(ctx, run, unit)
|
||||
if err != nil || want != "stopped" || state != "running" {
|
||||
return state, err
|
||||
}
|
||||
if r, ok := sys.(restReader); ok {
|
||||
rest, err := r.ServiceAtRest(ctx, run, unit)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if rest {
|
||||
return "stopped", nil
|
||||
}
|
||||
}
|
||||
return state, nil
|
||||
}
|
||||
|
||||
// serviceSettle is how long the host waits before looking at a unit a second time. A test sets it
|
||||
// to nothing; on a machine it is the window in which a daemon that refuses its configuration dies.
|
||||
var serviceSettle = 2 * time.Second
|
||||
@@ -2154,7 +2183,7 @@ func applyServiceIn(ctx context.Context, sys system.System, r *declaration.Servi
|
||||
// own stopping from anybody else's (novox/hq ADR 0269).
|
||||
out.declared = &store.FoundUnit{Unit: r.Unit, State: r.State, Boot: r.Boot}
|
||||
|
||||
before, err := sys.ServiceState(ctx, run, r.Unit)
|
||||
before, err := declaredState(ctx, sys, run, r.Unit, r.State)
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
@@ -2165,7 +2194,7 @@ func applyServiceIn(ctx context.Context, sys system.System, r *declaration.Servi
|
||||
|
||||
// Read back. A service manager accepting a command says the transaction was accepted,
|
||||
// not that the unit is running — one that starts and immediately dies satisfies it.
|
||||
after, err := sys.ServiceState(ctx, run, r.Unit)
|
||||
after, err := declaredState(ctx, sys, run, r.Unit, r.State)
|
||||
if err == nil && r.State == "running" {
|
||||
after, err = stayedRunning(ctx, sys, run, r.Unit)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user