tevent: fix crash bug in tevent_queue_immediate_trigger()
[sfrench/samba-autobuild/.git] / lib / tevent / tevent_select.c
1 /* 
2    Unix SMB/CIFS implementation.
3    main select loop and event handling
4    Copyright (C) Andrew Tridgell        2003-2005
5    Copyright (C) Stefan Metzmacher      2005-2009
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 #include "replace.h"
26 #include "system/filesys.h"
27 #include "system/select.h"
28 #include "tevent.h"
29 #include "tevent_util.h"
30 #include "tevent_internal.h"
31
32 struct select_event_context {
33         /* a pointer back to the generic event_context */
34         struct tevent_context *ev;
35
36         /* the maximum file descriptor number in fd_events */
37         int maxfd;
38
39         /* information for exiting from the event loop */
40         int exit_code;
41 };
42
43 /*
44   create a select_event_context structure.
45 */
46 static int select_event_context_init(struct tevent_context *ev)
47 {
48         struct select_event_context *select_ev;
49
50         /*
51          * We might be called during tevent_re_initialise()
52          * which means we need to free our old additional_data.
53          */
54         TALLOC_FREE(ev->additional_data);
55
56         select_ev = talloc_zero(ev, struct select_event_context);
57         if (!select_ev) return -1;
58         select_ev->ev = ev;
59
60         ev->additional_data = select_ev;
61         return 0;
62 }
63
64 /*
65   recalculate the maxfd
66 */
67 static void calc_maxfd(struct select_event_context *select_ev)
68 {
69         struct tevent_fd *fde;
70
71         select_ev->maxfd = 0;
72         for (fde = select_ev->ev->fd_events; fde; fde = fde->next) {
73                 if (fde->fd > select_ev->maxfd) {
74                         select_ev->maxfd = fde->fd;
75                 }
76         }
77 }
78
79
80 /* to mark the ev->maxfd invalid
81  * this means we need to recalculate it
82  */
83 #define EVENT_INVALID_MAXFD (-1)
84
85 /*
86   destroy an fd_event
87 */
88 static int select_event_fd_destructor(struct tevent_fd *fde)
89 {
90         struct tevent_context *ev = fde->event_ctx;
91         struct select_event_context *select_ev = NULL;
92
93         if (ev) {
94                 select_ev = talloc_get_type_abort(ev->additional_data,
95                                                   struct select_event_context);
96
97                 if (select_ev->maxfd == fde->fd) {
98                         select_ev->maxfd = EVENT_INVALID_MAXFD;
99                 }
100         }
101
102         return tevent_common_fd_destructor(fde);
103 }
104
105 /*
106   add a fd based event
107   return NULL on failure (memory allocation error)
108 */
109 static struct tevent_fd *select_event_add_fd(struct tevent_context *ev, TALLOC_CTX *mem_ctx,
110                                              int fd, uint16_t flags,
111                                              tevent_fd_handler_t handler,
112                                              void *private_data,
113                                              const char *handler_name,
114                                              const char *location)
115 {
116         struct select_event_context *select_ev =
117                 talloc_get_type_abort(ev->additional_data,
118                 struct select_event_context);
119         struct tevent_fd *fde;
120
121         if (fd < 0 || fd >= FD_SETSIZE) {
122                 errno = EBADF;
123                 return NULL;
124         }
125
126         fde = tevent_common_add_fd(ev, mem_ctx, fd, flags,
127                                    handler, private_data,
128                                    handler_name, location);
129         if (!fde) return NULL;
130
131         if ((select_ev->maxfd != EVENT_INVALID_MAXFD)
132             && (fde->fd > select_ev->maxfd)) {
133                 select_ev->maxfd = fde->fd;
134         }
135         talloc_set_destructor(fde, select_event_fd_destructor);
136
137         return fde;
138 }
139
140 /*
141   event loop handling using select()
142 */
143 static int select_event_loop_select(struct select_event_context *select_ev, struct timeval *tvalp)
144 {
145         fd_set r_fds, w_fds;
146         struct tevent_fd *fde;
147         int selrtn;
148         int select_errno;
149
150         /* we maybe need to recalculate the maxfd */
151         if (select_ev->maxfd == EVENT_INVALID_MAXFD) {
152                 calc_maxfd(select_ev);
153         }
154
155         FD_ZERO(&r_fds);
156         FD_ZERO(&w_fds);
157
158         /* setup any fd events */
159         for (fde = select_ev->ev->fd_events; fde; fde = fde->next) {
160                 if (fde->fd < 0 || fde->fd >= FD_SETSIZE) {
161                         tevent_debug(select_ev->ev, TEVENT_DEBUG_FATAL,
162                                      "ERROR: EBADF fd[%d] >= %d "
163                                      "select_event_loop_once\n",
164                                      fde->fd, FD_SETSIZE);
165                         errno = EBADF;
166                         return -1;
167                 }
168
169                 if (fde->flags & TEVENT_FD_READ) {
170                         FD_SET(fde->fd, &r_fds);
171                 }
172                 if (fde->flags & TEVENT_FD_WRITE) {
173                         FD_SET(fde->fd, &w_fds);
174                 }
175         }
176
177         if (select_ev->ev->signal_events &&
178             tevent_common_check_signal(select_ev->ev)) {
179                 return 0;
180         }
181
182         tevent_trace_point_callback(select_ev->ev, TEVENT_TRACE_BEFORE_WAIT);
183         selrtn = select(select_ev->maxfd+1, &r_fds, &w_fds, NULL, tvalp);
184         select_errno = errno;
185         tevent_trace_point_callback(select_ev->ev, TEVENT_TRACE_AFTER_WAIT);
186
187         if (selrtn == -1 && select_errno == EINTR &&
188             select_ev->ev->signal_events) {
189                 tevent_common_check_signal(select_ev->ev);
190                 return 0;
191         }
192
193         if (selrtn == -1 && select_errno == EBADF) {
194                 /* the socket is dead! this should never
195                    happen as the socket should have first been
196                    made readable and that should have removed
197                    the event, so this must be a bug. This is a
198                    fatal error. */
199                 tevent_debug(select_ev->ev, TEVENT_DEBUG_FATAL,
200                              "ERROR: EBADF on select_event_loop_once\n");
201                 select_ev->exit_code = EBADF;
202                 return -1;
203         }
204
205         if (selrtn == 0 && tvalp) {
206                 /* we don't care about a possible delay here */
207                 tevent_common_loop_timer_delay(select_ev->ev);
208                 return 0;
209         }
210
211         if (selrtn > 0) {
212                 /* at least one file descriptor is ready - check
213                    which ones and call the handler, being careful to allow
214                    the handler to remove itself when called */
215                 for (fde = select_ev->ev->fd_events; fde; fde = fde->next) {
216                         uint16_t flags = 0;
217
218                         if (FD_ISSET(fde->fd, &r_fds) && (fde->flags & TEVENT_FD_READ)) {
219                                 flags |= TEVENT_FD_READ;
220                         }
221                         if (FD_ISSET(fde->fd, &w_fds) && (fde->flags & TEVENT_FD_WRITE)) {
222                                 flags |= TEVENT_FD_WRITE;
223                         }
224                         if (flags) {
225                                 DLIST_DEMOTE(select_ev->ev->fd_events, fde, struct tevent_fd);
226                                 fde->handler(select_ev->ev, fde, flags, fde->private_data);
227                                 break;
228                         }
229                 }
230         }
231
232         return 0;
233 }
234
235 /*
236   do a single event loop using the events defined in ev 
237 */
238 static int select_event_loop_once(struct tevent_context *ev, const char *location)
239 {
240         struct select_event_context *select_ev =
241                 talloc_get_type_abort(ev->additional_data,
242                 struct select_event_context);
243         struct timeval tval;
244
245         if (ev->signal_events &&
246             tevent_common_check_signal(ev)) {
247                 return 0;
248         }
249
250         if (ev->immediate_events &&
251             tevent_common_loop_immediate(ev)) {
252                 return 0;
253         }
254
255         tval = tevent_common_loop_timer_delay(ev);
256         if (tevent_timeval_is_zero(&tval)) {
257                 return 0;
258         }
259
260         return select_event_loop_select(select_ev, &tval);
261 }
262
263 static const struct tevent_ops select_event_ops = {
264         .context_init           = select_event_context_init,
265         .add_fd                 = select_event_add_fd,
266         .set_fd_close_fn        = tevent_common_fd_set_close_fn,
267         .get_fd_flags           = tevent_common_fd_get_flags,
268         .set_fd_flags           = tevent_common_fd_set_flags,
269         .add_timer              = tevent_common_add_timer_v2,
270         .schedule_immediate     = tevent_common_schedule_immediate,
271         .add_signal             = tevent_common_add_signal,
272         .loop_once              = select_event_loop_once,
273         .loop_wait              = tevent_common_loop_wait,
274 };
275
276 _PRIVATE_ bool tevent_select_init(void)
277 {
278         return tevent_register_backend("select", &select_event_ops);
279 }