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