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