Coverage Report

Created: 2025-07-11 06:21

/src/libevent/signalfd.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Signal handling backend based on signalfd(2) system call
3
 * Written by Dmitry Antipov <dantipov@cloudlinux.com> 2022
4
 *
5
 * Redistribution and use in source and binary forms, with or without
6
 * modification, are permitted provided that the following conditions
7
 * are met:
8
 * 1. Redistributions of source code must retain the above copyright
9
 *    notice, this list of conditions and the following disclaimer.
10
 * 2. Redistributions in binary form must reproduce the above copyright
11
 *    notice, this list of conditions and the following disclaimer in the
12
 *    documentation and/or other materials provided with the distribution.
13
 * 3. The name of the author may not be used to endorse or promote products
14
 *    derived from this software without specific prior written permission.
15
 *
16
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
 */
27
28
#include "event2/event-config.h"
29
30
#include <unistd.h>
31
#include <sys/signalfd.h>
32
33
#include "event2/event.h"
34
#include "event-internal.h"
35
#include "evmap-internal.h"
36
#include "evsignal-internal.h"
37
#include "evthread-internal.h"
38
39
static int sigfd_add(struct event_base *, evutil_socket_t, short, short, void *);
40
static int sigfd_del(struct event_base *, evutil_socket_t, short, short, void *);
41
42
static const struct eventop sigfdops = {
43
  "signalfd_signal",
44
  NULL,
45
  sigfd_add,
46
  sigfd_del,
47
  NULL,
48
  NULL,
49
  0, 0, 0
50
};
51
52
static void
53
sigfd_cb(evutil_socket_t fd, short what, void *arg)
54
0
{
55
0
  struct signalfd_siginfo fdsi;
56
0
  struct event_base *base = arg;
57
0
  ssize_t ret = read(fd, &fdsi, sizeof(fdsi));
58
59
0
  EVUTIL_ASSERT(ret == sizeof(fdsi));
60
0
  EVUTIL_ASSERT(fdsi.ssi_signo > 0 && fdsi.ssi_signo < NSIG);
61
0
  EVUTIL_ASSERT(base->sig.ev_sigevent[fdsi.ssi_signo] != NULL);
62
63
0
  EVBASE_ACQUIRE_LOCK(base, th_base_lock);
64
0
  evmap_signal_active_(base, fdsi.ssi_signo, 1);
65
0
  EVBASE_RELEASE_LOCK(base, th_base_lock);
66
0
}
67
68
static void
69
sigfd_free_sigevent(struct event_base *base, int signo)
70
0
{
71
0
  int ret;
72
0
  struct event* sigev = base->sig.ev_sigevent[signo];
73
74
0
  EVUTIL_ASSERT(sigev != NULL);
75
0
  event_del_nolock_(sigev, EVENT_DEL_AUTOBLOCK);
76
0
  ret = close(sigev->ev_fd);
77
0
  EVUTIL_ASSERT(ret == 0);
78
0
  mm_free(sigev);
79
0
  base->sig.ev_sigevent[signo] = NULL;
80
0
}
81
82
static int
83
sigfd_add(struct event_base *base, int signo, short old, short events, void *p)
84
0
{
85
0
  int sigfd;
86
0
  sigset_t mask;
87
0
  struct event* sigev;
88
0
  struct evsig_info *sig = &base->sig;
89
90
  /* EV_SIGNAL event passed from evmap_signal_add_() when setting
91
           up and from evmap_signal_reinit_iter_fn() during reinit. */
92
0
  EVUTIL_ASSERT(p != NULL);
93
94
0
  EVUTIL_ASSERT(signo > 0 && signo < NSIG);
95
0
  sigev = base->sig.ev_sigevent[signo];
96
97
0
  if (sigev != NULL) {
98
0
    if (old) {
99
      /* We're performing reinit after fork(). This is
100
         required at least for epoll(2)-based backend
101
         because if the process uses fork(2) to create
102
         a child process, then the child will be able
103
         to read(2) signals that are sent to it using the
104
         signalfd(2) file descriptor, but epoll_wait(2)
105
         will not indicate that the signalfd file
106
         descriptor is ready. */
107
0
      sigfd_free_sigevent(base, signo);
108
0
    } else {
109
      /* We have an active signal fd
110
         for this signal already. */
111
0
      return 0;
112
0
    }
113
0
  }
114
115
  /* Save previous handler just like evsig_set_handler_() does. */
116
0
  if (evsig_ensure_saved_(sig, signo) < 0)
117
0
    return -1;
118
119
0
  sig->sh_old[signo] = mm_malloc(sizeof *sig->sh_old[signo]);
120
0
  if (sig->sh_old[signo] == NULL) {
121
0
    event_warn("malloc() failed");
122
0
    return -1;
123
0
  }
124
125
0
  if (sigaction(signo, NULL, sig->sh_old[signo]) == -1) {
126
0
    event_warn("sigaction() failed");
127
0
    mm_free(sig->sh_old[signo]);
128
0
    sig->sh_old[signo] = NULL;
129
0
    return -1;
130
0
  }
131
132
  /* Block the signal from being handled according to its default
133
     disposition so it may be received via the descriptor. */
134
0
  sigemptyset(&mask);
135
0
  sigaddset(&mask, signo);
136
0
  if (sigprocmask(SIG_BLOCK, &mask, NULL)) {
137
0
    event_warn("sigprocmask() failed");
138
0
    return -1;
139
0
  }
140
141
0
  sigfd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC);
