How to Handle Process Termination Signals Like SIGTERM in Node.js Socket Connections
The application handles SIGTERM signals by listening for Socket.IO disconnect events in socket/socket.js to kill child processes, while wget/index.js listens for the exit event to remove partial downloads before the process fully terminates.
The AhmadIbrahiim/Website-downloader repository implements a graceful shutdown mechanism for long-running background tasks. When a client disconnects or the server needs to terminate downloads, the system orchestrates cleanup between the socket layer and child process handlers to prevent orphaned wget processes and incomplete files.
The Socket.IO Disconnect Handler
The termination flow begins in socket/socket.js, where the server reacts to network-level disconnections. When a client closes their browser or loses connection, Socket.IO fires the disconnect event, which serves as the primary trigger for sending SIGTERM signals to active child processes.
Storing Process References
When a download starts, the socket instance stores references to both the wget and archiver processes. This attachment allows the disconnect handler to access and terminate these specific jobs later.
According to the source code in socket/socket.js (lines 5-9), the application assigns spawned processes directly to the socket object:
socket.wgetProcess = launchWget(data);
socket.archiverProcess = launchArchiver(data);
Triggering SIGTERM on Disconnect
The disconnect event handler checks for attached processes and invokes the .kill() method, which sends SIGTERM by default. This implementation appears in socket/socket.js (lines 11-22):
socket.on('disconnect', () => {
if (socket.wgetProcess) socket.wgetProcess.kill(); // Sends SIGTERM
if (socket.archiverProcess) socket.archiverProcess.abort();
});
Using socket.wgetProcess.kill() without arguments explicitly transmits SIGTERM, giving the child process an opportunity to handle cleanup before the operating system forcefully terminates it.
Handling SIGTERM in the wget Process
The wget/index.js module creates the actual download child process and registers signal-specific listeners. This separation of concerns keeps the socket layer lightweight while concentrating cleanup logic in the module responsible for the file system operations.
Listening for the Exit Event
When SIGTERM arrives, the Node.js child_process module emits an exit event containing the signal name. The handler in wget/index.js (lines 48-54) checks for the SIGTERM string specifically:
const child = exec(`wget -mkEpnp --no-if-modified-since ${data.website}`);
child.on('exit', (code, signal) => {
if (signal === 'SIGTERM') {
console.log('Process terminated');
removePartiallyDownloadedFiles(website);
}
});
This conditional check ensures that cleanup only runs when the process receives an intentional termination signal, distinguishing it from normal completion or crash exits.
Cleaning Up Partial Downloads
The removePartiallyDownloadedFiles() function, defined in wget/index.js (lines 56-64), synchronously deletes the partially downloaded folder created by wget. This prevents incomplete data from consuming disk space and ensures the system returns to a clean state before the process exits.
Archiver Process Termination
While the wget process receives SIGTERM via the .kill() method, the archiver uses a higher-level abstraction. The same disconnect handler calls socket.archiverProcess.abort() (lines 18-21 in socket/socket.js), which delegates termination logic to the archiver implementation rather than sending raw signals.
Practical Implementation Example
Here is the complete flow for handling process termination signals in a Node.js Socket.IO application:
// socket/socket.js
io.on('connection', socket => {
socket.on('request', data => {
// Spawn processes and store references on socket instance
socket.wgetProcess = launchWget(data);
socket.archiverProcess = launchArchiver(data);
});
// Handle termination signals on client disconnect
socket.on('disconnect', () => {
if (socket.wgetProcess) socket.wgetProcess.kill(); // Sends SIGTERM
if (socket.archiverProcess) socket.archiverProcess.abort();
});
});
// wget/index.js
const { exec } = require('child_process');
function downloadWebsite(website) {
const child = exec(`wget -mkEpnp ${website}`);
child.on('exit', (code, signal) => {
if (signal === 'SIGTERM') {
console.log('SIGTERM received, cleaning up...');
removePartiallyDownloadedFiles(website);
}
});
return child;
}
This pattern ensures that whether a user closes their browser tab or the server administrator sends a SIGTERM signal, the system gracefully terminates downloads and removes temporary files.
Summary
- Signal Origin: The Socket.IO
disconnectevent insocket/socket.jstriggers the termination chain. - SIGTERM Transmission: Calling
socket.wgetProcess.kill()sends SIGTERM to the wget child process. - Signal Handling:
wget/index.jslistens for theexitevent and checks ifsignal === 'SIGTERM'to initiate cleanup. - File Cleanup: The
removePartiallyDownloadedFiles()function removes incomplete downloads to prevent disk pollution. - Archiver Handling: The archiver process uses
abort()rather than SIGTERM for graceful termination.
Frequently Asked Questions
What is the difference between SIGTERM and SIGKILL in Node.js?
SIGTERM (signal 15) requests graceful termination, allowing the process to run cleanup handlers before exiting, while SIGKILL (signal 9) forces immediate termination without cleanup. The Website-downloader uses SIGTERM via .kill() to ensure partial downloads are removed before the process exits.
How does the socket keep track of active download processes?
The application stores child process references directly on the Socket.IO instance using socket.wgetProcess and socket.archiverProcess. This attachment ensures that when the disconnect event fires, the server has immediate access to terminate the specific processes associated with that client connection.
Why does the wget module check for the SIGTERM signal specifically?
The exit event fires whenever a child process terminates, including normal completion (null signal) or crashes. By checking if (signal === 'SIGTERM') in wget/index.js, the code distinguishes between a successful download and a manual termination, ensuring cleanup logic only runs when necessary.
Can this pattern handle server shutdowns as well as client disconnects?
While the current implementation focuses on Socket.IO disconnect events, the same pattern applies to server shutdowns. You can extend socket/socket.js to listen for process.on('SIGTERM') and iterate through all active socket connections to trigger the same cleanup sequence.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →