chiark / gitweb /
Merge disorder.macros branch.
authorRichard Kettlewell <rjk@greenend.org.uk>
Sun, 18 May 2008 21:29:17 +0000 (22:29 +0100)
committerRichard Kettlewell <rjk@greenend.org.uk>
Sun, 18 May 2008 21:29:17 +0000 (22:29 +0100)
This is a major rewrite of the web interface.  The template language
has been changed and is hopefuly easier to use.  Much of the
implementation has moved to lib/, along with some of the CGI support.
The CGI can now figure out its own URL, including HTTPS URLs.

The web interface documentation is no longer mixed into
disorder_config(5).  The top level is disorder.cgi(8) but there are
several related pages, much of the content generated from source code
comments.

The server now unsets track preferences if you try to set them to
their default value.  This resolves a long-standing TODO.  The server
is otherwise largely unchanged.

This changes fixes defects 2, 12 and 18 (the first and last of these
being the payoff for casual users).

13 files changed:
debian/changelog
doc/disorder_protocol.5.in
lib/trackdb.c
lib/trackdb.h
python/disorder.py.in
server/cgi.c [deleted file]
server/dcgi.c [deleted file]
server/dcgi.h [deleted file]
server/disorderd.c
server/server-cgi.h [deleted file]
server/server.c
server/state.c
tests/dtest.py

index f669591c66c78f993f66bd97234a3bb94c2efb93..ab46f89e4258cd9d57f95da379d99594d04fb3f7 100644 (file)
@@ -1,3 +1,9 @@
+disorder (3.0.99.dev) unstable; urgency=low
+
+  * Bodge version number
+
+ -- Richard Kettlewell <rjk@greenend.org.uk>  Sun, 18 May 2008 21:30:14 +0100
+
 disorder (3.0) unstable; urgency=low
 
   * DisOrder 3.0
index 5a2bf839a25e5601ad66b4e84824b48979ee3f4b..08470508c9be7c5e9c33ed7d12dad4d9fcf0fe74 100644 (file)
@@ -255,9 +255,21 @@ Requires one of the \fBremove mine\fR, \fBremove random\fR or
 \fBremove any\fR rights depending on how the
 track came to be added to the queue.
 .TP
-.B rescan
+.B rescan \fR[\fBwait\fR] \fR[\fBfresh\fR]
 Rescan all roots for new or obsolete tracks.
 Requires the \fBrescan\fR right.
+.IP
+If the \fBwait\fR flag is present then the response is delayed until the rescan
+completes.
+Otherwise the response arrives immediately.
+This is primarily intended for testing.
+.IP
+If the \fBfresh\fR flag is present a rescan is already underway then a second
+rescan will be started when it completes.
+The default behavior is to piggyback on the existing rescan.
+.IP
+NB that \fBfresh\fR is currently disabled in the server source, so using this
+flag will just provoke an error.
 .TP
 .B resolve \fITRACK\fR
 Resolve a track name, i.e. if this is an alias then return the real track name.