142
0
  if (sigfd < 0) {
143
0
    event_warn("signalfd() failed");
144
0
    goto unblock;
145
0
  }
146
147
  /* EV_READ event used to wakeup corresponding EV_SIGNAL ones. */
148
0
  sigev = event_new(base, sigfd, EV_READ | EV_PERSIST, sigfd_cb, base);
149
0
  if (!sigev)
150
0
    goto close_fd;
151
152
  /* This was blindly copied from evsig_init_(). */
153
0
  sigev->ev_flags |= EVLIST_INTERNAL;
154
0
  event_priority_set(sigev, 0);
155
156
0
  if (event_add_nolock_(sigev, NULL, 0) < 0)
157
0
    goto free_ev;
158
159
0
  base->sig.ev_sigevent[signo] = sigev;
160
0
  return 0;
161
0
free_ev:
162
0
  mm_free(sigev);
163
0
close_fd:
164
0
  close(sigfd);
165
0
unblock:
166
0
  sigprocmask(SIG_UNBLOCK, &mask, NULL);
167
0
  return -1;
168
0
}
169
170
static int
171
sigfd_del(struct event_base *base, int signo, short old, short events, void *p)
172
0
{
173
0
  sigset_t mask;
174
0
  struct event *sigev;
175
0
  struct evsig_info *sig = &base->sig;
176
177
0
  EVUTIL_ASSERT(signo > 0 && signo < NSIG);
178
0
  sigev = base->sig.ev_sigevent[signo];
179
0
  EVUTIL_ASSERT(sigev != NULL);
180
181
0
  sigemptyset(&mask);
182
0
  sigaddset(&mask, signo);
183
0
  if (sigprocmask(SIG_UNBLOCK, &mask, NULL)) {
184
0
    event_warn("sigprocmask() failed");
185
0
    return -1;
186
0
  }
187
188
  /* Restore previous handler, if any. */
189
0
  if (signo < sig->sh_old_max) {
190
0
    struct sigaction *sa = sig->sh_old[signo];
191
0
    if (sa) {
192
0
      if (sigaction(signo, sa, NULL) == -1) {
193
0
        event_warn("sigaction() failed");
194
0
        return -1;
195
0
      }
196
0
      mm_free(sa);
197
0
      sig->sh_old[signo] = NULL;
198
0
    }
199
0
  }
200
201
0
  sigfd_free_sigevent(base, signo);
202
0
  return 0;
203
0
}
204
205
int sigfd_init_(struct event_base *base)
206
0
{
207
0
  EVUTIL_ASSERT(base != NULL);
208
0
  if (!(base->flags & EVENT_BASE_FLAG_USE_SIGNALFD) &&
209
0
      !getenv("EVENT_USE_SIGNALFD"))
210
0
    return -1;
211
0
  base->evsigsel = &sigfdops;
212
0
  return 0;
213
0
}