X-Git-Url: http://git.ozlabs.org/?a=blobdiff_plain;f=ccan%2Foserver%2Foserver.c;h=c7a523c21ff121f9f0ace96895216e143d3d2131;hb=e3096dff04758d433eb415f00d586d144b2ffea4;hp=4503aa49610f1a13d55649d37dbfa89c0d9f24ac;hpb=5807ed8dff7520582ee88f16c98b3965389bf8ff;p=ccan-lca-2011.git diff --git a/ccan/oserver/oserver.c b/ccan/oserver/oserver.c index 4503aa4..c7a523c 100644 --- a/ccan/oserver/oserver.c +++ b/ccan/oserver/oserver.c @@ -3,7 +3,6 @@ #include #include #include -#include #include #include #include @@ -16,15 +15,26 @@ #include #include #include +#include enum state { + SENDING_GREETING, RECEIVING_USER_QUESTION, + SENDING_OTHER_QUESTION_PREFIX, + SENDING_OTHER_QUESTION, + RECEIVING_OTHER_ANSWER, + SENDING_ANSWER_PREFIX, SENDING_ANSWER, FINISHED }; static uint16_t state_flag_map[] = { + [SENDING_GREETING] = TEVENT_FD_WRITE, [RECEIVING_USER_QUESTION] = TEVENT_FD_READ, + [SENDING_OTHER_QUESTION_PREFIX] = TEVENT_FD_WRITE, + [SENDING_OTHER_QUESTION] = TEVENT_FD_WRITE, + [RECEIVING_OTHER_ANSWER] = TEVENT_FD_READ, + [SENDING_ANSWER_PREFIX] = TEVENT_FD_WRITE, [SENDING_ANSWER] = TEVENT_FD_WRITE, [FINISHED] = 0 }; @@ -37,14 +47,24 @@ struct client { int fd; /* The question we read from client. */ char *question; + /* The answer to the client. */ + char *answer; /* How many bytes of the reply we sent so far. */ size_t bytes_sent; + /* Our server. */ + struct oserver *oserver; + /* Whose question this client is answering. */ + struct client *subclient; + /* Who is answering our question. */ + struct client *oracle; }; -/* 5 clients should be enough for anybody! */ -static struct client *clients[5]; -static int sfd; -static struct tevent_fd *sfde; +struct oserver { + /* 5 clients should be enough for anybody! */ + struct client *clients[5]; + int fd; + struct tevent_fd *fde; +}; static ssize_t write_string(int fd, const char *str) { @@ -95,6 +115,45 @@ static bool send_string(struct client *c, const char *str) return true; } +static bool get_subclient(struct client *me) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(me->oserver->clients); i++) { + struct client *c = me->oserver->clients[i]; + if (!c || c == me) + continue; + if (c->oracle == NULL && input_finished(c->question)) { + me->subclient = c; + c->oracle = me; + return true; + } + } + return false; +} + +static bool get_oracle(struct client *me) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(me->oserver->clients); i++) { + struct client *c = me->oserver->clients[i]; + if (!c || c == me) + continue; + if (c->subclient == NULL && input_finished(c->question)) { + me->oracle = c; + c->subclient = me; + return true; + } + } + return false; +} + +static void wakeup(struct client *c) +{ + tevent_fd_set_flags(c->fde, state_flag_map[c->state]); +} + static void service_client(struct tevent_context *ev, struct tevent_fd *fde, uint16_t flags, void *_c) { @@ -102,20 +161,49 @@ static void service_client(struct tevent_context *ev, ssize_t len; switch (c->state) { + case SENDING_GREETING: + if (!send_string(c, "Welcome. Please ask your question.\n")) + goto fail; + break; case RECEIVING_USER_QUESTION: len = read_string(c->fd, &c->question); if (len <= 0) goto fail; - if (input_finished(c->question)) { - unsigned int i; - - for (i = 0; c->question[i]; i++) - c->question[i] = toupper(c->question[i]); - set_state(c, SENDING_ANSWER); + if (input_finished(c->question)) + set_state(c, SENDING_OTHER_QUESTION_PREFIX); + break; + case SENDING_OTHER_QUESTION_PREFIX: + if (!c->subclient) + goto need_subclient; + if (!send_string(c, "While the Oracle ponders," + " please answer the following question:\n")) + goto fail; + break; + case SENDING_OTHER_QUESTION: + if (!c->subclient) + goto need_subclient; + if (!send_string(c, c->subclient->question)) + goto fail; + break; + case RECEIVING_OTHER_ANSWER: + if (!c->subclient) + goto need_subclient; + len = read_string(c->fd, &c->subclient->answer); + if (len <= 0) + goto fail; + if (input_finished(c->subclient->answer)) { + set_state(c, SENDING_ANSWER_PREFIX); + wakeup(c->subclient); } break; + case SENDING_ANSWER_PREFIX: + if (!input_finished(c->answer)) + goto need_answer; + if (!send_string(c, "The Oracle spake thus:\n")) + goto fail; + break; case SENDING_ANSWER: - if (!send_string(c, c->question)) + if (!send_string(c, c->answer)) goto fail; break; default: @@ -124,19 +212,48 @@ static void service_client(struct tevent_context *ev, if (c->state != FINISHED) return; - fail: talloc_free(c); + return; + +need_subclient: + if (!get_subclient(c)) { + /* We can't get one: go to sleep until someone find_oracle() */ + tevent_fd_set_flags(c->fde, 0); + } else + /* In case they are waiting... */ + wakeup(c->subclient); + return; + +need_answer: + /* If we don't have an oracle and find one, that's OK. */ + if (!c->oracle && get_oracle(c)) { + /* In case they are waiting... */ + wakeup(c->oracle); + return; + } + + /* Either our oracle is not finished, or we don't have one: sleep. */ + tevent_fd_set_flags(c->fde, 0); } static int cleanup_client(struct client *client) { unsigned int i; - for (i = 0; i < ARRAY_SIZE(clients); i++) { - if (clients[i] == client) { - clients[i] = NULL; - tevent_fd_set_flags(sfde, TEVENT_FD_READ); + /* We were an oracle? */ + if (client->subclient) + client->subclient->oracle = NULL; + + /* We had an oracle? */ + if (client->oracle) + client->oracle->subclient = NULL; + + for (i = 0; i < ARRAY_SIZE(client->oserver->clients); i++) { + if (client->oserver->clients[i] == client) { + client->oserver->clients[i] = NULL; + tevent_fd_set_flags(client->oserver->fde, + TEVENT_FD_READ); return 0; } } @@ -144,67 +261,115 @@ static int cleanup_client(struct client *client) } static void add_client(struct tevent_context *ev, - struct tevent_fd *fde, uint16_t flags, void *unused) + struct tevent_fd *fde, uint16_t flags, void *_oserver) { + struct oserver *oserver = _oserver; struct client *client; unsigned int i; - client = talloc(sfde, struct client); - client->fd = accept(sfd, NULL, 0); + client = talloc(oserver, struct client); + client->fd = accept(oserver->fd, NULL, 0); if (client->fd < 0) err(1, "Accepting client connection"); - client->state = RECEIVING_USER_QUESTION; + client->state = SENDING_GREETING; client->bytes_sent = 0; client->question = talloc_strdup(client, ""); + client->oserver = oserver; + client->oracle = NULL; + client->subclient = NULL; + client->answer = talloc_strdup(client, ""); client->fde = tevent_add_fd(ev, client, client->fd, state_flag_map[client->state], service_client, client); tevent_fd_set_auto_close(client->fde); /* Find empty slot in array for this client. */ - for (i = 0; clients[i]; i++); - clients[i] = client; + for (i = 0; oserver->clients[i]; i++); + oserver->clients[i] = client; talloc_set_destructor(client, cleanup_client); /* Full? Stop listening... */ - if (i == ARRAY_SIZE(clients)-1) - tevent_fd_set_flags(sfde, 0); + if (i == ARRAY_SIZE(oserver->clients)-1) + tevent_fd_set_flags(oserver->fde, 0); } -void *oserver_setup(struct tevent_context *ev, unsigned short port) +static void clear_clients(struct oserver *oserver) { + memset(oserver->clients, 0, + ARRAY_SIZE(oserver->clients) * sizeof(oserver->clients[0])); +} + +static int destroy_oserver(struct oserver *oserver) +{ + close(oserver->fd); + return 0; +} + +static void talloc_dump(struct tevent_context *ev, + struct tevent_signal *se, + int signum, + int count, + void *siginfo, + void *_oserver) +{ + struct oserver *oserver = _oserver; + FILE *f = fopen("/var/run/oserver/talloc.dump", "w"); + if (f) { + talloc_report_full(oserver, f); + fclose(f); + } +} + +struct oserver *oserver_setup(struct tevent_context *ev, unsigned short port) +{ + struct oserver *oserver; int one = 1; union { struct sockaddr addr; struct sockaddr_in in; } u; - sfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (sfd < 0) - return false; + oserver = talloc(ev, struct oserver); + clear_clients(oserver); + oserver->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (oserver->fd < 0) { + talloc_free(oserver); + return NULL; + } + + talloc_set_destructor(oserver, destroy_oserver); - if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))) + if (setsockopt(oserver->fd, SOL_SOCKET, SO_REUSEADDR, + &one, sizeof(one))) warn("Setting socket reuse"); u.in.sin_family = AF_INET; u.in.sin_port = htons(port); u.in.sin_addr.s_addr = INADDR_ANY; - if (bind(sfd, &u.addr, sizeof(u.in)) == -1) { - close_noerr(sfd); + if (bind(oserver->fd, &u.addr, sizeof(u.in)) == -1) { + talloc_free(oserver); return NULL; } - if (listen(sfd, 0) != 0) { - close_noerr(sfd); + if (listen(oserver->fd, 0) != 0) { + talloc_free(oserver); return NULL; } - sfde = tevent_add_fd(ev, ev, sfd, TEVENT_FD_READ, add_client, NULL); - tevent_fd_set_auto_close(sfde); + oserver->fde = tevent_add_fd(ev, oserver, oserver->fd, + TEVENT_FD_READ, add_client, oserver); + if (!oserver->fde) { + talloc_free(oserver); + return NULL; + } /* Don't kill us if client dies. */ signal(SIGPIPE, SIG_IGN); - return sfde; + /* Show talloc tree on SIGUSR1. */ + tevent_add_signal(ev, oserver, SIGUSR1, SA_RESTART, + talloc_dump, oserver); + + return oserver; }