chiark / gitweb /
Merge from 3.0 fixes branch
[disorder] / lib / trackdb.c
1 /*
2  * This file is part of DisOrder
3  * Copyright (C) 2005-2008 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 /** @file lib/trackdb.c
21  * @brief Track database
22  *
23  * This file is getting in desparate need of splitting up...
24  */
25
26 #include <config.h>
27 #include "types.h"
28
29 #include <string.h>
30 #include <stdio.h>
31 #include <db.h>
32 #include <sys/socket.h>
33 #include <pcre.h>
34 #include <assert.h>
35 #include <unistd.h>
36 #include <errno.h>
37 #include <stddef.h>
38 #include <sys/time.h>
39 #include <sys/resource.h>
40 #include <time.h>
41 #include <arpa/inet.h>
42 #include <sys/wait.h>
43 #include <dirent.h>
44 #include <sys/stat.h>
45 #include <gcrypt.h>
46
47 #include "event.h"
48 #include "mem.h"
49 #include "kvp.h"
50 #include "log.h"
51 #include "vector.h"
52 #include "rights.h"
53 #include "trackdb.h"
54 #include "configuration.h"
55 #include "syscalls.h"
56 #include "wstat.h"
57 #include "printf.h"
58 #include "filepart.h"
59 #include "trackname.h"
60 #include "trackdb-int.h"
61 #include "logfd.h"
62 #include "cache.h"
63 #include "eventlog.h"
64 #include "hash.h"
65 #include "unicode.h"
66 #include "unidata.h"
67 #include "base64.h"
68
69 #define RESCAN "disorder-rescan"
70 #define DEADLOCK "disorder-deadlock"
71
72 static const char *getpart(const char *track,
73                            const char *context,
74                            const char *part,
75                            const struct kvp *p,
76                            int *used_db);
77 static char **trackdb_new_tid(int *ntracksp,
78                               int maxtracks,
79                               DB_TXN *tid);
80 static int trackdb_expire_noticed_tid(time_t earliest, DB_TXN *tid);
81 static char *normalize_tag(const char *s, size_t ns);
82
83 const struct cache_type cache_files_type = { 86400 };
84 unsigned long cache_files_hits, cache_files_misses;
85
86 /** @brief Set by trackdb_open() */
87 int trackdb_existing_database;
88
89 /* setup and teardown ********************************************************/
90
91 static const char *home;                /* home had better not change */
92 DB_ENV *trackdb_env;                    /* db environment */
93
94 /** @brief The tracks database
95  * - Keys are UTF-8(NFC(unicode(path name)))
96  * - Values are encoded key-value pairs
97  * - Data is reconstructable data about tracks that currently exist
98  */
99 DB *trackdb_tracksdb;
100
101 /** @brief The preferences database
102  *
103  * - Keys are UTF-8(NFC(unicode(path name)))
104  * - Values are encoded key-value pairs
105  * - Data is user data about tracks (that might not exist any more)
106  * and cannot be reconstructed
107  */
108 DB *trackdb_prefsdb;
109
110 /** @brief The search database
111  *
112  * - Keys are UTF-8(NFKC(casefold(search term)))
113  * - Values are UTF-8(NFC(unicode(path name)))
114  * - There can be more than one value per key
115  * - Presence of key,value means that path matches the search terms
116  * - Only tracks fond in @ref trackdb_tracksdb are represented here
117  * - This database can be reconstructed, it contains no user data
118  */
119 DB *trackdb_searchdb;
120
121 /** @brief The tags database
122  *
123  * - Keys are UTF-8(NFKC(casefold(tag)))
124  * - Values are UTF-8(NFC(unicode(path name)))
125  * - There can be more than one value per key
126  * - Presence of key,value means that path matches the tag
127  * - This is always in sync with the tags preference
128  * - This database can be reconstructed, it contains no user data
129  */
130 DB *trackdb_tagsdb;                     /* the tags database */
131
132 /** @brief The global preferences database
133  * - Keys are UTF-8(NFC(preference))
134  * - Values are global preference values
135  * - Data is user data and cannot be reconstructed
136  */
137 DB *trackdb_globaldb;                   /* global preferences */
138
139 /** @brief The noticed database
140  * - Keys are 64-bit big-endian timestamps
141  * - Values are UTF-8(NFC(unicode(path name)))
142  * - There can be more than one value per key
143  * - Presence of key,value means that path was added at the given time
144  * - Data cannot be reconstructed (but isn't THAT important)
145  */
146 DB *trackdb_noticeddb;                   /* when track noticed */
147
148 /** @brief The user database
149  * - Keys are usernames
150  * - Values are encoded key-value pairs
151  * - Data is user data and cannot be reconstructed
152  */
153 DB *trackdb_usersdb;
154
155 static pid_t db_deadlock_pid = -1;      /* deadlock manager PID */
156 static pid_t rescan_pid = -1;           /* rescanner PID */
157 static int initialized, opened;         /* state */
158
159 /* comparison function for keys */
160 static int compare(DB attribute((unused)) *db_,
161                    const DBT *a, const DBT *b) {
162   return compare_path_raw(a->data, a->size, b->data, b->size);
163 }
164
165 /** @brief Test whether the track database can be read
166  * @return 1 if it can, 0 if it cannot
167  */
168 int trackdb_readable(void) {
169   char *usersdb;
170
171   byte_xasprintf(&usersdb, "%s/users.db", config->home);
172   return access(usersdb, R_OK) == 0;
173 }
174
175 /** @brief Open database environment
176  * @param flags Flags word
177  *
178  * Flags should be one of:
179  * - @ref TRACKDB_NO_RECOVER
180  * - @ref TRACKDB_NORMAL_RECOVER
181  * - @ref TRACKDB_FATAL_RECOVER
182  * - @ref TRACKDB_MAY_CREATE
183  */
184 void trackdb_init(int flags) {
185   int err;
186   const int recover = flags & TRACKDB_RECOVER_MASK;
187   static int recover_type[] = { 0, DB_RECOVER, DB_RECOVER_FATAL };
188
189   /* sanity checks */
190   assert(initialized == 0);
191   ++initialized;
192   if(home) {
193     if(strcmp(home, config->home))
194       fatal(0, "cannot change db home without server restart");
195     home = config->home;
196   }
197
198   if(flags & TRACKDB_MAY_CREATE) {
199     DIR *dp;
200     struct dirent *de;
201     struct stat st;
202     char *p;
203
204     /* Remove world/group permissions on any regular files already in the
205      * database directory.  Actually we don't care about all of them but it's
206      * easier to just do the lot.  This can be revisited if it's a serious
207      * practical inconvenience for anyone.
208      *
209      * The socket, not being a regular file, is excepted.
210      */
211     if(!(dp = opendir(config->home)))
212       fatal(errno, "error reading %s", config->home);
213     while((de = readdir(dp))) {
214       byte_xasprintf(&p, "%s/%s", config->home, de->d_name);
215       if(lstat(p, &st) == 0
216          && S_ISREG(st.st_mode)
217          && (st.st_mode & 077)) {
218         if(chmod(p, st.st_mode & 07700) < 0)
219           fatal(errno, "cannot chmod %s", p);
220       }
221       xfree(p);
222     }
223     closedir(dp);
224   }
225
226   /* create environment */
227   if((err = db_env_create(&trackdb_env, 0))) fatal(0, "db_env_create: %s",
228                                                    db_strerror(err));
229   if((err = trackdb_env->set_alloc(trackdb_env,
230                                    xmalloc_noptr, xrealloc_noptr, xfree)))
231     fatal(0, "trackdb_env->set_alloc: %s", db_strerror(err));
232   if((err = trackdb_env->set_lk_max_locks(trackdb_env, 10000)))
233     fatal(0, "trackdb_env->set_lk_max_locks: %s", db_strerror(err));
234   if((err = trackdb_env->set_lk_max_objects(trackdb_env, 10000)))
235     fatal(0, "trackdb_env->set_lk_max_objects: %s", db_strerror(err));
236   if((err = trackdb_env->open(trackdb_env, config->home,
237                               DB_INIT_LOG
238                               |DB_INIT_LOCK
239                               |DB_INIT_MPOOL
240                               |DB_INIT_TXN
241                               |DB_CREATE
242                               |recover_type[recover],
243                               0600)))
244     fatal(0, "trackdb_env->open %s: %s", config->home, db_strerror(err));
245   trackdb_env->set_errpfx(trackdb_env, "DB");
246   trackdb_env->set_errfile(trackdb_env, stderr);
247   trackdb_env->set_verbose(trackdb_env, DB_VERB_DEADLOCK, 1);
248   trackdb_env->set_verbose(trackdb_env, DB_VERB_RECOVERY, 1);
249   trackdb_env->set_verbose(trackdb_env, DB_VERB_REPLICATION, 1);
250   D(("initialized database environment"));
251 }
252
253 /* called when deadlock manager terminates */
254 static int reap_db_deadlock(ev_source attribute((unused)) *ev,
255                             pid_t attribute((unused)) pid,
256                             int status,
257                             const struct rusage attribute((unused)) *rusage,
258                             void attribute((unused)) *u) {
259   db_deadlock_pid = -1;
260   if(initialized)
261     fatal(0, "deadlock manager unexpectedly terminated: %s",
262           wstat(status));
263   else
264     D(("deadlock manager terminated: %s", wstat(status)));
265   return 0;
266 }
267
268 static pid_t subprogram(ev_source *ev, int outputfd, const char *prog,
269                         ...) {
270   pid_t pid;
271   va_list ap;
272   const char *args[1024], **argp, *a;
273
274   argp = args;
275   *argp++ = prog;
276   *argp++ = "--config";
277   *argp++ = configfile;
278   *argp++ = debugging ? "--debug" : "--no-debug";
279   *argp++ = log_default == &log_syslog ? "--syslog" : "--no-syslog";
280   va_start(ap, prog);
281   while((a = va_arg(ap, const char *)))
282     *argp++ = a;
283   va_end(ap);
284   *argp = 0;
285   /* If we're in the background then trap subprocess stdout/stderr */
286   if(!(pid = xfork())) {
287     exitfn = _exit;
288     if(ev)
289       ev_signal_atfork(ev);
290     signal(SIGPIPE, SIG_DFL);
291     if(outputfd != -1) {
292       xdup2(outputfd, 1);
293       xclose(outputfd);
294     }
295     /* ensure we don't leak privilege anywhere */
296     if(setuid(geteuid()) < 0)
297       fatal(errno, "error calling setuid");
298     /* If we were negatively niced, undo it.  We don't bother checking for 
299     * error, it's not that important. */
300     setpriority(PRIO_PROCESS, 0, 0);
301     execvp(prog, (char **)args);
302     fatal(errno, "error invoking %s", prog);
303   }
304   return pid;
305 }
306
307 /* start deadlock manager */
308 void trackdb_master(ev_source *ev) {
309   assert(db_deadlock_pid == -1);
310   db_deadlock_pid = subprogram(ev, -1, DEADLOCK, (char *)0);
311   ev_child(ev, db_deadlock_pid, 0, reap_db_deadlock, 0);
312   D(("started deadlock manager"));
313 }
314
315 /* close environment */
316 void trackdb_deinit(void) {
317   int err;
318
319   /* sanity checks */
320   assert(initialized == 1);
321   --initialized;
322
323   /* close the environment */
324   if((err = trackdb_env->close(trackdb_env, 0)))
325     fatal(0, "trackdb_env->close: %s", db_strerror(err));
326
327   if(rescan_pid != -1) {
328     /* shut down the rescanner */
329     if(kill(rescan_pid, SIGTERM) < 0)
330       fatal(errno, "error killing rescanner");
331     /* wait for the rescanner to finish */
332     while(waitpid(rescan_pid, &err, 0) == -1 && errno == EINTR)
333       ;
334   }
335
336   /* TODO kill any stats subprocesses */
337
338   /* finally terminate the deadlock manager */
339   if(db_deadlock_pid != -1 && kill(db_deadlock_pid, SIGTERM) < 0)
340     fatal(errno, "error killing deadlock manager");
341   db_deadlock_pid = -1;
342
343   D(("deinitialized database environment"));
344 }
345
346 /* open a specific database */
347 static DB *open_db(const char *path,
348                    u_int32_t dbflags,
349                    DBTYPE dbtype,
350                    u_int32_t openflags,
351                    int mode) {
352   int err;
353   DB *db;
354
355   D(("open %s", path));
356   path = config_get_file(path);
357   if((err = db_create(&db, trackdb_env, 0)))
358     fatal(0, "db_create %s: %s", path, db_strerror(err));
359   if(dbflags)
360     if((err = db->set_flags(db, dbflags)))
361       fatal(0, "db->set_flags %s: %s", path, db_strerror(err));
362   if(dbtype == DB_BTREE)
363     if((err = db->set_bt_compare(db, compare)))
364       fatal(0, "db->set_bt_compare %s: %s", path, db_strerror(err));
365   if((err = db->open(db, 0, path, 0, dbtype,
366                      openflags | DB_AUTO_COMMIT, mode))) {
367     if((openflags & DB_CREATE) || errno != ENOENT)
368       fatal(0, "db->open %s: %s", path, db_strerror(err));
369     db->close(db, 0);
370     db = 0;
371   }
372   return db;
373 }
374
375 /** @brief Open track databases
376  * @param flags Flags flags word
377  *
378  * @p flags should have one of:
379  * - @p TRACKDB_NO_UPGRADE, if no upgrade should be attempted
380  * - @p TRACKDB_CAN_UPGRADE, if an upgrade may be attempted
381  * - @p TRACKDB_OPEN_FOR_UPGRADE, if this is disorder-dbupgrade
382  * Also it may have:
383  * - @p TRACKDB_READ_ONLY, read only access
384  */
385 void trackdb_open(int flags) {
386   int err;
387   pid_t pid;
388   uint32_t dbflags = flags & TRACKDB_READ_ONLY ? DB_RDONLY : DB_CREATE;
389
390   /* sanity checks */
391   assert(opened == 0);
392   ++opened;
393   /* check the database version first */
394   trackdb_globaldb = open_db("global.db", 0, DB_HASH, DB_RDONLY, 0666);
395   if(trackdb_globaldb) {
396     /* This is an existing database */
397     const char *s;
398     long oldversion;
399
400     s = trackdb_get_global("_dbversion");
401     /* Close the database again,  we'll open it property below */
402     if((err = trackdb_globaldb->close(trackdb_globaldb, 0)))
403       fatal(0, "error closing global.db: %s", db_strerror(err));
404     trackdb_globaldb = 0;
405     /* Convert version string to an integer */
406     oldversion = s ? atol(s) : 1;
407     if(oldversion > config->dbversion) {
408       /* Database is from the future; we never allow this. */
409       fatal(0, "this version of DisOrder is too old for database version %ld",
410             oldversion);
411     }
412     if(oldversion < config->dbversion) {
413       /* Database version is out of date */
414       switch(flags & TRACKDB_UPGRADE_MASK) {
415       case TRACKDB_NO_UPGRADE:
416         /* This database needs upgrading but this is not permitted */
417         fatal(0, "database needs upgrading from %ld to %ld",
418               oldversion, config->dbversion);
419       case TRACKDB_CAN_UPGRADE:
420         /* This database needs upgrading */
421         info("invoking disorder-dbupgrade to upgrade from %ld to %ld",
422              oldversion, config->dbversion);
423         pid = subprogram(0, -1, "disorder-dbupgrade", (char *)0);
424         while(waitpid(pid, &err, 0) == -1 && errno == EINTR)
425           ;
426         if(err)
427           fatal(0, "disorder-dbupgrade %s", wstat(err));
428         info("disorder-dbupgrade succeeded");
429         break;
430       case TRACKDB_OPEN_FOR_UPGRADE:
431         break;
432       default:
433         abort();
434       }
435     }
436     if(oldversion == config->dbversion && (flags & TRACKDB_OPEN_FOR_UPGRADE)) {
437       /* This doesn't make any sense */
438       fatal(0, "database is already at current version");
439     }
440     trackdb_existing_database = 1;
441   } else {
442     if(flags & TRACKDB_OPEN_FOR_UPGRADE) {
443       /* Cannot upgrade a new database */
444       fatal(0, "cannot upgrade a database that does not exist");
445     }
446     /* This is a brand new database */
447     trackdb_existing_database = 0;
448   }
449   /* open the databases */
450   if(!(trackdb_usersdb = open_db("users.db",
451                                  0, DB_HASH, dbflags, 0600)))
452     fatal(0, "cannot open users.db");
453   trackdb_tracksdb = open_db("tracks.db",
454                              DB_RECNUM, DB_BTREE, dbflags, 0666);
455   trackdb_searchdb = open_db("search.db",
456                              DB_DUP|DB_DUPSORT, DB_HASH, dbflags, 0666);
457   trackdb_tagsdb = open_db("tags.db",
458                            DB_DUP|DB_DUPSORT, DB_HASH, dbflags, 0666);
459   trackdb_prefsdb = open_db("prefs.db", 0, DB_HASH, dbflags, 0666);
460   trackdb_globaldb = open_db("global.db", 0, DB_HASH, dbflags, 0666);
461   trackdb_noticeddb = open_db("noticed.db",
462                              DB_DUPSORT, DB_BTREE, dbflags, 0666);
463   if(!trackdb_existing_database) {
464     /* Stash the database version */
465     char buf[32];
466
467     assert(!(flags & TRACKDB_OPEN_FOR_UPGRADE));
468     snprintf(buf, sizeof buf, "%ld", config->dbversion);
469     trackdb_set_global("_dbversion", buf, 0);
470   }
471   D(("opened databases"));
472 }
473
474 /* close track databases */
475 void trackdb_close(void) {
476   int err;
477
478   /* sanity checks */
479   assert(opened == 1);
480   --opened;
481   if((err = trackdb_tracksdb->close(trackdb_tracksdb, 0)))
482     fatal(0, "error closing tracks.db: %s", db_strerror(err));
483   if((err = trackdb_searchdb->close(trackdb_searchdb, 0)))
484     fatal(0, "error closing search.db: %s", db_strerror(err));
485   if((err = trackdb_tagsdb->close(trackdb_tagsdb, 0)))
486     fatal(0, "error closing tags.db: %s", db_strerror(err));
487   if((err = trackdb_prefsdb->close(trackdb_prefsdb, 0)))
488     fatal(0, "error closing prefs.db: %s", db_strerror(err));
489   if((err = trackdb_globaldb->close(trackdb_globaldb, 0)))
490     fatal(0, "error closing global.db: %s", db_strerror(err));
491   if((err = trackdb_noticeddb->close(trackdb_noticeddb, 0)))
492     fatal(0, "error closing noticed.db: %s", db_strerror(err));
493   if((err = trackdb_usersdb->close(trackdb_usersdb, 0)))
494     fatal(0, "error closing users.db: %s", db_strerror(err));
495   trackdb_tracksdb = trackdb_searchdb = trackdb_prefsdb = 0;
496   trackdb_tagsdb = trackdb_globaldb = 0;
497   D(("closed databases"));
498 }
499
500 /* generic db routines *******************************************************/
501
502 /* fetch and decode a database entry.  Returns 0, DB_NOTFOUND or
503  * DB_LOCK_DEADLOCK. */
504 int trackdb_getdata(DB *db,
505                     const char *track,
506                     struct kvp **kp,
507                     DB_TXN *tid) {
508   int err;
509   DBT key, data;
510
511   switch(err = db->get(db, tid, make_key(&key, track),
512                        prepare_data(&data), 0)) {
513   case 0:
514     *kp = kvp_urldecode(data.data, data.size);
515     return 0;
516   case DB_NOTFOUND:
517     *kp = 0;
518     return err;
519   case DB_LOCK_DEADLOCK:
520     error(0, "error querying database: %s", db_strerror(err));
521     return err;
522   default:
523     fatal(0, "error querying database: %s", db_strerror(err));
524   }
525 }
526
527 /* encode and store a database entry.  Returns 0, DB_KEYEXIST or
528  * DB_LOCK_DEADLOCK. */
529 int trackdb_putdata(DB *db,
530                     const char *track,
531                     const struct kvp *k,
532                     DB_TXN *tid,
533                     u_int32_t flags) {
534   int err;
535   DBT key, data;
536
537   switch(err = db->put(db, tid, make_key(&key, track),
538                        encode_data(&data, k), flags)) {
539   case 0:
540   case DB_KEYEXIST:
541     return err;
542   case DB_LOCK_DEADLOCK:
543     error(0, "error updating database: %s", db_strerror(err));
544     return err;
545   default:
546     fatal(0, "error updating database: %s", db_strerror(err));
547   }
548 }
549
550 /** @brief Delete a database entry
551  * @param db Database
552  * @param track Key to delete
553  * @param tid Transaction ID
554  * @return 0, DB_NOTFOUND or DB_LOCK_DEADLOCK
555  */
556 int trackdb_delkey(DB *db,
557                    const char *track,
558                    DB_TXN *tid) {
559   int err;
560
561   DBT key;
562   switch(err = db->del(db, tid, make_key(&key, track), 0)) {
563   case 0:
564   case DB_NOTFOUND:
565     return 0;
566   case DB_LOCK_DEADLOCK:
567     error(0, "error updating database: %s", db_strerror(err));
568     return err;
569   default:
570     fatal(0, "error updating database: %s", db_strerror(err));
571   }
572 }
573
574 /* open a database cursor */
575 DBC *trackdb_opencursor(DB *db, DB_TXN *tid) {
576   int err;
577   DBC *c;
578
579   switch(err = db->cursor(db, tid, &c, 0)) {
580   case 0: break;
581   default: fatal(0, "error creating cursor: %s", db_strerror(err));
582   }
583   return c;
584 }
585
586 /* close a database cursor; returns 0 or DB_LOCK_DEADLOCK */
587 int trackdb_closecursor(DBC *c) {
588   int err;
589
590   if(!c) return 0;
591   switch(err = c->c_close(c)) {
592   case 0:
593     return err;
594   case DB_LOCK_DEADLOCK:
595     error(0, "error closing cursor: %s", db_strerror(err));
596     return err;
597   default:
598     fatal(0, "error closing cursor: %s", db_strerror(err));
599   }
600 }
601
602 /* delete a (key,data) pair.  Returns 0, DB_NOTFOUND or DB_LOCK_DEADLOCK. */
603 int trackdb_delkeydata(DB *db,
604                        const char *word,
605                        const char *track,
606                        DB_TXN *tid) {
607   int err;
608   DBC *c;
609   DBT key, data;
610
611   c = trackdb_opencursor(db, tid);
612   switch(err = c->c_get(c, make_key(&key, word),
613                         make_key(&data, track), DB_GET_BOTH)) {
614   case 0:
615     switch(err = c->c_del(c, 0)) {
616     case 0:
617       break;
618     case DB_KEYEMPTY:
619       err = 0;
620       break;
621     case DB_LOCK_DEADLOCK:
622       error(0, "error updating database: %s", db_strerror(err));
623       break;
624     default:
625       fatal(0, "c->c_del: %s", db_strerror(err));
626     }
627     break;
628   case DB_NOTFOUND:
629     break;
630   case DB_LOCK_DEADLOCK:
631     error(0, "error updating database: %s", db_strerror(err));
632     break;
633   default:
634     fatal(0, "c->c_get: %s", db_strerror(err));
635   }
636   if(trackdb_closecursor(c)) err = DB_LOCK_DEADLOCK;
637   return err;
638 }
639
640 /* start a transaction */
641 DB_TXN *trackdb_begin_transaction(void) {
642   DB_TXN *tid;
643   int err;
644
645   if((err = trackdb_env->txn_begin(trackdb_env, 0, &tid, 0)))
646     fatal(0, "trackdb_env->txn_begin: %s", db_strerror(err));
647   return tid;
648 }
649
650 /* abort transaction */
651 void trackdb_abort_transaction(DB_TXN *tid) {
652   int err;
653
654   if(tid)
655     if((err = tid->abort(tid)))
656       fatal(0, "tid->abort: %s", db_strerror(err));
657 }
658
659 /* commit transaction */
660 void trackdb_commit_transaction(DB_TXN *tid) {
661   int err;
662
663   if((err = tid->commit(tid, 0)))
664     fatal(0, "tid->commit: %s", db_strerror(err));
665 }
666
667 /* search/tags shared code ***************************************************/
668
669 /* comparison function used by dedupe() */
670 static int wordcmp(const void *a, const void *b) {
671   return strcmp(*(const char **)a, *(const char **)b);
672 }
673
674 /* sort and de-dupe VEC */
675 static char **dedupe(char **vec, int nvec) {
676   int m, n;
677
678   qsort(vec, nvec, sizeof (char *), wordcmp);
679   m = n = 0;
680   if(nvec) {
681     vec[m++] = vec[0];
682     for(n = 1; n < nvec; ++n)
683       if(strcmp(vec[n], vec[m - 1]))
684         vec[m++] = vec[n];
685   }
686   vec[m] = 0;
687   return vec;
688 }
689
690 /* update a key/track database.  Returns 0 or DB_DEADLOCK. */
691 static int register_word(DB *db, const char *what,
692                          const char *track, const char *word,
693                          DB_TXN *tid) {
694   int err;
695   DBT key, data;
696
697   switch(err = db->put(db, tid, make_key(&key, word),
698                        make_key(&data, track), DB_NODUPDATA)) {
699   case 0:
700   case DB_KEYEXIST:
701     return 0;
702   case DB_LOCK_DEADLOCK:
703     error(0, "error updating %s.db: %s", what, db_strerror(err));
704     return err;
705   default:
706     fatal(0, "error updating %s.db: %s", what,  db_strerror(err));
707   }
708 }
709
710 /* search primitives *********************************************************/
711
712 /* return true iff NAME is a trackname_display_ pref */
713 static int is_display_pref(const char *name) {
714   static const char prefix[] = "trackname_display_";
715   return !strncmp(name, prefix, (sizeof prefix) - 1);
716 }
717
718 /** @brief Word_Break property tailor that treats underscores as spaces */
719 static int tailor_underscore_Word_Break_Other(uint32_t c) {
720   switch(c) {
721   default:
722     return -1;
723   case 0x005F: /* LOW LINE (SPACING UNDERSCORE) */
724     return unicode_Word_Break_Other;
725   }
726 }
727
728 /** @brief Remove all combining characters in-place
729  * @param s Pointer to start of string
730  * @param ns Length of string
731  * @return New, possiblby reduced, length
732  */
733 static size_t remove_combining_chars(uint32_t *s, size_t ns) {
734   uint32_t *start = s, *t = s, *end = s + ns;
735
736   while(s < end) {
737     const uint32_t c = *s++;
738     if(!utf32_combining_class(c))
739       *t++ = c;
740   }
741   return t - start;
742 }
743
744 /** @brief Normalize and split a string using a given tailoring */
745 static void word_split(struct vector *v,
746                        const char *s,
747                        unicode_property_tailor *pt) {
748   size_t nw, nt32, i;
749   uint32_t *t32, **w32;
750
751   /* Convert to UTF-32 */
752   if(!(t32 = utf8_to_utf32(s, strlen(s), &nt32)))
753     return;
754   /* Erase case distinctions */
755   if(!(t32 = utf32_casefold_compat(t32, nt32, &nt32)))
756     return;
757   /* Drop combining characters */
758   nt32 = remove_combining_chars(t32, nt32);
759   /* Split into words, treating _ as a space */
760   w32 = utf32_word_split(t32, nt32, &nw, pt);
761   /* Convert words back to UTF-8 and append to result */
762   for(i = 0; i < nw; ++i)
763     vector_append(v, utf32_to_utf8(w32[i], utf32_len(w32[i]), 0));
764 }
765
766 /** @brief Normalize a tag
767  * @param s Tag
768  * @param ns Length of tag
769  * @return Normalized string or NULL on error
770  *
771  * The return value will be:
772  * - case-folded
773  * - have no leading or trailing space
774  * - have no combining characters
775  * - all spacing between words will be a single U+0020 SPACE
776  */
777 static char *normalize_tag(const char *s, size_t ns) {
778   uint32_t *s32, **w32;
779   size_t ns32, nw32, i;
780   struct dynstr d[1];
781
782   if(!(s32 = utf8_to_utf32(s, ns, &ns32)))
783     return 0;
784   if(!(s32 = utf32_casefold_compat(s32, ns32, &ns32))) /* ->NFKD */
785     return 0;
786   ns32 = remove_combining_chars(s32, ns32);
787   /* Split into words, no Word_Break tailoring */
788   w32 = utf32_word_split(s32, ns32, &nw32, 0);
789   /* Compose back into a string */
790   dynstr_init(d);
791   for(i = 0; i < nw32; ++i) {
792     if(i)
793       dynstr_append(d, ' ');
794     dynstr_append_string(d, utf32_to_utf8(w32[i], utf32_len(w32[i]), 0));
795   }
796   dynstr_terminate(d);
797   return d->vec;
798 }
799
800 /* compute the words of a track name */
801 static char **track_to_words(const char *track,
802                              const struct kvp *p) {
803   struct vector v;
804   const char *rootless = track_rootless(track);
805
806   if(!rootless)
807     rootless = track;                   /* bodge */
808   vector_init(&v);
809   rootless = strip_extension(rootless);
810   word_split(&v, strip_extension(rootless), tailor_underscore_Word_Break_Other);
811   for(; p; p = p->next)
812     if(is_display_pref(p->name))
813       word_split(&v, p->value, 0);
814   vector_terminate(&v);
815   return dedupe(v.vec, v.nvec);
816 }
817
818 /* return nonzero iff WORD is a stopword */
819 static int stopword(const char *word) {
820   int n;
821
822   for(n = 0; n < config->stopword.n
823         && strcmp(word, config->stopword.s[n]); ++n)
824     ;
825   return n < config->stopword.n;
826 }
827
828 /* record that WORD appears in TRACK.  Returns 0 or DB_LOCK_DEADLOCK. */
829 static int register_search_word(const char *track, const char *word,
830                                 DB_TXN *tid) {
831   if(stopword(word)) return 0;
832   return register_word(trackdb_searchdb, "search", track, word, tid);
833 }
834
835 /* Tags **********************************************************************/
836
837 /* Return nonzero if C is a valid tag character */
838 static int tagchar(int c) {
839   switch(c) {
840   case ',':
841     return 0;
842   default:
843     return c >= ' ';
844   }
845 }
846
847 /* Parse and de-dupe a tag list.  If S=0 then assumes "". */
848 char **parsetags(const char *s) {
849   const char *t;
850   struct vector v;
851
852   vector_init(&v);
853   if(s) {
854     /* skip initial separators */
855     while(*s && (!tagchar(*s) || *s == ' '))
856       ++s;
857     while(*s) {
858       /* find the extent of the tag */
859       t = s;
860       while(*s && tagchar(*s))
861         ++s;
862       /* strip trailing spaces */
863       while(s > t && s[-1] == ' ')
864         --s;
865       /* add tag to list */
866       vector_append(&v, normalize_tag(t, (size_t)(s - t)));
867       /* skip intermediate and trailing separators */
868       while(*s && (!tagchar(*s) || *s == ' '))
869         ++s;
870     }
871   }
872   vector_terminate(&v);
873   return dedupe(v.vec, v.nvec);
874 }
875
876 /* Record that TRACK has TAG.  Returns 0 or DB_LOCK_DEADLOCK. */
877 static int register_tag(const char *track, const char *tag, DB_TXN *tid) {
878   return register_word(trackdb_tagsdb, "tags", track, tag, tid);
879 }
880
881 /* aliases *******************************************************************/
882
883 /* compute the alias and store at aliasp.  Returns 0 or DB_LOCK_DEADLOCK.  If
884  * there is no alias sets *aliasp to 0. */
885 static int compute_alias(char **aliasp,
886                          const char *track,
887                          const struct kvp *p,
888                          DB_TXN *tid) {
889   struct dynstr d;
890   const char *s = config->alias, *t, *expansion, *part;
891   int c, used_db = 0, slash_prefix, err;
892   struct kvp *at;
893   const char *const root = find_track_root(track);
894
895   if(!root) {
896     /* Bodge for tracks with no root */
897     *aliasp = 0;
898     return 0;
899   }
900   dynstr_init(&d);
901   dynstr_append_string(&d, root);
902   while((c = (unsigned char)*s++)) {
903     if(c != '{') {
904       dynstr_append(&d, c);
905       continue;
906     }
907     if((slash_prefix = (*s == '/')))
908       s++;
909     t = strchr(s, '}');
910     assert(t != 0);                     /* validated at startup */
911     part = xstrndup(s, t - s);
912     expansion = getpart(track, "display", part, p, &used_db);
913     if(*expansion) {
914       if(slash_prefix) dynstr_append(&d, '/');
915       dynstr_append_string(&d, expansion);
916     }
917     s = t + 1;                          /* skip {part} */
918   }
919   /* only admit to the alias if we used the db... */
920   if(!used_db) {
921     *aliasp = 0;
922     return 0;
923   }
924   dynstr_terminate(&d);
925   /* ...and the answer differs from the original... */
926   if(!strcmp(track, d.vec)) {
927     *aliasp = 0;
928     return 0;
929   }
930   /* ...and there isn't already a different track with that name (including as
931    * an alias) */
932   switch(err = trackdb_getdata(trackdb_tracksdb, d.vec, &at, tid)) {
933   case 0:
934     if((s = kvp_get(at, "_alias_for"))
935        && !strcmp(s, track)) {
936     case DB_NOTFOUND:
937       *aliasp = d.vec;
938     } else {
939       *aliasp = 0;
940     }
941     return 0;
942   default:
943     return err;
944   }
945 }
946
947 /* get track and prefs data (if tp/pp not null pointers).  Returns 0 on
948  * success, DB_NOTFOUND if the track does not exist or DB_LOCK_DEADLOCK.
949  * Always sets the return values, even if only to null pointers. */
950 static int gettrackdata(const char *track,
951                         struct kvp **tp,
952                         struct kvp **pp,
953                         const char **actualp,
954                         unsigned flags,
955 #define GTD_NOALIAS 0x0001
956                         DB_TXN *tid) {
957   int err;
958   const char *actual = track;
959   struct kvp *t = 0, *p = 0;
960
961   if((err = trackdb_getdata(trackdb_tracksdb, track, &t, tid))) goto done;
962   if((actual = kvp_get(t, "_alias_for"))) {
963     if(flags & GTD_NOALIAS) {
964       error(0, "alias passed to gettrackdata where real path required");
965       abort();
966     }
967     if((err = trackdb_getdata(trackdb_tracksdb, actual, &t, tid))) goto done;
968   } else
969     actual = track;
970   assert(actual != 0);
971   if(pp) {
972     if((err = trackdb_getdata(trackdb_prefsdb, actual, &p, tid)) == DB_LOCK_DEADLOCK)
973       goto done;
974   }
975   err = 0;
976 done:
977   if(actualp) *actualp = actual;
978   if(tp) *tp = t;
979   if(pp) *pp = p;
980   return err;
981 }
982
983 /* trackdb_notice() **********************************************************/
984
985 /** @brief notice a possibly new track
986  * @return @c DB_NOTFOUND if new, 0 if already known
987  */
988 int trackdb_notice(const char *track,
989                    const char *path) {
990   int err;
991   DB_TXN *tid;
992
993   for(;;) {
994     tid = trackdb_begin_transaction();
995     err = trackdb_notice_tid(track, path, tid);
996     if((err == DB_LOCK_DEADLOCK)) goto fail;
997     break;
998   fail:
999     trackdb_abort_transaction(tid);
1000   }
1001   trackdb_commit_transaction(tid);
1002   return err;
1003 }
1004
1005 /** @brief notice a possibly new track
1006  * @param track NFC UTF-8 track name
1007  * @param path Raw path name
1008  * @param tid Transaction ID
1009  * @return @c DB_NOTFOUND if new, 0 if already known, @c DB_LOCK_DEADLOCK also
1010  */
1011 int trackdb_notice_tid(const char *track,
1012                        const char *path,
1013                        DB_TXN *tid) {
1014   int err, n;
1015   struct kvp *t, *a, *p;
1016   int t_changed, ret;
1017   char *alias, **w;
1018
1019   /* notice whether the tracks.db entry changes */
1020   t_changed = 0;
1021   /* get any existing tracks entry */
1022   if((err = gettrackdata(track, &t, &p, 0, 0, tid)) == DB_LOCK_DEADLOCK)
1023     return err;
1024   ret = err;                            /* 0 or DB_NOTFOUND */
1025   /* this is a real track */
1026   t_changed += kvp_set(&t, "_alias_for", 0);
1027   t_changed += kvp_set(&t, "_path", path);
1028   /* if we have an alias record it in the database */
1029   if((err = compute_alias(&alias, track, p, tid))) return err;
1030   if(alias) {
1031     /* won't overwrite someone else's alias as compute_alias() checks */
1032     D(("%s: alias %s", track, alias));
1033     a = 0;
1034     kvp_set(&a, "_alias_for", track);
1035     if((err = trackdb_putdata(trackdb_tracksdb, alias, a, tid, 0))) return err;
1036   }
1037   /* update search.db */
1038   w = track_to_words(track, p);
1039   for(n = 0; w[n]; ++n)
1040     if((err = register_search_word(track, w[n], tid)))
1041       return err;
1042   /* update tags.db */
1043   w = parsetags(kvp_get(p, "tags"));
1044   for(n = 0; w[n]; ++n)
1045     if((err = register_tag(track, w[n], tid)))
1046       return err;
1047   /* only store the tracks.db entry if it has changed */
1048   if(t_changed && (err = trackdb_putdata(trackdb_tracksdb, track, t, tid, 0)))
1049     return err;
1050   if(ret == DB_NOTFOUND) {
1051     uint32_t timestamp[2];
1052     time_t now;
1053     DBT key, data;
1054
1055     time(&now);
1056     timestamp[0] = htonl((uint64_t)now >> 32);
1057     timestamp[1] = htonl((uint32_t)now);
1058     memset(&key, 0, sizeof key);
1059     key.data = timestamp;
1060     key.size = sizeof timestamp;
1061     switch(err = trackdb_noticeddb->put(trackdb_noticeddb, tid, &key,
1062                                         make_key(&data, track), 0)) {
1063     case 0: break;
1064     case DB_LOCK_DEADLOCK: return err;
1065     default: fatal(0, "error updating noticed.db: %s", db_strerror(err));
1066     }
1067   }
1068   return ret;
1069 }
1070
1071 /* trackdb_obsolete() ********************************************************/
1072
1073 /* obsolete a track */
1074 int trackdb_obsolete(const char *track, DB_TXN *tid) {
1075   int err, n;
1076   struct kvp *p;
1077   char *alias, **w;
1078
1079   if((err = gettrackdata(track, 0, &p, 0,
1080                          GTD_NOALIAS, tid)) == DB_LOCK_DEADLOCK)
1081     return err;
1082   else if(err == DB_NOTFOUND) return 0;
1083   /* compute the alias, if any, and delete it */
1084   if((err = compute_alias(&alias, track, p, tid))) return err;
1085   if(alias) {
1086     /* if the alias points to some other track then compute_alias won't
1087      * return it */
1088     if((err = trackdb_delkey(trackdb_tracksdb, alias, tid))
1089        && err != DB_NOTFOUND)
1090       return err;
1091   }
1092   /* update search.db */
1093   w = track_to_words(track, p);
1094   for(n = 0; w[n]; ++n)
1095     if(trackdb_delkeydata(trackdb_searchdb,
1096                           w[n], track, tid) == DB_LOCK_DEADLOCK)
1097       return err;
1098   /* update tags.db */
1099   w = parsetags(kvp_get(p, "tags"));
1100   for(n = 0; w[n]; ++n)
1101     if(trackdb_delkeydata(trackdb_tagsdb,
1102                           w[n], track, tid) == DB_LOCK_DEADLOCK)
1103       return err;
1104   /* update tracks.db */
1105   if(trackdb_delkey(trackdb_tracksdb, track, tid) == DB_LOCK_DEADLOCK)
1106     return err;
1107   /* We don't delete the prefs, so they survive temporary outages of the
1108    * (possibly virtual) track filesystem */
1109   return 0;
1110 }
1111
1112 /* trackdb_stats() ***********************************************************/
1113
1114 #define H(name) { #name, offsetof(DB_HASH_STAT, name) }
1115 #define B(name) { #name, offsetof(DB_BTREE_STAT, name) }
1116
1117 static const struct statinfo {
1118   const char *name;
1119   size_t offset;
1120 } statinfo_hash[] = {
1121   H(hash_magic),
1122   H(hash_version),
1123   H(hash_nkeys),
1124   H(hash_ndata),
1125   H(hash_pagesize),
1126   H(hash_ffactor),
1127   H(hash_buckets),
1128   H(hash_free),
1129   H(hash_bfree),
1130   H(hash_bigpages),
1131   H(hash_big_bfree),
1132   H(hash_overflows),
1133   H(hash_ovfl_free),
1134   H(hash_dup),
1135   H(hash_dup_free),
1136 }, statinfo_btree[] = {
1137   B(bt_magic),
1138   B(bt_version),
1139   B(bt_nkeys),
1140   B(bt_ndata),
1141   B(bt_pagesize),
1142   B(bt_minkey),
1143   B(bt_re_len),
1144   B(bt_re_pad),
1145   B(bt_levels),
1146   B(bt_int_pg),
1147   B(bt_leaf_pg),
1148   B(bt_dup_pg),
1149   B(bt_over_pg),
1150   B(bt_free),
1151   B(bt_int_pgfree),
1152   B(bt_leaf_pgfree),
1153   B(bt_dup_pgfree),
1154   B(bt_over_pgfree),
1155 };
1156
1157 /* look up stats for DB */
1158 static int get_stats(struct vector *v,
1159                      DB *database,
1160                      const struct statinfo *si,
1161                      size_t nsi,
1162                      DB_TXN *tid) {
1163   void *sp;
1164   size_t n;
1165   char *str;
1166   int err;
1167
1168   if(database) {
1169     switch(err = database->stat(database, tid, &sp, 0)) {
1170     case 0:
1171       break;
1172     case DB_LOCK_DEADLOCK:
1173       error(0, "error querying database: %s", db_strerror(err));
1174       return err;
1175     default:
1176       fatal(0, "error querying database: %s", db_strerror(err));
1177     }
1178     for(n = 0; n < nsi; ++n) {
1179       byte_xasprintf(&str, "%s=%"PRIuMAX, si[n].name,
1180                      (uintmax_t)*(u_int32_t *)((char *)sp + si[n].offset));
1181       vector_append(v, str);
1182     }
1183   }
1184   return 0;
1185 }
1186
1187 /** @brief One entry in the search league */
1188 struct search_entry {
1189   char *word;
1190   int n;
1191 };
1192
1193 /** @brief Add a word to the search league
1194  * @param se Pointer to search league
1195  * @param count Maximum size for search league
1196  * @param nse Current size of search league
1197  * @param word New word, or NULL
1198  * @param n How often @p word appears
1199  * @return New size of search league
1200  */
1201 static int register_search_entry(struct search_entry *se,
1202                                  int count,
1203                                  int nse,
1204                                  char *word,
1205                                  int n) {
1206   int i;
1207
1208   if(word && (nse < count || n > se[nse - 1].n)) {
1209     /* Find the starting point */
1210     if(nse == count)
1211       i = nse - 1;
1212     else
1213       i = nse++;
1214     /* Find the insertion point */
1215     while(i > 0 && n > se[i - 1].n)
1216       --i;
1217     memmove(&se[i + 1], &se[i], (nse - i - 1) * sizeof *se);
1218     se[i].word = word;
1219     se[i].n = n;
1220   }
1221   return nse;
1222 }
1223
1224 /* find the top COUNT words in the search database */
1225 static int search_league(struct vector *v, int count, DB_TXN *tid) {
1226   struct search_entry *se;
1227   DBT k, d;
1228   DBC *cursor;
1229   int err, n = 0, nse = 0, i;
1230   char *word = 0;
1231   size_t wl = 0;
1232   char *str;
1233
1234   cursor = trackdb_opencursor(trackdb_searchdb, tid);
1235   se = xmalloc(count * sizeof *se);
1236   /* Walk across the whole database counting up the number of times each
1237    * word appears. */
1238   while(!(err = cursor->c_get(cursor, prepare_data(&k), prepare_data(&d),
1239                               DB_NEXT))) {
1240     if(word && wl == k.size && !strncmp(word, k.data, wl))
1241       ++n;                              /* same word again */
1242     else {
1243       nse = register_search_entry(se, count, nse, word, n);
1244       word = xstrndup(k.data, wl = k.size);
1245       n = 1;
1246     }
1247   }
1248   switch(err) {
1249   case DB_NOTFOUND:
1250     err = 0;
1251     break;
1252   case DB_LOCK_DEADLOCK:
1253     error(0, "error querying search database: %s", db_strerror(err));
1254     break;
1255   default:
1256     fatal(0, "error querying search database: %s", db_strerror(err));
1257   }
1258   if(trackdb_closecursor(cursor)) err = DB_LOCK_DEADLOCK;
1259   if(err) return err;
1260   nse = register_search_entry(se, count, nse, word, n);
1261   byte_xasprintf(&str, "Top %d search words:", nse);
1262   vector_append(v, str);
1263   for(i = 0; i < nse; ++i) {
1264     byte_xasprintf(&str, "%4d: %5d %s", i + 1, se[i].n, se[i].word);
1265     vector_append(v, str);
1266   }
1267   return 0;
1268 }
1269
1270 #define SI(what) statinfo_##what, \
1271                  sizeof statinfo_##what / sizeof (struct statinfo)
1272
1273 /* return a list of database stats */
1274 char **trackdb_stats(int *nstatsp) {
1275   DB_TXN *tid;
1276   struct vector v;
1277
1278   vector_init(&v);
1279   for(;;) {
1280     tid = trackdb_begin_transaction();
1281     v.nvec = 0;
1282     vector_append(&v, (char *)"Tracks database stats:");
1283     if(get_stats(&v, trackdb_tracksdb, SI(btree), tid)) goto fail;
1284     vector_append(&v, (char *)"");
1285     vector_append(&v, (char *)"Search database stats:");
1286     if(get_stats(&v, trackdb_searchdb, SI(hash), tid)) goto fail;
1287     vector_append(&v, (char *)"");
1288     vector_append(&v, (char *)"Prefs database stats:");
1289     if(get_stats(&v, trackdb_prefsdb, SI(hash), tid)) goto fail;
1290     vector_append(&v, (char *)"");
1291     if(search_league(&v, 10, tid)) goto fail;
1292     vector_terminate(&v);
1293     break;
1294 fail:
1295     trackdb_abort_transaction(tid);
1296   }
1297   trackdb_commit_transaction(tid);
1298   if(nstatsp) *nstatsp = v.nvec;
1299   return v.vec;
1300 }
1301
1302 struct stats_details {
1303   void (*done)(char *data, void *u);
1304   void *u;
1305   int exited;                           /* subprocess exited */
1306   int closed;                           /* pipe close */
1307   int wstat;                            /* wait status from subprocess */
1308   struct dynstr data[1];                /* data read from pipe */
1309 };
1310
1311 static void stats_complete(struct stats_details *d) {
1312   char *s;
1313
1314   if(!(d->exited && d->closed))
1315     return;
1316   byte_xasprintf(&s, "\n"
1317                  "Server stats:\n"
1318                  "track lookup cache hits: %lu\n"
1319                  "track lookup cache misses: %lu\n",
1320                  cache_files_hits,
1321                  cache_files_misses);
1322   dynstr_append_string(d->data, s);
1323   dynstr_terminate(d->data);
1324   d->done(d->data->vec, d->u);
1325 }
1326
1327 static int stats_finished(ev_source attribute((unused)) *ev,
1328                           pid_t attribute((unused)) pid,
1329                           int status,
1330                           const struct rusage attribute((unused)) *rusage,
1331                           void *u) {
1332   struct stats_details *const d = u;
1333
1334   d->exited = 1;
1335   if(status)
1336     error(0, "disorder-stats %s", wstat(status));
1337   stats_complete(d);
1338   return 0;
1339 }
1340
1341 static int stats_read(ev_source attribute((unused)) *ev,
1342                       ev_reader *reader,
1343                       void *ptr,
1344                       size_t bytes,
1345                       int eof,
1346                       void *u) {
1347   struct stats_details *const d = u;
1348
1349   dynstr_append_bytes(d->data, ptr, bytes);
1350   ev_reader_consume(reader, bytes);
1351   if(eof)
1352     d->closed = 1;
1353   stats_complete(d);
1354   return 0;
1355 }
1356
1357 static int stats_error(ev_source attribute((unused)) *ev,
1358                        int errno_value,
1359                        void *u) {
1360   struct stats_details *const d = u;
1361
1362   error(errno_value, "error reading from pipe to disorder-stats");
1363   d->closed = 1;
1364   stats_complete(d);
1365   return 0;
1366 }
1367
1368 void trackdb_stats_subprocess(ev_source *ev,
1369                               void (*done)(char *data, void *u),
1370                               void *u) {
1371   int p[2];
1372   pid_t pid;
1373   struct stats_details *d = xmalloc(sizeof *d);
1374
1375   dynstr_init(d->data);
1376   d->done = done;
1377   d->u = u;
1378   xpipe(p);
1379   pid = subprogram(ev, p[1], "disorder-stats", (char *)0);
1380   xclose(p[1]);
1381   ev_child(ev, pid, 0, stats_finished, d);
1382   ev_reader_new(ev, p[0], stats_read, stats_error, d, "disorder-stats reader");
1383 }
1384
1385 /* set a pref (remove if value=0) */
1386 int trackdb_set(const char *track,
1387                 const char *name,
1388                 const char *value) {
1389   struct kvp *t, *p, *a;
1390   DB_TXN *tid;
1391   int err, cmp;
1392   char *oldalias, *newalias, **oldtags = 0, **newtags;
1393
1394   if(value) {
1395     /* TODO: if value matches default then set value=0 */
1396   }
1397
1398   for(;;) {
1399     tid = trackdb_begin_transaction();
1400     if((err = gettrackdata(track, &t, &p, 0,
1401                            0, tid)) == DB_LOCK_DEADLOCK)
1402       goto fail;
1403     if(err == DB_NOTFOUND) break;
1404     if(name[0] == '_') {
1405       if(kvp_set(&t, name, value))
1406         if(trackdb_putdata(trackdb_tracksdb, track, t, tid, 0))
1407           goto fail;
1408     } else {
1409       /* get the old alias name */
1410       if(compute_alias(&oldalias, track, p, tid)) goto fail;
1411       /* get the old tags */
1412       if(!strcmp(name, "tags"))
1413         oldtags = parsetags(kvp_get(p, "tags"));
1414       /* set the value */
1415       if(kvp_set(&p, name, value))
1416         if(trackdb_putdata(trackdb_prefsdb, track, p, tid, 0))
1417           goto fail;
1418       /* compute the new alias name */
1419       if((err = compute_alias(&newalias, track, p, tid))) goto fail;
1420       /* check whether alias has changed */
1421       if(!(oldalias == newalias
1422            || (oldalias && newalias && !strcmp(oldalias, newalias)))) {
1423         /* adjust alias records to fit change */
1424         if(oldalias
1425            && trackdb_delkey(trackdb_tracksdb, oldalias, tid) == DB_LOCK_DEADLOCK)
1426           goto fail;
1427         if(newalias) {
1428           a = 0;
1429           kvp_set(&a, "_alias_for", track);
1430           if(trackdb_putdata(trackdb_tracksdb, newalias, a, tid, 0)) goto fail;
1431         }
1432       }
1433       /* check whether tags have changed */
1434       if(!strcmp(name, "tags")) {
1435         newtags = parsetags(value);
1436         while(*oldtags || *newtags) {
1437           if(*oldtags && *newtags) {
1438             cmp = strcmp(*oldtags, *newtags);
1439             if(!cmp) {
1440               /* keeping this tag */
1441               ++oldtags;
1442               ++newtags;
1443             } else if(cmp < 0)
1444               /* old tag fits into a gap in the new list, so delete old */
1445               goto delete_old;
1446             else
1447               /* new tag fits into a gap in the old list, so insert new */
1448               goto insert_new;
1449           } else if(*oldtags) {
1450             /* we've run out of new tags, so remaining old ones are to be
1451              * deleted */
1452           delete_old:
1453             if(trackdb_delkeydata(trackdb_tagsdb,
1454                                   *oldtags, track, tid) == DB_LOCK_DEADLOCK)
1455               goto fail;
1456             ++oldtags;
1457           } else {
1458             /* we've run out of old tags, so remainig new ones are to be
1459              * inserted */
1460           insert_new:
1461             if(register_tag(track, *newtags, tid)) goto fail;
1462             ++newtags;
1463           }
1464         }
1465       }
1466     }
1467     err = 0;
1468     break;
1469 fail:
1470     trackdb_abort_transaction(tid);
1471   }
1472   trackdb_commit_transaction(tid);
1473   return err == 0 ? 0 : -1;
1474 }
1475
1476 /* get a pref */
1477 const char *trackdb_get(const char *track,
1478                         const char *name) {
1479   return kvp_get(trackdb_get_all(track), name);
1480 }
1481
1482 /* get all prefs as a 0-terminated array */
1483 struct kvp *trackdb_get_all(const char *track) {
1484   struct kvp *t, *p, **pp;
1485   DB_TXN *tid;
1486
1487   for(;;) {
1488     tid = trackdb_begin_transaction();
1489     if(gettrackdata(track, &t, &p, 0, 0, tid) == DB_LOCK_DEADLOCK)
1490       goto fail;
1491     break;
1492 fail:
1493     trackdb_abort_transaction(tid);
1494   }
1495   trackdb_commit_transaction(tid);
1496   for(pp = &p; *pp; pp = &(*pp)->next)
1497     ;
1498   *pp = t;
1499   return p;
1500 }
1501
1502 /* resolve alias */
1503 const char *trackdb_resolve(const char *track) {
1504   DB_TXN *tid;
1505   const char *actual;
1506
1507   for(;;) {
1508     tid = trackdb_begin_transaction();
1509     if(gettrackdata(track, 0, 0, &actual, 0, tid) == DB_LOCK_DEADLOCK)
1510       goto fail;
1511     break;
1512 fail:
1513     trackdb_abort_transaction(tid);
1514   }
1515   trackdb_commit_transaction(tid);
1516   return actual;
1517 }
1518
1519 int trackdb_isalias(const char *track) {
1520   const char *actual = trackdb_resolve(track);
1521
1522   return strcmp(actual, track);
1523 }
1524
1525 /* test whether a track exists (perhaps an alias) */
1526 int trackdb_exists(const char *track) {
1527   DB_TXN *tid;
1528   int err;
1529
1530   for(;;) {
1531     tid = trackdb_begin_transaction();
1532     /* unusually, here we want the return value */
1533     if((err = gettrackdata(track, 0, 0, 0, 0, tid)) == DB_LOCK_DEADLOCK)
1534       goto fail;
1535     break;
1536 fail:
1537     trackdb_abort_transaction(tid);
1538   }
1539   trackdb_commit_transaction(tid);
1540   return (err == 0);
1541 }
1542
1543 /* return the list of tags */
1544 char **trackdb_alltags(void) {
1545   int e;
1546   struct vector v[1];
1547
1548   vector_init(v);
1549   WITH_TRANSACTION(trackdb_listkeys(trackdb_tagsdb, v, tid));
1550   return v->vec;
1551 }
1552
1553 /** @brief List all the keys in @p db
1554  * @param db Database
1555  * @param v Vector to store keys in
1556  * @param tid Transaction ID
1557  * @return 0 or DB_LOCK_DEADLOCK
1558  */
1559 int trackdb_listkeys(DB *db, struct vector *v, DB_TXN *tid) {
1560   int e;
1561   DBT k, d;
1562   DBC *const c = trackdb_opencursor(db, tid);
1563
1564   v->nvec = 0;
1565   memset(&k, 0, sizeof k);
1566   while(!(e = c->c_get(c, &k, prepare_data(&d), DB_NEXT_NODUP)))
1567     vector_append(v, xstrndup(k.data, k.size));
1568   switch(e) {
1569   case DB_NOTFOUND:
1570     break;
1571   case DB_LOCK_DEADLOCK:
1572     return e;
1573   default:
1574     fatal(0, "c->c_get: %s", db_strerror(e));
1575   }
1576   if((e = trackdb_closecursor(c)))
1577     return e;
1578   vector_terminate(v);
1579   return 0;
1580 }
1581
1582 /* return 1 iff sorted tag lists A and B have at least one member in common */
1583 int tag_intersection(char **a, char **b) {
1584   int cmp;
1585
1586   /* Same sort of logic as trackdb_set() above */
1587   while(*a && *b) {
1588     if(!(cmp = strcmp(*a, *b))) return 1;
1589     else if(cmp < 0) ++a;
1590     else ++b;
1591   }
1592   return 0;
1593 }
1594
1595 static pid_t choose_pid = -1;
1596 static int choose_fd;
1597 static random_callback *choose_callback;
1598 static struct dynstr choose_output;
1599 static unsigned choose_complete;
1600 static int choose_status;
1601 #define CHOOSE_RUNNING 1
1602 #define CHOOSE_READING 2
1603
1604 static void choose_finished(ev_source *ev, unsigned which) {
1605   choose_complete |= which;
1606   if(choose_complete != (CHOOSE_RUNNING|CHOOSE_READING))
1607     return;
1608   choose_pid = -1;
1609   if(choose_status == 0 && choose_output.nvec > 0) {
1610     dynstr_terminate(&choose_output);
1611     choose_callback(ev, xstrdup(choose_output.vec));
1612   } else
1613     choose_callback(ev, 0);
1614 }
1615
1616 /** @brief Called when @c disorder-choose terminates */
1617 static int choose_exited(ev_source *ev,
1618                          pid_t attribute((unused)) pid,
1619                          int status,
1620                          const struct rusage attribute((unused)) *rusage,
1621                          void attribute((unused)) *u) {
1622   if(status)
1623     error(0, "disorder-choose %s", wstat(status));
1624   choose_status = status;
1625   choose_finished(ev, CHOOSE_RUNNING);
1626   return 0;
1627 }
1628
1629 /** @brief Called with data from @c disorder-choose pipe */
1630 static int choose_readable(ev_source *ev,
1631                            ev_reader *reader,
1632                            void *ptr,
1633                            size_t bytes,
1634                            int eof,
1635                            void attribute((unused)) *u) {
1636   dynstr_append_bytes(&choose_output, ptr, bytes);
1637   ev_reader_consume(reader, bytes);
1638   if(eof)
1639     choose_finished(ev, CHOOSE_READING);
1640   return 0;
1641 }
1642
1643 static int choose_read_error(ev_source *ev,
1644                              int errno_value,
1645                              void attribute((unused)) *u) {
1646   error(errno_value, "error reading disorder-choose pipe");
1647   choose_finished(ev, CHOOSE_READING);
1648   return 0;
1649 }
1650
1651 /** @brief Request a random track
1652  * @param ev Event source
1653  * @param callback Called with random track or NULL
1654  * @return 0 if a request was initiated, else -1
1655  *
1656  * Initiates a random track choice.  @p callback will later be called back with
1657  * the choice (or NULL on error).  If a choice is already underway then -1 is
1658  * returned and there will be no additional callback.
1659  *
1660  * The caller shouldn't assume that the track returned actually exists (it
1661  * might be removed between the choice and the callback, or between being added
1662  * to the queue and being played).
1663  */
1664 int trackdb_request_random(ev_source *ev,
1665                            random_callback *callback) {
1666   int p[2];
1667   
1668   if(choose_pid != -1)
1669     return -1;                          /* don't run concurrent chooses */
1670   xpipe(p);
1671   cloexec(p[0]);
1672   choose_pid = subprogram(ev, p[1], "disorder-choose", (char *)0);
1673   choose_fd = p[0];
1674   xclose(p[1]);
1675   choose_callback = callback;
1676   choose_output.nvec = 0;
1677   choose_complete = 0;
1678   ev_reader_new(ev, p[0], choose_readable, choose_read_error, 0,
1679                 "disorder-choose reader"); /* owns p[0] */
1680   ev_child(ev, choose_pid, 0, choose_exited, 0); /* owns the subprocess */
1681   return 0;
1682 }
1683
1684 /* get a track name given the prefs.  Set *used_db to 1 if we got the answer
1685  * from the prefs. */
1686 static const char *getpart(const char *track,
1687                            const char *context,
1688                            const char *part,
1689                            const struct kvp *p,
1690                            int *used_db) {
1691   const char *result;
1692   char *pref;
1693
1694   byte_xasprintf(&pref, "trackname_%s_%s", context, part);
1695   if((result = kvp_get(p, pref)))
1696     *used_db = 1;
1697   else
1698     result = trackname_part(track, context, part);
1699   assert(result != 0);
1700   return result;
1701 }
1702
1703 /* get a track name part, like trackname_part(), but taking the database into
1704  * account. */
1705 const char *trackdb_getpart(const char *track,
1706                             const char *context,
1707                             const char *part) {
1708   struct kvp *p;
1709   DB_TXN *tid;
1710   char *pref;
1711   const char *actual;
1712   int used_db, err;
1713
1714   /* construct the full pref */
1715   byte_xasprintf(&pref, "trackname_%s_%s", context, part);
1716   for(;;) {
1717     tid = trackdb_begin_transaction();
1718     if((err = gettrackdata(track, 0, &p, &actual, 0, tid)) == DB_LOCK_DEADLOCK)
1719       goto fail;
1720     break;
1721 fail:
1722     trackdb_abort_transaction(tid);
1723   }
1724   trackdb_commit_transaction(tid);
1725   return getpart(actual, context, part, p, &used_db);
1726 }
1727
1728 /* get the raw path name for @track@ (might be an alias) */
1729 const char *trackdb_rawpath(const char *track) {
1730   DB_TXN *tid;
1731   struct kvp *t;
1732   const char *path;
1733
1734   for(;;) {
1735     tid = trackdb_begin_transaction();
1736     if(gettrackdata(track, &t, 0, 0, 0, tid) == DB_LOCK_DEADLOCK)
1737       goto fail;
1738     break;
1739 fail:
1740     trackdb_abort_transaction(tid);
1741   }
1742   trackdb_commit_transaction(tid);
1743   if(!(path = kvp_get(t, "_path"))) path = track;
1744   return path;
1745 }
1746
1747 /* trackdb_list **************************************************************/
1748
1749 /* this is incredibly ugly, sorry, perhaps it will be rewritten to be actually
1750  * readable at some point */
1751
1752 /* return true if the basename of TRACK[0..TL-1], as defined by DL, matches RE.
1753  * If RE is a null pointer then it matches everything. */
1754 static int track_matches(size_t dl, const char *track, size_t tl,
1755                          const pcre *re) {
1756   int ovec[3], rc;
1757
1758   if(!re)
1759     return 1;
1760   track += dl + 1;
1761   tl -= (dl + 1);
1762   switch(rc = pcre_exec(re, 0, track, tl, 0, 0, ovec, 3)) {
1763   case PCRE_ERROR_NOMATCH: return 0;
1764   default:
1765     if(rc < 0) {
1766       error(0, "pcre_exec returned %d, subject '%s'", rc, track);
1767       return 0;
1768     }
1769     return 1;
1770   }
1771 }
1772
1773 static int do_list(struct vector *v, const char *dir,
1774                    enum trackdb_listable what, const pcre *re, DB_TXN *tid) {
1775   DBC *cursor;
1776   DBT k, d;
1777   size_t dl;
1778   char *ptr;
1779   int err;
1780   size_t l, last_dir_len = 0;
1781   char *last_dir = 0, *track, *alias;
1782   struct kvp *p;
1783
1784   dl = strlen(dir);
1785   cursor = trackdb_opencursor(trackdb_tracksdb, tid);
1786   make_key(&k, dir);
1787   prepare_data(&d);
1788   /* find the first key >= dir */
1789   err = cursor->c_get(cursor, &k, &d, DB_SET_RANGE);
1790   /* keep going while we're dealing with <dir/anything> */
1791   while(err == 0
1792         && k.size > dl
1793         && ((char *)k.data)[dl] == '/'
1794         && !memcmp(k.data, dir, dl)) {
1795     ptr = memchr((char *)k.data + dl + 1, '/', k.size - (dl + 1));
1796     if(ptr) {
1797       /* we have <dir/component/anything>, so <dir/component> is a directory */
1798       l = ptr - (char *)k.data;
1799       if(what & trackdb_directories)
1800         if(!(last_dir
1801              && l == last_dir_len
1802              && !memcmp(last_dir, k.data, l))) {
1803           last_dir = xstrndup(k.data, last_dir_len = l);
1804           if(track_matches(dl, k.data, l, re))
1805             vector_append(v, last_dir);
1806         }
1807     } else {
1808       /* found a plain file */
1809       if((what & trackdb_files)) {
1810         track = xstrndup(k.data, k.size);
1811         if((err = trackdb_getdata(trackdb_prefsdb,
1812                                   track, &p, tid)) == DB_LOCK_DEADLOCK)
1813           goto deadlocked;
1814         /* if this file has an alias in the same directory then we skip it */
1815         if((err = compute_alias(&alias, track, p, tid)))
1816           goto deadlocked;
1817         if(!(alias && !strcmp(d_dirname(alias), d_dirname(track))))
1818           if(track_matches(dl, k.data, k.size, re))
1819             vector_append(v, track);
1820       }
1821     }
1822     err = cursor->c_get(cursor, &k, &d, DB_NEXT);
1823   }
1824   switch(err) {
1825   case 0:
1826     break;
1827   case DB_NOTFOUND:
1828     err = 0;
1829     break;
1830   case DB_LOCK_DEADLOCK:
1831     error(0, "error querying database: %s", db_strerror(err));
1832     break;
1833   default:
1834     fatal(0, "error querying database: %s", db_strerror(err));
1835   }
1836 deadlocked:
1837   if(trackdb_closecursor(cursor)) err = DB_LOCK_DEADLOCK;
1838   return err;
1839 }
1840
1841 /* return the directories or files below @dir@ */
1842 char **trackdb_list(const char *dir, int *np, enum trackdb_listable what,
1843                     const pcre *re) {
1844   DB_TXN *tid;
1845   int n;
1846   struct vector v;
1847
1848   vector_init(&v);
1849   for(;;) {
1850     tid = trackdb_begin_transaction();
1851     v.nvec = 0;
1852     if(dir) {
1853       if(do_list(&v, dir, what, re, tid))
1854         goto fail;
1855     } else {
1856       for(n = 0; n < config->collection.n; ++n)
1857         if(do_list(&v, config->collection.s[n].root, what, re, tid))
1858           goto fail;
1859     }
1860     break;
1861 fail:
1862     trackdb_abort_transaction(tid);
1863   }
1864   trackdb_commit_transaction(tid);
1865   vector_terminate(&v);
1866   if(np)
1867     *np = v.nvec;
1868   return v.vec;
1869 }
1870
1871 /* If S is tag:something, return something.  Else return 0. */
1872 static const char *checktag(const char *s) {
1873   if(!strncmp(s, "tag:", 4))
1874     return s + 4;
1875   else
1876     return 0;
1877 }
1878
1879 /* return a list of tracks containing all of the words given.  If you
1880  * ask for only stopwords you get no tracks. */
1881 char **trackdb_search(char **wordlist, int nwordlist, int *ntracks) {
1882   const char **w, *best = 0, *tag;
1883   char **twords, **tags;
1884   char *istag;
1885   int i, j, n, err, what;
1886   DBC *cursor = 0;
1887   DBT k, d;
1888   struct vector u, v;
1889   DB_TXN *tid;
1890   struct kvp *p;
1891   int ntags = 0;
1892   DB *db;
1893   const char *dbname;
1894
1895   *ntracks = 0;                         /* for early returns */
1896   /* normalize all the words */
1897   w = xmalloc(nwordlist * sizeof (char *));
1898   istag = xmalloc_noptr(nwordlist);
1899   for(n = 0; n < nwordlist; ++n) {
1900     uint32_t *w32;
1901     size_t nw32;
1902
1903     w[n] = utf8_casefold_compat(wordlist[n], strlen(wordlist[n]), 0);
1904     if(checktag(w[n])) {
1905       ++ntags;         /* count up tags */
1906       /* Normalize the tag */
1907       w[n] = normalize_tag(w[n] + 4, strlen(w[n] + 4));
1908       istag[n] = 1;
1909     } else {
1910       /* Normalize the search term by removing combining characters */
1911       if(!(w32 = utf8_to_utf32(w[n], strlen(w[n]), &nw32)))
1912         return 0;
1913       nw32 = remove_combining_chars(w32, nw32);
1914       if(!(w[n] = utf32_to_utf8(w32, nw32, 0)))
1915         return 0;
1916       istag[n] = 0;
1917     }
1918   }
1919   /* find the longest non-stopword */
1920   for(n = 0; n < nwordlist; ++n)
1921     if(!istag[n] && !stopword(w[n]))
1922       if(!best || strlen(w[n]) > strlen(best))
1923         best = w[n];
1924   /* TODO: we should at least in principal be able to identify the word or tag
1925    * with the least matches in log time, and choose that as our primary search
1926    * term. */
1927   if(ntags && !best) {
1928     /* Only tags are listed.  We limit to the first and narrow down with the
1929      * rest. */
1930     best = istag[0] ? w[0] : 0;
1931     db = trackdb_tagsdb;
1932     dbname = "tags";
1933   } else if(best) {
1934     /* We can limit to some word. */
1935     db = trackdb_searchdb;
1936     dbname = "search";
1937   } else {
1938     /* Only stopwords */
1939     return 0;
1940   }
1941   vector_init(&u);
1942   vector_init(&v);
1943   for(;;) {
1944     tid = trackdb_begin_transaction();
1945     /* find all the tracks that have that word */
1946     make_key(&k, best);
1947     prepare_data(&d);
1948     what = DB_SET;
1949     v.nvec = 0;
1950     cursor = trackdb_opencursor(db, tid);
1951     while(!(err = cursor->c_get(cursor, &k, &d, what))) {
1952       vector_append(&v, xstrndup(d.data, d.size));
1953       what = DB_NEXT_DUP;
1954     }
1955     switch(err) {
1956     case DB_NOTFOUND:
1957       err = 0;
1958       break;
1959     case DB_LOCK_DEADLOCK:
1960       error(0, "error querying %s database: %s", dbname, db_strerror(err));
1961       break;
1962     default:
1963       fatal(0, "error querying %s database: %s", dbname, db_strerror(err));
1964     }
1965     if(trackdb_closecursor(cursor)) err = DB_LOCK_DEADLOCK;
1966     cursor = 0;
1967     /* do a naive search over that (hopefuly fairly small) list of tracks */
1968     u.nvec = 0;
1969     for(n = 0; n < v.nvec; ++n) {
1970       if((err = gettrackdata(v.vec[n], 0, &p, 0, 0, tid) == DB_LOCK_DEADLOCK))
1971         goto fail;
1972       else if(err) {
1973         error(0, "track %s unexpected error: %s", v.vec[n], db_strerror(err));
1974         continue;
1975       }
1976       twords = track_to_words(v.vec[n], p);
1977       tags = parsetags(kvp_get(p, "tags"));
1978       for(i = 0; i < nwordlist; ++i) {
1979         if(istag[i]) {
1980           tag = w[i];
1981           /* Track must have this tag */
1982           for(j = 0; tags[j]; ++j)
1983             if(!strcmp(tag, tags[j])) break; /* tag found */
1984           if(!tags[j]) break;           /* tag not found */
1985         } else {
1986           /* Track must contain this word */
1987           for(j = 0; twords[j]; ++j)
1988             if(!strcmp(w[i], twords[j])) break; /* word found */
1989           if(!twords[j]) break;         /* word not found */
1990         }
1991       }
1992       if(i >= nwordlist)                /* all words found */
1993         vector_append(&u, v.vec[n]);
1994     }
1995     break;
1996   fail:
1997     trackdb_closecursor(cursor);
1998     cursor = 0;
1999     trackdb_abort_transaction(tid);
2000     info("retrying search");
2001   }
2002   trackdb_commit_transaction(tid);
2003   vector_terminate(&u);
2004   if(ntracks)
2005     *ntracks = u.nvec;
2006   return u.vec;
2007 }
2008
2009 /* trackdb_scan **************************************************************/
2010
2011 int trackdb_scan(const char *root,
2012                  int (*callback)(const char *track,
2013                                  struct kvp *data,
2014                                  struct kvp *prefs,
2015                                  void *u,
2016                                  DB_TXN *tid),
2017                  void *u,
2018                  DB_TXN *tid) {
2019   DBC *cursor;
2020   DBT k, d, pd;
2021   const size_t root_len = root ? strlen(root) : 0;
2022   int err, cberr;
2023   struct kvp *data, *prefs;
2024   const char *track;
2025
2026   cursor = trackdb_opencursor(trackdb_tracksdb, tid);
2027   if(root)
2028     err = cursor->c_get(cursor, make_key(&k, root), prepare_data(&d),
2029                         DB_SET_RANGE);
2030   else {
2031     memset(&k, 0, sizeof k);
2032     err = cursor->c_get(cursor, &k, prepare_data(&d),
2033                         DB_FIRST);
2034   }
2035   while(!err) {
2036     if(!root
2037        || (k.size > root_len
2038            && !strncmp(k.data, root, root_len)
2039            && ((char *)k.data)[root_len] == '/')) {
2040       data = kvp_urldecode(d.data, d.size);
2041       if(kvp_get(data, "_path")) {
2042         track = xstrndup(k.data, k.size);
2043         /* TODO: trackdb_prefsdb is currently a DB_HASH.  This means we have to
2044          * do a lookup for every single track.  In fact this is quite quick:
2045          * with around 10,000 tracks a complete scan is around 0.3s on my
2046          * 2.2GHz Athlon.  However, if it were a DB_BTREE, we could do the same
2047          * linear walk as we already do over trackdb_tracksdb, and probably get
2048          * even higher performance.  That would require upgrade logic to
2049          * translate old databases though.
2050          */
2051         switch(err = trackdb_prefsdb->get(trackdb_prefsdb, tid, &k,
2052                                           prepare_data(&pd), 0)) {
2053         case 0:
2054           prefs = kvp_urldecode(pd.data, pd.size);
2055           break;
2056         case DB_NOTFOUND:
2057           prefs = 0;
2058           break;
2059         case DB_LOCK_DEADLOCK:
2060           error(0, "getting prefs: %s", db_strerror(err));
2061           trackdb_closecursor(cursor);
2062           return err;
2063         default:
2064           fatal(0, "getting prefs: %s", db_strerror(err));
2065         }
2066         /* Advance to the next track before the callback so that the callback
2067          * may safely delete the track */
2068         err = cursor->c_get(cursor, &k, &d, DB_NEXT);
2069         if((cberr = callback(track, data, prefs, u, tid))) {
2070           err = cberr;
2071           break;
2072         }
2073       } else
2074         err = cursor->c_get(cursor, &k, &d, DB_NEXT);
2075     } else
2076       break;
2077   }
2078   trackdb_closecursor(cursor);
2079   switch(err) {
2080   case EINTR:
2081     return err;
2082   case 0:
2083   case DB_NOTFOUND:
2084     return 0;
2085   case DB_LOCK_DEADLOCK:
2086     error(0, "c->c_get: %s", db_strerror(err));
2087     return err;
2088   default:
2089     fatal(0, "c->c_get: %s", db_strerror(err));
2090   }
2091 }
2092
2093 /* trackdb_rescan ************************************************************/
2094
2095 /* called when the rescanner terminates */
2096 static int reap_rescan(ev_source attribute((unused)) *ev,
2097                        pid_t pid,
2098                        int status,
2099                        const struct rusage attribute((unused)) *rusage,
2100                        void attribute((unused)) *u) {
2101   if(pid == rescan_pid) rescan_pid = -1;
2102   if(status)
2103     error(0, RESCAN": %s", wstat(status));
2104   else
2105     D((RESCAN" terminated: %s", wstat(status)));
2106   /* Our cache of file lookups is out of date now */
2107   cache_clean(&cache_files_type);
2108   eventlog("rescanned", (char *)0);
2109   return 0;
2110 }
2111
2112 /** @brief Initiate a rescan
2113  * @param ev Event loop or 0 to block
2114  * @param recheck 1 to recheck lengths, 0 to suppress check
2115  */
2116 void trackdb_rescan(ev_source *ev, int recheck) {
2117   int w;
2118
2119   if(rescan_pid != -1) {
2120     error(0, "rescan already underway");
2121     return;
2122   }
2123   rescan_pid = subprogram(ev, -1, RESCAN,
2124                           recheck ? "--check" : "--no-check",
2125                           (char *)0);
2126   if(ev) {
2127     ev_child(ev, rescan_pid, 0, reap_rescan, 0);
2128     D(("started rescanner"));
2129   } else {
2130     /* This is the first rescan, we block until it is complete */
2131     while(waitpid(rescan_pid, &w, 0) < 0 && errno == EINTR)
2132       ;
2133     reap_rescan(0, rescan_pid, w, 0, 0);
2134   }
2135 }
2136
2137 int trackdb_rescan_cancel(void) {
2138   if(rescan_pid == -1) return 0;
2139   if(kill(rescan_pid, SIGTERM) < 0)
2140     fatal(errno, "error killing rescanner");
2141   rescan_pid = -1;
2142   return 1;
2143 }
2144
2145 /* global prefs **************************************************************/
2146
2147 void trackdb_set_global(const char *name,
2148                         const char *value,
2149                         const char *who) {
2150   DB_TXN *tid;
2151   int err;
2152   int state;
2153
2154   for(;;) {
2155     tid = trackdb_begin_transaction();
2156     if(!(err = trackdb_set_global_tid(name, value, tid)))
2157       break;
2158     trackdb_abort_transaction(tid);
2159   }
2160   trackdb_commit_transaction(tid);
2161   /* log important state changes */
2162   if(!strcmp(name, "playing")) {
2163     state = !value || !strcmp(value, "yes");
2164     info("playing %s by %s",
2165          state ? "enabled" : "disabled",
2166          who ? who : "-");
2167     eventlog("state", state ? "enable_play" : "disable_play", (char *)0);
2168   }
2169   if(!strcmp(name, "random-play")) {
2170     state = !value || !strcmp(value, "yes");
2171     info("random play %s by %s",
2172          state ? "enabled" : "disabled",
2173          who ? who : "-");
2174     eventlog("state", state ? "enable_random" : "disable_random", (char *)0);
2175   }
2176 }
2177
2178 int trackdb_set_global_tid(const char *name,
2179                            const char *value,
2180                            DB_TXN *tid) {
2181   DBT k, d;
2182   int err;
2183
2184   memset(&k, 0, sizeof k);
2185   memset(&d, 0, sizeof d);
2186   k.data = (void *)name;
2187   k.size = strlen(name);
2188   if(value) {
2189     d.data = (void *)value;
2190     d.size = strlen(value);
2191   }
2192   if(value)
2193     err = trackdb_globaldb->put(trackdb_globaldb, tid, &k, &d, 0);
2194   else
2195     err = trackdb_globaldb->del(trackdb_globaldb, tid, &k, 0);
2196   if(err == DB_LOCK_DEADLOCK) return err;
2197   if(err)
2198     fatal(0, "error updating database: %s", db_strerror(err));
2199   return 0;
2200 }
2201
2202 const char *trackdb_get_global(const char *name) {
2203   DB_TXN *tid;
2204   int err;
2205   const char *r;
2206
2207   for(;;) {
2208     tid = trackdb_begin_transaction();
2209     if(!(err = trackdb_get_global_tid(name, tid, &r)))
2210       break;
2211     trackdb_abort_transaction(tid);
2212   }
2213   trackdb_commit_transaction(tid);
2214   return r;
2215 }
2216
2217 int trackdb_get_global_tid(const char *name,
2218                            DB_TXN *tid,
2219                            const char **rp) {
2220   DBT k, d;
2221   int err;
2222
2223   memset(&k, 0, sizeof k);
2224   k.data = (void *)name;
2225   k.size = strlen(name);
2226   switch(err = trackdb_globaldb->get(trackdb_globaldb, tid, &k,
2227                                      prepare_data(&d), 0)) {
2228   case 0:
2229     *rp = xstrndup(d.data, d.size);
2230     return 0;
2231   case DB_NOTFOUND:
2232     *rp = 0;
2233     return 0;
2234   case DB_LOCK_DEADLOCK:
2235     return err;
2236   default:
2237     fatal(0, "error reading database: %s", db_strerror(err));
2238   }
2239 }
2240
2241 /** @brief Retrieve the most recently added tracks
2242  * @param ntracksp Where to put count, or 0
2243  * @param maxtracks Maximum number of tracks to retrieve
2244  * @return null-terminated array of track names
2245  *
2246  * The most recently added track is first in the array.
2247  */
2248 char **trackdb_new(int *ntracksp,
2249                    int maxtracks) {
2250   DB_TXN *tid;
2251   char **tracks;
2252
2253   for(;;) {
2254     tid = trackdb_begin_transaction();
2255     tracks = trackdb_new_tid(ntracksp, maxtracks, tid);
2256     if(tracks)
2257       break;
2258     trackdb_abort_transaction(tid);
2259   }
2260   trackdb_commit_transaction(tid);
2261   return tracks;
2262 }
2263
2264 /** @brief Retrieve the most recently added tracks
2265  * @param ntracksp Where to put count, or 0
2266  * @param maxtracks Maximum number of tracks to retrieve, or 0 for all
2267  * @param tid Transaction ID
2268  * @return null-terminated array of track names, or NULL on deadlock
2269  *
2270  * The most recently added track is first in the array.
2271  */
2272 static char **trackdb_new_tid(int *ntracksp,
2273                               int maxtracks,
2274                               DB_TXN *tid) {
2275   DBC *c;
2276   DBT k, d;
2277   int err = 0;
2278   struct vector tracks[1];
2279
2280   vector_init(tracks);
2281   c = trackdb_opencursor(trackdb_noticeddb, tid);
2282   while((maxtracks <= 0 || tracks->nvec < maxtracks)
2283         && !(err = c->c_get(c, prepare_data(&k), prepare_data(&d), DB_PREV)))
2284     vector_append(tracks, xstrndup(d.data, d.size));
2285   switch(err) {
2286   case 0:                               /* hit maxtracks */
2287   case DB_NOTFOUND:                     /* ran out of tracks */
2288     break;
2289   case DB_LOCK_DEADLOCK:
2290     trackdb_closecursor(c);
2291     return 0;
2292   default:
2293     fatal(0, "error reading noticed.db: %s", db_strerror(err));
2294   }
2295   if((err = trackdb_closecursor(c)))
2296     return 0;                           /* deadlock */
2297   vector_terminate(tracks);
2298   if(ntracksp)
2299     *ntracksp = tracks->nvec;
2300   return tracks->vec;
2301 }
2302
2303 /** @brief Expire noticed.db
2304  * @param earliest Earliest timestamp to keep
2305  */
2306 void trackdb_expire_noticed(time_t earliest) {
2307   DB_TXN *tid;
2308
2309   for(;;) {
2310     tid = trackdb_begin_transaction();
2311     if(!trackdb_expire_noticed_tid(earliest, tid))
2312       break;
2313     trackdb_abort_transaction(tid);
2314   }
2315   trackdb_commit_transaction(tid);
2316 }
2317
2318 /** @brief Expire noticed.db
2319  * @param earliest Earliest timestamp to keep
2320  * @param tid Transaction ID
2321  * @return 0 or DB_LOCK_DEADLOCK
2322  */
2323 static int trackdb_expire_noticed_tid(time_t earliest, DB_TXN *tid) {
2324   DBC *c;
2325   DBT k, d;
2326   int err = 0, ret;
2327   time_t when;
2328   uint32_t *kk;
2329   int count = 0;
2330
2331   c = trackdb_opencursor(trackdb_noticeddb, tid);
2332   while(!(err = c->c_get(c, prepare_data(&k), prepare_data(&d), DB_NEXT))) {
2333     kk = k.data;
2334     when = (time_t)(((uint64_t)ntohl(kk[0]) << 32) + ntohl(kk[1]));
2335     if(when >= earliest)
2336       break;
2337     if((err = c->c_del(c, 0))) {
2338       if(err != DB_LOCK_DEADLOCK)
2339         fatal(0, "error deleting expired noticed.db entry: %s",
2340               db_strerror(err));
2341       break;
2342     }
2343     ++count;
2344   }
2345   if(err == DB_NOTFOUND)
2346     err = 0;
2347   if(err && err != DB_LOCK_DEADLOCK)
2348     fatal(0, "error expiring noticed.db: %s", db_strerror(err));
2349   ret = err;
2350   if((err = trackdb_closecursor(c))) {
2351     if(err != DB_LOCK_DEADLOCK)
2352       fatal(0, "error closing cursor: %s", db_strerror(err));
2353     ret = err;
2354   }
2355   if(!ret && count)
2356     info("expired %d tracks from noticed.db", count);
2357   return ret;
2358 }
2359
2360 /* tidying up ****************************************************************/
2361
2362 void trackdb_gc(void) {
2363   int err;
2364   char **logfiles;
2365
2366   if((err = trackdb_env->txn_checkpoint(trackdb_env,
2367                                         config->checkpoint_kbyte,
2368                                         config->checkpoint_min,
2369                                         0)))
2370     fatal(0, "trackdb_env->txn_checkpoint: %s", db_strerror(err));
2371   if((err = trackdb_env->log_archive(trackdb_env, &logfiles, DB_ARCH_REMOVE)))
2372     fatal(0, "trackdb_env->log_archive: %s", db_strerror(err));
2373   /* This makes catastrophic recovery impossible.  However, the user can still
2374    * preserve the important data by using disorder-dump to snapshot their
2375    * prefs, and later to restore it.  This is likely to have much small
2376    * long-term storage requirements than record the db logfiles. */
2377 }
2378
2379 /* user database *************************************************************/
2380
2381 /** @brief Return true if @p user is trusted */
2382 static int trusted(const char *user) {
2383   int n;
2384
2385   for(n = 0; (n < config->trust.n
2386               && strcmp(config->trust.s[n], user)); ++n)
2387     ;
2388   return n < config->trust.n;
2389 }
2390
2391 /** @brief Return non-zero for a valid username
2392  *
2393  * Currently we only allow the letters and digits in ASCII.  We could be more
2394  * liberal than this but it is a nice simple test.  It is critical that
2395  * semicolons are never allowed.
2396  */
2397 static int valid_username(const char *user) {
2398   if(!*user)
2399     return 0;
2400   while(*user) {
2401     const uint8_t c = *user++;
2402     /* For now we are very strict */
2403     if((c >= 'a' && c <= 'z')
2404        || (c >= 'A' && c <= 'Z')
2405        || (c >= '0' && c <= '9'))
2406       /* ok */;
2407     else
2408       return 0;
2409   }
2410   return 1;
2411 }
2412
2413 /** @brief Add a user */
2414 static int create_user(const char *user,
2415                        const char *password,
2416                        const char *rights,
2417                        const char *email,
2418                        const char *confirmation,
2419                        DB_TXN *tid,
2420                        uint32_t flags) {
2421   struct kvp *k = 0;
2422   char s[64];
2423
2424   /* sanity check user */
2425   if(!valid_username(user)) {
2426     error(0, "invalid username '%s'", user);
2427     return -1;
2428   }
2429   if(parse_rights(rights, 0, 1)) {
2430     error(0, "invalid rights string");
2431     return -1;
2432   }
2433   /* data for this user */
2434   if(password)
2435     kvp_set(&k, "password", password);
2436   kvp_set(&k, "rights", rights);
2437   if(email)
2438     kvp_set(&k, "email", email);
2439   if(confirmation)
2440     kvp_set(&k, "confirmation", confirmation);
2441   snprintf(s, sizeof s, "%jd", (intmax_t)time(0));
2442   kvp_set(&k, "created", s);
2443   return trackdb_putdata(trackdb_usersdb, user, k, tid, flags);
2444 }
2445
2446 /** @brief Add one pre-existing user */
2447 static int one_old_user(const char *user, const char *password,
2448                         DB_TXN *tid) {
2449   const char *rights;
2450
2451   /* www-data doesn't get added */
2452   if(!strcmp(user, "www-data")) {
2453     info("not adding www-data to user database");
2454     return 0;
2455   }
2456   /* pick rights */
2457   if(!strcmp(user, "root"))
2458     rights = "all";
2459   else if(trusted(user)) {
2460     rights_type r;
2461
2462     parse_rights(config->default_rights, &r, 1);
2463     r &= ~(rights_type)(RIGHT_SCRATCH__MASK|RIGHT_MOVE__MASK|RIGHT_REMOVE__MASK);
2464     r |= (RIGHT_ADMIN|RIGHT_RESCAN
2465           |RIGHT_SCRATCH_ANY|RIGHT_MOVE_ANY|RIGHT_REMOVE_ANY);
2466     rights = rights_string(r);
2467   } else
2468     rights = config->default_rights;
2469   return create_user(user, password, rights, 0/*email*/, 0/*confirmation*/,
2470                      tid, DB_NOOVERWRITE);
2471 }
2472
2473 static int trackdb_old_users_tid(DB_TXN *tid) {
2474   int n;
2475
2476   for(n = 0; n < config->allow.n; ++n) {
2477     switch(one_old_user(config->allow.s[n].s[0], config->allow.s[n].s[1],
2478                         tid)) {
2479     case 0:
2480       info("created user %s from 'allow' directive", config->allow.s[n].s[0]);
2481       break;
2482     case DB_KEYEXIST:
2483       error(0, "user %s already exists, delete 'allow' directive",
2484             config->allow.s[n].s[0]);
2485           /* This won't ever become fatal - eventually 'allow' will be
2486            * disabled. */
2487       break;
2488     case DB_LOCK_DEADLOCK:
2489       return DB_LOCK_DEADLOCK;
2490     }
2491   }
2492   return 0;
2493 }
2494
2495 /** @brief Read old 'allow' directives and copy them to the users database */
2496 void trackdb_old_users(void) {
2497   int e;
2498
2499   if(config->allow.n)
2500     WITH_TRANSACTION(trackdb_old_users_tid(tid));
2501 }
2502
2503 /** @brief Create a root user in the user database if there is none */
2504 void trackdb_create_root(void) {
2505   int e;
2506   uint8_t pwbin[12];
2507   char *pw;
2508
2509   /* Choose a new root password */
2510   gcry_randomize(pwbin, sizeof pwbin, GCRY_STRONG_RANDOM);
2511   pw = mime_to_base64(pwbin, sizeof pwbin);
2512   /* Create the root user if it does not exist */
2513   WITH_TRANSACTION(create_user("root", pw, "all",
2514                                0/*email*/, 0/*confirmation*/,
2515                                tid, DB_NOOVERWRITE));
2516   if(e == 0)
2517     info("created root user");
2518 }
2519
2520 /** @brief Find a user's password from the database
2521  * @param user Username
2522  * @return Password or NULL
2523  *
2524  * Only works if running as a user that can read the database!
2525  *
2526  * If the user exists but has no password, "" is returned.
2527  */
2528 const char *trackdb_get_password(const char *user) {
2529   int e;
2530   struct kvp *k;
2531   const char *password;
2532
2533   WITH_TRANSACTION(trackdb_getdata(trackdb_usersdb, user, &k, tid));
2534   if(e)
2535     return 0;
2536   password = kvp_get(k, "password");
2537   return password ? password : "";
2538 }
2539
2540 /** @brief Add a new user
2541  * @param user Username
2542  * @param password Password or NULL
2543  * @param rights Initial rights
2544  * @param email Email address or NULL
2545  * @param confirmation Confirmation string or NULL
2546  * @return 0 on success, non-0 on error
2547  */
2548 int trackdb_adduser(const char *user,
2549                     const char *password,
2550                     const char *rights,
2551                     const char *email,
2552                     const char *confirmation) {
2553   int e;
2554
2555   WITH_TRANSACTION(create_user(user, password, rights, email, confirmation,
2556                                tid, DB_NOOVERWRITE));
2557   if(e) {
2558     error(0, "cannot create user '%s' because they already exist", user);
2559     return -1;
2560   } else {
2561     if(email)
2562       info("created user '%s' with rights '%s' and email address '%s'",
2563            user, rights, email);
2564     else
2565       info("created user '%s' with rights '%s'", user, rights);
2566     return 0;
2567   }
2568 }
2569
2570 /** @brief Delete a user
2571  * @param user User to delete
2572  * @return 0 on success, non-0 if the user didn't exist anyway
2573  */
2574 int trackdb_deluser(const char *user) {
2575   int e;
2576
2577   WITH_TRANSACTION(trackdb_delkey(trackdb_usersdb, user, tid));
2578   if(e) {
2579     error(0, "cannot delete user '%s' because they do not exist", user);
2580     return -1;
2581   }
2582   info("deleted user '%s'", user);
2583   return 0;
2584 }
2585
2586 /** @brief Get user information
2587  * @param user User to query
2588  * @return Linked list of user information or NULL if user does not exist
2589  *
2590  * Every user has at least a @c rights entry so NULL can be used to mean no
2591  * such user safely.
2592  */
2593 struct kvp *trackdb_getuserinfo(const char *user) {
2594   int e;
2595   struct kvp *k;
2596
2597   WITH_TRANSACTION(trackdb_getdata(trackdb_usersdb, user, &k, tid));
2598   if(e)
2599     return 0;
2600   else
2601     return k;
2602 }
2603
2604 /** @brief Edit user information
2605  * @param user User to edit
2606  * @param key Key to change
2607  * @param value Value to set, or NULL to remove
2608  * @param tid Transaction ID
2609  * @return 0, DB_LOCK_DEADLOCK or DB_NOTFOUND
2610  */
2611 static int trackdb_edituserinfo_tid(const char *user, const char *key,
2612                                     const char *value, DB_TXN *tid) {
2613   struct kvp *k;
2614   int e;
2615
2616   if((e = trackdb_getdata(trackdb_usersdb, user, &k, tid)))
2617     return e;
2618   if(!kvp_set(&k, key, value))
2619     return 0;                           /* no change */
2620   return trackdb_putdata(trackdb_usersdb, user, k, tid, 0);
2621 }
2622
2623 /** @brief Edit user information
2624  * @param user User to edit
2625  * @param key Key to change
2626  * @param value Value to set, or NULL to remove
2627  * @return 0 on success, non-0 on error
2628  */
2629 int trackdb_edituserinfo(const char *user,
2630                          const char *key, const char *value) {
2631   int e;
2632
2633   if(!strcmp(key, "rights")) {
2634     if(!value) {
2635       error(0, "cannot remove 'rights' key from user '%s'", user);
2636       return -1;
2637     }
2638     if(parse_rights(value, 0, 1)) {
2639       error(0, "invalid rights string");
2640       return -1;
2641     }
2642   } else if(!strcmp(key, "email")) {
2643     if(*value) {
2644       if(!strchr(value, '@')) {
2645         error(0, "invalid email address '%s' for user '%s'", user, value);
2646         return -1;
2647       }
2648     } else
2649       value = 0;                        /* no email -> remove key */
2650   } else if(!strcmp(key, "created")) {
2651     error(0, "cannot change creation date for user '%s'", user);
2652     return -1;
2653   } else if(strcmp(key, "password")
2654             && !strcmp(key, "confirmation")) {
2655     error(0, "unknown user info key '%s' for user '%s'", key, user);
2656     return -1;
2657   }
2658   WITH_TRANSACTION(trackdb_edituserinfo_tid(user, key, value, tid));
2659   if(e) {
2660     error(0, "unknown user '%s'", user);
2661     return -1;
2662   } else
2663     return 0;
2664 }
2665
2666 /** @brief List all users
2667  * @return NULL-terminated list of users
2668  */
2669 char **trackdb_listusers(void) {
2670   int e;
2671   struct vector v[1];
2672
2673   vector_init(v);
2674   WITH_TRANSACTION(trackdb_listkeys(trackdb_usersdb, v, tid));
2675   return v->vec;
2676 }
2677
2678 /** @brief Confirm a user registration
2679  * @param user Username
2680  * @param confirmation Confirmation string
2681  * @param rightsp Where to put user rights
2682  * @param tid Transaction ID
2683  * @return 0 on success, non-0 on error
2684  */
2685 static int trackdb_confirm_tid(const char *user, const char *confirmation,
2686                                rights_type *rightsp,
2687                                DB_TXN *tid) {
2688   const char *stored_confirmation;
2689   struct kvp *k;
2690   int e;
2691   const char *rights;
2692   
2693   if((e = trackdb_getdata(trackdb_usersdb, user, &k, tid)))
2694     return e;
2695   if(!(stored_confirmation = kvp_get(k, "confirmation"))) {
2696     error(0, "already confirmed user '%s'", user);
2697     /* DB claims -30,800 to -30,999 so -1 should be a safe bet */
2698     return -1;
2699   }
2700   if(!(rights = kvp_get(k, "rights"))) {
2701     error(0, "no rights for unconfirmed user '%s'", user);
2702     return -1;
2703   }
2704   if(parse_rights(rights, rightsp, 1))
2705     return -1;
2706   if(strcmp(confirmation, stored_confirmation)) {
2707     error(0, "wrong confirmation string for user '%s'", user);
2708     return -1;
2709   }
2710   /* 'sall good */
2711   kvp_set(&k, "confirmation", 0);
2712   return trackdb_putdata(trackdb_usersdb, user, k, tid, 0);
2713 }
2714
2715 /** @brief Confirm a user registration
2716  * @param user Username
2717  * @param confirmation Confirmation string
2718  * @param rightsp Where to put user rights
2719  * @return 0 on success, non-0 on error
2720  */
2721 int trackdb_confirm(const char *user, const char *confirmation,
2722                     rights_type *rightsp) {
2723   int e;
2724
2725   WITH_TRANSACTION(trackdb_confirm_tid(user, confirmation, rightsp, tid));
2726   switch(e) {
2727   case 0:
2728     info("registration confirmed for user '%s'", user);
2729     return 0;
2730   case DB_NOTFOUND:
2731     error(0, "confirmation for nonexistent user '%s'", user);
2732     return -1;
2733   default:                              /* already reported */
2734     return -1;
2735   }
2736 }
2737
2738 /*
2739 Local Variables:
2740 c-basic-offset:2
2741 comment-column:40
2742 fill-column:79
2743 indent-tabs-mode:nil
2744 End:
2745 */