Add graceful shutdown with tunnel close on SIGINT

- Implement SIGINT signal handler for both wssshc and wsssht
- Add graceful shutdown logic that sends tunnel_close messages for active tunnels
- Implement 3-second timeout for tunnel close operations to complete
- Add double Ctrl+C detection for immediate exit without waiting
- Update signal handling throughout both programs to use graceful_shutdown flag

This ensures that active tunnels are properly closed when the programs
exit, either normally or via Ctrl+C, with a timeout mechanism and
fallback for immediate exit on second Ctrl+C.
parent d57e6c9c
......@@ -45,11 +45,20 @@
int global_debug = 0;
time_t start_time = 0;
volatile sig_atomic_t sigint_count = 0;
volatile sig_atomic_t graceful_shutdown = 0;
void sigint_handler(int sig __attribute__((unused))) {
fprintf(stderr, "[DEBUG] SIGINT handler called, setting sigint_received=1\n");
fflush(stderr);
sigint_received = 1;
sigint_count++;
if (sigint_count == 1) {
fprintf(stderr, "\nReceived SIGINT, attempting graceful shutdown...\n");
fflush(stderr);
graceful_shutdown = 1;
} else if (sigint_count >= 2) {
fprintf(stderr, "\nReceived second SIGINT, exiting immediately...\n");
fflush(stderr);
exit(1);
}
}
void print_status() {
......@@ -686,10 +695,10 @@ int connect_to_server(const wssshc_config_t *config) {
int timeout_seconds = 30; // Increased timeout for better reliability
while (1) {
// Check for SIGINT more frequently
if (sigint_received) {
// Check for graceful shutdown more frequently
if (graceful_shutdown) {
if (config->debug) {
fprintf(stderr, "[DEBUG] SIGINT received during registration, exiting...\n");
fprintf(stderr, "[DEBUG] Graceful shutdown requested during registration, exiting...\n");
fflush(stderr);
}
SSL_free(ssl);
......@@ -913,11 +922,50 @@ int connect_to_server(const wssshc_config_t *config) {
const int tunnel_list_interval = 60; // Show tunnel list every 60 seconds
while (1) {
// Check for SIGINT
if (sigint_received) {
fprintf(stderr, "Received SIGINT, cleaning up and exiting\n");
// Don't try to send tunnel_close messages as that would deadlock with SSL mutex
// Just break and let cleanup happen naturally
// Check for graceful shutdown
if (graceful_shutdown) {
fprintf(stderr, "Attempting graceful shutdown of active tunnels...\n");
// Send tunnel_close messages for all active tunnels
pthread_mutex_lock(&tunnel_mutex);
int tunnels_closed = 0;
for (int i = 0; i < active_tunnels_count; i++) {
tunnel_t *tunnel = active_tunnels[i];
if (tunnel && tunnel->active && tunnel->ssl) {
if (config->debug) {
printf("[DEBUG] Sending tunnel_close for tunnel %s\n", tunnel->request_id);
fflush(stdout);
}
send_tunnel_close(tunnel->ssl, tunnel->request_id, config->debug);
tunnels_closed++;
}
}
pthread_mutex_unlock(&tunnel_mutex);
if (tunnels_closed > 0) {
fprintf(stderr, "Sent close messages for %d tunnels, waiting up to 3 seconds...\n", tunnels_closed);
// Wait up to 3 seconds for tunnel close operations to complete
time_t shutdown_start = time(NULL);
while (time(NULL) - shutdown_start < 3) {
// Check if all tunnels are closed
pthread_mutex_lock(&tunnel_mutex);
int active_count = 0;
for (int i = 0; i < active_tunnels_count; i++) {
if (active_tunnels[i] && active_tunnels[i]->active) {
active_count++;
}
}
pthread_mutex_unlock(&tunnel_mutex);
if (active_count == 0) {
fprintf(stderr, "All tunnels closed gracefully\n");
break;
}
usleep(100000); // Sleep 100ms before checking again
}
}
break;
}
......@@ -1558,9 +1606,9 @@ int main(int argc, char *argv[]) {
time_t last_status_time = 0;
while (1) {
// Check for SIGINT before attempting to reconnect
if (sigint_received) {
printf("SIGINT received, exiting...\n");
// Check for graceful shutdown before attempting to reconnect
if (graceful_shutdown) {
printf("Graceful shutdown requested, exiting...\n");
cleanup_tunnel(config.debug);
break;
}
......
......@@ -43,6 +43,22 @@
#include "libwsssht/threads.h"
#include "libwsssht/control_messages.h"
volatile sig_atomic_t sigint_count = 0;
volatile sig_atomic_t graceful_shutdown = 0;
void sigint_handler(int sig __attribute__((unused))) {
sigint_count++;
if (sigint_count == 1) {
fprintf(stderr, "\nReceived SIGINT, attempting graceful shutdown...\n");
fflush(stderr);
graceful_shutdown = 1;
} else if (sigint_count >= 2) {
fprintf(stderr, "\nReceived second SIGINT, exiting immediately...\n");
fflush(stderr);
exit(1);
}
}
int main(int argc, char *argv[]) {
// Read config from wsssht.conf
char *config_domain = read_config_value_from_file("wssshd-host", "wsssht");
......@@ -149,6 +165,9 @@ int main(int argc, char *argv[]) {
pthread_mutex_init(&tunnel_mutex, NULL);
pthread_mutex_init(&ssl_mutex, NULL);
// Set up signal handler for SIGINT
signal(SIGINT, sigint_handler);
// Parse wsssht arguments
int remaining_argc;
char **remaining_argv;
......@@ -394,6 +413,34 @@ int main(int argc, char *argv[]) {
const int timeout_check_interval = 10; // Check timeouts every 10 seconds
while (1) {
// Check for graceful shutdown
if (graceful_shutdown) {
fprintf(stderr, "Attempting graceful shutdown of active tunnel...\n");
// Send tunnel_close message for the active tunnel
pthread_mutex_lock(&tunnel_mutex);
if (active_tunnel && active_tunnel->active && active_tunnel->ssl) {
if (config.debug) {
printf("[DEBUG] Sending tunnel_close for tunnel %s\n", active_tunnel->request_id);
fflush(stdout);
}
send_tunnel_close(active_tunnel->ssl, active_tunnel->request_id, config.debug);
// Wait up to 3 seconds for tunnel close to complete
fprintf(stderr, "Sent close message for tunnel, waiting up to 3 seconds...\n");
time_t shutdown_start = time(NULL);
while (time(NULL) - shutdown_start < 3) {
if (!active_tunnel->active) {
fprintf(stderr, "Tunnel closed gracefully\n");
break;
}
usleep(100000); // Sleep 100ms before checking again
}
}
pthread_mutex_unlock(&tunnel_mutex);
break;
}
// Get SSL fd with mutex protection
pthread_mutex_lock(&tunnel_mutex);
if (!active_tunnel || !active_tunnel->active) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment