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