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