chiark / gitweb /
disobedience now embeds image files
[disorder] / server / trackdb.c
index fabb203a45f05fd99d7674063678ae5096c643c3..2d369fe0eb1a372be272b933393494d1ce0e7b4f 100644 (file)
@@ -33,6 +33,7 @@
 #include <sys/time.h>
 #include <sys/resource.h>
 #include <time.h>
+#include <arpa/inet.h>
 
 #include "event.h"
 #include "mem.h"
@@ -65,6 +66,10 @@ static int trackdb_alltags_tid(DB_TXN *tid, char ***taglistp);
 static int trackdb_get_global_tid(const char *name,
                                   DB_TXN *tid,
                                   const char **rp);
+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;
@@ -78,6 +83,7 @@ DB *trackdb_prefsdb;                  /* preferences */
 DB *trackdb_searchdb;                  /* the search database */
 DB *trackdb_tagsdb;                    /* the tags database */
 DB *trackdb_globaldb;                   /* global preferences */
+DB *trackdb_noticeddb;                   /* when track noticed */
 static pid_t db_deadlock_pid = -1;      /* deadlock manager PID */
 static pid_t rescan_pid = -1;           /* rescanner PID */
 static int initialized, opened;         /* state */
@@ -124,7 +130,7 @@ void trackdb_init(int recover) {
                               |DB_CREATE
                               |recover_type[recover],
                               0666)))
-    fatal(0, "trackdb_env->open: %s", db_strerror(err));
+    fatal(0, "trackdb_env->open %s: %s", config->home, db_strerror(err));
   trackdb_env->set_errpfx(trackdb_env, "DB");
   trackdb_env->set_errfile(trackdb_env, stderr);
   trackdb_env->set_verbose(trackdb_env, DB_VERB_DEADLOCK, 1);
@@ -247,6 +253,8 @@ void trackdb_open(void) {
                            DB_DUP|DB_DUPSORT, DB_HASH, DB_CREATE, 0666);
   trackdb_prefsdb = open_db("prefs.db", 0, DB_HASH, DB_CREATE, 0666);
   trackdb_globaldb = open_db("global.db", 0, DB_HASH, DB_CREATE, 0666);
+  trackdb_noticeddb = open_db("noticed.db",
+                             DB_DUPSORT, DB_BTREE, DB_CREATE, 0666);
   D(("opened databases"));
 }
 
