Coverage Report

Created: 2025-07-01 06:46

/src/FreeRDP/libfreerdp/utils/signal.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * FreeRDP: A Remote Desktop Protocol Implementation
3
 * Signal handling
4
 *
5
 * Copyright 2011 Shea Levy <shea@shealevy.com>
6
 *
7
 * Licensed under the Apache License, Version 2.0 (the "License");
8
 * you may not use this file except in compliance with the License.
9
 * You may obtain a copy of the License at
10
 *
11
 *     http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 * Unless required by applicable law or agreed to in writing, software
14
 * distributed under the License is distributed on an "AS IS" BASIS,
15
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 * See the License for the specific language governing permissions and
17
 * limitations under the License.
18
 */
19
20
#include <freerdp/config.h>
21
22
#include <stddef.h>
23
#include <errno.h>
24
#include <string.h>
25
26
#include <winpr/crt.h>
27
28
#include <freerdp/utils/signal.h>
29
#include <freerdp/log.h>
30
31
#ifndef _WIN32
32
#include <signal.h>
33
#include <termios.h>
34
#endif
35
36
0
#define TAG FREERDP_TAG("utils.signal")
37
38
#ifdef _WIN32
39
40
int freerdp_handle_signals(void)
41
{
42
  errno = ENOSYS;
43
  return -1;
44
}
45
46
BOOL freerdp_add_signal_cleanup_handler(void* context, freerdp_signal_handler_t fkt)
47
{
48
  return FALSE;
49
}
50
51
BOOL freerdp_del_signal_cleanup_handler(void* context, freerdp_signal_handler_t fkt)
52
{
53
  return FALSE;
54
}
55
#else
56
57
#include <pthread.h>
58
#include <winpr/debug.h>
59
60
static BOOL handlers_registered = FALSE;
61
static pthread_mutex_t signal_handler_lock = PTHREAD_MUTEX_INITIALIZER;
62
63
typedef struct
64
{
65
  void* context;
66
  freerdp_signal_handler_t handler;
67
} cleanup_handler_t;
68
69
static size_t cleanup_handler_count = 0;
70
static cleanup_handler_t cleanup_handlers[20] = { 0 };
71
72
static void lock(void)
73
0
{
74
0
  const int rc = pthread_mutex_lock(&signal_handler_lock);
75
0
  if (rc != 0)
76
0
  {
77
0
    char ebuffer[256] = { 0 };
78
0
    WLog_ERR(TAG, "[pthread_mutex_lock] failed with %s [%d]",
79
0
             winpr_strerror(rc, ebuffer, sizeof(ebuffer)), rc);
80
0
  }
81
0
}
82
83
static void unlock(void)
84
0
{
85
0
  const int rc = pthread_mutex_unlock(&signal_handler_lock);
86
0
  if (rc != 0)
87
0
  {
88
0
    char ebuffer[256] = { 0 };
89
0
    WLog_ERR(TAG, "[pthread_mutex_lock] failed with %s [%d]",
90
0
             winpr_strerror(rc, ebuffer, sizeof(ebuffer)), rc);
91
0
  }
92
0
}
93
94
static void term_handler(int signum)
95
0
{
96
0
  static BOOL recursive = FALSE;
97
98
0
  if (!recursive)
99
0
  {
100
0
    recursive = TRUE;
101
    // NOLINTNEXTLINE(concurrency-mt-unsafe)
102
0
    WLog_ERR(TAG, "Caught signal '%s' [%d]", strsignal(signum), signum);
103
0
  }
104
105
0
  lock();
106
0
  for (size_t x = 0; x < cleanup_handler_count; x++)
107
0
  {
108
0
    const cleanup_handler_t empty = { 0 };
109
0
    cleanup_handler_t* cur = &cleanup_handlers[x];
110
0
    if (cur->handler)
111
0
    {
112
      // NOLINTNEXTLINE(concurrency-mt-unsafe)
113
0
      cur->handler(signum, strsignal(signum), cur->context);
114
0
    }
115
0
    *cur = empty;
116
0
  }
117
0
  cleanup_handler_count = 0;
118
0
  unlock();
119
0
}
120
121
static void fatal_handler(int signum)
122
0
{
123
0
  struct sigaction default_sigaction;
124
0
  sigset_t this_mask;
125
0
  static BOOL recursive = FALSE;
126
127
0
  if (!recursive)
128
0
  {
129
0
    recursive = TRUE;
130
    // NOLINTNEXTLINE(concurrency-mt-unsafe)
131
0
    WLog_ERR(TAG, "Caught signal '%s' [%d]", strsignal(signum), signum);
132
133
0
    winpr_log_backtrace(TAG, WLOG_ERROR, 20);
134
0
  }
135
136
0
  default_sigaction.sa_handler = SIG_DFL;
137
0
  sigfillset(&(default_sigaction.sa_mask));
138
0
  default_sigaction.sa_flags = 0;
139
0
  sigaction(signum, &default_sigaction, NULL);
140
0
  sigemptyset(&this_mask);
141
0
  sigaddset(&this_mask, signum);
142
0
  pthread_sigmask(SIG_UNBLOCK, &this_mask, NULL);
143
0
  (void)raise(signum);
144
0
}
145
146
static const int term_signals[] = { SIGINT, SIGKILL, SIGQUIT, SIGSTOP, SIGTERM };
147
148
static const int fatal_signals[] = { SIGABRT,   SIGALRM, SIGBUS,  SIGFPE,  SIGHUP,  SIGILL,
149
                                   SIGSEGV,   SIGTTIN, SIGTTOU, SIGUSR1, SIGUSR2,
150
#ifdef SIGPOLL
151
                                   SIGPOLL,
152
#endif
153
#ifdef SIGPROF
154
                                   SIGPROF,
155
#endif
156
#ifdef SIGSYS
157
                                   SIGSYS,
158
#endif
159
                                   SIGTRAP,
160
#ifdef SIGVTALRM
161
                                   SIGVTALRM,
162
#endif
163
                                   SIGXCPU,   SIGXFSZ };