index 8e775d5626bc15033c2bf554a784d6c51dc97c24..9125c0c3d14f31987574d514580c9779bda9b409 100644 (file)
@@ -2157,6 +2157,28 @@ int trackdb_scan(const char *root,
 
 /* trackdb_rescan ************************************************************/
 
+/** @brief Node in the list of rescan-complete callbacks */
+struct rescanned_node {
+  struct rescanned_node *next;
+  void (*rescanned)(void *ru);
+  void *ru;
+};
+
+/** @brief List of rescan-complete callbacks */
+static struct rescanned_node *rescanned_list;
+
+/** @brief Add a rescan completion callback */
+void trackdb_add_rescanned(void (*rescanned)(void *ru),
+                           void *ru) {
+  if(rescanned) {
+    struct rescanned_node *n = xmalloc(sizeof *n);
+    n->next = rescanned_list;
+    n->rescanned = rescanned;
+    n->ru = ru;
+    rescanned_list = n;
+  }
+}
+
 /* called when the rescanner terminates */
 static int reap_rescan(ev_source attribute((unused)) *ev,
                        pid_t pid,
@@ -2171,23 +2193,37 @@ static int reap_rescan(ev_source attribute((unused)) *ev,
   /* Our cache of file lookups is out of date now */
   cache_clean(&cache_files_type);
   eventlog("rescanned", (char *)0);
+  /* Call rescanned callbacks */
+  while(rescanned_list) {
+    void (*rescanned)(void *u) = rescanned_list->rescanned;
+    void *ru = rescanned_list->ru;
+
+    rescanned_list = rescanned_list->next;
+    rescanned(ru);
+  }
   return 0;
 }
 
 /** @brief Initiate a rescan
  * @param ev Event loop or 0 to block
  * @param recheck 1 to recheck lengths, 0 to suppress check
+ * @param rescanned Called on completion (if not NULL)
+ * @param u Passed to @p rescanned
  */
-void trackdb_rescan(ev_source *ev, int recheck) {
+void trackdb_rescan(ev_source *ev, int recheck,
+                    void (*rescanned)(void *ru),
+                    void *ru) {
   int w;
 
   if(rescan_pid != -1) {
+    trackdb_add_rescanned(rescanned, ru);
     error(0, "rescan already underway");
     return;
   }
   rescan_pid = subprogram(ev, -1, RESCAN,
                           recheck ? "--check" : "--no-check",
                           (char *)0);
+  trackdb_add_rescanned(rescanned, ru);
   if(ev) {
     ev_child(ev, rescan_pid, 0, reap_rescan, 0);
     D(("started rescanner"));
@@ -2207,6 +2243,11 @@ int trackdb_rescan_cancel(void) {
   return 1;
 }
 
+/** @brief Return true if a rescan is underway */
+int trackdb_rescan_underway(void) {
+  return rescan_pid != -1;
+}
+
 /* global prefs **************************************************************/
 
 void trackdb_set_global(const char *name,
index f372f4cf0586e9fbe57e6fa3138a157d10d5eacc..f97ee5f338319d724ae13205afc6bd69b616e0d4 100644 (file)
@@ -136,7 +136,9 @@ char **trackdb_search(char **wordlist, int nwordlist, int *ntracks);
 /* return a list of tracks containing all of the words given.  If you
  * ask for only stopwords you get no tracks. */
 
-void trackdb_rescan(struct ev_source *ev, int recheck);
+void trackdb_rescan(struct ev_source *ev, int recheck,
+                    void (*rescanned)(void *ru),
+                    void *ru);
 /* Start a rescan, if one is not running already */
 
 int trackdb_rescan_cancel(void);
@@ -177,6 +179,9 @@ typedef void random_callback(struct ev_source *ev,
                              const char *track);
 int trackdb_request_random(struct ev_source *ev,
                            random_callback *callback);
+void trackdb_add_rescanned(void (*rescanned)(void *ru),
+                           void *ru);
+int trackdb_rescan_underway(void);
 
 #endif /* TRACKDB_H */
 
index 23f840cccd1749de5746627c3e34566a2676d103..ef33106fa8a1f8f007f1405e6889ab29c872619a 100644 (file)
@@ -479,12 +479,12 @@ class client:
     """
     self._simple("reconfigure")
 
-  def rescan(self):
+  def rescan(self, *flags):
     """Rescan one or more collections.
 
     Only trusted users can perform this operation.
     """
-    self._simple("rescan")
+    self._simple("rescan", *flags)
 
   def version(self):
     """Return the server's version number."""
diff --git a/server/cgi.c b/server/cgi.c
deleted file mode 100644 (file)
index 5f9e17f..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * This file is part of DisOrder.
- * Copyright (C) 2004-2008 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
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- * USA
- */
-
-#include <config.h>
-#include "types.h"
-
-#include <string.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <sys/stat.h>
-#include <stddef.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <pcre.h>
-#include <limits.h>
-#include <fnmatch.h>
-#include <ctype.h>
-
-#include "mem.h"
-#include "log.h"
-#include "hex.h"
-#include "charset.h"
-#include "configuration.h"
-#include "table.h"
-#include "syscalls.h"
-#include "kvp.h"
-#include "vector.h"
-#include "split.h"
-#include "inputline.h"
-#include "regsub.h"
-#include "defs.h"
-#include "sink.h"
-#include "server-cgi.h"
-#include "printf.h"
-#include "mime.h"
-#include "unicode.h"
-#include "hash.h"
-
-void cgi_header(struct sink *output, const char *name, const char *value) {
-  sink_printf(output, "%s: %s\r\n", name, value);
-}
-
-void cgi_body(struct sink *output) {
-  sink_printf(output, "\r\n");
-}
-
-/*
-Local Variables:
-c-basic-offset:2
-comment-column:40
-End:
-*/
diff --git a/server/dcgi.c b/server/dcgi.c
deleted file mode 100644 (file)
index bf6fbe5..0000000
+++ /dev/null
@@ -1,178 +0,0 @@
-
-#include <stdio.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <time.h>
-#include <unistd.h>
-#include <string.h>
-#include <sys/wait.h>
-#include <pcre.h>
-#include <assert.h>
-
-#include "client.h"
-#include "mem.h"
-#include "vector.h"
-#include "sink.h"
-#include "server-cgi.h"
-#include "log.h"
-#include "configuration.h"
-#include "table.h"
-#include "queue.h"
-#include "plugin.h"
-#include "split.h"
-#include "wstat.h"
-#include "kvp.h"
-#include "syscalls.h"
-#include "printf.h"
-#include "regsub.h"
-#include "defs.h"
-#include "trackname.h"
-#include "charset.h"
-#include "dcgi.h"
-#include "url.h"
-#include "mime.h"
-#include "sendmail.h"
-#include "base64.h"
-
-struct entry {
-  const char *path;
-  const char *sort;
-  const char *display;
-};
-
-static int compare_entry(const void *a, const void *b) {
-  const struct entry *ea = a, *eb = b;
-
-  return compare_tracks(ea->sort, eb->sort,
-                       ea->display, eb->display,
-                       ea->path, eb->path);
-}
-
-static const char *front_url(void) {
-  char *url;
-  const char *mgmt;
-
-  /* preserve management interface visibility */
-  if((mgmt = cgi_get("mgmt")) && !strcmp(mgmt, "true")) {
-    byte_xasprintf(&url, "%s?mgmt=true", config->url);
-    return url;
-  }
-  return config->url;
-}
-
-static void redirect(struct sink *output) {
-  const char *back;
-
-  back = cgi_get("back");
-  cgi_header(output, "Location", back && *back ? back : front_url());
-  header_cookie(output);
-  cgi_body(output);
-}
-
-static void expand_template(dcgi_state *ds, cgi_sink *output,
-                           const char *action) {
-  cgi_header(output->sink, "Content-Type", "text/html");
-  header_cookie(output->sink);
-  cgi_body(output->sink);
-  expand(output, action, ds);
-}
-
-/* expansions *****************************************************************/
-
-struct trackinfo_state {
-  dcgi_state *ds;
-  const struct queue_entry *q;
-  long length;
-  time_t when;
-};
-
-struct result {
-  char *track;
-  const char *sort;
-};
-
-static int compare_result(const void *a, const void *b) {
-  const struct result *ra = a, *rb = b;
-  int c;
-
-  if(!(c = strcmp(ra->sort, rb->sort)))
-    c = strcmp(ra->track, rb->track);
-  return c;
-}
-
-static void exp_stats(int attribute((unused)) nargs,
-                     char attribute((unused)) **args,
-                     cgi_sink *output,
-                     void *u) {
-  dcgi_state *ds = u;
-  char **v;
-
-  cgi_opentag(output->sink, "pre", "class", "stats", (char *)0);
-  if(!disorder_stats(ds->g->client, &v, 0)) {
-    while(*v)
-      cgi_output(output, "%s\n", *v++);
-  }
-  cgi_closetag(output->sink, "pre");
-}
-
-static char *expandarg(const char *arg, dcgi_state *ds) {
-  struct dynstr d;
-  cgi_sink output;
-
-  dynstr_init(&d);
-  output.quote = 0;
-  output.sink = sink_dynstr(&d);
-  expandstring(&output, arg, ds);
-  dynstr_terminate(&d);
-  return d.vec;
-}
-
-static void exp_navigate(int attribute((unused)) nargs,
-                        char **args,
-                        cgi_sink *output,
-                        void *u) {
-  dcgi_state *ds = u;
-  dcgi_state substate;
-  const char *path = expandarg(args[0], ds);
-  const char *ptr;
-  int dirlen;
-
-  if(*path) {
-    memset(&substate, 0, sizeof substate);
-    substate.g = ds->g;
-    ptr = path + 1;                    /* skip root */
-    dirlen = 0;
-    substate.nav_path = path;
-    substate.first = 1;
-    while(*ptr) {
-      while(*ptr && *ptr != '/')
-       ++ptr;
-      substate.last = !*ptr;
-      substate.nav_len = ptr - path;
-      substate.nav_dirlen = dirlen;
-      expandstring(output, args[1], &substate);
-      dirlen = substate.nav_len;
-      if(*ptr) ++ptr;
-      substate.first = 0;
-    }
-  }
-}
-
-static void exp_fullname(int attribute((unused)) nargs,
-                        char attribute((unused)) **args,
-                        cgi_sink *output,
-                        void *u) {
-  dcgi_state *ds = u;
-  cgi_output(output, "%.*s", ds->nav_len, ds->nav_path);
-}
-
-/*
-Local Variables:
-c-basic-offset:2
-comment-column:40
-fill-column:79
-End:
-*/
diff --git a/server/dcgi.h b/server/dcgi.h
deleted file mode 100644 (file)
index 09e74da..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * This file is part of DisOrder.
- * Copyright (C) 2004, 2005, 2007, 2008 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
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- * USA
- */
-
-#ifndef DCGI_H
-#define DCGI_H
-
-typedef struct dcgi_global {
-  disorder_client *client;
-  unsigned flags;
-#define DC_QUEUE 0x0001
-#define DC_PLAYING 0x0002
-#define DC_RECENT 0x0004
-#define DC_VOLUME 0x0008
-#define DC_DIRS 0x0010
-#define DC_FILES 0x0020
-#define DC_NEW 0x0040
-#define DC_RIGHTS 0x0080
-  struct queue_entry *queue, *playing, *recent;
-  int volume_left, volume_right;
-  char **files, **dirs;
-  int nfiles, ndirs;
-  char **new;
-  int nnew;
-  rights_type rights;
-} dcgi_global;
-
-typedef struct dcgi_state {
-  dcgi_global *g;
-  struct queue_entry *track;
-  struct kvp *pref;
-  int index;
-  int first, last;
-  struct entry *entry;
-  /* for searching */
-  int ntracks;
-  char **tracks;
-  /* for @navigate@ */
-  const char *nav_path;
-  int nav_len, nav_dirlen;
-} dcgi_state;
-
-void disorder_cgi(cgi_sink *output, dcgi_state *ds);
-void disorder_cgi_error(cgi_sink *output, dcgi_state *ds,
-                       const char *msg);
-void disorder_cgi_login(dcgi_state *ds, cgi_sink *output);
-
-extern char *login_cookie;
-
-#endif /* DCGI_H */
-
-/*
-Local Variables:
-c-basic-offset:2
-comment-column:40
-End:
-*/
index 3a5e09339ce775c814d12731772f36b1e611d006..9f444b683dd84d246cd6342f6804d5ea713c9d85 100644 (file)
@@ -160,7 +160,7 @@ static void create_periodic(ev_source *ev_,
 }
 
 static void periodic_rescan(ev_source *ev_) {
-  trackdb_rescan(ev_, 1/*check*/);
+  trackdb_rescan(ev_, 1/*check*/, 0, 0);
 }
 
 static void periodic_database_gc(ev_source attribute((unused)) *ev_) {
diff --git a/server/server-cgi.h b/server/server-cgi.h
deleted file mode 100644 (file)
index 9fe27be..0000000
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * This file is part of DisOrder.
- * Copyright (C) 2004, 2005, 2007, 2008 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
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- * USA
- */
-
-#ifndef SERVER_CGI_H
-#define SERVER_CGI_H
-
-extern struct kvp *cgi_args;
-
-typedef struct {
-  int quote;
-  struct sink *sink;
-} cgi_sink;
-
-void cgi_parse(void);
-/* parse CGI args */
-
-const char *cgi_get(const char *name);
-/* get an argument */
-
-void cgi_header(struct sink *output, const char *name, const char *value);
-/* output a header.  @name@ and @value@ are ASCII. */
-
-void cgi_body(struct sink *output);
-/* indicate the start of the body */
-
-void cgi_output(cgi_sink *output, const char *fmt, ...)
-  attribute((format (printf, 2, 3)));
-/* SGML-quote formatted UTF-8 data and write it.  Checks errors. */
-
-char *cgi_sgmlquote(const char *s, int raw);
-/* SGML-quote multibyte string @s@ */
-
-void cgi_attr(struct sink *output, const char *name, const char *value);
-/* write an attribute */
-
-void cgi_opentag(struct sink *output, const char *name, ...);
-/* write an open tag, including attribute name-value pairs terminate
- * by (char *)0 */
-
-void cgi_closetag(struct sink *output, const char *name);
-/* write a close tag */
-
-struct cgi_expansion {
-  const char *name;
-  int minargs, maxargs;
-  unsigned flags;
-#define EXP_MAGIC 0x0001
-  void (*handler)(int nargs, char **args, cgi_sink *output, void *u);
-};
-
-void cgi_define(const char *name,
-               int nargs,
-               char **args,
-               const char *value);
-
-void cgi_expand(const char *name,
-               const struct cgi_expansion *expansions,
-               size_t nexpansions,
-               cgi_sink *output,
-               void *u);
-/* find @name@ and substitute for expansions */
-
-void cgi_expand_string(const char *name,
-                      const char *template,
-                      const struct cgi_expansion *expansions,
-                      size_t nexpansions,
-                      cgi_sink *output,
-                      void *u);
-/* same but @template@ is text of template */
-
-char *cgi_makeurl(const char *url, ...);
-/* make up a URL */
-
-const char *cgi_label(const char *key);
-/* look up the translated label @key@ */
-
-int cgi_label_exists(const char *key);
-
-char **cgi_columns(const char *name, int *nheadings);
-/* return the list of columns for @name@ */
-
-const char *cgi_transform(const char *type,
-                         const char *track,
-                         const char *context);
-/* transform a track or directory name for display */
-
-void cgi_set_option(const char *name, const char *value);
-/* set an option */
-
-#endif /* SERVER_CGI_H */
-
-/*
-Local Variables:
-c-basic-offset:2
-comment-column:40
-End:
-*/
index fd0fcd884ed29977d025beaeca07bc3ccff2a707..60cb869a8972bf85fc55a717e90358b0eb63be17 100644 (file)
@@ -124,6 +124,8 @@ struct conn {
   rights_type rights;
   /** @brief Next connection */
   struct conn *next;
+  /** @brief True if pending rescan had 'wait' set */
+  int rescan_wait;
 };
 
 /** @brief Linked list of connections */
@@ -355,13 +357,107 @@ static int c_reconfigure(struct conn *c,
   return 1;                            /* completed */
 }
 
+static void finished_rescan(void *ru) {
+  struct conn *const c = ru;
+
+  sink_writes(ev_writer_sink(c->w), "250 rescan completed\n");
+  /* Turn this connection back on */
+  ev_reader_enable(c->r);
+}
+
+static void start_fresh_rescan(void *ru) {
+  struct conn *const c = ru;
+
+  if(trackdb_rescan_underway()) {
+    /* Some other waiter beat us to it.  However in this case we're happy to
+     * piggyback; the requirement is that a new rescan be started, not that it
+     * was _our_ rescan. */
+    if(c->rescan_wait) {
+      /* We block until the rescan completes */
+      trackdb_add_rescanned(finished_rescan, c);
+    } else {
+      /* We report that the new rescan has started */
+      sink_writes(ev_writer_sink(c->w), "250 rescan initiated\n");
+      /* Turn this connection back on */
+      ev_reader_enable(c->r);
+    }
+  } else {
+    /* We are the first connection to get a callback so we must start a
+     * rescan. */
+    if(c->rescan_wait) {
+      /* We want to block until the new rescan completes */
+      trackdb_rescan(c->ev, 1/*check*/, finished_rescan, c);
+    } else {
+      /* We can report back immediately */
+      trackdb_rescan(c->ev, 1/*check*/, 0, 0);
+      sink_writes(ev_writer_sink(c->w), "250 rescan initiated\n");
+      /* Turn this connection back on */
+      ev_reader_enable(c->r);
+    }
+  }
+}
+
 static int c_rescan(struct conn *c,
-                   char attribute((unused)) **vec,
-                   int attribute((unused)) nvec) {
-  info("S%x rescan by %s", c->tag, c->who);
-  trackdb_rescan(c->ev, 1/*check*/);
-  sink_writes(ev_writer_sink(c->w), "250 initiated rescan\n");
-  return 1;                            /* completed */
+                   char **vec,
+                   int nvec) {
+  int wait = 0, fresh = 0, n;
+
+  /* Parse flags */
+  for(n = 0; n < nvec; ++n) {
+    if(!strcmp(vec[n], "wait"))
+      wait = 1;                                /* wait for rescan to complete */
+#if 0
+    /* Currently disabled because untested (and hard to test). */
+    else if(!strcmp(vec[n], "fresh"))
+      fresh = 1;                       /* don't piggyback underway rescan */
+#endif
+    else {
+      sink_writes(ev_writer_sink(c->w), "550 unknown flag\n");
+      return 1;                                /* completed */
+    }
+  }
+  /* Report what was requested */
+  info("S%x rescan by %s (%s %s)", c->tag, c->who,
+       wait ? "wait" : "",
+       fresh ? "fresh" : "");
+  if(trackdb_rescan_underway()) {
+    if(fresh) {
+      /* We want a fresh rescan but there is already one underway.  Arrange a
+       * callback when it completes and then set off a new one. */
+      c->rescan_wait = wait;
+      trackdb_add_rescanned(start_fresh_rescan, c);
+      if(wait)
+       return 0;
+      else {
+       sink_writes(ev_writer_sink(c->w), "250 rescan queued\n");
+       return 1;
+      }
+    } else {
+      /* There's a rescan underway, and it's acceptable to piggyback on it */
+      if(wait) {
+       /* We want to block until completion. */
+       trackdb_add_rescanned(finished_rescan, c);
+       return 0;
+      } else {
+       /* We don't want to block.  So we just report that things are in
+        * hand. */
+       sink_writes(ev_writer_sink(c->w), "250 rescan already underway\n");
+       return 1;
+      }
+    }
+  } else {
+    /* No rescan is underway.  fresh is therefore irrelevant. */
+    if(wait) {
+      /* We want to block until completion */
+      trackdb_rescan(c->ev, 1/*check*/, finished_rescan, c);
+      return 0;
+    } else {
+      /* We don't want to block. */
+      trackdb_rescan(c->ev, 1/*check*/, 0, 0);
+      sink_writes(ev_writer_sink(c->w), "250 rescan initiated\n");
+      return 1;                                /* completed */
+    }
+  }
 }
 
 static int c_version(struct conn *c,
@@ -1465,7 +1561,7 @@ static const struct command {
   { "register",       3, 3,       c_register,       RIGHT_REGISTER|RIGHT__LOCAL },
   { "reminder",       1, 1,       c_reminder,       RIGHT__LOCAL },
   { "remove",         1, 1,       c_remove,         RIGHT_REMOVE__MASK },
-  { "rescan",         0, 0,       c_rescan,         RIGHT_RESCAN },
+  { "rescan",         0, INT_MAX, c_rescan,         RIGHT_RESCAN },
   { "resolve",        1, 1,       c_resolve,        RIGHT_READ },
   { "resume",         0, 0,       c_resume,         RIGHT_PAUSE },
   { "revoke",         0, 0,       c_revoke,         RIGHT_READ },
index 3adcf2b88fcf51ef1d651d5d0c00e0e7e8e5979e..f88067b80eb8dd7b094fc62793d22bf28d64f6e2 100644 (file)
@@ -154,7 +154,7 @@ int reconfigure(ev_source *ev, int reload) {
     /* We only allow for upgrade at startup */
     trackdb_open(TRACKDB_CAN_UPGRADE);
   if(need_another_rescan)
-    trackdb_rescan(ev, 1/*check*/);
+    trackdb_rescan(ev, 1/*check*/, 0, 0);
   if(!ret) {
     queue_read();
     recent_read();
index 9439bcead790db6c5242339aedd0dfe240b0ccdf..5eac2ea6ba519f652cb17d95ba803e008bf6cf3a 100644 (file)
@@ -271,16 +271,10 @@ def create_user(username="fred", password="fredpass"):
              "--user", "root", "edituser", username, "rights", "all"])
 
 def rescan(c=None):
-    class rescan_monitor(disorder.monitor):
-        def rescanned(self):
-            return False
+    print " initiating rescan"
     if c is None:
         c = disorder.client()
-    m = rescan_monitor()
-    print " initiating rescan"
-    c.rescan()
-    print " waiting for rescan to complete"
-    m.run()
+    c.rescan('wait')
     print " rescan completed"
 
 def stop_daemon():