]> git.ozlabs.org Git - ccan-lca-2011.git/blob - ccan/oserver/oserver.c
lca2011: add greeting and prefix states.
[ccan-lca-2011.git] / ccan / oserver / oserver.c
1 #include <ccan/oserver/oserver.h>
2 #include <ccan/read_write_all/read_write_all.h>
3 #include <ccan/opt/opt.h>
4 #include <ccan/tevent/tevent.h>
5 #include <ccan/array_size/array_size.h>
6 #include <sys/types.h>
7 #include <sys/socket.h>
8 #include <netinet/in.h>
9 #include <netinet/tcp.h>
10 #include <err.h>
11 #include <stdio.h>
12 #include <unistd.h>
13 #include <ctype.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <errno.h>
17 #include <signal.h>
18
19 enum state {
20         SENDING_GREETING,
21         RECEIVING_USER_QUESTION,
22         SENDING_ANSWER_PREFIX,
23         SENDING_ANSWER,
24         FINISHED
25 };
26
27 static uint16_t state_flag_map[] = {
28         [SENDING_GREETING]              = TEVENT_FD_WRITE,
29         [RECEIVING_USER_QUESTION]       = TEVENT_FD_READ,
30         [SENDING_ANSWER_PREFIX]         = TEVENT_FD_WRITE,
31         [SENDING_ANSWER]                = TEVENT_FD_WRITE,
32         [FINISHED]                      = 0
33 };
34
35 struct client {
36         /* What are we doing today, brain? */
37         enum state state;
38         /* Our event info, and the file descriptor. */
39         struct tevent_fd *fde;
40         int fd;
41         /* The question we read from client. */
42         char *question;
43         /* How many bytes of the reply we sent so far. */
44         size_t bytes_sent;
45         /* Our server. */
46         struct oserver *oserver;
47 };
48
49 struct oserver {
50         /* 5 clients should be enough for anybody! */
51         struct client *clients[5];
52         int fd;
53         struct tevent_fd *fde;
54 };
55
56 static ssize_t write_string(int fd, const char *str)
57 {
58         return write(fd, str, strlen(str));
59 }
60
61 static ssize_t read_string(int fd, char **buf)
62 {
63         ssize_t ret, len, maxlen;
64
65         len = strlen(*buf);
66         maxlen = talloc_array_length(*buf);
67
68         if (maxlen < len + 100) {
69                 maxlen += 100;
70                 *buf = talloc_realloc(NULL, *buf, char, maxlen);
71         }
72
73         ret = read(fd, *buf + len, maxlen - len - 1);
74         if (ret >= 0)
75                 (*buf)[len + ret] = '\0';
76         return ret;
77 }
78
79 static bool input_finished(const char *str)
80 {
81         return strchr(str, '\n');
82 }
83
84 /* Update state, and set our READ/WRITE flags appropriately. */
85 static void set_state(struct client *c, enum state state)
86 {
87         c->state = state;
88         tevent_fd_set_flags(c->fde, state_flag_map[state]);
89 }
90
91 /* Returns false on error, increments state on finishing string. */
92 static bool send_string(struct client *c, const char *str)
93 {
94         ssize_t len = write_string(c->fd, str + c->bytes_sent);
95         if (len < 0)
96                 return false;
97         c->bytes_sent += len;
98         if (c->bytes_sent == strlen(str)) {
99                 c->bytes_sent = 0;
100                 set_state(c, c->state+1);
101         }
102         return true;
103 }
104
105 static void service_client(struct tevent_context *ev,
106                            struct tevent_fd *fde, uint16_t flags, void *_c)
107 {
108         struct client *c = _c;
109         ssize_t len;
110
111         switch (c->state) {
112         case SENDING_GREETING:
113                 if (!send_string(c, "Welcome.  Please ask your question.\n"))
114                         goto fail;
115                 break;
116         case RECEIVING_USER_QUESTION:
117                 len = read_string(c->fd, &c->question);
118                 if (len <= 0)
119                         goto fail;
120                 if (input_finished(c->question)) {
121                         unsigned int i;
122
123                         for (i = 0; c->question[i]; i++)
124                                 c->question[i] = toupper(c->question[i]);
125                         set_state(c, SENDING_ANSWER_PREFIX);
126                 }
127                 break;
128         case SENDING_ANSWER_PREFIX:
129                 if (!send_string(c, "Our answer is:\n"))
130                         goto fail;
131                 break;
132         case SENDING_ANSWER:
133                 if (!send_string(c, c->question))
134                         goto fail;
135                 break;
136         default:
137                 goto fail;
138         }
139
140         if (c->state != FINISHED)
141                 return;
142
143 fail:
144         talloc_free(c);
145 }
146
147 static int cleanup_client(struct client *client)
148 {
149         unsigned int i;
150
151         for (i = 0; i < ARRAY_SIZE(client->oserver->clients); i++) {
152                 if (client->oserver->clients[i] == client) {
153                         client->oserver->clients[i] = NULL;
154                         tevent_fd_set_flags(client->oserver->fde,
155                                             TEVENT_FD_READ);
156                         return 0;
157                 }
158         }
159         abort();
160 }
161
162 static void add_client(struct tevent_context *ev,
163                        struct tevent_fd *fde, uint16_t flags, void *_oserver)
164 {
165         struct oserver *oserver = _oserver;
166         struct client *client;
167         unsigned int i;
168
169         client = talloc(oserver, struct client);
170         client->fd = accept(oserver->fd, NULL, 0);
171         if (client->fd < 0)
172                 err(1, "Accepting client connection");
173
174         client->state = SENDING_GREETING;
175         client->bytes_sent = 0;
176         client->question = talloc_strdup(client, "");
177         client->oserver = oserver;
178         client->fde = tevent_add_fd(ev, client, client->fd,
179                                     state_flag_map[client->state],
180                                     service_client, client);
181         tevent_fd_set_auto_close(client->fde);
182
183         /* Find empty slot in array for this client. */
184         for (i = 0; oserver->clients[i]; i++);
185         oserver->clients[i] = client;
186         talloc_set_destructor(client, cleanup_client);
187
188         /* Full?  Stop listening... */
189         if (i == ARRAY_SIZE(oserver->clients)-1)
190                 tevent_fd_set_flags(oserver->fde, 0);
191 }
192
193 static void clear_clients(struct oserver *oserver)
194 {
195         memset(oserver->clients, 0,
196                ARRAY_SIZE(oserver->clients) * sizeof(oserver->clients[0]));
197 }
198
199 static int destroy_oserver(struct oserver *oserver)
200 {
201         close(oserver->fd);
202         return 0;
203 }
204
205 struct oserver *oserver_setup(struct tevent_context *ev, unsigned short port)
206 {
207         struct oserver *oserver;
208         int one = 1;
209         union {
210                 struct sockaddr addr;
211                 struct sockaddr_in in;
212         } u;
213
214         oserver = talloc(ev, struct oserver);
215         clear_clients(oserver);
216         oserver->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
217         if (oserver->fd < 0) {
218                 talloc_free(oserver);
219                 return NULL;
220         }
221         
222         talloc_set_destructor(oserver, destroy_oserver);
223
224         if (setsockopt(oserver->fd, SOL_SOCKET, SO_REUSEADDR,
225                        &one, sizeof(one)))
226                 warn("Setting socket reuse");
227
228         u.in.sin_family = AF_INET;
229         u.in.sin_port = htons(port);
230         u.in.sin_addr.s_addr = INADDR_ANY;
231         if (bind(oserver->fd, &u.addr, sizeof(u.in)) == -1) {
232                 talloc_free(oserver);
233                 return NULL;
234         }
235
236         if (listen(oserver->fd, 0) != 0) {
237                 talloc_free(oserver);
238                 return NULL;
239         }
240
241         oserver->fde = tevent_add_fd(ev, oserver, oserver->fd,
242                                      TEVENT_FD_READ, add_client, oserver);
243         if (!oserver->fde) {
244                 talloc_free(oserver);
245                 return NULL;
246         }
247
248         /* Don't kill us if client dies. */
249         signal(SIGPIPE, SIG_IGN);
250
251         return oserver;
252 }