+#include <ccan/oserver/oserver.c>
+#include <ccan/oserver/oserver.h>
+#include <ccan/str/str.h>
+#include <ccan/tap/tap.h>
+#include <sys/types.h>
+#include <sys/select.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/wait.h>
+
+static bool write_sall(int fd, const char *str)
+{
+ while (str[0]) {
+ ssize_t len = write(fd, str, strlen(str));
+ if (len < 0)
+ return false;
+ str += len;
+ }
+ return true;
+}
+
+static bool input_is(int fd, const char *str)
+{
+ while (str[0]) {
+ char buffer[1000];
+ ssize_t len = read(fd, buffer, strlen(str));
+ if (len < 0)
+ return false;
+ if (strncmp(str, buffer, len) != 0)
+ return false;
+ str += len;
+ }
+ return true;
+}
+
+static void run_client(int readyfd, bool die)
+{
+ union {
+ struct sockaddr addr;
+ struct sockaddr_in in;
+ } u;
+ int sfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ char c;
+
+ u.in.sin_family = AF_INET;
+ u.in.sin_port = htons(OSERVER_PORT);
+ u.in.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ if (connect(sfd, &u.addr, sizeof(u.in)) != 0)
+ exit(1);
+
+ if (!input_is(sfd, "Welcome. Please ask your question.\n"))
+ exit(1);
+
+ /* Ask a question. */
+ if (!write_sall(sfd, "Question?\n"))
+ exit(1);
+
+ if (!input_is(sfd, "While the Oracle ponders, please answer the following question:\nQuestion?\n"))
+ exit(1);
+
+ if (die)
+ exit(0);
+
+ read(readyfd, &c, 1);
+ if (!write_sall(sfd, "Answer?\n"))
+ exit(1);
+
+ /* Since other died, oracle won't say any more. */
+ read(sfd, &c, 1);
+ exit(1);
+}
+
+static void set_flag(struct tevent_context *ev,
+ struct tevent_signal *se,
+ int signum,
+ int count,
+ void *siginfo,
+ void *_flag)
+{
+ bool *flag = _flag;
+ *flag = true;
+}
+
+static unsigned int count_clients(struct oserver *oserver)
+{
+ unsigned int i, count = 0;
+
+ for (i = 0; i < ARRAY_SIZE(oserver->clients); i++) {
+ if (oserver->clients[i])
+ count++;
+ }
+ return count;
+}
+
+int main(int argc, char *argv[])
+{
+ int ready[2];
+ struct tevent_context *ev = tevent_context_init(NULL);
+ struct oserver *oserver;
+ bool done = false;
+ int status;
+
+ /* This is how many tests you plan to run */
+ plan_tests(2);
+
+ oserver = oserver_setup(ev, OSERVER_PORT);
+ if (!oserver)
+ err(1, "Failed to set up server");
+
+ tevent_add_signal(ev, ev, SIGCHLD, 0, set_flag, &done);
+ pipe(ready);
+
+ if (fork() == 0) {
+ /* This child will exit, doesn't need fd. */
+ close(ready[0]);
+ close(ready[1]);
+ run_client(-1, true);
+ }
+ if (fork() == 0) {
+ close(ready[1]);
+ run_client(ready[0], false);
+ }
+
+ /* Wait for dead child to exit... */
+ while (!done)
+ tevent_loop_once(ev);
+
+ /* Wait for client to be freed. */
+ while (count_clients(oserver) == 2)
+ tevent_loop_once(ev);
+
+ /* One child should be dead... */
+ ok1(waitpid(-1, &status, 0) > 0);
+
+ /* Tell other child to write answer. */
+ write(ready[1], &status, 1);
+
+ /* Process that. */
+ tevent_loop_once(ev);
+
+ /* Other child should be hung... */
+ ok1(waitpid(-1, &status, WNOHANG) == 0);
+
+ talloc_free(ev);
+ return exit_status();
+}