Coverage Report

Created: 2026-03-31 07:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/open62541/arch/posix/eventloop_posix_interrupt.c
Line
Count
Source
1
/* This Source Code Form is subject to the terms of the Mozilla Public
2
 * License, v. 2.0. If a copy of the MPL was not distributed with this
3
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
 *
5
 *    Copyright 2021, 2024 (c) Fraunhofer IOSB (Author: Julius Pfrommer)
6
 */
7
8
#include "eventloop_posix.h"
9
#include <signal.h>
10
11
#if defined(UA_ARCHITECTURE_POSIX) && !defined(UA_ARCHITECTURE_LWIP) || defined(UA_ARCHITECTURE_WIN32)
12
/* Different implementation approaches:
13
 * - Linux: Use signalfd
14
 * - Other: Use the self-pipe trick (http://cr.yp.to/docs/selfpipe.html) */
15
16
typedef struct UA_RegisteredSignal {
17
#ifdef UA_HAVE_EPOLL
18
    /* With epoll, register each signal with a socket.
19
     * This has to be the first element of the struct to allow casting. */
20
    UA_RegisteredFD rfd;
21
#endif
22
23
    LIST_ENTRY(UA_RegisteredSignal) listPointers;
24
25
    UA_InterruptCallback signalCallback;
26
    void *context;
27
    int signal; /* POSIX identifier of the interrupt signal */
28
29
    UA_Boolean active; /* Signals are only active when the EventLoop is started */
30
    UA_Boolean triggered;
31
} UA_RegisteredSignal;
32
33
typedef struct {
34
    UA_InterruptManager im;
35
36
    LIST_HEAD(, UA_RegisteredSignal) signals; /* Registered signals */
37
38
#ifndef UA_HAVE_EPOLL
39
    UA_DelayedCallback dc; /* Process all triggered signals */
40
#endif
41
} UA_POSIXInterruptManager;
42
43
/* The following methods have to be implemented for epoll/self-pipe each. */
44
static void activateSignal(UA_RegisteredSignal *rs);
45
static void deactivateSignal(UA_RegisteredSignal *rs);
46
47
#ifdef UA_HAVE_EPOLL
48
#include <sys/signalfd.h>
49
50
static void
51
0
handlePOSIXInterruptEvent(UA_EventSource *es, UA_RegisteredFD *rfd, short event) {
52
0
    UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX *)es->eventLoop;
53
0
    (void)el;
54
0
    UA_LOCK_ASSERT(&el->elMutex);
55
56
0
    UA_RegisteredSignal *rs = (UA_RegisteredSignal*)rfd;
57
0
    struct signalfd_siginfo fdsi;
58
0
    ssize_t s = read(rfd->fd, &fdsi, sizeof(fdsi));
59
0
    if(s < (ssize_t)sizeof(fdsi)) {
60
        /* A problem occured */
61
0
        deactivateSignal(rs);
62
0
        return;
63
0
    }
64
65
    /* Signal received */
66
0
    UA_LOG_DEBUG(es->eventLoop->logger, UA_LOGCATEGORY_EVENTLOOP,
67
0
                 "Interrupt %u\t| Received a signal %u",
68
0
                 (unsigned)rfd->fd, fdsi.ssi_signo);
69
70
0
    UA_UNLOCK(&el->elMutex);
71
0
    rs->signalCallback((UA_InterruptManager *)es, (uintptr_t)rfd->fd,
72
0
                       rs->context, &UA_KEYVALUEMAP_NULL);
73
0
    UA_LOCK(&el->elMutex);
74
0
}
75
76
static void
77
0
activateSignal(UA_RegisteredSignal *rs) {
78
0
    UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX *)rs->rfd.es->eventLoop;
79
0
    (void)el;
80
0
    UA_LOCK_ASSERT(&el->elMutex);
81
82
0
    if(rs->active)
83
0
        return;
84
85
    /* Block the normal signal handling */
86
0
    UA_RESET_ERRNO;
87
0
    sigset_t mask;
