source: vendor/current/lib/tevent/echo_server.c

Last change on this file was 988, checked in by Silvan Scherrer, 9 years ago

Samba Server: update vendor to version 4.4.3

File size: 14.1 KB
Line 
1/**
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.
4 **
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.
7 **/
8
9/*
10 * This file is being made available by the Samba Team under the following
11 * license:
12 *
13 * Permission to use, copy, modify, and distribute this sample file for any
14 * purpose is hereby granted without fee.
15 *
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.
19 */
20
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24#include <netinet/in.h>
25#include <sys/types.h>
26#include <sys/socket.h>
27#include <errno.h>
28#include <unistd.h>
29#include "tevent.h"
30#include "talloc.h"
31
32/**
33 * @brief Helper function to get a useful unix error from tevent_req
34 */
35
36static bool tevent_req_is_unix_error(struct tevent_req *req, int *perrno)
37{
38 enum tevent_req_state state;
39 uint64_t err;
40
41 if (!tevent_req_is_error(req, &state, &err)) {
42 return false;
43 }
44 switch (state) {
45 case TEVENT_REQ_TIMED_OUT:
46 *perrno = ETIMEDOUT;
47 break;
48 case TEVENT_REQ_NO_MEMORY:
49 *perrno = ENOMEM;
50 break;
51 case TEVENT_REQ_USER_ERROR:
52 *perrno = err;
53 break;
54 default:
55 *perrno = EINVAL;
56 break;
57 }
58 return true;
59}
60
61/**
62 * @brief Wrapper around accept(2)
63 */
64
65struct accept_state {
66 struct tevent_fd *fde;
67 int listen_sock;
68 socklen_t addrlen;
69 struct sockaddr addr;
70 int sock;
71};
72
73static void accept_handler(struct tevent_context *ev, struct tevent_fd *fde,
74 uint16_t flags, void *private_data);
75
76static struct tevent_req *accept_send(TALLOC_CTX *mem_ctx,
77 struct tevent_context *ev,
78 int listen_sock)
79{
80 struct tevent_req *req;
81 struct accept_state *state;
82
83 req = tevent_req_create(mem_ctx, &state, struct accept_state);
84 if (req == NULL) {
85 return NULL;
86 }
87
88 state->listen_sock = listen_sock;
89
90 state->fde = tevent_add_fd(ev, state, listen_sock, TEVENT_FD_READ,
91 accept_handler, req);
92 if (tevent_req_nomem(state->fde, req)) {
93 return tevent_req_post(req, ev);
94 }
95 return req;
96}
97
98static void accept_handler(struct tevent_context *ev, struct tevent_fd *fde,
99 uint16_t flags, void *private_data)
100{
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);
104 int ret;
105
106 TALLOC_FREE(state->fde);
107
108 if ((flags & TEVENT_FD_READ) == 0) {
109 tevent_req_error(req, EIO);
110 return;
111 }
112 state->addrlen = sizeof(state->addr);
113
114 ret = accept(state->listen_sock, &state->addr, &state->addrlen);
115 if (ret == -1) {
116 tevent_req_error(req, errno);
117 return;
118 }
119 state->sock = ret;
120 tevent_req_done(req);
121}
122
123static int accept_recv(struct tevent_req *req, struct sockaddr *paddr,
124 socklen_t *paddrlen, int *perr)
125{
126 struct accept_state *state = tevent_req_data(req, struct accept_state);
127 int err;
128
129 if (tevent_req_is_unix_error(req, &err)) {
130 if (perr != NULL) {
131 *perr = err;
132 }
133 return -1;
134 }
135 if (paddr != NULL) {
136 *paddr = state->addr;
137 }
138 if (paddrlen != NULL) {
139 *paddrlen = state->addrlen;
140 }
141 return state->sock;
142}
143
144/**
145 * @brief Wrapper around read(2)
146 */
147
148struct read_state {
149 struct tevent_fd *fde;
150 int fd;
151 void *buf;
152 size_t count;
153
154 ssize_t nread;
155};
156
157static void read_handler(struct tevent_context *ev, struct tevent_fd *fde,
158 uint16_t flags, void *private_data);
159
160static struct tevent_req *read_send(TALLOC_CTX *mem_ctx,
161 struct tevent_context *ev,
162 int fd, void *buf, size_t count)
163{
164 struct tevent_req *req;
165 struct read_state *state;
166
167 req = tevent_req_create(mem_ctx, &state, struct read_state);
168 if (req == NULL) {
169 return NULL;
170 }
171
172 state->fd = fd;
173 state->buf = buf;
174 state->count = count;
175
176 state->fde = tevent_add_fd(ev, state, fd, TEVENT_FD_READ,
177 read_handler, req);
178 if (tevent_req_nomem(state->fde, req)) {
179 return tevent_req_post(req, ev);
180 }
181 return req;
182}
183
184static void read_handler(struct tevent_context *ev, struct tevent_fd *fde,
185 uint16_t flags, void *private_data)
186{
187 struct tevent_req *req = talloc_get_type_abort(
188 private_data, struct tevent_req);
189 struct read_state *state = tevent_req_data(req, struct read_state);
190 ssize_t ret;
191
192 TALLOC_FREE(state->fde);
193
194 if ((flags & TEVENT_FD_READ) == 0) {
195 tevent_req_error(req, EIO);
196 return;
197 }
198
199 ret = read(state->fd, state->buf, state->count);
200 if (ret == -1) {
201 tevent_req_error(req, errno);
202 return;
203 }
204 state->nread = ret;
205 tevent_req_done(req);
206}
207
208static ssize_t read_recv(struct tevent_req *req, int *perr)
209{
210 struct read_state *state = tevent_req_data(req, struct read_state);
211 int err;
212
213 if (tevent_req_is_unix_error(req, &err)) {
214 if (perr != NULL) {
215 *perr = err;
216 }
217 return -1;
218 }
219 return state->nread;
220}
221
222/**
223 * @brief Wrapper around write(2)
224 */
225
226struct write_state {
227 struct tevent_fd *fde;
228 int fd;
229 const void *buf;
230 size_t count;
231
232 ssize_t nwritten;
233};
234
235static void write_handler(struct tevent_context *ev, struct tevent_fd *fde,
236 uint16_t flags, void *private_data);
237
238static struct tevent_req *write_send(TALLOC_CTX *mem_ctx,
239 struct tevent_context *ev,
240 int fd, const void *buf, size_t count)
241{
242 struct tevent_req *req;
243 struct write_state *state;
244
245 req = tevent_req_create(mem_ctx, &state, struct write_state);
246 if (req == NULL) {
247 return NULL;
248 }
249
250 state->fd = fd;
251 state->buf = buf;
252 state->count = count;
253
254 state->fde = tevent_add_fd(ev, state, fd, TEVENT_FD_WRITE,
255 write_handler, req);
256 if (tevent_req_nomem(state->fde, req)) {
257 return tevent_req_post(req, ev);
258 }
259 return req;
260}
261
262static void write_handler(struct tevent_context *ev, struct tevent_fd *fde,
263 uint16_t flags, void *private_data)
264{
265 struct tevent_req *req = talloc_get_type_abort(
266 private_data, struct tevent_req);
267 struct write_state *state = tevent_req_data(req, struct write_state);
268 ssize_t ret;
269
270 TALLOC_FREE(state->fde);
271
272 if ((flags & TEVENT_FD_WRITE) == 0) {
273 tevent_req_error(req, EIO);
274 return;
275 }
276
277 ret = write(state->fd, state->buf, state->count);
278 if (ret == -1) {
279 tevent_req_error(req, errno);
280 return;
281 }
282 state->nwritten = ret;
283 tevent_req_done(req);
284}
285
286static ssize_t write_recv(struct tevent_req *req, int *perr)
287{
288 struct write_state *state = tevent_req_data(req, struct write_state);
289 int err;
290
291 if (tevent_req_is_unix_error(req, &err)) {
292 if (perr != NULL) {
293 *perr = err;
294 }
295 return -1;
296 }
297 return state->nwritten;
298}
299
300/**
301 * @brief Wrapper function that deals with short writes
302 */
303
304struct writeall_state {
305 struct tevent_context *ev;
306 int fd;
307 const void *buf;
308 size_t count;
309 size_t nwritten;
310};
311
312static void writeall_done(struct tevent_req *subreq);
313
314static struct tevent_req *writeall_send(TALLOC_CTX *mem_ctx,
315 struct tevent_context *ev,
316 int fd, const void *buf, size_t count)
317{
318 struct tevent_req *req, *subreq;
319 struct writeall_state *state;
320
321 req = tevent_req_create(mem_ctx, &state, struct writeall_state);
322 if (req == NULL) {
323 return NULL;
324 }
325 state->ev = ev;
326 state->fd = fd;
327 state->buf = buf;
328 state->count = count;
329 state->nwritten = 0;
330
331 subreq = write_send(state, state->ev, state->fd,
332 ((char *)state->buf)+state->nwritten,
333 state->count - state->nwritten);
334 if (tevent_req_nomem(subreq, req)) {
335 return tevent_req_post(req, ev);
336 }
337 tevent_req_set_callback(subreq, writeall_done, req);
338 return req;
339}
340
341static void writeall_done(struct tevent_req *subreq)
342{
343 struct tevent_req *req = tevent_req_callback_data(
344 subreq, struct tevent_req);
345 struct writeall_state *state = tevent_req_data(
346 req, struct writeall_state);
347 ssize_t nwritten;
348 int err = 0;
349
350 nwritten = write_recv(subreq, &err);
351 TALLOC_FREE(subreq);
352 if (nwritten == -1) {
353 tevent_req_error(req, err);
354 return;
355 }
356
357 state->nwritten += nwritten;
358
359 if (state->nwritten < state->count) {
360 subreq = write_send(state, state->ev, state->fd,
361 ((char *)state->buf)+state->nwritten,
362 state->count - state->nwritten);
363 if (tevent_req_nomem(subreq, req)) {
364 return;
365 }
366 tevent_req_set_callback(subreq, writeall_done, req);
367 return;
368 }
369 tevent_req_done(req);
370}
371
372static ssize_t writeall_recv(struct tevent_req *req, int *perr)
373{
374 struct writeall_state *state = tevent_req_data(
375 req, struct writeall_state);
376 int err;
377
378 if (tevent_req_is_unix_error(req, &err)) {
379 if (perr != NULL) {
380 *perr = err;
381 }
382 return -1;
383 }
384 return state->nwritten;
385}
386
387/**
388 * @brief Async echo handler code dealing with one client
389 */
390
391struct echo_state {
392 struct tevent_context *ev;
393 int fd;
394 uint8_t *buf;
395};
396
397static int echo_state_destructor(struct echo_state *s);
398static void echo_read_done(struct tevent_req *subreq);
399static void echo_writeall_done(struct tevent_req *subreq);
400
401static struct tevent_req *echo_send(TALLOC_CTX *mem_ctx,
402 struct tevent_context *ev,
403 int fd, size_t bufsize)
404{
405 struct tevent_req *req, *subreq;
406 struct echo_state *state;
407
408 req = tevent_req_create(mem_ctx, &state, struct echo_state);
409 if (req == NULL) {
410 return NULL;
411 }
412 state->ev = ev;
413 state->fd = fd;
414
415 talloc_set_destructor(state, echo_state_destructor);
416
417 state->buf = talloc_array(state, uint8_t, bufsize);
418 if (tevent_req_nomem(state->buf, req)) {
419 return tevent_req_post(req, ev);
420 }
421
422 subreq = read_send(state, state->ev, state->fd,
423 state->buf, talloc_get_size(state->buf));
424 if (tevent_req_nomem(subreq, req)) {
425 return tevent_req_post(req, ev);
426 }
427 tevent_req_set_callback(subreq, echo_read_done, req);
428 return req;
429}
430
431static int echo_state_destructor(struct echo_state *s)
432{
433 if (s->fd != -1) {
434 printf("Closing client fd %d\n", s->fd);
435 close(s->fd);
436 s->fd = -1;
437 }
438 return 0;
439}
440
441static void echo_read_done(struct tevent_req *subreq)
442{
443 struct tevent_req *req = tevent_req_callback_data(
444 subreq, struct tevent_req);
445 struct echo_state *state = tevent_req_data(
446 req, struct echo_state);
447 ssize_t nread;
448 int err;
449
450 nread = read_recv(subreq, &err);
451 TALLOC_FREE(subreq);
452 if (nread == -1) {
453 tevent_req_error(req, err);
454 return;
455 }
456 if (nread == 0) {
457 tevent_req_done(req);
458 return;
459 }
460
461 subreq = writeall_send(state, state->ev, state->fd, state->buf, nread);
462 if (tevent_req_nomem(subreq, req)) {
463 return;
464 }
465 tevent_req_set_callback(subreq, echo_writeall_done, req);
466}
467
468static void echo_writeall_done(struct tevent_req *subreq)
469{
470 struct tevent_req *req = tevent_req_callback_data(
471 subreq, struct tevent_req);
472 struct echo_state *state = tevent_req_data(
473 req, struct echo_state);
474 ssize_t nwritten;
475 int err;
476
477 nwritten = writeall_recv(subreq, &err);
478 TALLOC_FREE(subreq);
479 if (nwritten == -1) {
480 if (err == EPIPE) {
481 tevent_req_done(req);
482 return;
483 }
484 tevent_req_error(req, err);
485 return;
486 }
487
488 subreq = read_send(state, state->ev, state->fd,
489 state->buf, talloc_get_size(state->buf));
490 if (tevent_req_nomem(subreq, req)) {
491 return;
492 }
493 tevent_req_set_callback(subreq, echo_read_done, req);
494}
495
496static bool echo_recv(struct tevent_req *req, int *perr)
497{
498 int err;
499
500 if (tevent_req_is_unix_error(req, &err)) {
501 *perr = err;
502 return false;
503 }
504 return true;
505}
506
507/**
508 * @brief Full echo handler code accepting and handling clients
509 */
510
511struct echo_server_state {
512 struct tevent_context *ev;
513 int listen_sock;
514};
515
516static void echo_server_accepted(struct tevent_req *subreq);
517static void echo_server_client_done(struct tevent_req *subreq);
518
519static struct tevent_req *echo_server_send(TALLOC_CTX *mem_ctx,
520 struct tevent_context *ev,
521 int listen_sock)
522{
523 struct tevent_req *req, *subreq;
524 struct echo_server_state *state;
525
526 req = tevent_req_create(mem_ctx, &state,
527 struct echo_server_state);
528 if (req == NULL) {
529 return NULL;
530 }
531 state->ev = ev;
532 state->listen_sock = listen_sock;
533
534 subreq = accept_send(state, state->ev, state->listen_sock);
535 if (tevent_req_nomem(subreq, req)) {
536 return tevent_req_post(req, ev);
537 }
538 tevent_req_set_callback(subreq, echo_server_accepted, req);
539 return req;
540}
541
542static void echo_server_accepted(struct tevent_req *subreq)
543{
544 struct tevent_req *req = tevent_req_callback_data(
545 subreq, struct tevent_req);
546 struct echo_server_state *state = tevent_req_data(
547 req, struct echo_server_state);
548 int sock, err;
549
550 sock = accept_recv(subreq, NULL, NULL, &err);
551 TALLOC_FREE(subreq);
552 if (sock == -1) {
553 tevent_req_error(req, err);
554 return;
555 }
556
557 printf("new client fd %d\n", sock);
558
559 subreq = echo_send(state, state->ev, sock, 100);
560 if (tevent_req_nomem(subreq, req)) {
561 return;
562 }
563 tevent_req_set_callback(subreq, echo_server_client_done, req);
564
565 subreq = accept_send(state, state->ev, state->listen_sock);
566 if (tevent_req_nomem(subreq, req)) {
567 return;
568 }
569 tevent_req_set_callback(subreq, echo_server_accepted, req);
570}
571
572static void echo_server_client_done(struct tevent_req *subreq)
573{
574 bool ret;
575 int err;
576
577 ret = echo_recv(subreq, &err);
578 TALLOC_FREE(subreq);
579
580 if (ret) {
581 printf("Client done\n");
582 } else {
583 printf("Client failed: %s\n", strerror(err));
584 }
585}
586
587static bool echo_server_recv(struct tevent_req *req, int *perr)
588{
589 int err;
590
591 if (tevent_req_is_unix_error(req, &err)) {
592 *perr = err;
593 return false;
594 }
595 return true;
596}
597
598int main(int argc, const char **argv)
599{
600 int ret, port, listen_sock, err;
601 struct tevent_context *ev;
602 struct sockaddr_in addr;
603 struct tevent_req *req;
604 bool result;
605
606 if (argc != 2) {
607 fprintf(stderr, "Usage: %s <port>\n", argv[0]);
608 exit(1);
609 }
610
611 port = atoi(argv[1]);
612
613 printf("listening on port %d\n", port);
614
615 listen_sock = socket(AF_INET, SOCK_STREAM, 0);
616
617 if (listen_sock == -1) {
618 perror("socket() failed");
619 exit(1);
620 }
621
622 addr = (struct sockaddr_in) {
623 .sin_family = AF_INET,
624 .sin_port = htons(port)
625 };
626
627 ret = bind(listen_sock, (struct sockaddr *)&addr, sizeof(addr));
628 if (ret == -1) {
629 perror("bind() failed");
630 exit(1);
631 }
632
633 ret = listen(listen_sock, 5);
634 if (ret == -1) {
635 perror("listen() failed");
636 exit(1);
637 }
638
639 ev = tevent_context_init(NULL);
640 if (ev == NULL) {
641 fprintf(stderr, "tevent_context_init failed\n");
642 exit(1);
643 }
644
645 req = echo_server_send(ev, ev, listen_sock);
646 if (req == NULL) {
647 fprintf(stderr, "echo_server_send failed\n");
648 exit(1);
649 }
650
651 if (!tevent_req_poll(req, ev)) {
652 perror("tevent_req_poll() failed");
653 exit(1);
654 }
655
656 result = echo_server_recv(req, &err);
657 TALLOC_FREE(req);
658 if (!result) {
659 fprintf(stderr, "echo_server failed: %s\n", strerror(err));
660 exit(1);
661 }
662
663 return 0;
664}
Note: See TracBrowser for help on using the repository browser.