You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
oans exits 0 when it could not read part of the tree it was asked to scan. For the scheduled/systemd deployment this is the difference between a monitored job and an unmonitored one — systemd marks the unit SUCCESS, OnFailure= never fires, and a wrapper script's if oans ...; then takes the happy path.
The shipped systemd/[email protected] is Type=oneshot with no OnFailure=, so today there is no configuration of it that can notice this.
This is the actionable half of #145: that one makes the skips visible, this one makes them automatable.
Proposed solution
Once the scan-side skip/error counters from #145 exist, use them to drive the exit status.
Preferred: a --strict flag (name open to bikeshedding — --fail-on-error?) that exits non-zero when any error bucket is non-zero, leaving the default at today's behaviour. Config-driven buckets (excluded, too_small, not_regular) must never count — those are the user asking for the skip.
Then set SuccessExitStatus=/document OnFailure= in systemd/[email protected], and mention the flag in docs/nas-quickstart.md as the recommended setting for an unattended job.
A distinct exit code (e.g. 2 = completed with skips, reserving 1 for a real failure) would let a wrapper tell "degraded" from "broken" without parsing --json.
A narrower case that should not wait for --strict: an unusable scan root
A path named explicitly on the command line that cannot be resolved is warned about and then ignored, and the run still exits 0:
$ oans -rq --hashfile=/tmp/h.db '/srv/media/*'Error 2: No such file or directory while getting path to file /srv/media/*. Skipping.Warning: not storing root "/srv/media/*" in the hashfile: No such file or directory. A later replay of this hashfile will not cover it.
$ echo$?0
That reproduction is an accidentally quoted shell glob, but a plain typo, an unmounted path, or a renamed share behave identically: oans scans nothing (or only part of what was asked for) and reports success. On the scheduled path it is worse than a mid-tree skip, because the root is also silently dropped from the stored scan config, so every later replay covers less than the user thinks.
This deserves to be separated from the general --strict question above, and is much less contentious:
A root is explicit user input, not something discovered mid-walk. There is no equivalent of "a NAS tree routinely contains a handful of files the scanner can't open" — the user named this path, so failing to use it is always worth an error.
It is a much smaller behaviour change than failing on any skip: it fires only when a named argument was unusable.
Suggested: exit non-zero when any root given on the command line could not be scanned, regardless of --strict. Failing when all roots are unusable is already the behaviour for the replay path (drop_missing_roots() refuses when none survive, so the stat-prune cannot wipe the hashfile); the command-line path has no equivalent and should get one.
The related "did you mean to leave the glob unquoted?" hint for a non-existent root containing *, ? or [ is cosmetic and can ride along or be skipped.
Open question worth deciding in the issue, not the PR: should --strict be the default? Arguments for: a dedupe tool that silently ignores part of its input is surprising, and the failure is currently undetectable. Against: it's a behaviour change for existing scripts, and a NAS tree will routinely contain a handful of files the scanner legitimately can't open. My inclination is opt-in for now, revisited after the counters land and we can see typical real-world bucket counts.
Alternatives considered
Parse --json in a wrapper. Works, but requires every user to build the same wrapper, and --json is a separate invocation that reads the hashfile — it can't report on the run that just happened without a second process and a jq dependency.
Always exit non-zero on any skip. Too noisy: --min-filesize and --exclude skips are the normal case, and a single unreadable lock file would fail every run. Note this does not apply to the unusable-root case above, which is an explicitly named argument rather than something met during the walk.
Treat an unusable root as part of --strict. Possible, but it would leave the default behaviour — silently scanning less than asked, exit 0 — in place for everyone who has not opted in, for a case where there is no legitimate reason to continue.
Rely on the journal. Requires a human to read it, which defeats the point of a timer.
What problem would this solve?
oansexits 0 when it could not read part of the tree it was asked to scan. For the scheduled/systemd deployment this is the difference between a monitored job and an unmonitored one —systemdmarks the unitSUCCESS,OnFailure=never fires, and a wrapper script'sif oans ...; thentakes the happy path.The shipped
systemd/[email protected]isType=oneshotwith noOnFailure=, so today there is no configuration of it that can notice this.This is the actionable half of #145: that one makes the skips visible, this one makes them automatable.
Proposed solution
Once the scan-side skip/error counters from #145 exist, use them to drive the exit status.
Preferred: a
--strictflag (name open to bikeshedding —--fail-on-error?) that exits non-zero when any error bucket is non-zero, leaving the default at today's behaviour. Config-driven buckets (excluded,too_small,not_regular) must never count — those are the user asking for the skip.Then set
SuccessExitStatus=/documentOnFailure=insystemd/[email protected], and mention the flag indocs/nas-quickstart.mdas the recommended setting for an unattended job.A distinct exit code (e.g.
2= completed with skips, reserving1for a real failure) would let a wrapper tell "degraded" from "broken" without parsing--json.A narrower case that should not wait for
--strict: an unusable scan rootA path named explicitly on the command line that cannot be resolved is warned about and then ignored, and the run still exits 0:
That reproduction is an accidentally quoted shell glob, but a plain typo, an unmounted path, or a renamed share behave identically: oans scans nothing (or only part of what was asked for) and reports success. On the scheduled path it is worse than a mid-tree skip, because the root is also silently dropped from the stored scan config, so every later replay covers less than the user thinks.
This deserves to be separated from the general
--strictquestion above, and is much less contentious:Suggested: exit non-zero when any root given on the command line could not be scanned, regardless of
--strict. Failing when all roots are unusable is already the behaviour for the replay path (drop_missing_roots()refuses when none survive, so the stat-prune cannot wipe the hashfile); the command-line path has no equivalent and should get one.The related "did you mean to leave the glob unquoted?" hint for a non-existent root containing
*,?or[is cosmetic and can ride along or be skipped.Open question worth deciding in the issue, not the PR: should
--strictbe the default? Arguments for: a dedupe tool that silently ignores part of its input is surprising, and the failure is currently undetectable. Against: it's a behaviour change for existing scripts, and a NAS tree will routinely contain a handful of files the scanner legitimately can't open. My inclination is opt-in for now, revisited after the counters land and we can see typical real-world bucket counts.Alternatives considered
--jsonin a wrapper. Works, but requires every user to build the same wrapper, and--jsonis a separate invocation that reads the hashfile — it can't report on the run that just happened without a second process and ajqdependency.--min-filesizeand--excludeskips are the normal case, and a single unreadable lock file would fail every run. Note this does not apply to the unusable-root case above, which is an explicitly named argument rather than something met during the walk.--strict. Possible, but it would leave the default behaviour — silently scanning less than asked, exit 0 — in place for everyone who has not opted in, for a case where there is no legitimate reason to continue.