88
0
    sigemptyset(&mask);
89
0
    sigaddset(&mask, rs->signal);
90
0
    int res2 = sigprocmask(SIG_BLOCK, &mask, NULL);
91
0
    if(res2 == -1) {
92
0
        UA_LOG_SOCKET_ERRNO_WRAP(
93
0
            UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP,
94
0
                           "Interrupt\t| Could not block the default "
95
0
                           "signal handling with an error: %s",
96
0
                           errno_str));
97
0
        return;
98
0
    }
99
100
    /* Create the fd */
101
0
    UA_RESET_ERRNO;
102
0
    UA_FD newfd = signalfd(-1, &mask, 0);
103
0
    if(newfd < 0) {
104
0
        UA_LOG_SOCKET_ERRNO_WRAP(
105
0
            UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP,
106
0
                           "Interrupt\t| Could not create a signal file "
107
0
                           "description with error: %s",
108
0
                           errno_str));
109
0
        sigprocmask(SIG_UNBLOCK, &mask, NULL); /* restore signal */
110
0
        return;
111
0
    }
112
113
0
    rs->rfd.fd = newfd;
114
0
    rs->rfd.eventSourceCB = handlePOSIXInterruptEvent;
115
0
    rs->rfd.listenEvents = UA_FDEVENT_IN;
116
117
    /* Register the fd in the EventLoop */
118
0
    UA_StatusCode res = UA_EventLoopPOSIX_registerFD(el, &rs->rfd);
119
0
    if(res != UA_STATUSCODE_GOOD) {
120
0
        UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP,
121
0
                       "Interrupt\t| Could not register the a signal file "
122
0
                       "description in the EventLoop");
123
0
        UA_close(newfd);
124
0
        sigprocmask(SIG_UNBLOCK, &mask, NULL); /* restore signal */
125
0
        return;
126
0
    }
127
128
0
    rs->active = true;
129
0
}
130
131
static void
132
0
deactivateSignal(UA_RegisteredSignal *rs) {
133
0
    UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX *)rs->rfd.es->eventLoop;
134
0
    (void)el;
135
0
    UA_LOCK_ASSERT(&el->elMutex);
136
137
    /* Only dectivate if active */
138
0
    if(!rs->active)
139
0
        return;
140
0
    rs->active = false;
141
142
    /* Stop receiving the signal on the FD */
143
0
    UA_EventLoopPOSIX_deregisterFD(el, &rs->rfd);
144
145
    /* Unblock the signal */
146
0
    sigset_t mask;
147
0
    sigemptyset(&mask);
148
0
    sigaddset(&mask, (int)rs->signal);
149
0
    sigprocmask(SIG_UNBLOCK, &mask, NULL);
150
151
    /* Clean up locally */
152
0
    UA_close(rs->rfd.fd);
153
0
}
154
155
#else /* !UA_HAVE_EPOLL */
156
157
/* When using signal() a global pointer to the interrupt manager is required.
158
 * We have no other we to get additional data with the interrupt. */
159
static UA_POSIXInterruptManager *singletonIM = NULL;
160
161
/* Execute all triggered interrupts in a delayed callback by the EventLoop */
162
static void
163
executeTriggeredPOSIXInterrupts(UA_POSIXInterruptManager *im, void *_) {
164
    im->dc.callback = NULL; /* Allow to re-arm the delayed callback */
165
166
    UA_RegisteredSignal *rs, *rs_tmp;
167
    LIST_FOREACH_SAFE(rs, &im->signals, listPointers, rs_tmp) {
168
        rs->triggered = false;
169
        rs->signalCallback(&im->im, (uintptr_t)rs->signal,
170
                           rs->context, &UA_KEYVALUEMAP_NULL);
171
    }
172
}
173
174
/* Mark the signal entry as triggered and make the EventLoop process it "soon"
175
 * with a delayed callback. This is an interrupt handler and cannot take a
176
 * lock. */
