chiark / gitweb /
disobedience now embeds image files
[disorder] / server / trackdb.c
1 /*
2  * This file is part of DisOrder
3  * Copyright (C) 2005, 2006, 2007 Richard Kettlewell
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
18  * USA
19  */
20
21 #include <config.h>
22 #include "types.h"
23
24 #include <string.h>
25 #include <stdio.h>
26 #include <db.h>
27 #include <sys/socket.h>
28 #include <pcre.h>
29 #include <assert.h>
30 #include <unistd.h>
31 #include <errno.h>
32 #include <stddef.h>
33 #include <sys/time.h>
34 #include <sys/resource.h>
35 #include <time.h>
36 #include <arpa/inet.h>
37
38 #include "event.h"
39 #include "mem.h"
40 #include "kvp.h"
41 #include "log.h"
42 #include "vector.h"
43 #include "trackdb.h"
44 #include "configuration.h"
45 #include "syscalls.h"
46 #include "wstat.h"
47 #include "words.h"
48 #include "printf.h"
49 #include "filepart.h"
50 #include "trackname.h"
51 #include "trackdb-int.h"
52 #include "logfd.h"
53 #include "cache.h"
54 #include "eventlog.h"
55 #include "hash.h"
56
57 #define RESCAN "disorder-rescan"
58 #define DEADLOCK "disorder-deadlock"
59
60 static const char *getpart(const char *track,
61                            const char *context,
62                            const char *part,
63                            const struct kvp *p,
64                            int *used_db);
65 static int trackdb_alltags_tid(DB_TXN *tid, char ***taglistp);
66 static int trackdb_get_global_tid(const char *name,
67                                   DB_TXN *tid,
68                                   const char **rp);
69 static char **trackdb_new_tid(int *ntracksp,
70                               int maxtracks,
71                               DB_TXN *tid);
72 static int trackdb_expire_noticed_tid(time_t earliest, DB_TXN *tid);
73
74 const struct cache_type cache_files_type = { 86400 };
75 unsigned long cache_files_hits, cache_files_misses;
76
77 /* setup and teardown ********************************************************/
78
79 static const char *home;                /* home had better not change */
80 DB_ENV *trackdb_env;                    /* db environment */
81 DB *trackdb_tracksdb;                   /* the db itself */
82 DB *trackdb_prefsdb;                    /* preferences */
83 DB *trackdb_searchdb;                   /* the search database */
84 DB *trackdb_tagsdb;                     /* the tags database */
85 DB *trackdb_globaldb;                   /* global preferences */
86 DB *trackdb_noticeddb;                   /* when track noticed */
87 static pid_t db_deadlock_pid = -1;      /* deadlock manager PID */
88 static pid_t rescan_pid = -1;           /* rescanner PID */
89 static int initialized, opened;         /* state */
90
91 /* tracks matched by required_tags */
92 static char **reqtracks;
93 static size_t nreqtracks;
94
95 /* comparison function for keys */
96 static int compare(DB attribute((unused)) *db_,
97                    const DBT *a, const DBT *b) {
98   return compare_path_raw(a->data, a->size, b->data, b->size);
99 }
100
101 /* open environment */
102 void trackdb_init(int recover) {
103   int err;
104   static int recover_type[] = { 0, DB_RECOVER, DB_RECOVER_FATAL };
105
106   /* sanity checks */
107   assert(initialized == 0);
108   ++initialized;
109   if(home) {
110     if(strcmp(home, config->home))
111       fatal(0, "cannot change db home without server restart");
112     home = config->home;
113   }
114
115   /* create environment */
116   if((err = db_env_create(&trackdb_env, 0))) fatal(0, "db_env_create: %s",
117                                                    db_strerror(err));
118   if((err = trackdb_env->set_alloc(trackdb_env,
119                                    xmalloc_noptr, xrealloc_noptr, xfree)))
120     fatal(0, "trackdb_env->set_alloc: %s", db_strerror(err));
121   if((err = trackdb_env->set_lk_max_locks(trackdb_env, 10000)))
122     fatal(0, "trackdb_env->set_lk_max_locks: %s", db_strerror(err));
123   if((err = trackdb_env->set_lk_max_objects(trackdb_env, 10000)))
124     fatal(0, "trackdb_env->set_lk_max_objects: %s", db_strerror(err));
125   if((err = trackdb_env->open(trackdb_env, config->home,
126                               DB_INIT_LOG
127                               |DB_INIT_LOCK
128                               |DB_INIT_MPOOL
129                               |DB_INIT_TXN
130                               |DB_CREATE
131                               |recover_type[recover],
132                               0666)))
133     fatal(0, "trackdb_env->open %s: %s", config->home, db_strerror(err));
134   trackdb_env->set_errpfx(trackdb_env, "DB");
135   trackdb_env->set_errfile(trackdb_env, stderr);
136   trackdb_env->set_verbose(trackdb_env, DB_VERB_DEADLOCK, 1);
137   trackdb_env->set_verbose(trackdb_env, DB_VERB_RECOVERY, 1);
138   trackdb_env->set_verbose(trackdb_env, DB_VERB_REPLICATION, 1);
139   D(("initialized database environment"));
140 }
141
142 /* called when deadlock manager terminates */
143 static int reap_db_deadlock(ev_source attribute((unused)) *ev,
144                             pid_t attribute((unused)) pid,
145                             int status,
146                             const struct rusage attribute((unused)) *rusage,
147                             void attribute((unused)) *u) {
148   db_deadlock_pid = -1;
149   if(initialized)
150     fatal(0, "deadlock manager unexpectedly terminated: %s",
151           wstat(status));
152   else
153     D(("deadlock manager terminated: %s", wstat(status)));
154   return 0;
155 }
156
157 static pid_t subprogram(ev_source *ev, const char *prog) {
158   pid_t pid;
159   int lfd;
160
161   /* If we're in the background then trap subprocess stdout/stderr */
162   if(!isatty(2))
163     lfd = logfd(ev, prog);
164   else
165     lfd = -1;
166   if(!(pid = xfork())) {
167     exitfn = _exit;
168     ev_signal_atfork(ev);
169     signal(SIGPIPE, SIG_DFL);
170     if(lfd != -1) {
171       xdup2(lfd, 1);
172       xdup2(lfd, 2);
173     }
174     /* If we were negatively niced, undo it.  We don't bother checking for
175      * error, it's not that important. */
176     setpriority(PRIO_PROCESS, 0, 0);
177     execlp(prog, prog, "--config", configfile,
178            debugging ? "--debug" : "--no-debug",
179            (char *)0);
180     fatal(errno, "error invoking %s", prog);
181   }
182   if(lfd != -1) xclose(lfd);
183   return pid;
184 }
185
186 /* start deadlock manager */
187 void trackdb_master(ev_source *ev) {
188   assert(db_deadlock_pid == -1);
189   db_deadlock_pid = subprogram(ev, DEADLOCK);
190   ev_child(ev, db_deadlock_pid, 0, reap_db_deadlock, 0);
191   D(("started deadlock manager"));
192 }
193
194 /* close environment */
195 void trackdb_deinit(void) {
196   int err;
197
198   /* sanity checks */
199   assert(initialized == 1);
200   --initialized;
201
202   /* close the environment */
203   if((err = trackdb_env->close(trackdb_env, 0)))
204     fatal(0, "trackdb_env->close: %s", db_strerror(err));
205
206   if(rescan_pid != -1 && kill(rescan_pid, SIGTERM) < 0)
207     fatal(errno, "error killing rescanner");
208
209   /* terminate the deadlock manager */
210   if(db_deadlock_pid != -1 && kill(db_deadlock_pid, SIGTERM) < 0)
211     fatal(errno, "error killing deadlock manager");
212   db_deadlock_pid = -1;
213
214   D(("deinitialized database environment"));
215 }
216
217 /* open a specific database */
218 static DB *open_db(const char *path,
219                    u_int32_t dbflags,
220                    DBTYPE dbtype,
221                    u_int32_t openflags,
222                    int mode) {
223   int err;
224   DB *db;
225
226   D(("open %s", path));
227   path = config_get_file(path);
228   if((err = db_create(&db, trackdb_env, 0)))
229     fatal(0, "db_create %s: %s", path, db_strerror(err));
230   if(dbflags)
231     if((err = db->set_flags(db, dbflags)))
232       fatal(0, "db->set_flags %s: %s", path, db_strerror(err));
233   if(dbtype == DB_BTREE)
234     if((err = db->set_bt_compare(db, compare)))
235       fatal(0, "db->set_bt_compare %s: %s", path, db_strerror(err));
236   if((err = db->open(db, 0, path, 0, dbtype,
237                      openflags | DB_AUTO_COMMIT, mode)))
238     fatal(0, "db->open %s: %s", path, db_strerror(err));
239   return db;
240 }
241
242 /* open track databases */
243 void trackdb_open(void) {
244   /* sanity checks */
245   assert(opened == 0);
246   ++opened;
247   /* open the databases */
248   trackdb_tracksdb = open_db("tracks.db",
249                              DB_RECNUM, DB_BTREE, DB_CREATE, 0666);
250   trackdb_searchdb = open_db("search.db",
251                              DB_DUP|DB_DUPSORT, DB_HASH, DB_CREATE, 0666);
252   trackdb_tagsdb = open_db("tags.db",
253                            DB_DUP|DB_DUPSORT, DB_HASH, DB_CREATE, 0666);
254   trackdb_prefsdb = open_db("prefs.db", 0, DB_HASH, DB_CREATE, 0666);
255   trackdb_globaldb = open_db("global.db", 0, DB_HASH, DB_CREATE, 0666);
256   trackdb_noticeddb = open_db("noticed.db",
257                              DB_DUPSORT, DB_BTREE, DB_CREATE, 0666);
258   D(("opened databases"));
259 }
260
261 /* close track databases */
262 void trackdb_close(void) {
263   int err;
264   
265   /* sanity checks */
266   assert(opened == 1);
267   --opened;
268   if((err = trackdb_tracksdb->close(trackdb_tracksdb, 0)))
269     fatal(0, "error closing tracks.db: %s", db_strerror(err));
270   if((err = trackdb_searchdb->close(trackdb_searchdb, 0)))
271     fatal(0, "error closing search.db: %s", db_strerror(err));
272   if((err = trackdb_tagsdb->close(trackdb_tagsdb, 0)))
273     fatal(0, "error closing tags.db: %s", db_strerror(err));
274   if((err = trackdb_prefsdb->close(trackdb_prefsdb, 0)))
275     fatal(0, "error closing prefs.db: %s", db_strerror(err));
276   if((err = trackdb_globaldb->close(trackdb_globaldb, 0)))
277     fatal(0, "error closing global.db: %s", db_strerror(err));
278   if((err = trackdb_noticeddb->close(trackdb_noticeddb, 0)))
279     fatal(0, "error closing noticed.db: %s", db_strerror(err));
280   trackdb_tracksdb = trackdb_searchdb = trackdb_prefsdb = 0;
281   trackdb_tagsdb = trackdb_globaldb = 0;
282   D(("closed databases"));
283 }
284
285 /* generic db routines *******************************************************/
286
287 /* fetch and decode a database entry.  Returns 0, DB_NOTFOUND or
288  * DB_LOCK_DEADLOCK. */
289 int trackdb_getdata(DB *db,
290                     const char *track,
291                     struct kvp **kp,
292                     DB_TXN *tid) {
293   int err;
294   DBT key, data;
295
296   switch(err = db->get(db, tid, make_key(&key, track),
297                        prepare_data(&data), 0)) {
298   case 0:
299     *kp = kvp_urldecode(data.data, data.size);
300     return 0;
301   case DB_NOTFOUND:
302     *kp = 0;
303     return err;
304   case DB_LOCK_DEADLOCK:
305     error(0, "error querying database: %s", db_strerror(err));
306     return err;
307   default:
308     fatal(0, "error querying database: %s", db_strerror(err));
309   }
310 }
311
312 /* encode and store a database entry.  Returns 0, DB_KEYEXIST or
313  * DB_LOCK_DEADLOCK. */
314 int trackdb_putdata(DB *db,
315                     const char *track,
316                     const struct kvp *k,
317                     DB_TXN *tid,
318                     u_int32_t flags) {
319   int err;
320   DBT key, data;
321
322   switch(err = db->put(db, tid, make_key(&key, track),
323                        encode_data(&data, k), flags)) {
324   case 0:
325   case DB_KEYEXIST:
326     return err;
327   case DB_LOCK_DEADLOCK:
328     error(0, "error updating database: %s", db_strerror(err));
329     return err;
330   default:
331     fatal(0, "error updating database: %s", db_strerror(err));
332   }
333 }
334
335 /* delete a database entry */
336 int trackdb_delkey(DB *db,
337                    const char *track,
338                    DB_TXN *tid) {
339   int err;
340
341   DBT key;
342   switch(err = db->del(db, tid, make_key(&key, track), 0)) {
343   case 0:
344   case DB_NOTFOUND:
345     return 0;
346   case DB_LOCK_DEADLOCK:
347     error(0, "error updating database: %s", db_strerror(err));
348     return err;
349   default:
350     fatal(0, "error updating database: %s", db_strerror(err));
351   }
352 }
353
354 /* open a database cursor */
355 DBC *trackdb_opencursor(DB *db, DB_TXN *tid) {
356   int err;
357   DBC *c;
358
359   switch(err = db->cursor(db, tid, &c, 0)) {
360   case 0: break;
361   default: fatal(0, "error creating cursor: %s", db_strerror(err));
362   }
363   return c;
364 }
365
366 /* close a database cursor; returns 0 or DB_LOCK_DEADLOCK */
367 int trackdb_closecursor(DBC *c) {
368   int err;
369
370   if(!c) return 0;
371   switch(err = c->c_close(c)) {
372   case 0:
373     return err;
374   case DB_LOCK_DEADLOCK:
375     error(0, "error closing cursor: %s", db_strerror(err));
376     return err;
377   default:
378     fatal(0, "error closing cursor: %s", db_strerror(err));
379   }
380 }
381
382 /* delete a (key,data) pair.  Returns 0, DB_NOTFOUND or DB_LOCK_DEADLOCK. */
383 int trackdb_delkeydata(DB *db,
384                        const char *word,
385                        const char *track,
386                        DB_TXN *tid) {
387   int err;
388   DBC *c;
389   DBT key, data;
390
391   c = trackdb_opencursor(db, tid);
392   switch(err = c->c_get(c, make_key(&key, word),
393                         make_key(&data, track), DB_GET_BOTH)) {
394   case 0:
395     switch(err = c->c_del(c, 0)) {
396     case 0:
397       break;
398     case DB_KEYEMPTY:
399       err = 0;
400       break;
401     case DB_LOCK_DEADLOCK:
402       error(0, "error updating database: %s", db_strerror(err));
403       break;
404     default:
405       fatal(0, "c->c_del: %s", db_strerror(err));
406     }
407     break;
408   case DB_NOTFOUND:
409     break;
410   case DB_LOCK_DEADLOCK:
411     error(0, "error updating database: %s", db_strerror(err));
412     break;
413   default:
414     fatal(0, "c->c_get: %s", db_strerror(err));
415   }
416   if(trackdb_closecursor(c)) err = DB_LOCK_DEADLOCK;
417   return err;
418 }
419
420 /* start a transaction */
421 DB_TXN *trackdb_begin_transaction(void) {
422   DB_TXN *tid;
423   int err;
424
425   if((err = trackdb_env->txn_begin(trackdb_env, 0, &tid, 0)))
426     fatal(0, "trackdb_env->txn_begin: %s", db_strerror(err));
427   return tid;
428 }
429
430 /* abort transaction */
431 void trackdb_abort_transaction(DB_TXN *tid) {
432   int err;
433
434   if(tid)
435     if((err = tid->abort(tid)))
436       fatal(0, "tid->abort: %s", db_strerror(err));
437 }
438
439 /* commit transaction */
440 void trackdb_commit_transaction(DB_TXN *tid) {
441   int err;
442
443   if((err = tid->commit(tid, 0)))
444     fatal(0, "tid->commit: %s", db_strerror(err));
445 }
446
447 /* search/tags shared code ***************************************************/
448
449 /* comparison function used by dedupe() */
450 static int wordcmp(const void *a, const void *b) {
451   return strcmp(*(const char **)a, *(const char **)b);
452 }
453
454 /* sort and de-dupe VEC */
455 static char **dedupe(char **vec, int nvec) {
456   int m, n;
457
458   qsort(vec, nvec, sizeof (char *), wordcmp);
459   m = n = 0;
460   if(nvec) {
461     vec[m++] = vec[0];
462     for(n = 1; n < nvec; ++n)
463       if(strcmp(vec[n], vec[m - 1]))
464         vec[m++] = vec[n];
465   }
466   vec[m] = 0;
467   return vec;
468 }
469
470 /* update a key/track database.  Returns 0 or DB_DEADLOCK. */
471 static int register_word(DB *db, const char *what,
472                          const char *track, const char *word,
473                          DB_TXN *tid) {
474   int err;
475   DBT key, data;
476
477   switch(err = db->put(db, tid, make_key(&key, word),
478                        make_key(&data, track), DB_NODUPDATA)) {
479   case 0:
480   case DB_KEYEXIST:
481     return 0;
482   case DB_LOCK_DEADLOCK:
483     error(0, "error updating %s.db: %s", what, db_strerror(err));
484     return err;
485   default:
486     fatal(0, "error updating %s.db: %s", what,  db_strerror(err));
487   }
488 }
489
490 /* search primitives *********************************************************/
491
492 /* return true iff NAME is a trackname_display_ pref */
493 static int is_display_pref(const char *name) {
494   static const char prefix[] = "trackname_display_";
495   return !strncmp(name, prefix, (sizeof prefix) - 1);
496 }
497
498 /* compute the words of a track name */
499 static char **track_to_words(const char *track,
500                              const struct kvp *p) {
501   struct vector v;
502   char **w;
503   int nw;
504   const char *rootless = track_rootless(track);
505
506   if(!rootless)
507     rootless = track;                   /* bodge */
508   vector_init(&v);
509   if((w = words(casefold(strip_extension(rootless)), &nw)))
510     vector_append_many(&v, w, nw);
511
512   for(; p; p = p->next)
513     if(is_display_pref(p->name))
514       if((w = words(casefold(p->value), &nw)))
515         vector_append_many(&v, w, nw);
516   vector_terminate(&v);
517   return dedupe(v.vec, v.nvec);
518 }
519
520 /* return nonzero iff WORD is a stopword */
521 static int stopword(const char *word) {
522   int n;
523
524   for(n = 0; n < config->stopword.n
525         && strcmp(word, config->stopword.s[n]); ++n)
526     ;
527   return n < config->stopword.n;
528 }
529
530 /* record that WORD appears in TRACK.  Returns 0 or DB_LOCK_DEADLOCK. */
531 static int register_search_word(const char *track, const char *word,
532                                 DB_TXN *tid) {
533   if(stopword(word)) return 0;
534   return register_word(trackdb_searchdb, "search", track, word, tid);
535 }
536
537 /* Tags **********************************************************************/
538
539 /* Return nonzero if C is a valid tag character */
540 static int tagchar(int c) {
541   switch(c) {
542   case ',':
543     return 0;
544   default:
545     return c >= ' ';
546   }
547 }
548
549 /* Parse and de-dupe a tag list.  If S=0 then assumes "". */
550 static char **parsetags(const char *s) {
551   const char *t;
552   struct vector v;
553
554   vector_init(&v);
555   if(s) {
556     /* skip initial separators */
557     while(*s && (!tagchar(*s) || *s == ' '))
558       ++s;
559     while(*s) {
560       /* find the extent of the tag */
561       t = s;
562       while(*s && tagchar(*s))
563         ++s;
564       /* strip trailing spaces */
565       while(s > t && s[-1] == ' ')
566         --s;
567       vector_append(&v, xstrndup(t, s - t));
568       /* skip intermediate and trailing separators */
569       while(*s && (!tagchar(*s) || *s == ' '))
570         ++s;
571     }
572   }
573   vector_terminate(&v);
574   return dedupe(v.vec, v.nvec);
575 }
576
577 /* Record that TRACK has TAG.  Returns 0 or DB_LOCK_DEADLOCK. */
578 static int register_tag(const char *track, const char *tag, DB_TXN *tid) {
579   return register_word(trackdb_tagsdb, "tags", track, tag, tid);
580 }
581
582 /* aliases *******************************************************************/
583
584 /* compute the alias and store at aliasp.  Returns 0 or DB_LOCK_DEADLOCK.  If
585  * there is no alias sets *aliasp to 0. */
586 static int compute_alias(char **aliasp,
587                          const char *track,
588                          const struct kvp *p,
589                          DB_TXN *tid) {
590   struct dynstr d;
591   const char *s = config->alias, *t, *expansion, *part;
592   int c, used_db = 0, slash_prefix, err;
593   struct kvp *at;
594   const char *const root = find_track_root(track);
595
596   if(!root) {
597     /* Bodge for tracks with no root */
598     *aliasp = 0;
599     return 0;
600   }
601   dynstr_init(&d);
602   dynstr_append_string(&d, root);
603   while((c = (unsigned char)*s++)) {
604     if(c != '{') {
605       dynstr_append(&d, c);
606       continue;
607     }
608     if((slash_prefix = (*s == '/')))
609       s++;
610     t = strchr(s, '}');
611     assert(t != 0);                     /* validated at startup */
612     part = xstrndup(s, t - s);
613     expansion = getpart(track, "display", part, p, &used_db);
614     if(*expansion) {
615       if(slash_prefix) dynstr_append(&d, '/');
616       dynstr_append_string(&d, expansion);
617     }
618     s = t + 1;                          /* skip {part} */
619   }
620   /* only admit to the alias if we used the db... */
621   if(!used_db) {
622     *aliasp = 0;
623     return 0;
624   }
625   dynstr_terminate(&d);
626   /* ...and the answer differs from the original... */
627   if(!strcmp(track, d.vec)) {
628     *aliasp = 0;
629     return 0;
630   }
631   /* ...and there isn't already a different track with that name (including as
632    * an alias) */
633   switch(err = trackdb_getdata(trackdb_tracksdb, d.vec, &at, tid)) {
634   case 0:
635     if((s = kvp_get(at, "_alias_for"))
636        && !strcmp(s, track)) {
637     case DB_NOTFOUND:
638       *aliasp = d.vec;
639     } else {
640       *aliasp = 0;
641     }
642     return 0;
643   default:
644     return err;
645   }
646 }
647
648 /* get track and prefs data (if tp/pp not null pointers).  Returns 0 on
649  * success, DB_NOTFOUND if the track does not exist or DB_LOCK_DEADLOCK.
650  * Always sets the return values, even if only to null pointers. */
651 static int gettrackdata(const char *track,
652                         struct kvp **tp,
653                         struct kvp **pp,
654                         const char **actualp,
655                         unsigned flags,
656 #define GTD_NOALIAS 0x0001
657                         DB_TXN *tid) {
658   int err;
659   const char *actual = track;
660   struct kvp *t = 0, *p = 0;
661   
662   if((err = trackdb_getdata(trackdb_tracksdb, track, &t, tid))) goto done;
663   if((actual = kvp_get(t, "_alias_for"))) {
664     if(flags & GTD_NOALIAS) {
665       error(0, "alias passed to gettrackdata where real path required");
666       abort();
667     }
668     if((err = trackdb_getdata(trackdb_tracksdb, actual, &t, tid))) goto done;
669   } else
670     actual = track;
671   assert(actual != 0);
672   if(pp) {
673     if((err = trackdb_getdata(trackdb_prefsdb, actual, &p, tid)) == DB_LOCK_DEADLOCK)
674       goto done;
675   }
676   err = 0;
677 done:
678   if(actualp) *actualp = actual;
679   if(tp) *tp = t;
680   if(pp) *pp = p;
681   return err;
682 }
683
684 /* trackdb_notice() **********************************************************/
685
686 /** @brief notice a possibly new track
687  * @return @c DB_NOTFOUND if new, 0 if already known
688  */
689 int trackdb_notice(const char *track,
690                    const char *path) {
691   int err;
692   DB_TXN *tid;
693   
694   for(;;) {
695     tid = trackdb_begin_transaction();
696     err = trackdb_notice_tid(track, path, tid);
697     if((err == DB_LOCK_DEADLOCK)) goto fail;
698     break;
699   fail:
700     trackdb_abort_transaction(tid);
701   }
702   trackdb_commit_transaction(tid);
703   return err;
704 }
705
706 /** @brief notice a possibly new track
707  * @return @c DB_NOTFOUND if new, 0 if already known, @c DB_LOCK_DEADLOCK also
708  */
709 int trackdb_notice_tid(const char *track,
710                        const char *path,
711                        DB_TXN *tid) {
712   int err, n;
713   struct kvp *t, *a, *p;
714   int t_changed, ret;
715   char *alias, **w;
716
717   /* notice whether the tracks.db entry changes */
718   t_changed = 0;
719   /* get any existing tracks entry */
720   if((err = gettrackdata(track, &t, &p, 0, 0, tid)) == DB_LOCK_DEADLOCK)
721     return err;
722   ret = err;                            /* 0 or DB_NOTFOUND */
723   /* this is a real track */
724   t_changed += kvp_set(&t, "_alias_for", 0);
725   t_changed += kvp_set(&t, "_path", path);
726   /* if we have an alias record it in the database */
727   if((err = compute_alias(&alias, track, p, tid))) return err;
728   if(alias) {
729     /* won't overwrite someone else's alias as compute_alias() checks */
730     D(("%s: alias %s", track, alias));
731     a = 0;
732     kvp_set(&a, "_alias_for", track);
733     if((err = trackdb_putdata(trackdb_tracksdb, alias, a, tid, 0))) return err;
734   }
735   /* update search.db */
736   w = track_to_words(track, p);
737   for(n = 0; w[n]; ++n)
738     if((err = register_search_word(track, w[n], tid)))
739       return err;
740   /* update tags.db */
741   w = parsetags(kvp_get(p, "tags"));
742   for(n = 0; w[n]; ++n)
743     if((err = register_tag(track, w[n], tid)))
744       return err;
745   reqtracks = 0;
746   /* only store the tracks.db entry if it has changed */
747   if(t_changed && (err = trackdb_putdata(trackdb_tracksdb, track, t, tid, 0)))
748     return err;
749   if(ret == DB_NOTFOUND) {
750     uint32_t timestamp[2];
751     time_t now;
752     DBT key, data;
753
754     time(&now);
755     timestamp[0] = htonl((uint64_t)now >> 32);
756     timestamp[1] = htonl((uint32_t)now);
757     memset(&key, 0, sizeof key);
758     key.data = timestamp;
759     key.size = sizeof timestamp;
760     switch(err = trackdb_noticeddb->put(trackdb_noticeddb, tid, &key,
761                                         make_key(&data, track), 0)) {
762     case 0: break;
763     case DB_LOCK_DEADLOCK: return err;
764     default: fatal(0, "error updating noticed.db: %s", db_strerror(err));
765     }
766   }
767   return ret;
768 }
769
770 /* trackdb_obsolete() ********************************************************/
771
772 /* obsolete a track */
773 int trackdb_obsolete(const char *track, DB_TXN *tid) {
774   int err, n;
775   struct kvp *p;
776   char *alias, **w;
777
778   if((err = gettrackdata(track, 0, &p, 0,
779                          GTD_NOALIAS, tid)) == DB_LOCK_DEADLOCK)
780     return err;
781   else if(err == DB_NOTFOUND) return 0;
782   /* compute the alias, if any, and delete it */
783   if(compute_alias(&alias, track, p, tid)) return err;
784   if(alias) {
785     /* if the alias points to some other track then compute_alias won't
786      * return it */
787     if(trackdb_delkey(trackdb_tracksdb, alias, tid))
788       return err;
789   }
790   /* update search.db */
791   w = track_to_words(track, p);
792   for(n = 0; w[n]; ++n)
793     if(trackdb_delkeydata(trackdb_searchdb,
794                           w[n], track, tid) == DB_LOCK_DEADLOCK)
795       return err;
796   /* update tags.db */
797   w = parsetags(kvp_get(p, "tags"));
798   for(n = 0; w[n]; ++n)
799     if(trackdb_delkeydata(trackdb_tagsdb,
800                           w[n], track, tid) == DB_LOCK_DEADLOCK)
801       return err;
802   reqtracks = 0;
803   /* update tracks.db */
804   if(trackdb_delkey(trackdb_tracksdb, track, tid) == DB_LOCK_DEADLOCK)
805     return err;
806   /* We don't delete the prefs, so they survive temporary outages of the
807    * (possibly virtual) track filesystem */
808   return 0;
809 }
810
811 /* trackdb_stats() ***********************************************************/
812
813 #define H(name) { #name, offsetof(DB_HASH_STAT, name) }
814 #define B(name) { #name, offsetof(DB_BTREE_STAT, name) }
815
816 static const struct statinfo {
817   const char *name;
818   size_t offset;
819 } statinfo_hash[] = {
820   H(hash_magic),
821   H(hash_version),
822   H(hash_nkeys),
823   H(hash_ndata),
824   H(hash_pagesize),
825   H(hash_ffactor),
826   H(hash_buckets),
827   H(hash_free),
828   H(hash_bfree),
829   H(hash_bigpages),
830   H(hash_big_bfree),
831   H(hash_overflows),
832   H(hash_ovfl_free),
833   H(hash_dup),
834   H(hash_dup_free),
835 }, statinfo_btree[] = {
836   B(bt_magic),
837   B(bt_version),
838   B(bt_nkeys),
839   B(bt_ndata),
840   B(bt_pagesize),
841   B(bt_minkey),
842   B(bt_re_len),
843   B(bt_re_pad),
844   B(bt_levels),
845   B(bt_int_pg),
846   B(bt_leaf_pg),
847   B(bt_dup_pg),
848   B(bt_over_pg),
849   B(bt_free),
850   B(bt_int_pgfree),
851   B(bt_leaf_pgfree),
852   B(bt_dup_pgfree),
853   B(bt_over_pgfree),
854 };
855
856 /* look up stats for DB */
857 static int get_stats(struct vector *v,
858                      DB *database,
859                      const struct statinfo *si,
860                      size_t nsi,
861                      DB_TXN *tid) {
862   void *sp;
863   size_t n;
864   char *str;
865   int err;
866
867   if(database) {
868     switch(err = database->stat(database, tid, &sp, 0)) {
869     case 0:
870       break;
871     case DB_LOCK_DEADLOCK:
872       error(0, "error querying database: %s", db_strerror(err));
873       return err;
874     default:
875       fatal(0, "error querying database: %s", db_strerror(err));
876     }
877     for(n = 0; n < nsi; ++n) {
878       byte_xasprintf(&str, "%s=%"PRIuMAX, si[n].name,
879                      (uintmax_t)*(u_int32_t *)((char *)sp + si[n].offset));
880       vector_append(v, str);
881     }
882   }
883   return 0;
884 }
885
886 struct search_entry {
887   char *word;
888   int n;
889 };
890
891 /* find the top COUNT words in the search database */
892 static int search_league(struct vector *v, int count, DB_TXN *tid) {
893   struct search_entry *se;
894   DBT k, d;
895   DBC *cursor;
896   int err, n = 0, nse = 0, i;
897   char *word = 0;
898   size_t wl = 0;
899   char *str;
900
901   cursor = trackdb_opencursor(trackdb_searchdb, tid);
902   se = xmalloc(count * sizeof *se);
903   while(!(err = cursor->c_get(cursor, prepare_data(&k), prepare_data(&d),
904                               DB_NEXT))) {
905     if(word && wl == k.size && !strncmp(word, k.data, wl))
906       ++n;
907     else {
908 #define FINALIZE() do {                                         \
909   if(word && (nse < count || n > se[nse - 1].n)) {              \
910     if(nse == count)                                            \
911       i = nse - 1;                                              \
912     else                                                        \
913       i = nse++;                                                \
914     while(i > 0 && n > se[i - 1].n)                             \
915       --i;                                                      \
916     memmove(&se[i + 1], &se[i], (nse - i) * sizeof *se);        \
917     se[i].word = word;                                          \
918     se[i].n = n;                                                \
919   }                                                             \
920 } while(0)
921       FINALIZE();
922       word = xstrndup(k.data, wl = k.size);
923       n = 1;
924     }
925   }
926   switch(err) {
927   case DB_NOTFOUND:
928     err = 0;
929     break;
930   case DB_LOCK_DEADLOCK:
931     error(0, "error querying search database: %s", db_strerror(err));
932     break;
933   default:
934     fatal(0, "error querying search database: %s", db_strerror(err));
935   }
936   if(trackdb_closecursor(cursor)) err = DB_LOCK_DEADLOCK;
937   if(err) return err;
938   FINALIZE();
939   byte_xasprintf(&str, "Top %d search words:", nse);
940   vector_append(v, str);
941   for(i = 0; i < nse; ++i) {
942     byte_xasprintf(&str, "%4d: %5d %s", i + 1, se[i].n, se[i].word);
943     vector_append(v, str);
944   }
945   return 0;
946 }
947
948 #define SI(what) statinfo_##what, \
949                  sizeof statinfo_##what / sizeof (struct statinfo)
950
951 /* return a list of database stats */
952 char **trackdb_stats(int *nstatsp) {
953   DB_TXN *tid;
954   struct vector v;
955   char *s;
956   
957   vector_init(&v);
958   for(;;) {
959     tid = trackdb_begin_transaction();
960     v.nvec = 0;
961     vector_append(&v, (char *)"Tracks database stats:");
962     if(get_stats(&v, trackdb_tracksdb, SI(btree), tid)) goto fail;
963     vector_append(&v, (char *)"");
964     vector_append(&v, (char *)"Search database stats:");
965     if(get_stats(&v, trackdb_searchdb, SI(hash), tid)) goto fail;
966     vector_append(&v, (char *)"");
967     vector_append(&v, (char *)"Prefs database stats:");
968     if(get_stats(&v, trackdb_prefsdb, SI(hash), tid)) goto fail;
969     vector_append(&v, (char *)"");
970     if(search_league(&v, 10, tid)) goto fail;
971     vector_append(&v, (char *)"");
972     vector_append(&v, (char *)"Server stats:");
973     byte_xasprintf(&s, "track lookup cache hits: %lu", cache_files_hits);
974     vector_append(&v, (char *)s);
975     byte_xasprintf(&s, "track lookup cache misses: %lu", cache_files_misses);
976     vector_append(&v, (char *)s);
977     vector_terminate(&v);
978     break;
979 fail:
980     trackdb_abort_transaction(tid);
981   }
982   trackdb_commit_transaction(tid);
983   if(nstatsp) *nstatsp = v.nvec;
984   return v.vec;
985 }
986
987 /* set a pref (remove if value=0) */
988 int trackdb_set(const char *track,
989                 const char *name,
990                 const char *value) {
991   struct kvp *t, *p, *a;
992   DB_TXN *tid;
993   int err, cmp;
994   char *oldalias, *newalias, **oldtags = 0, **newtags;
995
996   if(value) {
997     /* TODO: if value matches default then set value=0 */
998   }
999   
1000   for(;;) {
1001     tid = trackdb_begin_transaction();
1002     if((err = gettrackdata(track, &t, &p, 0,
1003                            0, tid)) == DB_LOCK_DEADLOCK)
1004       goto fail;
1005     if(err == DB_NOTFOUND) break;
1006     if(name[0] == '_') {
1007       if(kvp_set(&t, name, value))
1008         if(trackdb_putdata(trackdb_tracksdb, track, t, tid, 0))
1009           goto fail;
1010     } else {
1011       /* get the old alias name */
1012       if(compute_alias(&oldalias, track, p, tid)) goto fail;
1013       /* get the old tags */
1014       if(!strcmp(name, "tags"))
1015         oldtags = parsetags(kvp_get(p, "tags"));
1016       /* set the value */
1017       if(kvp_set(&p, name, value))
1018         if(trackdb_putdata(trackdb_prefsdb, track, p, tid, 0))
1019           goto fail;
1020       /* compute the new alias name */
1021       if((err = compute_alias(&newalias, track, p, tid))) goto fail;
1022       /* check whether alias has changed */
1023       if(!(oldalias == newalias
1024            || (oldalias && newalias && !strcmp(oldalias, newalias)))) {
1025         /* adjust alias records to fit change */
1026         if(oldalias
1027            && trackdb_delkey(trackdb_tracksdb, oldalias, tid)) goto fail;
1028         if(newalias) {
1029           a = 0;
1030           kvp_set(&a, "_alias_for", track);
1031           if(trackdb_putdata(trackdb_tracksdb, newalias, a, tid, 0)) goto fail;
1032         }
1033       }
1034       /* check whether tags have changed */
1035       if(!strcmp(name, "tags")) {
1036         newtags = parsetags(value);
1037         while(*oldtags || *newtags) {
1038           if(*oldtags && *newtags) {
1039             cmp = strcmp(*oldtags, *newtags);
1040             if(!cmp) {
1041               /* keeping this tag */
1042               ++oldtags;
1043               ++newtags;
1044             } else if(cmp < 0)
1045               /* old tag fits into a gap in the new list, so delete old */
1046               goto delete_old;
1047             else
1048               /* new tag fits into a gap in the old list, so insert new */
1049               goto insert_new;
1050           } else if(*oldtags) {
1051             /* we've run out of new tags, so remaining old ones are to be
1052              * deleted */
1053           delete_old:
1054             if(trackdb_delkeydata(trackdb_tagsdb,
1055                                   *oldtags, track, tid) == DB_LOCK_DEADLOCK)
1056               goto fail;
1057             ++oldtags;
1058           } else {
1059             /* we've run out of old tags, so remainig new ones are to be
1060              * inserted */
1061           insert_new:
1062             if(register_tag(track, *newtags, tid)) goto fail;
1063             ++newtags;
1064           }
1065         }
1066         reqtracks = 0;
1067       }
1068     }
1069     err = 0;
1070     break;
1071 fail:
1072     trackdb_abort_transaction(tid);
1073   }
1074   trackdb_commit_transaction(tid);
1075   return err == 0 ? 0 : -1;
1076 }
1077
1078 /* get a pref */
1079 const char *trackdb_get(const char *track,
1080                         const char *name) {
1081   return kvp_get(trackdb_get_all(track), name);
1082 }
1083
1084 /* get all prefs as a 0-terminated array */
1085 struct kvp *trackdb_get_all(const char *track) {
1086   struct kvp *t, *p, **pp;
1087   DB_TXN *tid;
1088
1089   for(;;) {
1090     tid = trackdb_begin_transaction();
1091     if(gettrackdata(track, &t, &p, 0, 0, tid) == DB_LOCK_DEADLOCK)
1092       goto fail;
1093     break;
1094 fail:
1095     trackdb_abort_transaction(tid);
1096   }
1097   trackdb_commit_transaction(tid);
1098   for(pp = &p; *pp; pp = &(*pp)->next)
1099     ;
1100   *pp = t;
1101   return p;
1102 }
1103
1104 /* resolve alias */
1105 const char *trackdb_resolve(const char *track) {
1106   DB_TXN *tid;
1107   const char *actual;
1108   
1109   for(;;) {
1110     tid = trackdb_begin_transaction();
1111     if(gettrackdata(track, 0, 0, &actual, 0, tid) == DB_LOCK_DEADLOCK)
1112       goto fail;
1113     break;
1114 fail:
1115     trackdb_abort_transaction(tid);
1116   }
1117   trackdb_commit_transaction(tid);
1118   return actual;
1119 }
1120
1121 int trackdb_isalias(const char *track) {
1122   const char *actual = trackdb_resolve(track);
1123
1124   return strcmp(actual, track);
1125 }
1126
1127 /* test whether a track exists (perhaps an alias) */
1128 int trackdb_exists(const char *track) {
1129   DB_TXN *tid;
1130   int err;
1131
1132   for(;;) {
1133     tid = trackdb_begin_transaction();
1134     /* unusually, here we want the return value */
1135     if((err = gettrackdata(track, 0, 0, 0, 0, tid)) == DB_LOCK_DEADLOCK)
1136       goto fail;
1137     break;
1138 fail:
1139     trackdb_abort_transaction(tid);
1140   }
1141   trackdb_commit_transaction(tid);
1142   return (err == 0);
1143 }
1144
1145 /* return the list of tags */
1146 char **trackdb_alltags(void) {
1147   DB_TXN *tid;
1148   int err;
1149   char **taglist;
1150
1151   for(;;) {
1152     tid = trackdb_begin_transaction();
1153     err = trackdb_alltags_tid(tid, &taglist);
1154     if(!err) break;
1155     trackdb_abort_transaction(tid);
1156   }
1157   trackdb_commit_transaction(tid);
1158   return taglist;
1159 }
1160
1161 static int trackdb_alltags_tid(DB_TXN *tid, char ***taglistp) {
1162   struct vector v;
1163   DBC *c;
1164   DBT k, d;
1165   int err;
1166
1167   vector_init(&v);
1168   c = trackdb_opencursor(trackdb_tagsdb, tid);
1169   memset(&k, 0, sizeof k);
1170   while(!(err = c->c_get(c, &k, prepare_data(&d), DB_NEXT_NODUP)))
1171     vector_append(&v, xstrndup(k.data, k.size));
1172   switch(err) {
1173   case DB_NOTFOUND:
1174     break;
1175   case DB_LOCK_DEADLOCK:
1176       return err;
1177   default:
1178     fatal(0, "c->c_get: %s", db_strerror(err));
1179   }
1180   if((err = trackdb_closecursor(c))) return err;
1181   vector_terminate(&v);
1182   *taglistp = v.vec;
1183   return 0;
1184 }
1185
1186 /* return 1 iff sorted tag lists A and B have at least one member in common */
1187 static int tag_intersection(char **a, char **b) {
1188   int cmp;
1189
1190   /* Same sort of logic as trackdb_set() above */
1191   while(*a && *b) {
1192     if(!(cmp = strcmp(*a, *b))) return 1;
1193     else if(cmp < 0) ++a;
1194     else ++b;
1195   }
1196   return 0;
1197 }
1198
1199 /* Check whether a track is suitable for random play.  Returns 0 if it is,
1200  * DB_NOTFOUND if it is not or DB_LOCK_DEADLOCK if the database gave us
1201  * that. */
1202 static int check_suitable(const char *track,
1203                           DB_TXN *tid,
1204                           char **required_tags,
1205                           char **prohibited_tags) {
1206   char **track_tags;
1207   time_t last, now;
1208   struct kvp *p, *t;
1209   const char *pick_at_random, *played_time;
1210
1211   /* don't pick tracks that aren't in any surviving collection (for instance
1212    * you've edited the config but the rescan hasn't done its job yet) */
1213   if(!find_track_root(track)) {
1214     info("found track not in any collection: %s", track);
1215     return DB_NOTFOUND;
1216   }
1217   /* don't pick aliases - only pick the canonical form */
1218   if(gettrackdata(track, &t, &p, 0, 0, tid) == DB_LOCK_DEADLOCK)
1219     return DB_LOCK_DEADLOCK;
1220   if(kvp_get(t, "_alias_for"))
1221     return DB_NOTFOUND;
1222   /* check that random play is not suppressed for this track */
1223   if((pick_at_random = kvp_get(p, "pick_at_random"))
1224      && !strcmp(pick_at_random, "0"))
1225     return DB_NOTFOUND;
1226   /* don't pick a track that's been played in the last 8 hours */
1227   if((played_time = kvp_get(p, "played_time"))) {
1228     last = atoll(played_time);
1229     now = time(0);
1230     if(now < last + 8 * 3600)       /* TODO configurable */
1231       return DB_NOTFOUND;
1232   }
1233   track_tags = parsetags(kvp_get(p, "tags"));
1234   /* check that no prohibited tag is present for this track */
1235   if(prohibited_tags && tag_intersection(track_tags, prohibited_tags))
1236     return DB_NOTFOUND;
1237   /* check that at least one required tags is present for this track */
1238   if(*required_tags && !tag_intersection(track_tags, required_tags))
1239     return DB_NOTFOUND;
1240   return 0;
1241 }
1242
1243 /* attempt to pick a random non-alias track */
1244 const char *trackdb_random(int tries) {
1245   DBT key, data;
1246   DB_BTREE_STAT *sp;
1247   int err, n;
1248   DB_TXN *tid;
1249   const char *track, *candidate;
1250   db_recno_t r;
1251   const char *tags;
1252   char **required_tags, **prohibited_tags, **tp;
1253   hash *h;
1254   DBC *c = 0;
1255
1256   for(;;) {
1257     tid = trackdb_begin_transaction();
1258     if((err = trackdb_get_global_tid("required-tags", tid, &tags)))
1259       goto fail;
1260     required_tags = parsetags(tags);
1261     if((err = trackdb_get_global_tid("prohibited-tags", tid, &tags)))
1262       goto fail;
1263     prohibited_tags = parsetags(tags);
1264     track = 0;
1265     if(*required_tags) {
1266       /* Bung all the suitable tracks into a hash and convert to a list of keys
1267        * (to eliminate duplicates).  We cache this list since it is possible
1268        * that it will be very large. */
1269       if(!reqtracks) {
1270         h = hash_new(0);
1271         for(tp = required_tags; *tp; ++tp) {
1272           c = trackdb_opencursor(trackdb_tagsdb, tid);
1273           memset(&key, 0, sizeof key);
1274           key.data = *tp;
1275           key.size = strlen(*tp);
1276           n = 0;
1277           err = c->c_get(c, &key, prepare_data(&data), DB_SET);
1278           while(err == 0) {
1279             hash_add(h, xstrndup(data.data, data.size), 0,
1280                      HASH_INSERT_OR_REPLACE);
1281             ++n;
1282             err = c->c_get(c, &key, prepare_data(&data), DB_NEXT_DUP);
1283           }
1284           switch(err) {
1285           case 0:
1286           case DB_NOTFOUND:
1287             break;
1288           case DB_LOCK_DEADLOCK:
1289             goto fail;
1290           default:
1291             fatal(0, "error querying tags.db: %s", db_strerror(err));
1292           }
1293           trackdb_closecursor(c);
1294           c = 0;
1295           if(!n)
1296             error(0, "required tag %s does not match any tracks", *tp);
1297         }
1298         nreqtracks = hash_count(h);
1299         reqtracks = hash_keys(h);
1300       }
1301       while(nreqtracks && !track && tries-- > 0) {
1302         r = (rand() * (double)nreqtracks / (RAND_MAX + 1.0));
1303         candidate = reqtracks[r];
1304         switch(check_suitable(candidate, tid,
1305                               required_tags, prohibited_tags)) {
1306         case 0:
1307           track = candidate;
1308           break;
1309         case DB_NOTFOUND:
1310           break;
1311         case DB_LOCK_DEADLOCK:
1312           goto fail;
1313         }
1314       }
1315     } else {
1316       /* No required tags.  We pick random record numbers in the database
1317        * instead. */
1318       switch(err = trackdb_tracksdb->stat(trackdb_tracksdb, tid, &sp, 0)) {
1319       case 0:
1320         break;
1321       case DB_LOCK_DEADLOCK:
1322         error(0, "error querying tracks.db: %s", db_strerror(err));
1323         goto fail;
1324       default:
1325         fatal(0, "error querying tracks.db: %s", db_strerror(err));
1326       }
1327       if(!sp->bt_nkeys)
1328         error(0, "cannot pick tracks at random from an empty database");
1329       while(sp->bt_nkeys && !track && tries-- > 0) {
1330         /* record numbers count from 1 upwards */
1331         r = 1 + (rand() * (double)sp->bt_nkeys / (RAND_MAX + 1.0));
1332         memset(&key, sizeof key, 0);
1333         key.flags = DB_DBT_MALLOC;
1334         key.size = sizeof r;
1335         key.data = &r;
1336         switch(err = trackdb_tracksdb->get(trackdb_tracksdb, tid, &key, prepare_data(&data),
1337                                            DB_SET_RECNO)) {
1338         case 0:
1339           break;
1340         case DB_LOCK_DEADLOCK:
1341           error(0, "error querying tracks.db: %s", db_strerror(err));
1342           goto fail;
1343         default:
1344           fatal(0, "error querying tracks.db: %s", db_strerror(err));
1345         }
1346         candidate = xstrndup(key.data, key.size);
1347         switch(check_suitable(candidate, tid,
1348                               required_tags, prohibited_tags)) {
1349         case 0:
1350           track = candidate;
1351           break;
1352         case DB_NOTFOUND:
1353           break;
1354         case DB_LOCK_DEADLOCK:
1355           goto fail;
1356         }
1357       }
1358     }
1359     break;
1360 fail:
1361     trackdb_closecursor(c);
1362     c = 0;
1363     trackdb_abort_transaction(tid);
1364   }
1365   trackdb_commit_transaction(tid);
1366   if(!track)
1367     error(0, "could not pick a random track");
1368   return track;
1369 }
1370
1371 /* get a track name given the prefs.  Set *used_db to 1 if we got the answer
1372  * from the prefs. */
1373 static const char *getpart(const char *track,
1374                            const char *context,
1375                            const char *part,
1376                            const struct kvp *p,
1377                            int *used_db) {
1378   const char *result;
1379   char *pref;
1380
1381   byte_xasprintf(&pref, "trackname_%s_%s", context, part);
1382   if((result = kvp_get(p, pref)))
1383     *used_db = 1;
1384   else
1385     result = trackname_part(track, context, part);
1386   assert(result != 0);
1387   return result;
1388 }
1389
1390 /* get a track name part, like trackname_part(), but taking the database into
1391  * account. */
1392 const char *trackdb_getpart(const char *track,
1393                             const char *context,
1394                             const char *part) {
1395   struct kvp *p;
1396   DB_TXN *tid;
1397   char *pref;
1398   const char *actual;
1399   int used_db, err;
1400
1401   /* construct the full pref */
1402   byte_xasprintf(&pref, "trackname_%s_%s", context, part);
1403   for(;;) {
1404     tid = trackdb_begin_transaction();
1405     if((err = gettrackdata(track, 0, &p, &actual, 0, tid)) == DB_LOCK_DEADLOCK)
1406       goto fail;
1407     break;
1408 fail:
1409     trackdb_abort_transaction(tid);
1410   }
1411   trackdb_commit_transaction(tid);
1412   return getpart(actual, context, part, p, &used_db);
1413 }
1414
1415 /* get the raw path name for @track@ (might be an alias) */
1416 const char *trackdb_rawpath(const char *track) {
1417   DB_TXN *tid;
1418   struct kvp *t;
1419   const char *path;
1420
1421   for(;;) {
1422     tid = trackdb_begin_transaction();
1423     if(gettrackdata(track, &t, 0, 0, 0, tid) == DB_LOCK_DEADLOCK)
1424       goto fail;
1425     break;
1426 fail:
1427     trackdb_abort_transaction(tid);
1428   }
1429   trackdb_commit_transaction(tid);
1430   if(!(path = kvp_get(t, "_path"))) path = track;
1431   return path;
1432 }
1433
1434 /* trackdb_list **************************************************************/
1435
1436 /* this is incredibly ugly, sorry, perhaps it will be rewritten to be actually
1437  * readable at some point */
1438
1439 /* return true if the basename of TRACK[0..TL-1], as defined by DL, matches RE.
1440  * If RE is a null pointer then it matches everything. */
1441 static int track_matches(size_t dl, const char *track, size_t tl,
1442                          const pcre *re) {
1443   int ovec[3], rc;
1444
1445   if(!re)
1446     return 1;
1447   track += dl + 1;
1448   tl -= (dl + 1);
1449   switch(rc = pcre_exec(re, 0, track, tl, 0, 0, ovec, 3)) {
1450   case PCRE_ERROR_NOMATCH: return 0;
1451   default:
1452     if(rc < 0) {
1453       error(0, "pcre_exec returned %d, subject '%s'", rc, track);
1454       return 0;
1455     }
1456     return 1;
1457   }
1458 }
1459
1460 static int do_list(struct vector *v, const char *dir,
1461                    enum trackdb_listable what, const pcre *re, DB_TXN *tid) {
1462   DBC *cursor;
1463   DBT k, d;
1464   size_t dl;
1465   char *ptr;
1466   int err;
1467   size_t l, last_dir_len = 0;
1468   char *last_dir = 0, *track, *alias;
1469   struct kvp *p;
1470   
1471   dl = strlen(dir);
1472   cursor = trackdb_opencursor(trackdb_tracksdb, tid);
1473   make_key(&k, dir);
1474   prepare_data(&d);
1475   /* find the first key >= dir */
1476   err = cursor->c_get(cursor, &k, &d, DB_SET_RANGE);
1477   /* keep going while we're dealing with <dir/anything> */
1478   while(err == 0
1479         && k.size > dl
1480         && ((char *)k.data)[dl] == '/'
1481         && !memcmp(k.data, dir, dl)) {
1482     ptr = memchr((char *)k.data + dl + 1, '/', k.size - (dl + 1));
1483     if(ptr) {
1484       /* we have <dir/component/anything>, so <dir/component> is a directory */
1485       l = ptr - (char *)k.data;
1486       if(what & trackdb_directories)
1487         if(!(last_dir
1488              && l == last_dir_len
1489              && !memcmp(last_dir, k.data, l))) {
1490           last_dir = xstrndup(k.data, last_dir_len = l);
1491           if(track_matches(dl, k.data, l, re))
1492             vector_append(v, last_dir);
1493         }
1494     } else {
1495       /* found a plain file */
1496       if((what & trackdb_files)) {
1497         track = xstrndup(k.data, k.size);
1498         if((err = trackdb_getdata(trackdb_prefsdb,
1499                                   track, &p, tid)) == DB_LOCK_DEADLOCK)
1500           goto deadlocked;
1501         /* if this file has an alias in the same directory then we skip it */
1502         if((err = compute_alias(&alias, track, p, tid)))
1503           goto deadlocked;
1504         if(!(alias && !strcmp(d_dirname(alias), d_dirname(track))))
1505           if(track_matches(dl, k.data, k.size, re))
1506             vector_append(v, track);
1507       }
1508     }
1509     err = cursor->c_get(cursor, &k, &d, DB_NEXT);
1510   }
1511   switch(err) {
1512   case 0:
1513     break;
1514   case DB_NOTFOUND:
1515     err = 0;
1516     break;
1517   case DB_LOCK_DEADLOCK:
1518     error(0, "error querying database: %s", db_strerror(err));
1519     break;
1520   default:
1521     fatal(0, "error querying database: %s", db_strerror(err));
1522   }
1523 deadlocked:
1524   if(trackdb_closecursor(cursor)) err = DB_LOCK_DEADLOCK;
1525   return err;
1526 }
1527
1528 /* return the directories or files below @dir@ */
1529 char **trackdb_list(const char *dir, int *np, enum trackdb_listable what,
1530                     const pcre *re) {
1531   DB_TXN *tid;
1532   int n;
1533   struct vector v;
1534
1535   vector_init(&v);
1536   for(;;) {
1537     tid = trackdb_begin_transaction();
1538     v.nvec = 0;
1539     if(dir) {
1540       if(do_list(&v, dir, what, re, tid))
1541         goto fail;
1542     } else {
1543       for(n = 0; n < config->collection.n; ++n)
1544         if(do_list(&v, config->collection.s[n].root, what, re, tid))
1545           goto fail;
1546     }
1547     break;
1548 fail:
1549     trackdb_abort_transaction(tid);
1550   }
1551   trackdb_commit_transaction(tid);
1552   vector_terminate(&v);
1553   if(np)
1554     *np = v.nvec;
1555   return v.vec;
1556 }  
1557
1558 /* If S is tag:something, return something.  Else return 0. */
1559 static const char *checktag(const char *s) {
1560   if(!strncmp(s, "tag:", 4))
1561     return s + 4;
1562   else
1563     return 0;
1564 }
1565
1566 /* return a list of tracks containing all of the words given.  If you
1567  * ask for only stopwords you get no tracks. */
1568 char **trackdb_search(char **wordlist, int nwordlist, int *ntracks) {
1569   const char **w, *best = 0, *tag;
1570   char **twords, **tags;
1571   int i, j, n, err, what;
1572   DBC *cursor = 0;
1573   DBT k, d;
1574   struct vector u, v;
1575   DB_TXN *tid;
1576   struct kvp *p;
1577   int ntags = 0;
1578   DB *db;
1579   const char *dbname;
1580
1581   *ntracks = 0;                         /* for early returns */
1582   /* casefold all the words */
1583   w = xmalloc(nwordlist * sizeof (char *));
1584   for(n = 0; n < nwordlist; ++n) {
1585     w[n] = casefold(wordlist[n]);
1586     if(checktag(w[n])) ++ntags;         /* count up tags */
1587   }
1588   /* find the longest non-stopword */
1589   for(n = 0; n < nwordlist; ++n)
1590     if(!stopword(w[n]) && !checktag(w[n]))
1591       if(!best || strlen(w[n]) > strlen(best))
1592         best = w[n];
1593   /* TODO: we should at least in principal be able to identify the word or tag
1594    * with the least matches in log time, and choose that as our primary search
1595    * term. */
1596   if(ntags && !best) {
1597     /* Only tags are listed.  We limit to the first and narrow down with the
1598      * rest. */
1599     best = checktag(w[0]);
1600     db = trackdb_tagsdb;
1601     dbname = "tags";
1602   } else if(best) {
1603     /* We can limit to some word. */
1604     db = trackdb_searchdb;
1605     dbname = "search";
1606   } else {
1607     /* Only stopwords */
1608     return 0;
1609   }
1610   vector_init(&u);
1611   vector_init(&v);
1612   for(;;) {
1613     tid = trackdb_begin_transaction();
1614     /* find all the tracks that have that word */
1615     make_key(&k, best);
1616     prepare_data(&d);
1617     what = DB_SET;
1618     v.nvec = 0;
1619     cursor = trackdb_opencursor(db, tid);
1620     while(!(err = cursor->c_get(cursor, &k, &d, what))) {
1621       vector_append(&v, xstrndup(d.data, d.size));
1622       what = DB_NEXT_DUP;
1623     }
1624     switch(err) {
1625     case DB_NOTFOUND:
1626       err = 0;
1627       break;
1628     case DB_LOCK_DEADLOCK:
1629       error(0, "error querying %s database: %s", dbname, db_strerror(err));
1630       break;
1631     default:
1632       fatal(0, "error querying %s database: %s", dbname, db_strerror(err));
1633     }
1634     if(trackdb_closecursor(cursor)) err = DB_LOCK_DEADLOCK;
1635     cursor = 0;
1636     /* do a naive search over that (hopefuly fairly small) list of tracks */
1637     u.nvec = 0;
1638     for(n = 0; n < v.nvec; ++n) {
1639       if((err = gettrackdata(v.vec[n], 0, &p, 0, 0, tid) == DB_LOCK_DEADLOCK))
1640         goto fail;
1641       else if(err) {
1642         error(0, "track %s unexpected error: %s", v.vec[n], db_strerror(err));
1643         continue;
1644       }
1645       twords = track_to_words(v.vec[n], p);
1646       tags = parsetags(kvp_get(p, "tags"));
1647       for(i = 0; i < nwordlist; ++i) {
1648         if((tag = checktag(w[i]))) {
1649           /* Track must have this tag */
1650           for(j = 0; tags[j]; ++j)
1651             if(!strcmp(tag, tags[j])) break; /* tag found */
1652           if(!tags[j]) break;           /* tag not found */
1653         } else {
1654           /* Track must contain this word */
1655           for(j = 0; twords[j]; ++j)
1656             if(!strcmp(w[i], twords[j])) break; /* word found */
1657           if(!twords[j]) break;         /* word not found */
1658         }
1659       }
1660       if(i >= nwordlist)                /* all words found */
1661         vector_append(&u, v.vec[n]);
1662     }
1663     break;
1664   fail:
1665     trackdb_closecursor(cursor);
1666     cursor = 0;
1667     trackdb_abort_transaction(tid);
1668     info("retrying search");
1669   }
1670   trackdb_commit_transaction(tid);
1671   vector_terminate(&u);
1672   if(ntracks)
1673     *ntracks = u.nvec;
1674   return u.vec;
1675 }
1676
1677 /* trackdb_scan **************************************************************/
1678
1679 int trackdb_scan(const char *root,
1680                  int (*callback)(const char *track,
1681                                  struct kvp *data,
1682                                  void *u,
1683                                  DB_TXN *tid),
1684                  void *u,
1685                  DB_TXN *tid) {
1686   DBC *cursor;
1687   DBT k, d;
1688   const size_t root_len = root ? strlen(root) : 0;
1689   int err, cberr;
1690   struct kvp *data;
1691   const char *track;
1692
1693   cursor = trackdb_opencursor(trackdb_tracksdb, tid);
1694   if(root)
1695     err = cursor->c_get(cursor, make_key(&k, root), prepare_data(&d),
1696                         DB_SET_RANGE);
1697   else {
1698     memset(&k, 0, sizeof k);
1699     err = cursor->c_get(cursor, &k, prepare_data(&d),
1700                         DB_FIRST);
1701   }
1702   while(!err) {
1703     if(!root
1704        || (k.size > root_len
1705            && !strncmp(k.data, root, root_len)
1706            && ((char *)k.data)[root_len] == '/')) {
1707       data = kvp_urldecode(d.data, d.size);
1708       if(kvp_get(data, "_path")) {
1709         track = xstrndup(k.data, k.size);
1710         /* Advance to the next track before the callback so that the callback
1711          * may safely delete the track */
1712         err = cursor->c_get(cursor, &k, &d, DB_NEXT);
1713         if((cberr = callback(track, data, u, tid))) {
1714           err = cberr;
1715           break;
1716         }
1717       } else
1718         err = cursor->c_get(cursor, &k, &d, DB_NEXT);
1719     } else
1720       break;
1721   }
1722   trackdb_closecursor(cursor);
1723   switch(err) {
1724   case EINTR:
1725     return err;
1726   case 0:
1727   case DB_NOTFOUND:
1728     return 0;
1729   case DB_LOCK_DEADLOCK:
1730     error(0, "c->c_get: %s", db_strerror(err));
1731     return err;
1732   default:
1733     fatal(0, "c->c_get: %s", db_strerror(err));
1734   }
1735 }
1736
1737 /* trackdb_rescan ************************************************************/
1738
1739 /* called when the rescanner terminates */
1740 static int reap_rescan(ev_source attribute((unused)) *ev,
1741                        pid_t pid,
1742                        int status,
1743                        const struct rusage attribute((unused)) *rusage,
1744                        void attribute((unused)) *u) {
1745   if(pid == rescan_pid) rescan_pid = -1;
1746   if(status)
1747     error(0, "disorderd-rescan: %s", wstat(status));
1748   else
1749     D(("disorderd-rescan terminate: %s", wstat(status)));
1750   /* Our cache of file lookups is out of date now */
1751   cache_clean(&cache_files_type);
1752   eventlog("rescanned", (char *)0);
1753   return 0;
1754 }
1755
1756 void trackdb_rescan(ev_source *ev) {
1757   if(rescan_pid != -1) {
1758     error(0, "rescan already underway");
1759     return;
1760   }
1761   rescan_pid = subprogram(ev, RESCAN);
1762   ev_child(ev, rescan_pid, 0, reap_rescan, 0);
1763   D(("started rescanner"));
1764   
1765 }
1766
1767 int trackdb_rescan_cancel(void) {
1768   if(rescan_pid == -1) return 0;
1769   if(kill(rescan_pid, SIGTERM) < 0)
1770     fatal(errno, "error killing rescanner");
1771   rescan_pid = -1;
1772   return 1;
1773 }
1774
1775 /* global prefs **************************************************************/
1776
1777 void trackdb_set_global(const char *name,
1778                         const char *value,
1779                         const char *who) {
1780   DB_TXN *tid;
1781   DBT k, d;
1782   int err;
1783   int state;
1784
1785   memset(&k, 0, sizeof k);
1786   memset(&d, 0, sizeof d);
1787   k.data = (void *)name;
1788   k.size = strlen(name);
1789   if(value) {
1790     d.data = (void *)value;
1791     d.size = strlen(value);
1792   }
1793   for(;;) {
1794     tid = trackdb_begin_transaction();
1795     if(value)
1796       err = trackdb_globaldb->put(trackdb_globaldb, tid, &k, &d, 0);
1797     else
1798       err = trackdb_globaldb->del(trackdb_globaldb, tid, &k, 0);
1799     if(!err || err == DB_NOTFOUND) break;
1800     if(err != DB_LOCK_DEADLOCK)
1801       fatal(0, "error updating database: %s", db_strerror(err));
1802     trackdb_abort_transaction(tid);
1803   }
1804   trackdb_commit_transaction(tid);
1805   /* log important state changes */
1806   if(!strcmp(name, "playing")) {
1807     state = !value || !strcmp(value, "yes");
1808     info("playing %s by %s",
1809          state ? "enabled" : "disabled",
1810          who ? who : "-");
1811     eventlog("state", state ? "enable_play" : "disable_play", (char *)0);
1812   }
1813   if(!strcmp(name, "random-play")) {
1814     state = !value || !strcmp(value, "yes");
1815     info("random play %s by %s",
1816          state ? "enabled" : "disabled",
1817          who ? who : "-");
1818     eventlog("state", state ? "enable_random" : "disable_random", (char *)0);
1819   }
1820   if(!strcmp(name, "required-tags"))
1821     reqtracks = 0;
1822 }
1823
1824 const char *trackdb_get_global(const char *name) {
1825   DB_TXN *tid;
1826   int err;
1827   const char *r;
1828
1829   for(;;) {
1830     tid = trackdb_begin_transaction();
1831     if(!(err = trackdb_get_global_tid(name, tid, &r)))
1832       break;
1833     trackdb_abort_transaction(tid);
1834   }
1835   trackdb_commit_transaction(tid);
1836   return r;
1837 }
1838
1839 static int trackdb_get_global_tid(const char *name,
1840                                   DB_TXN *tid,
1841                                   const char **rp) {
1842   DBT k, d;
1843   int err;
1844
1845   memset(&k, 0, sizeof k);
1846   k.data = (void *)name;
1847   k.size = strlen(name);
1848   switch(err = trackdb_globaldb->get(trackdb_globaldb, tid, &k,
1849                                      prepare_data(&d), 0)) {
1850   case 0:
1851     *rp = xstrndup(d.data, d.size);
1852     return 0;
1853   case DB_NOTFOUND:
1854     *rp = 0;
1855     return 0;
1856   case DB_LOCK_DEADLOCK:
1857     return err;
1858   default:
1859     fatal(0, "error updating database: %s", db_strerror(err));
1860   }
1861 }
1862
1863 /** @brief Retrieve the most recently added tracks
1864  * @param ntracksp Where to put count, or 0
1865  * @param maxtracks Maximum number of tracks to retrieve
1866  * @return null-terminated array of track names
1867  *
1868  * The most recently added track is first in the array.
1869  */
1870 char **trackdb_new(int *ntracksp,
1871                    int maxtracks) {
1872   DB_TXN *tid;
1873   char **tracks;
1874
1875   for(;;) {
1876     tid = trackdb_begin_transaction();
1877     tracks = trackdb_new_tid(ntracksp, maxtracks, tid);
1878     if(tracks)
1879       break;
1880     trackdb_abort_transaction(tid);
1881   }
1882   trackdb_commit_transaction(tid);
1883   return tracks;
1884 }
1885
1886 /** @brief Retrieve the most recently added tracks
1887  * @param ntracksp Where to put count, or 0
1888  * @param maxtracks Maximum number of tracks to retrieve, or 0 for all
1889  * @param tid Transaction ID
1890  * @return null-terminated array of track names, or NULL on deadlock
1891  *
1892  * The most recently added track is first in the array.
1893  */
1894 static char **trackdb_new_tid(int *ntracksp,
1895                               int maxtracks,
1896                               DB_TXN *tid) {
1897   DBC *c;
1898   DBT k, d;
1899   int err = 0;
1900   struct vector tracks[1];
1901
1902   vector_init(tracks);
1903   c = trackdb_opencursor(trackdb_noticeddb, tid);
1904   while((maxtracks <= 0 || tracks->nvec < maxtracks)
1905         && !(err = c->c_get(c, prepare_data(&k), prepare_data(&d), DB_PREV)))
1906     vector_append(tracks, xstrndup(d.data, d.size));
1907   switch(err) {
1908   case 0:                               /* hit maxtracks */
1909   case DB_NOTFOUND:                     /* ran out of tracks */
1910     break;
1911   case DB_LOCK_DEADLOCK:
1912     trackdb_closecursor(c);
1913     return 0;
1914   default:
1915     fatal(0, "error reading noticed.db: %s", db_strerror(err));
1916   }
1917   if((err = trackdb_closecursor(c)))
1918     return 0;                           /* deadlock */
1919   vector_terminate(tracks);
1920   if(ntracksp)
1921     *ntracksp = tracks->nvec;
1922   return tracks->vec;
1923 }
1924
1925 /** @brief Expire noticed.db
1926  * @param earliest Earliest timestamp to keep
1927  */
1928 void trackdb_expire_noticed(time_t earliest) {
1929   DB_TXN *tid;
1930
1931   for(;;) {
1932     tid = trackdb_begin_transaction();
1933     if(!trackdb_expire_noticed_tid(earliest, tid))
1934       break;
1935     trackdb_abort_transaction(tid);
1936   }
1937   trackdb_commit_transaction(tid);
1938 }
1939
1940 /** @brief Expire noticed.db
1941  * @param earliest Earliest timestamp to keep
1942  * @param tid Transaction ID
1943  * @return 0 or DB_LOCK_DEADLOCK
1944  */
1945 static int trackdb_expire_noticed_tid(time_t earliest, DB_TXN *tid) {
1946   DBC *c;
1947   DBT k, d;
1948   int err = 0, ret;
1949   time_t when;
1950   uint32_t *kk;
1951   int count = 0;
1952
1953   c = trackdb_opencursor(trackdb_noticeddb, tid);
1954   while(!(err = c->c_get(c, prepare_data(&k), prepare_data(&d), DB_NEXT))) {
1955     kk = k.data;
1956     when = (time_t)(((uint64_t)ntohl(kk[0]) << 32) + ntohl(kk[1]));
1957     if(when >= earliest)
1958       break;
1959     if((err = c->c_del(c, 0))) {
1960       if(err != DB_LOCK_DEADLOCK)
1961         fatal(0, "error deleting expired noticed.db entry: %s",
1962               db_strerror(err));
1963       break;
1964     }
1965     ++count;
1966   }
1967   if(err == DB_NOTFOUND)
1968     err = 0;
1969   if(err && err != DB_LOCK_DEADLOCK)
1970     fatal(0, "error expiring noticed.db: %s", db_strerror(err));
1971   ret = err;
1972   if((err = trackdb_closecursor(c))) {
1973     if(err != DB_LOCK_DEADLOCK)
1974       fatal(0, "error closing cursor: %s", db_strerror(err));
1975     ret = err;
1976   }
1977   if(!ret && count)
1978     info("expired %d tracks from noticed.db", count);
1979   return ret;
1980 }
1981
1982 /* tidying up ****************************************************************/
1983
1984 void trackdb_gc(void) {
1985   int err;
1986   char **logfiles;
1987
1988   if((err = trackdb_env->txn_checkpoint(trackdb_env,
1989                                         config->checkpoint_kbyte,
1990                                         config->checkpoint_min,
1991                                         0)))
1992     fatal(0, "trackdb_env->txn_checkpoint: %s", db_strerror(err));
1993   if((err = trackdb_env->log_archive(trackdb_env, &logfiles, DB_ARCH_REMOVE)))
1994     fatal(0, "trackdb_env->log_archive: %s", db_strerror(err));
1995   /* This makes catastrophic recovery impossible.  However, the user can still
1996    * preserve the important data by using disorder-dump to snapshot their
1997    * prefs, and later to restore it.  This is likely to have much small
1998    * long-term storage requirements than record the db logfiles. */
1999 }
2000
2001 /*
2002 Local Variables:
2003 c-basic-offset:2
2004 comment-column:40
2005 fill-column:79
2006 indent-tabs-mode:nil
2007 End:
2008 */