Coverage Report

Created: 2025-12-31 06:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/lib/tevent/tevent_standard.c
Line
Count
Source
1
/*
2
   Unix SMB/CIFS implementation.
3
   main select loop and event handling
4
   Copyright (C) Stefan Metzmacher      2013
5
   Copyright (C) Jeremy Allison         2013
6
7
     ** NOTE! The following LGPL license applies to the tevent
8
     ** library. This does NOT imply that all of Samba is released
9
     ** under the LGPL
10
11
   This library is free software; you can redistribute it and/or
12
   modify it under the terms of the GNU Lesser General Public
13
   License as published by the Free Software Foundation; either
14
   version 3 of the License, or (at your option) any later version.
15
16
   This library is distributed in the hope that it will be useful,
17
   but WITHOUT ANY WARRANTY; without even the implied warranty of
18
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19
   Lesser General Public License for more details.
20
21
   You should have received a copy of the GNU Lesser General Public
22
   License along with this library; if not, see <http://www.gnu.org/licenses/>.
23
*/
24
25
/*
26
  This is SAMBA's default event loop code
27
28
  - we try to use epoll if configure detected support for it
29
    otherwise we use poll()
30
  - if epoll is broken on the system or the kernel doesn't support it
31
    at runtime we fallback to poll()
32
*/
33
34
#include "replace.h"
35
#include "tevent.h"
36
#include "tevent_util.h"
37
#include "tevent_internal.h"
38
39
struct std_event_glue {
40
  const struct tevent_ops *epoll_ops;
41
  const struct tevent_ops *poll_ops;
42
  struct tevent_ops *glue_ops;
43
  bool fallback_replay;
44
};
45
46
static int std_event_context_init(struct tevent_context *ev);
47
48
static const struct tevent_ops std_event_ops = {
49
  .context_init           = std_event_context_init,
50
};
51
52
/*
53
  If this function gets called. epoll failed at runtime.
54
  Move us to using poll instead. If we return false here,
55
  caller should abort().
56
*/
57
#ifdef HAVE_EPOLL
58
static bool std_fallback_to_poll(struct tevent_context *ev, bool replay)
59
0
{
60
0
  void *glue_ptr = talloc_parent(ev->ops);
61
0
  struct std_event_glue *glue =
62
0
    talloc_get_type_abort(glue_ptr,
63
0
    struct std_event_glue);
64
0
  int ret;
65
0
  struct tevent_fd *fde;
66
67
0
  glue->fallback_replay = replay;
68
69
  /* First switch all the ops to poll. */
70
0
  glue->epoll_ops = NULL;
71
72
  /*
73
   * Set custom_ops the same as poll.
74
   */
75
0
  *glue->glue_ops = *glue->poll_ops;
76
0
  glue->glue_ops->context_init = std_event_context_init;
77
78
  /* Next initialize the poll backend. */
79
0
  ret = glue->poll_ops->context_init(ev);
80
0
  if (ret != 0) {
81
0
    return false;
82
0
  }
83
84
  /*
85
   * Now we have to change all the existing file descriptor
86
   * events from the epoll backend to the poll backend.
87
   */
88
0
  for (fde = ev->fd_events; fde; fde = fde->next) {
89
0
    bool ok;
90
91
    /* Re-add this event as a poll backend event. */
92
0
    ok = tevent_poll_event_add_fd_internal(ev, fde);
93
0
    if (!ok) {
94
0
      return false;
95
0
    }
96
0
  }
97
98
0
  return true;
99
0
}
100
#endif
101
102
static int std_event_loop_once(struct tevent_context *ev, const char *location)
103
0
{
104
0
  void *glue_ptr = talloc_parent(ev->ops);
105
0
  struct std_event_glue *glue =
106
0
    talloc_get_type_abort(glue_ptr,
107
0
    struct std_event_glue);
108
0
  int ret;
109
110
0
  ret = glue->epoll_ops->loop_once(ev, location);
111
  /*
112
   * If the above hasn't panicked due to an epoll interface failure,
113
   * std_fallback_to_poll() wasn't called, and hasn't cleared epoll_ops to
114
   * signify fallback to poll_ops.
115
   */
116
0
  if (glue->epoll_ops != NULL) {
117
    /* No fallback */
118
0
    return ret;
119
0
  }
120
121
0
  if (!glue->fallback_replay) {
122
    /*
123
     * The problem happened while modifying an event.
124
     * An event handler was triggered in this case
125
     * and there is no need to call loop_once() again.
126
     */
127
0
    return ret;
128
0
  }
129
130
0
  return glue->poll_ops->loop_once(ev, location);
131
0
}
132
133
static int std_event_loop_wait(struct tevent_context *ev, const char *location)
134
0
{
135
0
  void *glue_ptr = talloc_parent(ev->ops);
136
0
  struct std_event_glue *glue =
137
0
    talloc_get_type_abort(glue_ptr,
138
0
    struct std_event_glue);
139
0
  int ret;
140
141
0
  ret = glue->epoll_ops->loop_wait(ev, location);
142
  /*
143
   * If the above hasn't panicked due to an epoll interface failure,
144
   * std_fallback_to_poll() wasn't called, and hasn't cleared epoll_ops to
145
   * signify fallback to poll_ops.
146
   */
147
0
  if (glue->epoll_ops != NULL) {
148
    /* No fallback */
149
0
    return ret;
150
0
  }
151
152
0
  return glue->poll_ops->loop_wait(ev, location);
153
0
}
154
/*
155
  Initialize the epoll backend and allow it to call a
156
  switch function if epoll fails at runtime.
157
*/
158
static int std_event_context_init(struct tevent_context *ev)
159
0
{
160
0
  struct std_event_glue *glue;
161
0
  int ret;
162
163
  /*
164
   * If this is the first initialization
165
   * we need to set up the allocated ops
166
   * pointers.
167
   */
168
169
0
  if (ev->ops->loop_once == NULL) {
170
0
    glue = talloc_zero(ev, struct std_event_glue);
171
0
    if (glue == NULL) {
172
0
      return -1;
173
0
    }
174
175
0
    glue->epoll_ops = tevent_find_ops_byname("epoll");
176
177
0
    glue->poll_ops = tevent_find_ops_byname("poll");
178
0
    if (glue->poll_ops == NULL) {
179
0
      return -1;
180
0
    }
181
182
    /*
183
     * Allocate space for our custom ops.
184
     * Allocate as a child of our epoll_ops pointer
185
     * so we can easily get to it using talloc_parent.
186
     */
187
0
    glue->glue_ops = talloc_zero(glue, struct tevent_ops);
188
0
    if (glue->glue_ops == NULL) {
189
0
      talloc_free(glue);
190
0
      return -1;
191
0
    }
192
193
0
    ev->ops = glue->glue_ops;
194
0
  } else {
195
0
    void *glue_ptr = talloc_parent(ev->ops);
196
0
    glue = talloc_get_type_abort(glue_ptr, struct std_event_glue);
197
0
  }
198
199
0
  if (glue->epoll_ops != NULL) {
200
    /*
201
     * Set custom_ops the same as epoll,
202
     * except re-init using std_event_context_init()
203
     * and use std_event_loop_once() to add the
204
     * ability to fallback to a poll backend on
205
     * epoll runtime error.
206
     */
207
0
    *glue->glue_ops = *glue->epoll_ops;
208
0
    glue->glue_ops->context_init = std_event_context_init;
209
0
    glue->glue_ops->loop_once = std_event_loop_once;
210
0
    glue->glue_ops->loop_wait = std_event_loop_wait;
211
212
0
    ret = glue->epoll_ops->context_init(ev);
213
0
    if (ret == -1) {
214
0
      goto fallback;
215
0
    }
216
0
#ifdef HAVE_EPOLL
217
0
    tevent_epoll_set_panic_fallback(ev, std_fallback_to_poll);
218
0
#endif
219
220
0
    return ret;
221
0
  }
222
223
0
fallback:
224
0
  glue->epoll_ops = NULL;
225
226
  /*
227
   * Set custom_ops the same as poll.
228
   */
229
0
  *glue->glue_ops = *glue->poll_ops;
230
0
  glue->glue_ops->context_init = std_event_context_init;
231
232
0
  return glue->poll_ops->context_init(ev);
233
0
}
234
235
_PRIVATE_ bool tevent_standard_init(void)
236
0
{
237
0
  return tevent_register_backend("standard", &std_event_ops);
238
0
}