1 #include <ccan/oserver/oserver.h>
2 #include <ccan/oserver/oserver_types.h>
3 #include <ccan/read_write_all/read_write_all.h>
4 #include <ccan/opt/opt.h>
5 #include <ccan/tevent/tevent.h>
6 #include <ccan/array_size/array_size.h>
8 #include <sys/socket.h>
9 #include <netinet/in.h>
10 #include <netinet/tcp.h>
21 static uint16_t state_flag_map[] = {
22 [SENDING_GREETING] = TEVENT_FD_WRITE,
23 [RECEIVING_USER_QUESTION] = TEVENT_FD_READ,
24 [SENDING_OTHER_QUESTION_PREFIX] = TEVENT_FD_WRITE,
25 [SENDING_OTHER_QUESTION] = TEVENT_FD_WRITE,
26 [RECEIVING_OTHER_ANSWER] = TEVENT_FD_READ,
27 [SENDING_ANSWER_PREFIX] = TEVENT_FD_WRITE,
28 [SENDING_ANSWER] = TEVENT_FD_WRITE,
32 static ssize_t write_string(int fd, const char *str)
34 return write(fd, str, strlen(str));
37 static ssize_t read_string(int fd, char **buf)
39 ssize_t ret, len, maxlen;
42 maxlen = talloc_array_length(*buf);
44 if (maxlen < len + 100) {
46 *buf = talloc_realloc(NULL, *buf, char, maxlen);
49 ret = read(fd, *buf + len, maxlen - len - 1);
51 (*buf)[len + ret] = '\0';
55 static bool input_finished(const char *str)
57 return strchr(str, '\n');
60 /* Update state, and set our READ/WRITE flags appropriately. */
61 static void set_state(struct client *c, enum state state)
64 tevent_fd_set_flags(c->fde, state_flag_map[state]);
67 /* Returns false on error, increments state on finishing string. */
68 static bool send_string(struct client *c, const char *str)
70 ssize_t len = write_string(c->fd, str + c->bytes_sent);
74 if (c->bytes_sent == strlen(str)) {
76 set_state(c, c->state+1);
81 static bool get_subclient(struct client *me)
85 for (i = 0; i < ARRAY_SIZE(me->oserver->clients); i++) {
86 struct client *c = me->oserver->clients[i];
89 if (c->oracle == NULL && input_finished(c->question)) {
98 static bool get_oracle(struct client *me)
102 for (i = 0; i < ARRAY_SIZE(me->oserver->clients); i++) {
103 struct client *c = me->oserver->clients[i];
106 if (c->subclient == NULL && input_finished(c->question)) {
115 static void wakeup(struct client *c)
117 tevent_fd_set_flags(c->fde, state_flag_map[c->state]);
120 static void service_client(struct tevent_context *ev,
121 struct tevent_fd *fde, uint16_t flags, void *_c)
123 struct client *c = _c;
127 case SENDING_GREETING:
128 if (!send_string(c, "Welcome. Please ask your question.\n"))
131 case RECEIVING_USER_QUESTION:
132 len = read_string(c->fd, &c->question);
135 if (input_finished(c->question))
136 set_state(c, SENDING_OTHER_QUESTION_PREFIX);
138 case SENDING_OTHER_QUESTION_PREFIX:
141 if (!send_string(c, "While the Oracle ponders,"
142 " please answer the following question:\n"))
145 case SENDING_OTHER_QUESTION:
148 if (!send_string(c, c->subclient->question))
151 case RECEIVING_OTHER_ANSWER:
154 len = read_string(c->fd, &c->subclient->answer);
157 if (input_finished(c->subclient->answer)) {
158 set_state(c, SENDING_ANSWER_PREFIX);
159 wakeup(c->subclient);
162 case SENDING_ANSWER_PREFIX:
163 if (!input_finished(c->answer))
165 if (!send_string(c, "The Oracle spake thus:\n"))
169 if (!send_string(c, c->answer))
176 if (c->state != FINISHED)
183 if (!get_subclient(c)) {
184 /* We can't get one: go to sleep until someone find_oracle() */
185 tevent_fd_set_flags(c->fde, 0);
187 /* In case they are waiting... */
188 wakeup(c->subclient);
192 /* If we don't have an oracle and find one, that's OK. */
193 if (!c->oracle && get_oracle(c)) {
194 /* In case they are waiting... */
199 /* Either our oracle is not finished, or we don't have one: sleep. */
200 tevent_fd_set_flags(c->fde, 0);
203 static int cleanup_client(struct client *client)
207 /* We were an oracle? */
208 if (client->subclient)
209 client->subclient->oracle = NULL;
211 /* We had an oracle? */
213 client->oracle->subclient = NULL;
215 for (i = 0; i < ARRAY_SIZE(client->oserver->clients); i++) {
216 if (client->oserver->clients[i] == client) {
217 client->oserver->clients[i] = NULL;
218 tevent_fd_set_flags(client->oserver->fde,
226 static void add_client(struct tevent_context *ev,
227 struct tevent_fd *fde, uint16_t flags, void *_oserver)
229 struct oserver *oserver = _oserver;
230 struct client *client;
233 client = talloc(oserver, struct client);
234 client->fd = accept(oserver->fd, NULL, 0);
236 err(1, "Accepting client connection");
238 client->state = SENDING_GREETING;
239 client->bytes_sent = 0;
240 client->question = talloc_strdup(client, "");
241 client->oserver = oserver;
242 client->oracle = NULL;
243 client->subclient = NULL;
244 client->answer = talloc_strdup(client, "");
245 client->fde = tevent_add_fd(ev, client, client->fd,
246 state_flag_map[client->state],
247 service_client, client);
248 tevent_fd_set_auto_close(client->fde);
250 /* Find empty slot in array for this client. */
251 for (i = 0; oserver->clients[i]; i++);
252 oserver->clients[i] = client;
253 talloc_set_destructor(client, cleanup_client);
255 /* Full? Stop listening... */
256 if (i == ARRAY_SIZE(oserver->clients)-1)
257 tevent_fd_set_flags(oserver->fde, 0);
260 static void clear_clients(struct oserver *oserver)
262 memset(oserver->clients, 0,
263 ARRAY_SIZE(oserver->clients) * sizeof(oserver->clients[0]));
266 static int destroy_oserver(struct oserver *oserver)
272 static void talloc_dump(struct tevent_context *ev,
273 struct tevent_signal *se,
279 struct oserver *oserver = _oserver;
282 /* Fork off a child for the report, so we aren't stopped. */
284 f = fopen("/var/run/oserver/talloc.dump", "w");
286 talloc_report_full(oserver, f);
293 struct oserver *oserver_setup(struct tevent_context *ev, unsigned short port)
295 struct oserver *oserver;
298 struct sockaddr addr;
299 struct sockaddr_in in;
302 oserver = talloc(ev, struct oserver);
303 clear_clients(oserver);
304 oserver->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
305 if (oserver->fd < 0) {
306 talloc_free(oserver);
310 talloc_set_destructor(oserver, destroy_oserver);
312 if (setsockopt(oserver->fd, SOL_SOCKET, SO_REUSEADDR,
314 warn("Setting socket reuse");
316 u.in.sin_family = AF_INET;
317 u.in.sin_port = htons(port);
318 u.in.sin_addr.s_addr = INADDR_ANY;
319 if (bind(oserver->fd, &u.addr, sizeof(u.in)) == -1) {
320 talloc_free(oserver);
324 if (listen(oserver->fd, 0) != 0) {
325 talloc_free(oserver);
329 oserver->fde = tevent_add_fd(ev, oserver, oserver->fd,
330 TEVENT_FD_READ, add_client, oserver);
332 talloc_free(oserver);
336 /* Don't kill us if client dies. */
337 signal(SIGPIPE, SIG_IGN);
339 /* Show talloc tree on SIGUSR1. */
340 tevent_add_signal(ev, oserver, SIGUSR1, SA_RESTART,
341 talloc_dump, oserver);