2 Unix SMB/CIFS implementation.
4 Copyright (C) Andrew Tridgell 1992-1998
5 Copyright (C) Volker Lendecke 2005-2007
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "lib/tevent/tevent_internal.h"
23 #include "../lib/util/select.h"
24 #include "system/select.h"
26 struct tevent_poll_private
{
28 * Index from file descriptor into the pollfd array
33 * Cache for s3_event_loop_once to avoid reallocs
38 static struct tevent_poll_private
*tevent_get_poll_private(
39 struct tevent_context
*ev
)
41 struct tevent_poll_private
*state
;
43 state
= (struct tevent_poll_private
*)ev
->additional_data
;
45 state
= talloc_zero(ev
, struct tevent_poll_private
);
46 ev
->additional_data
= (void *)state
;
48 DEBUG(10, ("talloc failed\n"));
54 static void count_fds(struct tevent_context
*ev
,
55 int *pnum_fds
, int *pmax_fd
)
57 struct tevent_fd
*fde
;
61 for (fde
= ev
->fd_events
; fde
!= NULL
; fde
= fde
->next
) {
62 if (fde
->flags
& (TEVENT_FD_READ
|TEVENT_FD_WRITE
)) {
64 if (fde
->fd
> max_fd
) {
73 bool event_add_to_poll_args(struct tevent_context
*ev
, TALLOC_CTX
*mem_ctx
,
74 struct pollfd
**pfds
, int *pnum_pfds
,
77 struct tevent_poll_private
*state
;
78 struct tevent_fd
*fde
;
79 int i
, num_fds
, max_fd
, num_pollfds
, idx_len
;
81 struct timeval now
, diff
;
84 state
= tevent_get_poll_private(ev
);
88 count_fds(ev
, &num_fds
, &max_fd
);
92 if (talloc_array_length(state
->pollfd_idx
) < idx_len
) {
93 state
->pollfd_idx
= talloc_realloc(
94 state
, state
->pollfd_idx
, int, idx_len
);
95 if (state
->pollfd_idx
== NULL
) {
96 DEBUG(10, ("talloc_realloc failed\n"));
102 num_pollfds
= *pnum_pfds
;
104 if (talloc_array_length(fds
) < num_pollfds
+ num_fds
) {
105 fds
= talloc_realloc(mem_ctx
, fds
, struct pollfd
,
106 num_pollfds
+ num_fds
);
108 DEBUG(10, ("talloc_realloc failed\n"));
113 memset(&fds
[num_pollfds
], 0, sizeof(struct pollfd
) * num_fds
);
116 * This needs tuning. We need to cope with multiple fde's for a file
117 * descriptor. The problem is that we need to re-use pollfd_idx across
118 * calls for efficiency. One way would be a direct bitmask that might
119 * be initialized quicker, but our bitmap_init implementation is
120 * pretty heavy-weight as well.
122 for (i
=0; i
<idx_len
; i
++) {
123 state
->pollfd_idx
[i
] = -1;
126 for (fde
= ev
->fd_events
; fde
; fde
= fde
->next
) {
129 if ((fde
->flags
& (TEVENT_FD_READ
|TEVENT_FD_WRITE
)) == 0) {
133 if (state
->pollfd_idx
[fde
->fd
] == -1) {
135 * We haven't seen this fd yet. Allocate a new pollfd.
137 state
->pollfd_idx
[fde
->fd
] = num_pollfds
;
138 pfd
= &fds
[num_pollfds
];
142 * We have already seen this fd. OR in the flags.
144 pfd
= &fds
[state
->pollfd_idx
[fde
->fd
]];
149 if (fde
->flags
& TEVENT_FD_READ
) {
150 pfd
->events
|= (POLLIN
|POLLHUP
);
152 if (fde
->flags
& TEVENT_FD_WRITE
) {
153 pfd
->events
|= POLLOUT
;
157 *pnum_pfds
= num_pollfds
;
159 if (ev
->immediate_events
!= NULL
) {
163 if (ev
->timer_events
== NULL
) {
164 *ptimeout
= MIN(*ptimeout
, INT_MAX
);
168 now
= timeval_current();
169 diff
= timeval_until(&now
, &ev
->timer_events
->next_event
);
170 timeout
= timeval_to_msec(diff
);
172 if (timeout
< *ptimeout
) {
179 bool run_events_poll(struct tevent_context
*ev
, int pollrtn
,
180 struct pollfd
*pfds
, int num_pfds
)
182 struct tevent_poll_private
*state
;
184 struct tevent_fd
*fde
;
186 if (ev
->signal_events
&&
187 tevent_common_check_signal(ev
)) {
191 if (ev
->immediate_events
&&
192 tevent_common_loop_immediate(ev
)) {
199 tval
= tevent_common_loop_timer_delay(ev
);
200 if (tevent_timeval_is_zero(&tval
)) {
210 state
= (struct tevent_poll_private
*)ev
->additional_data
;
211 pollfd_idx
= state
->pollfd_idx
;
213 for (fde
= ev
->fd_events
; fde
; fde
= fde
->next
) {
217 if ((fde
->flags
& (TEVENT_FD_READ
|TEVENT_FD_WRITE
)) == 0) {
221 if (pollfd_idx
[fde
->fd
] >= num_pfds
) {
222 DEBUG(1, ("internal error: pollfd_idx[fde->fd] (%d) "
223 ">= num_pfds (%d)\n", pollfd_idx
[fde
->fd
],
227 pfd
= &pfds
[pollfd_idx
[fde
->fd
]];
229 if (pfd
->fd
!= fde
->fd
) {
230 DEBUG(1, ("internal error: pfd->fd (%d) "
231 "!= fde->fd (%d)\n", pollfd_idx
[fde
->fd
],
236 if (pfd
->revents
& (POLLHUP
|POLLERR
)) {
237 /* If we only wait for EVENT_FD_WRITE, we
238 should not tell the event handler about it,
239 and remove the writable flag, as we only
240 report errors when waiting for read events
241 to match the select behavior. */
242 if (!(fde
->flags
& EVENT_FD_READ
)) {
243 EVENT_FD_NOT_WRITEABLE(fde
);
246 flags
|= EVENT_FD_READ
;
249 if (pfd
->revents
& POLLIN
) {
250 flags
|= EVENT_FD_READ
;
252 if (pfd
->revents
& POLLOUT
) {
253 flags
|= EVENT_FD_WRITE
;
255 if (flags
& fde
->flags
) {
256 DLIST_DEMOTE(ev
->fd_events
, fde
, struct tevent_fd
);
257 fde
->handler(ev
, fde
, flags
, fde
->private_data
);
265 struct timeval
*get_timed_events_timeout(struct tevent_context
*ev
,
266 struct timeval
*to_ret
)
270 if ((ev
->timer_events
== NULL
) && (ev
->immediate_events
== NULL
)) {
273 if (ev
->immediate_events
!= NULL
) {
274 *to_ret
= timeval_zero();
278 now
= timeval_current();
279 *to_ret
= timeval_until(&now
, &ev
->timer_events
->next_event
);
281 DEBUG(10, ("timed_events_timeout: %d/%d\n", (int)to_ret
->tv_sec
,
282 (int)to_ret
->tv_usec
));
287 static int s3_event_loop_once(struct tevent_context
*ev
, const char *location
)
289 struct tevent_poll_private
*state
;
296 state
= tevent_get_poll_private(ev
);
302 if (run_events_poll(ev
, 0, NULL
, 0)) {
307 if (!event_add_to_poll_args(ev
, state
,
308 &state
->pfds
, &num_pfds
, &timeout
)) {
312 ret
= poll(state
->pfds
, num_pfds
, timeout
);
313 if (ret
== -1 && errno
!= EINTR
) {
314 tevent_debug(ev
, TEVENT_DEBUG_FATAL
,
315 "poll() failed: %d:%s\n",
316 errno
, strerror(errno
));
320 run_events_poll(ev
, ret
, state
->pfds
, num_pfds
);
324 static int s3_event_context_init(struct tevent_context
*ev
)
329 void dump_event_list(struct tevent_context
*ev
)
331 struct tevent_timer
*te
;
332 struct tevent_fd
*fe
;
333 struct timeval evt
, now
;
339 now
= timeval_current();
341 DEBUG(10,("dump_event_list:\n"));
343 for (te
= ev
->timer_events
; te
; te
= te
->next
) {
345 evt
= timeval_until(&now
, &te
->next_event
);
347 DEBUGADD(10,("Timed Event \"%s\" %p handled in %d seconds (at %s)\n",
351 http_timestring(talloc_tos(), te
->next_event
.tv_sec
)));
354 for (fe
= ev
->fd_events
; fe
; fe
= fe
->next
) {
356 DEBUGADD(10,("FD Event %d %p, flags: 0x%04x\n",
363 static const struct tevent_ops s3_event_ops
= {
364 .context_init
= s3_event_context_init
,
365 .add_fd
= tevent_common_add_fd
,
366 .set_fd_close_fn
= tevent_common_fd_set_close_fn
,
367 .get_fd_flags
= tevent_common_fd_get_flags
,
368 .set_fd_flags
= tevent_common_fd_set_flags
,
369 .add_timer
= tevent_common_add_timer
,
370 .schedule_immediate
= tevent_common_schedule_immediate
,
371 .add_signal
= tevent_common_add_signal
,
372 .loop_once
= s3_event_loop_once
,
373 .loop_wait
= tevent_common_loop_wait
,
376 static bool s3_tevent_init(void)
378 static bool initialized
;
382 initialized
= tevent_register_backend("s3", &s3_event_ops
);
383 tevent_set_default_backend("s3");
388 this is used to catch debug messages from events
390 static void s3_event_debug(void *context
, enum tevent_debug_level level
,
391 const char *fmt
, va_list ap
) PRINTF_ATTRIBUTE(3,0);
393 static void s3_event_debug(void *context
, enum tevent_debug_level level
,
394 const char *fmt
, va_list ap
)
396 int samba_level
= -1;
399 case TEVENT_DEBUG_FATAL
:
402 case TEVENT_DEBUG_ERROR
:
405 case TEVENT_DEBUG_WARNING
:
408 case TEVENT_DEBUG_TRACE
:
413 if (CHECK_DEBUGLVL(samba_level
)) {
414 if (vasprintf(&s
, fmt
, ap
) == -1) {
417 DEBUG(samba_level
, ("s3_event: %s", s
));
422 struct tevent_context
*s3_tevent_context_init(TALLOC_CTX
*mem_ctx
)
424 struct tevent_context
*ev
;
428 ev
= tevent_context_init_byname(mem_ctx
, "s3");
430 tevent_set_debug(ev
, s3_event_debug
, NULL
);
437 struct tevent_timer
*te
;
438 struct timeval interval
;
440 bool (*handler
)(const struct timeval
*now
, void *private_data
);
444 static void smbd_idle_event_handler(struct tevent_context
*ctx
,
445 struct tevent_timer
*te
,
449 struct idle_event
*event
=
450 talloc_get_type_abort(private_data
, struct idle_event
);
452 TALLOC_FREE(event
->te
);
454 DEBUG(10,("smbd_idle_event_handler: %s %p called\n",
455 event
->name
, event
->te
));
457 if (!event
->handler(&now
, event
->private_data
)) {
458 DEBUG(10,("smbd_idle_event_handler: %s %p stopped\n",
459 event
->name
, event
->te
));
460 /* Don't repeat, delete ourselves */
465 DEBUG(10,("smbd_idle_event_handler: %s %p rescheduled\n",
466 event
->name
, event
->te
));
468 event
->te
= tevent_add_timer(ctx
, event
,
469 timeval_sum(&now
, &event
->interval
),
470 smbd_idle_event_handler
, event
);
472 /* We can't do much but fail here. */
473 SMB_ASSERT(event
->te
!= NULL
);
476 struct idle_event
*event_add_idle(struct tevent_context
*event_ctx
,
478 struct timeval interval
,
480 bool (*handler
)(const struct timeval
*now
,
484 struct idle_event
*result
;
485 struct timeval now
= timeval_current();
487 result
= talloc(mem_ctx
, struct idle_event
);
488 if (result
== NULL
) {
489 DEBUG(0, ("talloc failed\n"));
493 result
->interval
= interval
;
494 result
->handler
= handler
;
495 result
->private_data
= private_data
;
497 if (!(result
->name
= talloc_asprintf(result
, "idle_evt(%s)", name
))) {
498 DEBUG(0, ("talloc failed\n"));
503 result
->te
= tevent_add_timer(event_ctx
, result
,
504 timeval_sum(&now
, &interval
),
505 smbd_idle_event_handler
, result
);
506 if (result
->te
== NULL
) {
507 DEBUG(0, ("event_add_timed failed\n"));
512 DEBUG(10,("event_add_idle: %s %p\n", result
->name
, result
->te
));