chiark / gitweb /
more automation of test logic
[disorder] / server / trackdb.c
index d0147e6e023b66cc68ce5f8ad57788bd990fdd0e..8cfd71f76f7b4b72d5c594eeae4937d6f251fee9 100644 (file)
@@ -69,6 +69,7 @@ static int trackdb_get_global_tid(const char *name,
 static char **trackdb_new_tid(int *ntracksp,
                               int maxtracks,
                               DB_TXN *tid);
+static int trackdb_expire_noticed_tid(time_t earliest, DB_TXN *tid);
 
 const struct cache_type cache_files_type = { 86400 };
 unsigned long cache_files_hits, cache_files_misses;
@@ -153,39 +154,35 @@ static int reap_db_deadlock(ev_source attribute((unused)) *ev,
   return 0;
 }
 
-static pid_t subprogram(ev_source *ev, const char *prog) {
+static pid_t subprogram(ev_source *ev, const char *prog,
+                        int outputfd) {
   pid_t pid;
-  int lfd;
 
   /* If we're in the background then trap subprocess stdout/stderr */
-  if(!isatty(2))
-    lfd = logfd(ev, prog);
-  else
-    lfd = -1;
   if(!(pid = xfork())) {
     exitfn = _exit;
     ev_signal_atfork(ev);
     signal(SIGPIPE, SIG_DFL);
-    if(lfd != -1) {
-      xdup2(lfd, 1);
-      xdup2(lfd, 2);
+    if(outputfd != -1) {
+      xdup2(outputfd, 1);
+      xclose(outputfd);
     }
     /* If we were negatively niced, undo it.  We don't bother checking for
      * error, it's not that important. */
     setpriority(PRIO_PROCESS, 0, 0);
     execlp(prog, prog, "--config", configfile,
            debugging ? "--debug" : "--no-debug",
+           log_default == &log_syslog ? "--syslog" : "--no-syslog",
            (char *)0);
     fatal(errno, "error invoking %s", prog);
   }
-  if(lfd != -1) xclose(lfd);
   return pid;
 }
 
 /* start deadlock manager */
 void trackdb_master(ev_source *ev) {
   assert(db_deadlock_pid == -1);
-  db_deadlock_pid = subprogram(ev, DEADLOCK);
+  db_deadlock_pid = subprogram(ev, DEADLOCK, -1);
   ev_child(ev, db_deadlock_pid, 0, reap_db_deadlock, 0);
   D(("started deadlock manager"));
 }
@@ -682,7 +679,7 @@ done:
 
 /* trackdb_notice() **********************************************************/
 
-/** @brief notice a possiby new  track
+/** @brief notice a possibly new track
  * @return @c DB_NOTFOUND if new, 0 if already known
  */
 int trackdb_notice(const char *track,
@@ -702,7 +699,7 @@ int trackdb_notice(const char *track,
   return err;
 }
 
-/** @brief notice a possiby new  track
+/** @brief notice a possibly new track
  * @return @c DB_NOTFOUND if new, 0 if already known, @c DB_LOCK_DEADLOCK also
  */
 int trackdb_notice_tid(const char *track,
@@ -951,7 +948,6 @@ static int search_league(struct vector *v, int count, DB_TXN *tid) {
 char **trackdb_stats(int *nstatsp) {
   DB_TXN *tid;
   struct vector v;
-  char *s;
   
   vector_init(&v);
   for(;;) {
@@ -967,12 +963,6 @@ char **trackdb_stats(int *nstatsp) {
     if(get_stats(&v, trackdb_prefsdb, SI(hash), tid)) goto fail;
     vector_append(&v, (char *)"");
     if(search_league(&v, 10, tid)) goto fail;
-    vector_append(&v, (char *)"");
-    vector_append(&v, (char *)"Server stats:");
-    byte_xasprintf(&s, "track lookup cache hits: %lu", cache_files_hits);
-    vector_append(&v, (char *)s);
-    byte_xasprintf(&s, "track lookup cache misses: %lu", cache_files_misses);
-    vector_append(&v, (char *)s);
     vector_terminate(&v);
     break;
 fail:
@@ -983,6 +973,89 @@ fail:
   return v.vec;
 }
 
+struct stats_details {
+  void (*done)(char *data, void *u);
+  void *u;
+  int exited;                           /* subprocess exited */
+  int closed;                           /* pipe close */
+  int wstat;                            /* wait status from subprocess */
+  struct dynstr data[1];                /* data read from pipe */
+};
+
+static void stats_complete(struct stats_details *d) {
+  char *s;
+  
+  if(!(d->exited && d->closed))
+    return;
+  byte_xasprintf(&s, "\n"
+                 "Server stats:\n"
+                 "track lookup cache hits: %lu\n"
+                 "track lookup cache misses: %lu\n",
+                 cache_files_hits,
+                 cache_files_misses);
+  dynstr_append_string(d->data, s);
+  dynstr_terminate(d->data);
+  d->done(d->data->vec, d->u);
+}
+
+static int stats_finished(ev_source attribute((unused)) *ev,
+                          pid_t attribute((unused)) pid,
+                          int status,
+                          const struct rusage attribute((unused)) *rusage,
+                          void *u) {
+  struct stats_details *const d = u;
+
+  d->exited = 1;
+  if(status)
+    error(0, "disorder-stats %s", wstat(status));
+  stats_complete(d);
+  return 0;
+}
+
+static int stats_read(ev_source attribute((unused)) *ev,
+                      ev_reader *reader,
+                      void *ptr,
+                      size_t bytes,
+                      int eof,
+                      void *u) {
+  struct stats_details *const d = u;
+
+  dynstr_append_bytes(d->data, ptr, bytes);
+  ev_reader_consume(reader, bytes);
+  if(eof)
+    d->closed = 1;
+  stats_complete(d);
+  return 0;
+}
+
+static int stats_error(ev_source attribute((unused)) *ev,
+                       int errno_value,
+                       void *u) {
+  struct stats_details *const d = u;
+
+  error(errno_value, "error reading from pipe to disorder-stats");
+  d->closed = 1;
+  stats_complete(d);
+  return 0;
+}
+
+void trackdb_stats_subprocess(ev_source *ev,
+                              void (*done)(char *data, void *u),
+                              void *u) {
+  int p[2];
+  pid_t pid;
+  struct stats_details *d = xmalloc(sizeof *d);
+
+  dynstr_init(d->data);
+  d->done = done;
+  d->u = u;
+  xpipe(p);
+  pid = subprogram(ev, "disorder-stats", p[1]);
+  xclose(p[1]);
+  ev_child(ev, pid, 0, stats_finished, d);
+  ev_reader_new(ev, p[0], stats_read, stats_error, d, "disorder-stats reader");
+}
+
 /* set a pref (remove if value=0) */
 int trackdb_set(const char *track,
                 const char *name,
@@ -1757,7 +1830,7 @@ void trackdb_rescan(ev_source *ev) {
     error(0, "rescan already underway");
     return;
   }
-  rescan_pid = subprogram(ev, RESCAN);
+  rescan_pid = subprogram(ev, RESCAN, -1);
   ev_child(ev, rescan_pid, 0, reap_rescan, 0);
   D(("started rescanner"));
   
@@ -1921,6 +1994,63 @@ static char **trackdb_new_tid(int *ntracksp,
   return tracks->vec;
 }
 
+/** @brief Expire noticed.db
+ * @param earliest Earliest timestamp to keep
+ */
+void trackdb_expire_noticed(time_t earliest) {
+  DB_TXN *tid;
+
+  for(;;) {
+    tid = trackdb_begin_transaction();
+    if(!trackdb_expire_noticed_tid(earliest, tid))
+      break;
+    trackdb_abort_transaction(tid);
+  }
+  trackdb_commit_transaction(tid);
+}
+
+/** @brief Expire noticed.db
+ * @param earliest Earliest timestamp to keep
+ * @param tid Transaction ID
+ * @return 0 or DB_LOCK_DEADLOCK
+ */
+static int trackdb_expire_noticed_tid(time_t earliest, DB_TXN *tid) {
+  DBC *c;
+  DBT k, d;
+  int err = 0, ret;
+  time_t when;
+  uint32_t *kk;
+  int count = 0;
+
+  c = trackdb_opencursor(trackdb_noticeddb, tid);
+  while(!(err = c->c_get(c, prepare_data(&k), prepare_data(&d), DB_NEXT))) {
+    kk = k.data;
+    when = (time_t)(((uint64_t)ntohl(kk[0]) << 32) + ntohl(kk[1]));
+    if(when >= earliest)
+      break;
+    if((err = c->c_del(c, 0))) {
+      if(err != DB_LOCK_DEADLOCK)
+        fatal(0, "error deleting expired noticed.db entry: %s",
+              db_strerror(err));
+      break;
+    }
+    ++count;
+  }
+  if(err == DB_NOTFOUND)
+    err = 0;
+  if(err && err != DB_LOCK_DEADLOCK)
+    fatal(0, "error expiring noticed.db: %s", db_strerror(err));
+  ret = err;
+  if((err = trackdb_closecursor(c))) {
+    if(err != DB_LOCK_DEADLOCK)
+      fatal(0, "error closing cursor: %s", db_strerror(err));
+    ret = err;
+  }
+  if(!ret && count)
+    info("expired %d tracks from noticed.db", count);
+  return ret;
+}
+
 /* tidying up ****************************************************************/
 
 void trackdb_gc(void) {