177
static void
178
triggerPOSIXInterruptEvent(int sig) {
179
    UA_assert(singletonIM != NULL);
180
181
    /* Find the signal */
182
    UA_RegisteredSignal *rs;
183
    LIST_FOREACH(rs, &singletonIM->signals, listPointers) {
184
        if(rs->signal == sig)
185
            break;
186
    }
187
    if(!rs || rs->triggered || !rs->active)
188
        return;
189
190
    /* Mark as triggered */
191
    rs->triggered = true;
192
193
#ifdef UA_ARCHITECTURE_WIN32
194
    /* On WIN32 we have to re-arm the signal or it will go back to SIG_DFL */
195
    signal(sig, triggerPOSIXInterruptEvent);
196
#endif
197
198
    UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)singletonIM->im.eventSource.eventLoop;
199
200
    /* Arm the delayed callback for processing the received signals */
201
    if(!singletonIM->dc.callback) {
202
        singletonIM->dc.callback = (UA_Callback)executeTriggeredPOSIXInterrupts;
203
        singletonIM->dc.application = singletonIM;
204
        singletonIM->dc.context = NULL;
205
        UA_EventLoopPOSIX_addDelayedCallback(&el->eventLoop, &singletonIM->dc);
206
    }
207
208
    /* Cancel the EventLoop if it is currently waiting with a timeout */
209
    UA_EventLoopPOSIX_cancel(el);
210
}
211
212
static void
213
activateSignal(UA_RegisteredSignal *rs) {
214
    UA_assert(singletonIM != NULL);
215
    UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)singletonIM->im.eventSource.eventLoop;
216
    (void)el;
217
    UA_LOCK_ASSERT(&el->elMutex);
218
219
    /* Register the signal on the OS level */
220
    if(rs->active)
221
        return;
222
223
    UA_RESET_ERRNO;
224
    void (*prev)(int);
225
    prev = signal(rs->signal, triggerPOSIXInterruptEvent);
226
    if(prev == SIG_ERR) {
227
        UA_LOG_SOCKET_ERRNO_WRAP(
228
           UA_LOG_WARNING(singletonIM->im.eventSource.eventLoop->logger,
229
                          UA_LOGCATEGORY_EVENTLOOP,
230
                          "Error registering the signal: %s", errno_str));
231
        return;
232
    }
233
234
    rs->active = true;
235
}
236
237
static void
238
deactivateSignal(UA_RegisteredSignal *rs) {
239
    UA_assert(singletonIM != NULL);
240
    UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)singletonIM->im.eventSource.eventLoop;
241
    (void)el;
242
    UA_LOCK_ASSERT(&el->elMutex);
243
244
    /* Only dectivate if active */
245
    if(!rs->active)
246
        return;
247
248
    /* Stop receiving the signal */
249
    signal(rs->signal, SIG_DFL);
250
251
    rs->triggered = false;
252
    rs->active = false;
253
}
254
255
#endif /* !UA_HAVE_EPOLL */
256
257
static UA_StatusCode
258
registerPOSIXInterrupt(UA_InterruptManager *im, uintptr_t interruptHandle,
259
                       const UA_KeyValueMap *params,
260
0
                       UA_InterruptCallback callback, void *interruptContext) {
261
0
    UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX *)im->eventSource.eventLoop;
262
0
    if(!UA_KeyValueMap_isEmpty(params)) {
263
0
        UA_LOG_ERROR(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP,
264
0
                     "Interrupt\t| Supplied parameters invalid for the "
265
0
                     "POSIX InterruptManager");
266
0
        return UA_STATUSCODE_BADINTERNALERROR;
267
0
    }
268
269
0
    UA_LOCK(&el->elMutex);
270
271
    /* Was the signal already registered? */
272
0
    int signal = (int)interruptHandle;
273
0
    UA_POSIXInterruptManager *pim = (UA_POSIXInterruptManager *)im;
274
0
    UA_RegisteredSignal *rs;
275
0
    LIST_FOREACH(rs, &pim->signals, listPointers) {
276
0
        if(rs->signal == signal)
277
0
            break;
278
0
    }