@@ -267,6 +275,8 @@ void trackdb_close(void) {
     fatal(0, "error closing prefs.db: %s", db_strerror(err));
   if((err = trackdb_globaldb->close(trackdb_globaldb, 0)))
     fatal(0, "error closing global.db: %s", db_strerror(err));
+  if((err = trackdb_noticeddb->close(trackdb_noticeddb, 0)))
+    fatal(0, "error closing noticed.db: %s", db_strerror(err));
   trackdb_tracksdb = trackdb_searchdb = trackdb_prefsdb = 0;
   trackdb_tagsdb = trackdb_globaldb = 0;
   D(("closed databases"));
@@ -673,7 +683,9 @@ done:
 
 /* trackdb_notice() **********************************************************/
 
-/* notice a track */
+/** @brief notice a possibly new track
+ * @return @c DB_NOTFOUND if new, 0 if already known
+ */
 int trackdb_notice(const char *track,
                    const char *path) {
   int err;
@@ -691,6 +703,9 @@ int trackdb_notice(const char *track,
   return err;
 }
 
+/** @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,
                        const char *path,
                        DB_TXN *tid) {
@@ -698,13 +713,13 @@ int trackdb_notice_tid(const char *track,
   struct kvp *t, *a, *p;
   int t_changed, ret;
   char *alias, **w;
-  
+
   /* notice whether the tracks.db entry changes */
   t_changed = 0;
   /* get any existing tracks entry */
   if((err = gettrackdata(track, &t, &p, 0, 0, tid)) == DB_LOCK_DEADLOCK)
     return err;
-  ret = err;
+  ret = err;                            /* 0 or DB_NOTFOUND */
   /* this is a real track */
   t_changed += kvp_set(&t, "_alias_for", 0);
   t_changed += kvp_set(&t, "_path", path);
@@ -731,6 +746,24 @@ int trackdb_notice_tid(const char *track,
   /* only store the tracks.db entry if it has changed */
   if(t_changed && (err = trackdb_putdata(trackdb_tracksdb, track, t, tid, 0)))
     return err;
+  if(ret == DB_NOTFOUND) {
+    uint32_t timestamp[2];
+    time_t now;
+    DBT key, data;
+
+    time(&now);
+    timestamp[0] = htonl((uint64_t)now >> 32);
+    timestamp[1] = htonl((uint32_t)now);
+    memset(&key, 0, sizeof key);
+    key.data = timestamp;
+    key.size = sizeof timestamp;
+    switch(err = trackdb_noticeddb->put(trackdb_noticeddb, tid, &key,
+                                        make_key(&data, track), 0)) {
+    case 0: break;
+    case DB_LOCK_DEADLOCK: return err;
+    default: fatal(0, "error updating noticed.db: %s", db_strerror(err));
+    }
+  }
   return ret;
 }
 
@@ -1716,6 +1749,7 @@ static int reap_rescan(ev_source attribute((unused)) *ev,
     D(("disorderd-rescan terminate: %s", wstat(status)));
   /* Our cache of file lookups is out of date now */
   cache_clean(&cache_files_type);
+  eventlog("rescanned", (char *)0);
   return 0;
 }
 
@@ -1826,6 +1860,125 @@ static int trackdb_get_global_tid(const char *name,
   }
 }
 
+/** @brief Retrieve the most recently added tracks
+ * @param ntracksp Where to put count, or 0
+ * @param maxtracks Maximum number of tracks to retrieve
+ * @return null-terminated array of track names
+ *
+ * The most recently added track is first in the array.
+ */
+char **trackdb_new(int *ntracksp,
+                   int maxtracks) {
+  DB_TXN *tid;
+  char **tracks;
+
+  for(;;) {
+    tid = trackdb_begin_transaction();
+    tracks = trackdb_new_tid(ntracksp, maxtracks, tid);
+    if(tracks)
+      break;
+    trackdb_abort_transaction(tid);
+  }
+  trackdb_commit_transaction(tid);
+  return tracks;
+}
+
+/** @brief Retrieve the most recently added tracks
+ * @param ntracksp Where to put count, or 0
+ * @param maxtracks Maximum number of tracks to retrieve, or 0 for all
+ * @param tid Transaction ID
+ * @return null-terminated array of track names, or NULL on deadlock
+ *
+ * The most recently added track is first in the array.
+ */
+static char **trackdb_new_tid(int *ntracksp,
+                              int maxtracks,
+                              DB_TXN *tid) {
+  DBC *c;
+  DBT k, d;
+  int err = 0;
+  struct vector tracks[1];
+
+  vector_init(tracks);
+  c = trackdb_opencursor(trackdb_noticeddb, tid);
+  while((maxtracks <= 0 || tracks->nvec < maxtracks)
+        && !(err = c->c_get(c, prepare_data(&k), prepare_data(&d), DB_PREV)))
+    vector_append(tracks, xstrndup(d.data, d.size));
+  switch(err) {
+  case 0:                               /* hit maxtracks */
+  case DB_NOTFOUND:                     /* ran out of tracks */
+    break;
+  case DB_LOCK_DEADLOCK:
+    trackdb_closecursor(c);
+    return 0;
+  default:
+    fatal(0, "error reading noticed.db: %s", db_strerror(err));
+  }
+  if((err = trackdb_closecursor(c)))
+    return 0;                           /* deadlock */
+  vector_terminate(tracks);
+  if(ntracksp)
+    *ntracksp = tracks->nvec;
+  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) {