2 ** NOTE! The following liberal license applies to this sample file only.
3 ** This does NOT imply that all of Samba is released under this license.
5 ** This file is meant as a starting point for libtevent users to be used
6 ** in any program linking against the LGPL licensed libtevent.
10 * This file is being made available by the Samba Team under the following
13 * Permission to use, copy, modify, and distribute this sample file for any
14 * purpose is hereby granted without fee.
16 * This work is distributed in the hope that it will be useful, but
17 * WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
24 #include <netinet/in.h>
25 #include <sys/types.h>
26 #include <sys/socket.h>
33 * @brief Helper function to get a useful unix error from tevent_req
36 static bool tevent_req_is_unix_error(struct tevent_req
*req
, int *perrno
)
38 enum tevent_req_state state
;
41 if (!tevent_req_is_error(req
, &state
, &err
)) {
45 case TEVENT_REQ_TIMED_OUT
:
48 case TEVENT_REQ_NO_MEMORY
:
51 case TEVENT_REQ_USER_ERROR
:
62 * @brief Wrapper around accept(2)
66 struct tevent_fd
*fde
;
69 struct sockaddr_storage addr
;
73 static void accept_handler(struct tevent_context
*ev
, struct tevent_fd
*fde
,
74 uint16_t flags
, void *private_data
);
76 static struct tevent_req
*accept_send(TALLOC_CTX
*mem_ctx
,
77 struct tevent_context
*ev
,
80 struct tevent_req
*req
;
81 struct accept_state
*state
;
83 req
= tevent_req_create(mem_ctx
, &state
, struct accept_state
);
88 state
->listen_sock
= listen_sock
;
90 state
->fde
= tevent_add_fd(ev
, state
, listen_sock
, TEVENT_FD_READ
,
92 if (tevent_req_nomem(state
->fde
, req
)) {
93 return tevent_req_post(req
, ev
);
98 static void accept_handler(struct tevent_context
*ev
, struct tevent_fd
*fde
,
99 uint16_t flags
, void *private_data
)
101 struct tevent_req
*req
= talloc_get_type_abort(
102 private_data
, struct tevent_req
);
103 struct accept_state
*state
= tevent_req_data(req
, struct accept_state
);
106 TALLOC_FREE(state
->fde
);
108 if ((flags
& TEVENT_FD_READ
) == 0) {
109 tevent_req_error(req
, EIO
);
112 state
->addrlen
= sizeof(state
->addr
);
114 ret
= accept(state
->listen_sock
,
115 (struct sockaddr
*)&state
->addr
,
118 tevent_req_error(req
, errno
);
122 tevent_req_done(req
);
125 static int accept_recv(struct tevent_req
*req
, struct sockaddr
*paddr
,
126 socklen_t
*paddrlen
, int *perr
)
128 struct accept_state
*state
= tevent_req_data(req
, struct accept_state
);
131 if (tevent_req_is_unix_error(req
, &err
)) {
138 memcpy(paddr
, &state
->addr
, state
->addrlen
);
140 if (paddrlen
!= NULL
) {
141 *paddrlen
= state
->addrlen
;
147 * @brief Wrapper around read(2)
151 struct tevent_fd
*fde
;
159 static void read_handler(struct tevent_context
*ev
, struct tevent_fd
*fde
,
160 uint16_t flags
, void *private_data
);
162 static struct tevent_req
*read_send(TALLOC_CTX
*mem_ctx
,
163 struct tevent_context
*ev
,
164 int fd
, void *buf
, size_t count
)
166 struct tevent_req
*req
;
167 struct read_state
*state
;
169 req
= tevent_req_create(mem_ctx
, &state
, struct read_state
);
176 state
->count
= count
;
178 state
->fde
= tevent_add_fd(ev
, state
, fd
, TEVENT_FD_READ
,
180 if (tevent_req_nomem(state
->fde
, req
)) {
181 return tevent_req_post(req
, ev
);
186 static void read_handler(struct tevent_context
*ev
, struct tevent_fd
*fde
,
187 uint16_t flags
, void *private_data
)
189 struct tevent_req
*req
= talloc_get_type_abort(
190 private_data
, struct tevent_req
);
191 struct read_state
*state
= tevent_req_data(req
, struct read_state
);
194 TALLOC_FREE(state
->fde
);
196 if ((flags
& TEVENT_FD_READ
) == 0) {
197 tevent_req_error(req
, EIO
);
201 ret
= read(state
->fd
, state
->buf
, state
->count
);
203 tevent_req_error(req
, errno
);
207 tevent_req_done(req
);
210 static ssize_t
read_recv(struct tevent_req
*req
, int *perr
)
212 struct read_state
*state
= tevent_req_data(req
, struct read_state
);
215 if (tevent_req_is_unix_error(req
, &err
)) {
225 * @brief Wrapper around write(2)
229 struct tevent_fd
*fde
;
237 static void write_handler(struct tevent_context
*ev
, struct tevent_fd
*fde
,
238 uint16_t flags
, void *private_data
);
240 static struct tevent_req
*write_send(TALLOC_CTX
*mem_ctx
,
241 struct tevent_context
*ev
,
242 int fd
, const void *buf
, size_t count
)
244 struct tevent_req
*req
;
245 struct write_state
*state
;
247 req
= tevent_req_create(mem_ctx
, &state
, struct write_state
);
254 state
->count
= count
;
256 state
->fde
= tevent_add_fd(ev
, state
, fd
, TEVENT_FD_WRITE
,
258 if (tevent_req_nomem(state
->fde
, req
)) {
259 return tevent_req_post(req
, ev
);
264 static void write_handler(struct tevent_context
*ev
, struct tevent_fd
*fde
,
265 uint16_t flags
, void *private_data
)
267 struct tevent_req
*req
= talloc_get_type_abort(
268 private_data
, struct tevent_req
);
269 struct write_state
*state
= tevent_req_data(req
, struct write_state
);
272 TALLOC_FREE(state
->fde
);
274 if ((flags
& TEVENT_FD_WRITE
) == 0) {
275 tevent_req_error(req
, EIO
);
279 ret
= write(state
->fd
, state
->buf
, state
->count
);
281 tevent_req_error(req
, errno
);
284 state
->nwritten
= ret
;
285 tevent_req_done(req
);
288 static ssize_t
write_recv(struct tevent_req
*req
, int *perr
)
290 struct write_state
*state
= tevent_req_data(req
, struct write_state
);
293 if (tevent_req_is_unix_error(req
, &err
)) {
299 return state
->nwritten
;
303 * @brief Wrapper function that deals with short writes
306 struct writeall_state
{
307 struct tevent_context
*ev
;
314 static void writeall_done(struct tevent_req
*subreq
);
316 static struct tevent_req
*writeall_send(TALLOC_CTX
*mem_ctx
,
317 struct tevent_context
*ev
,
318 int fd
, const void *buf
, size_t count
)
320 struct tevent_req
*req
, *subreq
;
321 struct writeall_state
*state
;
323 req
= tevent_req_create(mem_ctx
, &state
, struct writeall_state
);
330 state
->count
= count
;
333 subreq
= write_send(state
, state
->ev
, state
->fd
,
334 ((char *)state
->buf
)+state
->nwritten
,
335 state
->count
- state
->nwritten
);
336 if (tevent_req_nomem(subreq
, req
)) {
337 return tevent_req_post(req
, ev
);
339 tevent_req_set_callback(subreq
, writeall_done
, req
);
343 static void writeall_done(struct tevent_req
*subreq
)
345 struct tevent_req
*req
= tevent_req_callback_data(
346 subreq
, struct tevent_req
);
347 struct writeall_state
*state
= tevent_req_data(
348 req
, struct writeall_state
);
352 nwritten
= write_recv(subreq
, &err
);
354 if (nwritten
== -1) {
355 tevent_req_error(req
, err
);
359 state
->nwritten
+= nwritten
;
361 if (state
->nwritten
< state
->count
) {
362 subreq
= write_send(state
, state
->ev
, state
->fd
,
363 ((char *)state
->buf
)+state
->nwritten
,
364 state
->count
- state
->nwritten
);
365 if (tevent_req_nomem(subreq
, req
)) {
368 tevent_req_set_callback(subreq
, writeall_done
, req
);
371 tevent_req_done(req
);
374 static ssize_t
writeall_recv(struct tevent_req
*req
, int *perr
)
376 struct writeall_state
*state
= tevent_req_data(
377 req
, struct writeall_state
);
380 if (tevent_req_is_unix_error(req
, &err
)) {
386 return state
->nwritten
;
390 * @brief Async echo handler code dealing with one client
394 struct tevent_context
*ev
;
399 static int echo_state_destructor(struct echo_state
*s
);
400 static void echo_read_done(struct tevent_req
*subreq
);
401 static void echo_writeall_done(struct tevent_req
*subreq
);
403 static struct tevent_req
*echo_send(TALLOC_CTX
*mem_ctx
,
404 struct tevent_context
*ev
,
405 int fd
, size_t bufsize
)
407 struct tevent_req
*req
, *subreq
;
408 struct echo_state
*state
;
410 req
= tevent_req_create(mem_ctx
, &state
, struct echo_state
);
417 talloc_set_destructor(state
, echo_state_destructor
);
419 state
->buf
= talloc_array(state
, uint8_t, bufsize
);
420 if (tevent_req_nomem(state
->buf
, req
)) {
421 return tevent_req_post(req
, ev
);
424 subreq
= read_send(state
, state
->ev
, state
->fd
,
425 state
->buf
, talloc_get_size(state
->buf
));
426 if (tevent_req_nomem(subreq
, req
)) {
427 return tevent_req_post(req
, ev
);
429 tevent_req_set_callback(subreq
, echo_read_done
, req
);
433 static int echo_state_destructor(struct echo_state
*s
)
436 printf("Closing client fd %d\n", s
->fd
);
443 static void echo_read_done(struct tevent_req
*subreq
)
445 struct tevent_req
*req
= tevent_req_callback_data(
446 subreq
, struct tevent_req
);
447 struct echo_state
*state
= tevent_req_data(
448 req
, struct echo_state
);
452 nread
= read_recv(subreq
, &err
);
455 tevent_req_error(req
, err
);
459 tevent_req_done(req
);
463 subreq
= writeall_send(state
, state
->ev
, state
->fd
, state
->buf
, nread
);
464 if (tevent_req_nomem(subreq
, req
)) {
467 tevent_req_set_callback(subreq
, echo_writeall_done
, req
);
470 static void echo_writeall_done(struct tevent_req
*subreq
)
472 struct tevent_req
*req
= tevent_req_callback_data(
473 subreq
, struct tevent_req
);
474 struct echo_state
*state
= tevent_req_data(
475 req
, struct echo_state
);
479 nwritten
= writeall_recv(subreq
, &err
);
481 if (nwritten
== -1) {
483 tevent_req_done(req
);
486 tevent_req_error(req
, err
);
490 subreq
= read_send(state
, state
->ev
, state
->fd
,
491 state
->buf
, talloc_get_size(state
->buf
));
492 if (tevent_req_nomem(subreq
, req
)) {
495 tevent_req_set_callback(subreq
, echo_read_done
, req
);
498 static bool echo_recv(struct tevent_req
*req
, int *perr
)
502 if (tevent_req_is_unix_error(req
, &err
)) {
510 * @brief Full echo handler code accepting and handling clients
513 struct echo_server_state
{
514 struct tevent_context
*ev
;
518 static void echo_server_accepted(struct tevent_req
*subreq
);
519 static void echo_server_client_done(struct tevent_req
*subreq
);
521 static struct tevent_req
*echo_server_send(TALLOC_CTX
*mem_ctx
,
522 struct tevent_context
*ev
,
525 struct tevent_req
*req
, *subreq
;
526 struct echo_server_state
*state
;
528 req
= tevent_req_create(mem_ctx
, &state
,
529 struct echo_server_state
);
534 state
->listen_sock
= listen_sock
;
536 subreq
= accept_send(state
, state
->ev
, state
->listen_sock
);
537 if (tevent_req_nomem(subreq
, req
)) {
538 return tevent_req_post(req
, ev
);
540 tevent_req_set_callback(subreq
, echo_server_accepted
, req
);
544 static void echo_server_accepted(struct tevent_req
*subreq
)
546 struct tevent_req
*req
= tevent_req_callback_data(
547 subreq
, struct tevent_req
);
548 struct echo_server_state
*state
= tevent_req_data(
549 req
, struct echo_server_state
);
552 sock
= accept_recv(subreq
, NULL
, NULL
, &err
);
555 tevent_req_error(req
, err
);
559 printf("new client fd %d\n", sock
);
561 subreq
= echo_send(state
, state
->ev
, sock
, 100);
562 if (tevent_req_nomem(subreq
, req
)) {
565 tevent_req_set_callback(subreq
, echo_server_client_done
, req
);
567 subreq
= accept_send(state
, state
->ev
, state
->listen_sock
);
568 if (tevent_req_nomem(subreq
, req
)) {
571 tevent_req_set_callback(subreq
, echo_server_accepted
, req
);
574 static void echo_server_client_done(struct tevent_req
*subreq
)
579 ret
= echo_recv(subreq
, &err
);
583 printf("Client done\n");
585 printf("Client failed: %s\n", strerror(err
));
589 static bool echo_server_recv(struct tevent_req
*req
, int *perr
)
593 if (tevent_req_is_unix_error(req
, &err
)) {
600 int main(int argc
, const char **argv
)
602 int ret
, port
, listen_sock
, err
;
603 struct tevent_context
*ev
;
604 struct sockaddr_in addr
;
605 struct tevent_req
*req
;
609 fprintf(stderr
, "Usage: %s <port>\n", argv
[0]);
613 port
= atoi(argv
[1]);
615 printf("listening on port %d\n", port
);
617 listen_sock
= socket(AF_INET
, SOCK_STREAM
, 0);
619 if (listen_sock
== -1) {
620 perror("socket() failed");
624 addr
= (struct sockaddr_in
) {
625 .sin_family
= AF_INET
,
626 .sin_port
= htons(port
)
629 ret
= bind(listen_sock
, (struct sockaddr
*)&addr
, sizeof(addr
));
631 perror("bind() failed");
635 ret
= listen(listen_sock
, 5);
637 perror("listen() failed");
641 ev
= tevent_context_init(NULL
);
643 fprintf(stderr
, "tevent_context_init failed\n");
647 req
= echo_server_send(ev
, ev
, listen_sock
);
649 fprintf(stderr
, "echo_server_send failed\n");
653 if (!tevent_req_poll(req
, ev
)) {
654 perror("tevent_req_poll() failed");
658 result
= echo_server_recv(req
, &err
);
661 fprintf(stderr
, "echo_server failed: %s\n", strerror(err
));