164
165
static BOOL register_handlers(const int* signals, size_t count, void (*handler)(int))
166
0
{
167
0
  WINPR_ASSERT(signals || (count == 0));
168
0
  WINPR_ASSERT(handler);
169
170
0
  sigset_t orig_set = { 0 };
171
0
  struct sigaction saction = { 0 };
172
173
0
  pthread_sigmask(SIG_BLOCK, &(saction.sa_mask), &orig_set);
174
175
0
  sigfillset(&(saction.sa_mask));
176
0
  sigdelset(&(saction.sa_mask), SIGCONT);
177
178
0
  saction.sa_handler = handler;
179
0
  saction.sa_flags = 0;
180
181
0
  for (size_t x = 0; x < count; x++)
182
0
  {
183
0
    struct sigaction orig_sigaction = { 0 };
184
0
    if (sigaction(signals[x], NULL, &orig_sigaction) == 0)
185
0
    {
186
0
      if (orig_sigaction.sa_handler != SIG_IGN)
187
0
      {
188
0
        sigaction(signals[x], &saction, NULL);
189
0
      }
190
0
    }
191
0
  }
192
193
0
  pthread_sigmask(SIG_SETMASK, &orig_set, NULL);
194
195
0
  return TRUE;
196
0
}
197
198
int freerdp_handle_signals(void)
199
0
{
200
0
  int rc = -1;
201
202
0
  lock();
203
204
0
  WLog_DBG(TAG, "Registering signal hook...");
205
206
0
  if (!register_handlers(fatal_signals, ARRAYSIZE(fatal_signals), fatal_handler))
207
0
    goto fail;
208
0
  if (!register_handlers(term_signals, ARRAYSIZE(term_signals), term_handler))
209
0
    goto fail;
210
211
  /* Ignore SIGPIPE signal. */
212
0
  (void)signal(SIGPIPE, SIG_IGN);
213
0
  handlers_registered = TRUE;
214
0
  rc = 0;
215
0
fail:
216
0
  unlock();
217
0
  return rc;
218
0
}
219
220
BOOL freerdp_add_signal_cleanup_handler(void* context, freerdp_signal_handler_t handler)
221
0
{
222
0
  BOOL rc = FALSE;
223
0
  lock();
224
0
  if (handlers_registered)
225
0
  {
226
0
    if (cleanup_handler_count < ARRAYSIZE(cleanup_handlers))
227
0
    {
228
0
      cleanup_handler_t* cur = &cleanup_handlers[cleanup_handler_count++];
229
0
      cur->context = context;
230
0
      cur->handler = handler;
231
0
    }
232
0
    else
233
0
      WLog_WARN(TAG, "Failed to register cleanup handler, only %" PRIuz " handlers supported",
234
0
                ARRAYSIZE(cleanup_handlers));
235
0
  }
236
0
  rc = TRUE;
237
0
  unlock();
238
0
  return rc;
239
0
}
240
241
BOOL freerdp_del_signal_cleanup_handler(void* context, freerdp_signal_handler_t handler)
242
0
{
243
0
  BOOL rc = FALSE;
244
0
  lock();
245
0
  if (handlers_registered)
246
0
  {
247
0
    for (size_t x = 0; x < cleanup_handler_count; x++)
248
0
    {
249
0
      cleanup_handler_t* cur = &cleanup_handlers[x];
250
0
      if ((cur->context == context) && (cur->handler == handler))
251
0
      {
252
0
        const cleanup_handler_t empty = { 0 };
253
0
        for (size_t y = x + 1; y < cleanup_handler_count - 1; y++)
254
0
        {
255
0
          *cur++ = cleanup_handlers[y];
256
0
        }
257
258
0
        *cur = empty;
259
0
        cleanup_handler_count--;
260
0
        break;
261
0
      }
262
0
    }
263
0
  }
264
0
  rc = TRUE;
265
0
  unlock();
266
0
  return rc;
267
0
}
268
269
#endif