Ignore SIGPIPE in the CLI (silent exit when a connection dies, e.g. after sleep/wake)#481
Open
kk1987 wants to merge 1 commit into
Open
Ignore SIGPIPE in the CLI (silent exit when a connection dies, e.g. after sleep/wake)#481kk1987 wants to merge 1 commit into
kk1987 wants to merge 1 commit into
Conversation
Nothing in axel handles SIGPIPE, so the first write to a connection whose peer has already gone away kills the process silently with the signal's default action -- no error message, output cut off mid-line. The typical real-world trigger is waking a laptop from sleep: the server has dropped the connections in the meantime, and the writes that run while recovering from that (SSL_shutdown() sending close_notify into a reset socket, tcp_write() re-sending a request) raise the fatal signal before the existing write-error handling gets a chance to run. Ignore SIGPIPE in main(), as wget, curl and aria2 do, so failed writes surface as EPIPE/SSL errors and flow into the reconnect logic that already exists.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
axel dies silently — no error message, output cut off mid-progress-line — whenever a write hits a connection that the peer has already closed, because nothing in axel handles
SIGPIPEand its default action terminates the process. The easiest way to hit this in real life: start a download, close the laptop lid, reopen it ten minutes later — the server (or a NAT box) has dropped the connections during sleep, axel wakes up, writes to a dead socket, and is gone without a trace (exit status 141 = 128+SIGPIPE).Root cause
The only signal handlers axel installs are
SIGINT/SIGTERM(src/text.c). There is nosignal(SIGPIPE, ...), noSO_NOSIGPIPE, noMSG_NOSIGNALanywhere in the tree. So the first write on a reset connection raisesSIGPIPEand kills the process before any of the existing error paths can run. The writes that trigger it are exactly the ones that run while recovering from dead connections:ssl_disconnect()→SSL_shutdown()sending close_notify into a reset socket (ssl.c), andtcp_write()re-sending an HTTP/FTP request (http.c / ftp.c). The error handling for failed writes is already in place — it just never gets a chance, because the signal fires first.This is what wget, curl and aria2 all do: wget runs
signal(SIGPIPE, SIG_IGN)inmain()("What we want is to ignore SIGPIPE and just check for the return value of write()"), aria2 setsSIG_IGNin its signal setup, and libcurl wraps its internals insigpipe_ignore()/sigpipe_restore()(plusSO_NOSIGPIPEwhere available).Steps to reproduce
Tested on macOS Tahoe 26.4.1 (Darwin 25.4.0), MacBook Pro M4 Max, axel 2.17.14 from Homebrew. (Linux is affected too — the default SIGPIPE action is the same; macOS users just hit the sleep/wake trigger constantly.)
Quick proof that the handler is missing:
The process disappears mid-progress-line with no message;
exit=141.Realistic reproduction of the wake-from-sleep path — a local TLS server that serves a 200 MB file slowly and, 8 seconds in, force-RSTs every connection (
SO_LINGER(1,0)+ close), which is what axel finds when the machine wakes up:With 2.17.14 the moment the RSTs land axel vanishes mid-line — no error output of any kind — with
exit=141. With this patch the same RST burst produces the normal connection-error/reconnect handling: axel re-establishes all four connections (the server keeps listening) and the 200 MB download completes with exit 0.Real-world case
This was tracked down from a download that was left running when a MacBook's lid was closed; on wake ~10 minutes later, the axel process had vanished without printing anything. During sleep the server had dropped the connections, and on wake axel's own recovery path was what killed it: the connection-timeout cleanup calls
conn_disconnect()→SSL_shutdown(), whose close_notify write into a reset socket raised the fatal SIGPIPE.Fix
Ignore
SIGPIPEinmain(), exactly as wget does, so failed writes surface asEPIPE/SSL errors and flow into the reconnect logic that already exists. One-line change plus a comment.