Coverage Report

Created: 2024-05-20 06:11

/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
0
    WLog_ERR(TAG, "Caught signal '%s' [%d]", strsignal(signum), signum);
102
0
  }
103
104
0
  lock();
105
0
  for (size_t x = 0; x < cleanup_handler_count; x++)
106
0
  {
107
0
    const cleanup_handler_t empty = { 0 };
108
0
    cleanup_handler_t* cur = &cleanup_handlers[x];
109
0
    if (cur->handler)
110
0
      cur->handler(signum, strsignal(signum), cur->context);
111
0
    *cur = empty;
112
0
  }
113
0
  cleanup_handler_count = 0;
114
0
  unlock();
115
0
}
116
117
static void fatal_handler(int signum)
118
0
{
119
0
  struct sigaction default_sigaction;
120
0
  sigset_t this_mask;
121
0
  static BOOL recursive = FALSE;
122
123
0
  if (!recursive)
124
0
  {
125
0
    recursive = TRUE;
126
0
    WLog_ERR(TAG, "Caught signal '%s' [%d]", strsignal(signum), signum);
127
128
0
    winpr_log_backtrace(TAG, WLOG_ERROR, 20);
129
0
  }
130
131
0
  default_sigaction.sa_handler = SIG_DFL;
132
0
  sigfillset(&(default_sigaction.sa_mask));
133
0
  default_sigaction.sa_flags = 0;
134
0
  sigaction(signum, &default_sigaction, NULL);
135
0
  sigemptyset(&this_mask);
136
0
  sigaddset(&this_mask, signum);
137
0
  pthread_sigmask(SIG_UNBLOCK, &this_mask, NULL);
138
0
  raise(signum);
139
0
}
140
141
static const int term_signals[] = { SIGINT, SIGKILL, SIGQUIT, SIGSTOP, SIGTERM };
142
143
static const int fatal_signals[] = { SIGABRT,   SIGALRM, SIGBUS,  SIGFPE,  SIGHUP,  SIGILL,
144
                                   SIGSEGV,   SIGTSTP, SIGTTIN, SIGTTOU, SIGUSR1, SIGUSR2,
145
#ifdef SIGPOLL
146
                                   SIGPOLL,
147
#endif
148
#ifdef SIGPROF
149
                                   SIGPROF,
150
#endif
151
#ifdef SIGSYS
152
                                   SIGSYS,
153
#endif
154
                                   SIGTRAP,
155
#ifdef SIGVTALRM
156
                                   SIGVTALRM,
157
#endif
158
                                   SIGXCPU,   SIGXFSZ };
159
160
static BOOL register_handlers(const int* signals, size_t count, void (*handler)(int))
161
0
{
162
0
  WINPR_ASSERT(signals || (count == 0));
163
0
  WINPR_ASSERT(handler);
164
165
0
  sigset_t orig_set = { 0 };
166
0
  struct sigaction saction = { 0 };
167
168
0
  pthread_sigmask(SIG_BLOCK, &(saction.sa_mask), &orig_set);
169
170
0
  sigfillset(&(saction.sa_mask));
171
0
  sigdelset(&(saction.sa_mask), SIGCONT);
172
173
0
  saction.sa_handler = handler;
174
0
  saction.sa_flags = 0;
175
176
0
  for (size_t x = 0; x < count; x++)
177
0
  {
178
0
    struct sigaction orig_sigaction = { 0 };
179
0
    if (sigaction(signals[x], NULL, &orig_sigaction) == 0)
180
0
    {
181
0
      if (orig_sigaction.sa_handler != SIG_IGN)
182
0
      {
183
0
        sigaction(signals[x], &saction, NULL);
184
0
      }
185
0
    }
186
0
  }
187
188
0
  pthread_sigmask(SIG_SETMASK, &orig_set, NULL);
189
190
0
  return TRUE;
191
0
}
192
193
int freerdp_handle_signals(void)
194
0
{
195
0
  int rc = -1;
196
197
0
  lock();
198
199
0
  WLog_DBG(TAG, "Registering signal hook...");
200
201
0
  if (!register_handlers(fatal_signals, ARRAYSIZE(fatal_signals), fatal_handler))
202
0
    goto fail;
203
0
  if (!register_handlers(term_signals, ARRAYSIZE(term_signals), term_handler))
204
0
    goto fail;
205
206
  /* Ignore SIGPIPE signal. */
207
0
  signal(SIGPIPE, SIG_IGN);
208
0
  handlers_registered = TRUE;
209
0
  rc = 0;
210
0
fail:
211
0
  unlock();
212
0
  return rc;
213
0
}
214
215
BOOL freerdp_add_signal_cleanup_handler(void* context, freerdp_signal_handler_t handler)
216
0
{
217
0
  BOOL rc = FALSE;
218
0
  lock();
219
0
  if (handlers_registered)
220
0
  {
221
0
    if (cleanup_handler_count < ARRAYSIZE(cleanup_handlers))
222
0
    {
223
0
      cleanup_handler_t* cur = &cleanup_handlers[cleanup_handler_count++];
224
0
      cur->context = context;
225
0
      cur->handler = handler;
226
0
    }
227
0
    else
228
0
      WLog_WARN(TAG, "Failed to register cleanup handler, only %" PRIuz " handlers supported",
229
0
                ARRAYSIZE(cleanup_handlers));
230
0
  }
231
0
  rc = TRUE;
232
0
  unlock();
233
0
  return rc;
234
0
}
235
236
BOOL freerdp_del_signal_cleanup_handler(void* context, freerdp_signal_handler_t handler)
237
0
{
238
0
  BOOL rc = FALSE;
239
0
  lock();
240
0
  if (handlers_registered)
241
0
  {
242
0
    for (size_t x = 0; x < cleanup_handler_count; x++)
243
0
    {
244
0
      cleanup_handler_t* cur = &cleanup_handlers[x];
245
0
      if ((cur->context == context) && (cur->handler == handler))
246
0
      {
247
0
        const cleanup_handler_t empty = { 0 };
248
0
        for (size_t y = x + 1; y < cleanup_handler_count - 1; y++)
249
0
        {
250
0
          *cur++ = cleanup_handlers[y];
251
0
        }
252
253
0
        *cur = empty;
254
0
        cleanup_handler_count--;
255
0
        break;
256
0
      }
257
0
    }
258
0
  }
259
0
  rc = TRUE;
260
0
  unlock();
261
0
  return rc;
262
0
}
263
264
#endif