Skip to content

A mirror that aborts on a fatal error still exits 0 - #1419

Open
xroche wants to merge 3 commits into
masterfrom
fix-1394-exit-status
Open

A mirror that aborts on a fatal error still exits 0#1419
xroche wants to merge 3 commits into
masterfrom
fix-1394-exit-status

Conversation

@xroche

@xroche xroche commented Aug 24, 2026

Copy link
Copy Markdown
Owner

hts_main2() returned 0 whatever the mirror did, so a script could not tell a finished mirror from one the engine gave up on halfway. It now returns 2 for a mirror that started and did not finish. This changes an exported API's documented return, which Xavier approved ahead of the work.

Nine live sites set exit_xh = -1 and all of them reach that status. Eight already existed (a write the engine cannot retry, on a body, a page, a file or the cache zip, plus htsAddLink refusing a link past -#L); the ninth is added here, because the parser hit that same -#L cap at htsparse.c:3182 and returned quietly, so one limit gave two answers depending on which path found it. A mirror the caller ended on purpose still returns 0: SIGINT, hts_request_stop(), a callback returning 0 and WebHTTrack's cancel all set exit_xh to 1, and htsserver.c branches on this status, so an abort there would put a red error page on the cancel button. The value is 2 because -1 (255 to the shell) is a refused command line and 1 is a failed -#E or -#R. hts_errmsg() was empty on this path, so the abort now fills it; without that the status arrived as a bare * on stderr.

No soname bump, and I do not read this as an ABI break: the signature is unchanged, no installed struct changed shape (the htsopt.h edit is a comment), and HTS_EXIT_MIRROR_ABORTED is a new macro. Only the value a caller reads back moved.

A question for you. hts_is_exiting() also returns 2, for a session that transferred nothing and was rolled back, which is the opposite of an abort. Both are exported, so one integer now means two things on two channels. I moved neither value; the header and the new man page say plainly that the two are unrelated. Whether the exit status should move off 2 is yours to call.

360 asserts the status across four producers, asserts 0 for a mirror that finishes and for one that fits under the link cap, and requires a refused command line to exit 255 exactly. 350 asserts it on its /dev/full arms, 0 on its EPIPE arm, and 0 on its SIGTERM arm for the carve-out above. 240 already called its -#L pass an aborted mirror in its own header, so it now asserts the status there, and asserts 0 for the dead-server pass, where the session is rolled back rather than left half written. Mutants: reverting the return reds both files, widening the check to exit_xh != 0 reds 350's SIGTERM arm, and dropping the htsparse.c line reds 360's link-cap arm. The httpmirror() == 0 branch also sets the status and is live rather than dead, but all six returns behind it are allocation or size-overflow guards, so no black-box arm reaches them.

Done. still goes to stdout for an aborted mirror: that is #1420, whose fix lands on the opt->shell TRANSFER DONE token WinHTTrack and htsserver parse. #1417 also rewrites parts of 350, so whichever lands second needs a hunk-by-hunk merge in that file.

Closes #1394

hts_main2() returned 0 whatever the mirror did, so a script could not
tell a finished mirror from one the engine gave up on halfway. It now
returns HTS_EXIT_MIRROR_ABORTED (2) for a mirror that started and did
not finish, covering all nine sites that set exit_xh = -1 plus the
httpmirror() bailout that already printed an error before exiting 0.

A mirror the caller ended on purpose keeps returning 0: hts_request_stop(),
a callback returning 0, SIGINT and WebHTTrack's cancel all set exit_xh to
1, and reporting those as failures would break the cancel button.

httrack.c prints hts_errmsg() on a non-zero return and that buffer was
empty here, so the abort now fills it. The man page gains an EXIT STATUS
section, and httrack-library.h documents the contract for embedders.

Closes #1394

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Xavier Roche <roche@httrack.com>
xroche and others added 2 commits August 24, 2026 22:53
Its crawl_onto_full helper ran local_crawl under set -e without meaning
to read the status, so the abort it provokes on purpose ended the script
once an aborted mirror stopped exiting 0. Capture the status on the line
itself, before the log read clobbers $?, and assert it; the EPIPE arm,
where a non-fatal write error leaves the mirror running, asserts 0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Xavier Roche <roche@httrack.com>
htsftp.c:939 is commented-out code, so there are 8 live exit_xh = -1
producers, not 9; the count in httrack-library.h claimed an FTP fatal
the engine cannot raise, which is the worst place for it since embedders
read that header.

The -#L link cap reported two different statuses depending on which path
found it: htsAddLink aborted, the parser at htsparse.c:3182 logged "Too
many URLs" and returned quietly. It aborts now, which is the ninth
producer and the first one a test can reach without a full disk.

350's SIGTERM arm and 240's two passes assert the status, pinning the
exit_xh == 1 carve-out (widening the check to != 0 would put an error
page on WebHTTrack's cancel button) and the rollback that is not an
abort. The header and the man page now say the exit status and exit_xh
are separate channels that happen to share the integer 2.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Xavier Roche <roche@httrack.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A mirror that aborts on a fatal error still exits 0

1 participant