tevent: Add echo server sample code
[sfrench/samba-autobuild/.git] / lib / tevent / echo_server.c
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
36 static 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
65 struct accept_state {
66         struct tevent_fd *fde;
67         int listen_sock;
68         socklen_t addrlen;
69         struct sockaddr addr;
70         int sock;
71 };
72
73 static void accept_handler(struct tevent_context *ev, struct tevent_fd *fde,
74                            uint16_t flags, void *private_data);
75
76 static 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
98 static 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
123 static 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
148 struct read_state {
149         struct tevent_fd *fde;
150         int fd;
151         void *buf;
152         size_t count;
153
154         ssize_t nread;
155 };
156
157 static void read_handler(struct tevent_context *ev, struct tevent_fd *fde,
158                          uint16_t flags, void *private_data);
159
160 static 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
184 static 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
208 static 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
226 struct 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
235 static void write_handler(struct tevent_context *ev, struct tevent_fd *fde,
236                          uint16_t flags, void *private_data);
237
238 static 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
262 static 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
286 static 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
304 struct writeall_state {
305         struct tevent_context *ev;
306         int fd;
307         const void *buf;
308         size_t count;
309         size_t nwritten;
310 };
311
312 static void writeall_done(struct tevent_req *subreq);
313
314 static 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
341 static 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
372 static 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
391 struct echo_state {
392         struct tevent_context *ev;
393         int fd;
394         uint8_t *buf;
395 };
396
397 static int echo_state_destructor(struct echo_state *s);
398 static void echo_read_done(struct tevent_req *subreq);
399 static void echo_writeall_done(struct tevent_req *subreq);
400
401 static 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
431 static 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
441 static 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
468 static 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
496 static 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
511 struct echo_server_state {
512         struct tevent_context *ev;
513         int listen_sock;
514 };
515
516 static void echo_server_accepted(struct tevent_req *subreq);
517 static void echo_server_client_done(struct tevent_req *subreq);
518
519 static 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
542 static 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
572 static 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
587 static 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
598 int 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         memset(&addr, 0, sizeof(addr));
623
624         addr.sin_family = AF_INET;
625         addr.sin_port = htons(port);
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 }