timer: handle time going backwards.
[ccan] / ccan / io / test / run-06-idle.c
1 #include <ccan/io/io.h>
2 /* Include the C files directly. */
3 #include <ccan/io/poll.c>
4 #include <ccan/io/io.c>
5 #include <ccan/tap/tap.h>
6 #include <sys/wait.h>
7 #include <stdio.h>
8 #include <sys/types.h>
9 #include <sys/stat.h>
10 #include <fcntl.h>
11
12 #define PORT "65006"
13
14 static struct io_conn *idler;
15
16 struct data {
17         int state;
18         char buf[4];
19 };
20
21 static struct io_plan *read_done(struct io_conn *conn, struct data *d)
22 {
23         ok1(d->state == 2 || d->state == 3);
24         d->state++;
25         return io_close(conn);
26 }
27
28 static void finish_waker(struct io_conn *conn, struct data *d)
29 {
30         io_wake(d);
31         ok1(d->state == 1);
32         d->state++;
33 }
34
35 static void finish_idle(struct io_conn *conn, struct data *d)
36 {
37         ok1(d->state == 3);
38         d->state++;
39         io_break(d);
40 }
41
42 static struct io_plan *never(struct io_conn *conn, void *arg)
43 {
44         abort();
45 }
46
47 static struct io_plan *read_buf(struct io_conn *conn, struct data *d)
48 {
49         return io_read(conn, d->buf, sizeof(d->buf), read_done, d);
50 }
51
52 static struct io_plan *init_waker(struct io_conn *conn, void *unused)
53 {
54         /* This is /dev/null, so will never succeed. */
55         return io_read(conn, unused, 1, never, NULL);
56 }
57
58 static struct io_plan *init_idle(struct io_conn *conn, struct data *d)
59 {
60         int fd2;
61
62         ok1(d->state == 0);
63         d->state++;
64         idler = conn;
65         io_set_finish(conn, finish_idle, d);
66
67         /* This will wake us up, as read will fail. */
68         fd2 = open("/dev/null", O_RDONLY);
69         ok1(fd2 >= 0);
70         io_set_finish(io_new_conn(NULL, fd2, init_waker, d), finish_waker, d);
71
72         return io_wait(conn, d, read_buf, d);
73 }
74
75 static int make_listen_fd(const char *port, struct addrinfo **info)
76 {
77         int fd, on = 1;
78         struct addrinfo *addrinfo, hints;
79
80         memset(&hints, 0, sizeof(hints));
81         hints.ai_family = AF_UNSPEC;
82         hints.ai_socktype = SOCK_STREAM;
83         hints.ai_flags = AI_PASSIVE;
84         hints.ai_protocol = 0;
85
86         if (getaddrinfo(NULL, port, &hints, &addrinfo) != 0)
87                 return -1;
88
89         fd = socket(addrinfo->ai_family, addrinfo->ai_socktype,
90                     addrinfo->ai_protocol);
91         if (fd < 0)
92                 return -1;
93
94         setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
95         if (bind(fd, addrinfo->ai_addr, addrinfo->ai_addrlen) != 0) {
96                 close(fd);
97                 return -1;
98         }
99         if (listen(fd, 1) != 0) {
100                 close(fd);
101                 return -1;
102         }
103         *info = addrinfo;
104         return fd;
105 }
106
107 int main(void)
108 {
109         struct data *d = malloc(sizeof(*d));
110         struct addrinfo *addrinfo;
111         struct io_listener *l;
112         int fd, status;
113
114         /* This is how many tests you plan to run */
115         plan_tests(13);
116         d->state = 0;
117         fd = make_listen_fd(PORT, &addrinfo);
118         ok1(fd >= 0);
119         l = io_new_listener(NULL, fd, init_idle, d);
120         ok1(l);
121         fflush(stdout);
122         if (!fork()) {
123                 int i;
124
125                 io_close_listener(l);
126                 fd = socket(addrinfo->ai_family, addrinfo->ai_socktype,
127                             addrinfo->ai_protocol);
128                 if (fd < 0)
129                         exit(1);
130                 if (connect(fd, addrinfo->ai_addr, addrinfo->ai_addrlen) != 0)
131                         exit(2);
132                 signal(SIGPIPE, SIG_IGN);
133                 for (i = 0; i < strlen("hellothere"); i++) {
134                         if (write(fd, "hellothere" + i, 1) != 1)
135                                 break;
136                 }
137                 close(fd);
138                 freeaddrinfo(addrinfo);
139                 free(d);
140                 exit(0);
141         }
142         freeaddrinfo(addrinfo);
143
144         ok1(io_loop(NULL, NULL) == d);
145         ok1(d->state == 4);
146         ok1(memcmp(d->buf, "hellothere", sizeof(d->buf)) == 0);
147         free(d);
148         io_close_listener(l);
149
150         ok1(wait(&status));
151         ok1(WIFEXITED(status));
152         ok1(WEXITSTATUS(status) == 0);
153
154         /* This exits depending on whether all tests passed */
155         return exit_status();
156 }