279
0
    if(rs) {
280
0
        UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP,
281
0
                       "Interrupt\t| Signal %u already registered",
282
0
                       (unsigned)interruptHandle);
283
0
        UA_UNLOCK(&el->elMutex);
284
0
        return UA_STATUSCODE_BADINTERNALERROR;
285
0
    }
286
287
    /* Create and populate the new context object */
288
0
    rs = (UA_RegisteredSignal *)UA_calloc(1, sizeof(UA_RegisteredSignal));
289
0
    if(!rs) {
290
0
        UA_UNLOCK(&el->elMutex);
291
0
        return UA_STATUSCODE_BADOUTOFMEMORY;
292
0
    }
293
294
0
#ifdef UA_HAVE_EPOLL
295
0
    rs->rfd.es = &im->eventSource;
296
0
#endif
297
0
    rs->signal = (int)interruptHandle;
298
0
    rs->signalCallback = callback;
299
0
    rs->context = interruptContext;
300
301
    /* Add to the InterruptManager */
302
0
    LIST_INSERT_HEAD(&pim->signals, rs, listPointers);
303
304
    /* Activate if we are already running */
305
0
    if(pim->im.eventSource.state == UA_EVENTSOURCESTATE_STARTED)
306
0
        activateSignal(rs);
307
308
0
    UA_UNLOCK(&el->elMutex);
309
0
    return UA_STATUSCODE_GOOD;
310
0
}
311
312
static void
313
0
deregisterPOSIXInterrupt(UA_InterruptManager *im, uintptr_t interruptHandle) {
314
0
    UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX *)im->eventSource.eventLoop;
315
0
    (void)el;
316
0
    UA_POSIXInterruptManager *pim = (UA_POSIXInterruptManager *)im;
317
0
    UA_LOCK(&el->elMutex);
318
319
0
    int signal = (int)interruptHandle;
320
0
    UA_RegisteredSignal *rs;
321
0
    LIST_FOREACH(rs, &pim->signals, listPointers) {
322
0
        if(rs->signal == signal)
323
0
            break;
324
0
    }
325
0
    if(rs) {
326
0
        deactivateSignal(rs);
327
0
        LIST_REMOVE(rs, listPointers);
328
0
        UA_free(rs);
329
0
    }
330
331
0
    UA_UNLOCK(&el->elMutex);
332
0
}
333
334
static UA_StatusCode
335
553
startPOSIXInterruptManager(UA_EventSource *es) {
336
553
    UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX *)es->eventLoop;
337
553
    (void)el;
338
553
    UA_LOCK(&el->elMutex);
339
340
    /* Check the state */
341
553
    if(es->state != UA_EVENTSOURCESTATE_STOPPED) {
342
0
        UA_LOG_ERROR(es->eventLoop->logger, UA_LOGCATEGORY_EVENTLOOP,
343
0
                     "Interrupt\t| To start the InterruptManager, "
344
0
                     "it has to be registered in an EventLoop and not started");
345
0
        UA_UNLOCK(&el->elMutex);
346
0
        return UA_STATUSCODE_BADINTERNALERROR;
347
0
    }
348
349
553
    UA_POSIXInterruptManager *pim = (UA_POSIXInterruptManager *)es;
350
553
    UA_LOG_DEBUG(es->eventLoop->logger, UA_LOGCATEGORY_EVENTLOOP,
351
553
                 "Interrupt\t| Starting the InterruptManager");
352
353
    /* Activate the registered signal handlers */
354
553
    UA_RegisteredSignal*rs;
355
553
    LIST_FOREACH(rs, &pim->signals, listPointers) {
356
0
        activateSignal(rs);
357
0
    }
358
359
    /* Set the EventSource to the started state */
360
553
    es->state = UA_EVENTSOURCESTATE_STARTED;
361
362
553
    UA_UNLOCK(&el->elMutex);
363
553
    return UA_STATUSCODE_GOOD;
