summaryrefslogtreecommitdiffstats
path: root/libfreerdp/utils/signal.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 01:24:41 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 01:24:41 +0000
commita9bcc81f821d7c66f623779fa5147e728eb3c388 (patch)
tree98676963bcdd537ae5908a067a8eb110b93486a6 /libfreerdp/utils/signal.c
parentInitial commit. (diff)
downloadfreerdp3-a9bcc81f821d7c66f623779fa5147e728eb3c388.tar.xz
freerdp3-a9bcc81f821d7c66f623779fa5147e728eb3c388.zip
Adding upstream version 3.3.0+dfsg1.upstream/3.3.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'libfreerdp/utils/signal.c')
-rw-r--r--libfreerdp/utils/signal.c264
1 files changed, 264 insertions, 0 deletions
diff --git a/libfreerdp/utils/signal.c b/libfreerdp/utils/signal.c
new file mode 100644
index 0000000..487b9b0
--- /dev/null
+++ b/libfreerdp/utils/signal.c
@@ -0,0 +1,264 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * Signal handling
+ *
+ * Copyright 2011 Shea Levy <shea@shealevy.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <freerdp/config.h>
+
+#include <stddef.h>
+#include <errno.h>
+#include <string.h>
+
+#include <winpr/crt.h>
+
+#include <freerdp/utils/signal.h>
+#include <freerdp/log.h>
+
+#ifndef _WIN32
+#include <signal.h>
+#include <termios.h>
+#endif
+
+#define TAG FREERDP_TAG("utils.signal")
+
+#ifdef _WIN32
+
+int freerdp_handle_signals(void)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+BOOL freerdp_add_signal_cleanup_handler(void* context, freerdp_signal_handler_t fkt)
+{
+ return FALSE;
+}
+
+BOOL freerdp_del_signal_cleanup_handler(void* context, freerdp_signal_handler_t fkt)
+{
+ return FALSE;
+}
+#else
+
+#include <pthread.h>
+#include <winpr/debug.h>
+
+static BOOL handlers_registered = FALSE;
+static pthread_mutex_t signal_handler_lock = PTHREAD_MUTEX_INITIALIZER;
+
+typedef struct
+{
+ void* context;
+ freerdp_signal_handler_t handler;
+} cleanup_handler_t;
+
+static size_t cleanup_handler_count = 0;
+static cleanup_handler_t cleanup_handlers[20] = { 0 };
+
+static void lock(void)
+{
+ const int rc = pthread_mutex_lock(&signal_handler_lock);
+ if (rc != 0)
+ {
+ char ebuffer[256] = { 0 };
+ WLog_ERR(TAG, "[pthread_mutex_lock] failed with %s [%d]",
+ winpr_strerror(rc, ebuffer, sizeof(ebuffer)), rc);
+ }
+}
+
+static void unlock(void)
+{
+ const int rc = pthread_mutex_unlock(&signal_handler_lock);
+ if (rc != 0)
+ {
+ char ebuffer[256] = { 0 };
+ WLog_ERR(TAG, "[pthread_mutex_lock] failed with %s [%d]",
+ winpr_strerror(rc, ebuffer, sizeof(ebuffer)), rc);
+ }
+}
+
+static void term_handler(int signum)
+{
+ static BOOL recursive = FALSE;
+
+ if (!recursive)
+ {
+ recursive = TRUE;
+ WLog_ERR(TAG, "Caught signal '%s' [%d]", strsignal(signum), signum);
+ }
+
+ lock();
+ for (size_t x = 0; x < cleanup_handler_count; x++)
+ {
+ const cleanup_handler_t empty = { 0 };
+ cleanup_handler_t* cur = &cleanup_handlers[x];
+ if (cur->handler)
+ cur->handler(signum, strsignal(signum), cur->context);
+ *cur = empty;
+ }
+ cleanup_handler_count = 0;
+ unlock();
+}
+
+static void fatal_handler(int signum)
+{
+ struct sigaction default_sigaction;
+ sigset_t this_mask;
+ static BOOL recursive = FALSE;
+
+ if (!recursive)
+ {
+ recursive = TRUE;
+ WLog_ERR(TAG, "Caught signal '%s' [%d]", strsignal(signum), signum);
+
+ winpr_log_backtrace(TAG, WLOG_ERROR, 20);
+ }
+
+ default_sigaction.sa_handler = SIG_DFL;
+ sigfillset(&(default_sigaction.sa_mask));
+ default_sigaction.sa_flags = 0;
+ sigaction(signum, &default_sigaction, NULL);
+ sigemptyset(&this_mask);
+ sigaddset(&this_mask, signum);
+ pthread_sigmask(SIG_UNBLOCK, &this_mask, NULL);
+ raise(signum);
+}
+
+static const int term_signals[] = { SIGINT, SIGKILL, SIGQUIT, SIGSTOP, SIGTERM };
+
+static const int fatal_signals[] = { SIGABRT, SIGALRM, SIGBUS, SIGFPE, SIGHUP, SIGILL,
+ SIGSEGV, SIGTSTP, SIGTTIN, SIGTTOU, SIGUSR1, SIGUSR2,
+#ifdef SIGPOLL
+ SIGPOLL,
+#endif
+#ifdef SIGPROF
+ SIGPROF,
+#endif
+#ifdef SIGSYS
+ SIGSYS,
+#endif
+ SIGTRAP,
+#ifdef SIGVTALRM
+ SIGVTALRM,
+#endif
+ SIGXCPU, SIGXFSZ };
+
+static BOOL register_handlers(const int* signals, size_t count, void (*handler)(int))
+{
+ WINPR_ASSERT(signals || (count == 0));
+ WINPR_ASSERT(handler);
+
+ sigset_t orig_set = { 0 };
+ struct sigaction saction = { 0 };
+
+ pthread_sigmask(SIG_BLOCK, &(saction.sa_mask), &orig_set);
+
+ sigfillset(&(saction.sa_mask));
+ sigdelset(&(saction.sa_mask), SIGCONT);
+
+ saction.sa_handler = handler;
+ saction.sa_flags = 0;
+
+ for (size_t x = 0; x < count; x++)
+ {
+ struct sigaction orig_sigaction = { 0 };
+ if (sigaction(signals[x], NULL, &orig_sigaction) == 0)
+ {
+ if (orig_sigaction.sa_handler != SIG_IGN)
+ {
+ sigaction(signals[x], &saction, NULL);
+ }
+ }
+ }
+
+ pthread_sigmask(SIG_SETMASK, &orig_set, NULL);
+
+ return TRUE;
+}
+
+int freerdp_handle_signals(void)
+{
+ int rc = -1;
+
+ lock();
+
+ WLog_DBG(TAG, "Registering signal hook...");
+
+ if (!register_handlers(fatal_signals, ARRAYSIZE(fatal_signals), fatal_handler))
+ goto fail;
+ if (!register_handlers(term_signals, ARRAYSIZE(term_signals), term_handler))
+ goto fail;
+
+ /* Ignore SIGPIPE signal. */
+ signal(SIGPIPE, SIG_IGN);
+ handlers_registered = TRUE;
+ rc = 0;
+fail:
+ unlock();
+ return rc;
+}
+
+BOOL freerdp_add_signal_cleanup_handler(void* context, freerdp_signal_handler_t handler)
+{
+ BOOL rc = FALSE;
+ lock();
+ if (handlers_registered)
+ {
+ if (cleanup_handler_count < ARRAYSIZE(cleanup_handlers))
+ {
+ cleanup_handler_t* cur = &cleanup_handlers[cleanup_handler_count++];
+ cur->context = context;
+ cur->handler = handler;
+ }
+ else
+ WLog_WARN(TAG, "Failed to register cleanup handler, only %" PRIuz " handlers supported",
+ ARRAYSIZE(cleanup_handlers));
+ }
+ rc = TRUE;
+ unlock();
+ return rc;
+}
+
+BOOL freerdp_del_signal_cleanup_handler(void* context, freerdp_signal_handler_t handler)
+{
+ BOOL rc = FALSE;
+ lock();
+ if (handlers_registered)
+ {
+ for (size_t x = 0; x < cleanup_handler_count; x++)
+ {
+ cleanup_handler_t* cur = &cleanup_handlers[x];
+ if ((cur->context == context) && (cur->handler == handler))
+ {
+ const cleanup_handler_t empty = { 0 };
+ for (size_t y = x + 1; y < cleanup_handler_count - 1; y++)
+ {
+ *cur++ = cleanup_handlers[y];
+ }
+
+ *cur = empty;
+ cleanup_handler_count--;
+ break;
+ }
+ }
+ }
+ rc = TRUE;
+ unlock();
+ return rc;
+}
+
+#endif