Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 47 additions & 10 deletions src/ctrlm_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@
#include <sys/sysinfo.h>
#include <poll.h>
#include <glib.h>
#include <glib-unix.h>
#include <string.h>
#include <semaphore.h>
#include <errno.h>
#include <dlfcn.h>
#include <memory>
#include <algorithm>
Expand Down Expand Up @@ -347,6 +349,8 @@ static gboolean ctrlm_authservice_expired(gpointer user_data);
static gboolean ctrlm_ntp_check(gpointer user_data);
static void ctrlm_signals_register(void);
static void ctrlm_signal_handler(int signal);
static gboolean ctrlm_unix_signal_terminate(gpointer user_data);
static gboolean ctrlm_unix_signal_quit(gpointer user_data);

static void ctrlm_main_iarm_call_status_get_(ctrlm_main_iarm_call_status_t *status);
static void ctrlm_main_iarm_call_property_get_(ctrlm_main_iarm_call_property_t *property);
Expand Down Expand Up @@ -1098,27 +1102,51 @@ gboolean ctrlm_authservice_poll(gpointer user_data) {
}
#endif

// GLib main-loop callbacks for Unix signals — run in the main loop context so
// they are safe to call g_main_loop_quit() and other non-async-signal-safe APIs.
static gboolean ctrlm_unix_signal_terminate(gpointer user_data) {
int sig = GPOINTER_TO_INT(user_data);
XLOGD_INFO("Received %s", sig == SIGINT ? "SIGINT" : "SIGTERM");
ctrlm_quit_main_loop();
return G_SOURCE_CONTINUE;
}

static gboolean ctrlm_unix_signal_quit(gpointer user_data) {
XLOGD_INFO("Received SIGQUIT");
#ifdef BREAKPAD_SUPPORT
ctrlm_crash();
#endif
return G_SOURCE_CONTINUE;
}

void ctrlm_signals_register(void) {
// Handle these signals
// Use g_unix_signal_add() so callbacks run inside the GLib main loop context
// rather than from an async signal handler, avoiding undefined behavior from
// calling non-async-signal-safe functions (e.g. g_main_loop_quit).
XLOGD_INFO("Registering SIGINT...");
if(signal(SIGINT, ctrlm_signal_handler) == SIG_ERR) {
if(0 == g_unix_signal_add(SIGINT, ctrlm_unix_signal_terminate, GINT_TO_POINTER(SIGINT))) {
XLOGD_ERROR("Unable to register for SIGINT.");
}
XLOGD_INFO("Registering SIGTERM...");
if(signal(SIGTERM, ctrlm_signal_handler) == SIG_ERR) {
if(0 == g_unix_signal_add(SIGTERM, ctrlm_unix_signal_terminate, GINT_TO_POINTER(SIGTERM))) {
XLOGD_ERROR("Unable to register for SIGTERM.");
}
XLOGD_INFO("Registering SIGQUIT...");
if(signal(SIGQUIT, ctrlm_signal_handler) == SIG_ERR) {
if(0 == g_unix_signal_add(SIGQUIT, ctrlm_unix_signal_quit, NULL)) {
XLOGD_ERROR("Unable to register for SIGQUIT.");
}
XLOGD_INFO("Registering SIGPIPE...");
if(signal(SIGPIPE, ctrlm_signal_handler) == SIG_ERR) {
XLOGD_ERROR("Unable to register for SIGPIPE.");
// Ignore SIGPIPE — broken-pipe errors are handled at the call site via errno.
XLOGD_INFO("Ignoring SIGPIPE...");
errno = 0;
if(SIG_ERR == signal(SIGPIPE, SIG_IGN)) {
int errsv = errno;
XLOGD_ERROR("Unable to ignore SIGPIPE <%s>", strerror(errsv));
}
}

void ctrlm_signal_handler(int signal) {
// Direct-call fallback only (e.g. from ctrlm_on_network_assert when kill() fails).
// No longer registered as an OS signal handler, so non-async-signal-safe calls are safe.
static void ctrlm_signal_handler(int signal) {
switch(signal) {
case SIGTERM:
case SIGINT: {
Expand Down Expand Up @@ -1233,9 +1261,18 @@ void ctrlm_on_network_assert(ctrlm_network_id_t network_id) {
// Invalidate main thread so terminate does not attempt to terminate it
g_ctrlm.main_thread = NULL;
}
// g_main_loop_quit() will be called in ctrlm_signal_handler(SIGTERM)
// g_main_loop_quit() will be called when the SIGTERM GLib source fires,
// or directly via ctrlm_signal_handler() in the kill() fallback below.
g_ctrlm.return_code = -1;
ctrlm_signal_handler(SIGTERM);

errno = 0;
int rc = kill(getpid(), SIGTERM);
if(rc != 0) {
int errsv = errno;
XLOGD_ERROR("Failed to send SIGTERM to self <%s> - invoking shutdown handler directly", strerror(errsv));
ctrlm_signal_handler(SIGTERM);
}
Comment thread
dwolaver marked this conversation as resolved.
Comment thread
dwolaver marked this conversation as resolved.

Comment thread
dwolaver marked this conversation as resolved.
// give main() time to clean up
sleep(5);
// Exit here in case main fails to exit
Expand Down
34 changes: 32 additions & 2 deletions src/rf4ce/ctrlm_rf4ce_network.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include <algorithm>
#include <regex>
#include <limits.h>
#include <errno.h>
#include <sys/sysinfo.h>
#include "ctrlm.h"
#include "ctrlm_log.h"
Expand Down Expand Up @@ -411,8 +412,37 @@ ctrlm_hal_result_t ctrlm_obj_network_rf4ce_t::hal_init_request(GThread *ctrlm_ma

// Block until initialization is complete or a timeout occurs
XLOGD_INFO("Waiting for %s initialization...", name_get());
sem_wait(&semaphore_);
sem_destroy(&semaphore_);
struct timespec timeout;

int sem_result = 0;
errno = 0;
int rc = clock_gettime(CLOCK_REALTIME, &timeout);
if(rc != 0) {
int errsv = errno;
// If we fail to get the current time, we should still wait on the semaphore, but we will wait indefinitely instead of timing out
XLOGD_ERROR("Failed to get current time <%s>. wait indefinitely", strerror(errsv));
errno = 0;
sem_result = sem_wait(&semaphore_);
} else {
Comment thread
dwolaver marked this conversation as resolved.
timeout.tv_sec += 60; // this operation has been tested to complete in about 6 seconds in worst case scenario (set the timeout to 10x)

errno = 0;
sem_result = sem_timedwait(&semaphore_, &timeout);
}

if(sem_result == -1) {
if(errno == ETIMEDOUT) {
XLOGD_ERROR("Timeout waiting for %s initialization", name_get());
} else if(errno == EINTR) {
XLOGD_ERROR("Interrupted while waiting for %s initialization", name_get());
} else {
int errsv = errno;
XLOGD_ERROR("Error waiting for %s initialization <%s>", name_get(), strerror(errsv));
}
init_result_ = CTRLM_HAL_RESULT_ERROR;
} else {
sem_destroy(&semaphore_);
}
Comment thread
dwolaver marked this conversation as resolved.

ready_ = (CTRLM_HAL_RESULT_SUCCESS == init_result_);

Expand Down
Loading