chiark / gitweb /
avoid crashes on log client teardown
[disorder] / server / server.c
index c5fb0108f3516bfd5794f09199fa02723701358d..1e4c6ca91930f9d8b1d4ac40849e7926828f236e 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * This file is part of DisOrder.
- * Copyright (C) 2004, 2005, 2006 Richard Kettlewell
+ * Copyright (C) 2004, 2005, 2006, 2007 Richard Kettlewell
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -42,6 +42,7 @@
 #include "server.h"
 #include "syscalls.h"
 #include "queue.h"
+#include "server-queue.h"
 #include "play.h"
 #include "log.h"
 #include "mem.h"
@@ -114,10 +115,12 @@ static int writer_error(ev_source attribute((unused)) *ev,
     /* writer is done */
     c->w = 0;
     if(c->r == 0) {
-      D(("server writer_error closes %d", fd));
+      //D(("server writer_error closes %d", fd));
+      info("server writer_error closes %d because all done", fd); /* TODO */
       xclose(fd);              /* reader is done too, close */
     } else {
-      D(("server writer_error shutdown %d SHUT_WR", fd));
+      //D(("server writer_error shutdown %d SHUT_WR", fd));
+      info("server writer_error shutdown %d SHUT_WR", fd); /* TODO */
       xshutdown(fd, SHUT_WR);  /* reader is not done yet */
     }
   } else {
@@ -125,6 +128,7 @@ static int writer_error(ev_source attribute((unused)) *ev,
       error(errno_value, "S%x write error on socket", c->tag);
     if(c->r)
       ev_reader_cancel(c->r);
+    info("server writer_error closes %d because errno=%d", fd, errno_value); /* TODO */
     xclose(fd);
   }
   return 0;
@@ -138,7 +142,12 @@ static int reader_error(ev_source attribute((unused)) *ev,
 
   D(("server reader_error %d %d", fd, errno_value));
   error(errno, "S%x read error on socket", c->tag);
-  ev_writer_cancel(c->w);
+  if(c->w) {
+    ev_writer_cancel(c->w);
+    c->w = 0;
+  }
+  ev_report(ev);
+  info("reader_error closing fd %d", fd); /* TODO */
   xclose(fd);
   return 0;
 }
@@ -662,21 +671,19 @@ static int c_random_enabled(struct conn *c,
   return 1;                    /* completed */
 }
 
+static void got_stats(char *stats, void *u) {
+  struct conn *const c = u;
+
+  sink_printf(ev_writer_sink(c->w), "253 stats\n%s\n.\n", stats);
+  /* Now we can start processing commands again */
+  ev_reader_enable(c->r);
+}
+
 static int c_stats(struct conn *c,
                   char attribute((unused)) **vec,
                   int attribute((unused)) nvec) {
-  char **v;
-  int nv, n;
-
-  v = trackdb_stats(&nv);
-  sink_printf(ev_writer_sink(c->w), "253 stats\n");
-  for(n = 0; n < nv; ++n) {
-    if(v[n][0] == '.')
-      sink_writes(ev_writer_sink(c->w), ".");
-    sink_printf(ev_writer_sink(c->w), "%s\n", v[n]);
-  }
-  sink_writes(ev_writer_sink(c->w), ".\n");
-  return 1;
+  trackdb_stats_subprocess(c->ev, got_stats, c);
+  return 0;                            /* not yet complete */
 }
 
 static int c_volume(struct conn *c,
@@ -728,8 +735,11 @@ static int logging_reader_callback(ev_source *ev,
 
   /* don't log to this conn any more */
   eventlog_remove(c->lo);
-  /* terminate the log output */
-  sink_writes(ev_writer_sink(c->w), ".\n");
+  if(c->w) {
+    /* Terminate the log output, but only if the writer hasn't been killed off
+     * from a failure on some earlier write */
+    sink_writes(ev_writer_sink(c->w), ".\n");
+  }
   /* restore the reader callback */
   c->reader = reader_callback;
   /* ...and exit via it */
@@ -739,6 +749,11 @@ static int logging_reader_callback(ev_source *ev,
 static void logclient(const char *msg, void *user) {
   struct conn *c = user;
 
+  if(!c->w || !c->r) {
+    /* This connection has gone up in smoke for some reason */
+    eventlog_remove(c->lo);
+    return;
+  }
   sink_printf(ev_writer_sink(c->w), "%"PRIxMAX" %s\n",
              (uintmax_t)time(0), msg);
 }
@@ -777,8 +792,8 @@ static int c_log(struct conn *c,
 static void post_move_cleanup(void) {
   struct queue_entry *q;
 
-  /* If we have caused the random track to not be at the end then we make it no
-   * longer be random. */
+  /* If we have caused any random tracks to not be at the end then we make them
+   * no longer be random. */
   for(q = qhead.next; q != &qhead; q = q->next)
     if(q->state == playing_random && q->next != &qhead)
       q->state = playing_unplayed;
@@ -930,6 +945,18 @@ static int c_new(struct conn *c,
 
 }
 
+static int c_rtp_address(struct conn *c,
+                        char attribute((unused)) **vec,
+                        int attribute((unused)) nvec) {
+  if(config->speaker_backend == BACKEND_NETWORK) {
+    sink_printf(ev_writer_sink(c->w), "252 %s %s\n",
+               quoteutf8(config->broadcast.s[0]),
+               quoteutf8(config->broadcast.s[1]));
+  } else
+    sink_writes(ev_writer_sink(c->w), "550 No RTP\n");
+  return 1;
+}
 #define C_AUTH         0001            /* must be authenticated */
 #define C_TRUSTED      0002            /* must be trusted user */
 
@@ -970,6 +997,7 @@ static const struct command {
   { "rescan",         0, 0,       c_rescan,         C_AUTH|C_TRUSTED },
   { "resolve",        1, 1,       c_resolve,        C_AUTH },
   { "resume",         0, 0,       c_resume,         C_AUTH },
+  { "rtp-address",    0, 0,       c_rtp_address,    C_AUTH },
   { "scratch",        0, 1,       c_scratch,        C_AUTH },
   { "search",         1, 1,       c_search,         C_AUTH },
   { "set",            3, 3,       c_set,            C_AUTH, },
@@ -1096,8 +1124,10 @@ static int listen_callback(ev_source *ev,
   cloexec(fd);
   c->tag = tags++;
   c->ev = ev;
-  c->w = ev_writer_new(ev, fd, writer_error, c);
-  c->r = ev_reader_new(ev, fd, redirect_reader_callback, reader_error, c);
+  c->w = ev_writer_new(ev, fd, writer_error, c,
+                      "client writer");
+  c->r = ev_reader_new(ev, fd, redirect_reader_callback, reader_error, c,
+                      "client reader");
   c->fd = fd;
   c->reader = reader_callback;
   c->l = l;
@@ -1133,7 +1163,8 @@ int server_start(ev_source *ev, int pf,
   cloexec(fd);
   l->name = name;
   l->pf = pf;
-  if(ev_listen(ev, fd, listen_callback, l)) exit(EXIT_FAILURE);
+  if(ev_listen(ev, fd, listen_callback, l, "server listener"))
+    exit(EXIT_FAILURE);
   return fd;
 }