364
553
}
365
366
static void
367
553
stopPOSIXInterruptManager(UA_EventSource *es) {
368
553
    UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX *)es->eventLoop;
369
553
    (void)el;
370
553
    UA_LOCK(&el->elMutex);
371
372
553
    if(es->state != UA_EVENTSOURCESTATE_STARTED) {
373
0
        UA_UNLOCK(&el->elMutex);
374
0
        return;
375
0
    }
376
377
553
    UA_LOG_DEBUG(es->eventLoop->logger, UA_LOGCATEGORY_EVENTLOOP,
378
553
                 "Interrupt\t| Stopping the InterruptManager");
379
380
    /* Close all registered signals */
381
553
    UA_POSIXInterruptManager *pim = (UA_POSIXInterruptManager *)es;
382
553
    UA_RegisteredSignal*rs;
383
553
    LIST_FOREACH(rs, &pim->signals, listPointers) {
384
0
        deactivateSignal(rs);
385
0
    }
386
387
    /* Immediately set to stopped */
388
553
    es->state = UA_EVENTSOURCESTATE_STOPPED;
389
390
553
    UA_UNLOCK(&el->elMutex);
391
553
}
392
393
static UA_StatusCode
394
553
freePOSIXInterruptmanager(UA_EventSource *es) {
395
553
    UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX *)es->eventLoop;
396
553
    (void)el;
397
553
    UA_LOCK_ASSERT(&el->elMutex);
398
399
553
    if(es->state >= UA_EVENTSOURCESTATE_STARTING) {
400
0
        UA_LOG_ERROR(es->eventLoop->logger, UA_LOGCATEGORY_EVENTLOOP,
401
0
                     "Interrupt\t| The EventSource must be stopped "
402
0
                     "before it can be deleted");
403
0
        return UA_STATUSCODE_BADINTERNALERROR;
404
0
    }
405
406
    /* Deactivate and remove all registered signals */
407
553
    UA_POSIXInterruptManager *pim = (UA_POSIXInterruptManager *)es;
408
553
    UA_RegisteredSignal *rs, *rs_tmp;
409
553
    LIST_FOREACH_SAFE(rs, &pim->signals, listPointers, rs_tmp) {
410
0
        deactivateSignal(rs);
411
0
        LIST_REMOVE(rs, listPointers);
412
0
        UA_free(rs);
413
0
    }
414
415
553
    UA_String_clear(&es->name);
416
553
    UA_free(es);
417
418
#ifndef UA_HAVE_EPOLL
419
    singletonIM = NULL; /* Reset the global singleton pointer */
420
#endif
421
422
553
    return UA_STATUSCODE_GOOD;
423
553
}
424
425
UA_InterruptManager *
426
553
UA_InterruptManager_new_POSIX(const UA_String eventSourceName) {
427
#ifndef UA_HAVE_EPOLL
428
    /* There can be only one InterruptManager if epoll is not present */
429
    if(singletonIM)
430
        return NULL;
431
#endif
432
433
553
    UA_POSIXInterruptManager *pim = (UA_POSIXInterruptManager *)
434
553
        UA_calloc(1, sizeof(UA_POSIXInterruptManager));
435
553
    if(!pim)
436
0
        return NULL;
437
438
#ifndef UA_HAVE_EPOLL
439
    singletonIM = pim; /* Register the singleton singleton pointer */
440
#endif
441
442
553
    UA_InterruptManager *im = &pim->im;
443
553
    im->eventSource.eventSourceType = UA_EVENTSOURCETYPE_INTERRUPTMANAGER;
444
553
    UA_String_copy(&eventSourceName, &im->eventSource.name);
445
553
    im->eventSource.start = startPOSIXInterruptManager;
446
553
    im->eventSource.stop = stopPOSIXInterruptManager;
447
553
    im->eventSource.free = freePOSIXInterruptmanager;
448
553
    im->registerInterrupt = registerPOSIXInterrupt;
449
553
    im->deregisterInterrupt = deregisterPOSIXInterrupt;
450
553
    return im;
451
553
}
452
453
#endif