Coverage Report

Created: 2023-09-25 06:56

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