X-Git-Url: https://git.distorted.org.uk/~mdw/disorder/blobdiff_plain/3036551977f003d9ef3bc15e682201272f400840..91c9324af56bd50592e818fd4763e6cdcea68857:/lib/trackdb.c diff --git a/lib/trackdb.c b/lib/trackdb.c index 8239a69..ce00d1a 100644 --- a/lib/trackdb.c +++ b/lib/trackdb.c @@ -1,6 +1,6 @@ /* * This file is part of DisOrder - * Copyright (C) 2005, 2006, 2007 Richard Kettlewell + * Copyright (C) 2005-2008 Richard Kettlewell * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -259,10 +259,23 @@ static int reap_db_deadlock(ev_source attribute((unused)) *ev, return 0; } -static pid_t subprogram(ev_source *ev, const char *prog, - int outputfd) { +static pid_t subprogram(ev_source *ev, int outputfd, const char *prog, + ...) { pid_t pid; - + va_list ap; + const char *args[1024], **argp, *a; + + argp = args; + *argp++ = prog; + *argp++ = "--config"; + *argp++ = configfile; + *argp++ = debugging ? "--debug" : "--no-debug"; + *argp++ = log_default == &log_syslog ? "--syslog" : "--no-syslog"; + va_start(ap, prog); + while((a = va_arg(ap, const char *))) + *argp++ = a; + va_end(ap); + *argp = 0; /* If we're in the background then trap subprocess stdout/stderr */ if(!(pid = xfork())) { exitfn = _exit; @@ -279,10 +292,7 @@ static pid_t subprogram(ev_source *ev, const char *prog, /* If we were negatively niced, undo it. We don't bother checking for * error, it's not that important. */ setpriority(PRIO_PROCESS, 0, 0); - execlp(prog, prog, "--config", configfile, - debugging ? "--debug" : "--no-debug", - log_default == &log_syslog ? "--syslog" : "--no-syslog", - (char *)0); + execvp(prog, (char **)args); fatal(errno, "error invoking %s", prog); } return pid; @@ -291,7 +301,7 @@ static pid_t subprogram(ev_source *ev, const char *prog, /* start deadlock manager */ void trackdb_master(ev_source *ev) { assert(db_deadlock_pid == -1); - db_deadlock_pid = subprogram(ev, DEADLOCK, -1); + db_deadlock_pid = subprogram(ev, -1, DEADLOCK, (char *)0); ev_child(ev, db_deadlock_pid, 0, reap_db_deadlock, 0); D(("started deadlock manager")); } @@ -308,10 +318,18 @@ void trackdb_deinit(void) { if((err = trackdb_env->close(trackdb_env, 0))) fatal(0, "trackdb_env->close: %s", db_strerror(err)); - if(rescan_pid != -1 && kill(rescan_pid, SIGTERM) < 0) - fatal(errno, "error killing rescanner"); + if(rescan_pid != -1) { + /* shut down the rescanner */ + if(kill(rescan_pid, SIGTERM) < 0) + fatal(errno, "error killing rescanner"); + /* wait for the rescanner to finish */ + while(waitpid(rescan_pid, &err, 0) == -1 && errno == EINTR) + ; + } + + /* TODO kill any stats subprocesses */ - /* terminate the deadlock manager */ + /* finally terminate the deadlock manager */ if(db_deadlock_pid != -1 && kill(db_deadlock_pid, SIGTERM) < 0) fatal(errno, "error killing deadlock manager"); db_deadlock_pid = -1; @@ -396,7 +414,7 @@ void trackdb_open(int flags) { /* This database needs upgrading */ info("invoking disorder-dbupgrade to upgrade from %ld to %ld", oldversion, config->dbversion); - pid = subprogram(0, "disorder-dbupgrade", -1); + pid = subprogram(0, -1, "disorder-dbupgrade", (char *)0); while(waitpid(pid, &err, 0) == -1 && errno == EINTR) ; if(err) @@ -423,6 +441,9 @@ void trackdb_open(int flags) { trackdb_existing_database = 0; } /* open the databases */ + if(!(trackdb_usersdb = open_db("users.db", + 0, DB_HASH, dbflags, 0600))) + fatal(0, "cannot open users.db"); trackdb_tracksdb = open_db("tracks.db", DB_RECNUM, DB_BTREE, dbflags, 0666); trackdb_searchdb = open_db("search.db", @@ -433,8 +454,6 @@ void trackdb_open(int flags) { trackdb_globaldb = open_db("global.db", 0, DB_HASH, dbflags, 0666); trackdb_noticeddb = open_db("noticed.db", DB_DUPSORT, DB_BTREE, dbflags, 0666); - trackdb_usersdb = open_db("users.db", - 0, DB_HASH, dbflags, 0600); if(!trackdb_existing_database) { /* Stash the database version */ char buf[32]; @@ -820,7 +839,7 @@ static int tagchar(int c) { } /* Parse and de-dupe a tag list. If S=0 then assumes "". */ -static char **parsetags(const char *s) { +char **parsetags(const char *s) { const char *t; struct vector v; @@ -1353,7 +1372,7 @@ void trackdb_stats_subprocess(ev_source *ev, d->done = done; d->u = u; xpipe(p); - pid = subprogram(ev, "disorder-stats", p[1]); + pid = subprogram(ev, p[1], "disorder-stats", (char *)0); xclose(p[1]); ev_child(ev, pid, 0, stats_finished, d); ev_reader_new(ev, p[0], stats_read, stats_error, d, "disorder-stats reader"); @@ -1558,7 +1577,7 @@ int trackdb_listkeys(DB *db, struct vector *v, DB_TXN *tid) { } /* return 1 iff sorted tag lists A and B have at least one member in common */ -static int tag_intersection(char **a, char **b) { +int tag_intersection(char **a, char **b) { int cmp; /* Same sort of logic as trackdb_set() above */ @@ -2072,15 +2091,16 @@ char **trackdb_search(char **wordlist, int nwordlist, int *ntracks) { int trackdb_scan(const char *root, int (*callback)(const char *track, struct kvp *data, + struct kvp *prefs, void *u, DB_TXN *tid), void *u, DB_TXN *tid) { DBC *cursor; - DBT k, d; + DBT k, d, pd; const size_t root_len = root ? strlen(root) : 0; int err, cberr; - struct kvp *data; + struct kvp *data, *prefs; const char *track; cursor = trackdb_opencursor(trackdb_tracksdb, tid); @@ -2100,10 +2120,33 @@ int trackdb_scan(const char *root, data = kvp_urldecode(d.data, d.size); if(kvp_get(data, "_path")) { track = xstrndup(k.data, k.size); + /* TODO: trackdb_prefsdb is currently a DB_HASH. This means we have to + * do a lookup for every single track. In fact this is quite quick: + * with around 10,000 tracks a complete scan is around 0.3s on my + * 2.2GHz Athlon. However, if it were a DB_BTREE, we could do the same + * linear walk as we already do over trackdb_tracksdb, and probably get + * even higher performance. That would require upgrade logic to + * translate old databases though. + */ + switch(err = trackdb_prefsdb->get(trackdb_prefsdb, tid, &k, + prepare_data(&pd), 0)) { + case 0: + prefs = kvp_urldecode(pd.data, pd.size); + break; + case DB_NOTFOUND: + prefs = 0; + break; + case DB_LOCK_DEADLOCK: + error(0, "getting prefs: %s", db_strerror(err)); + trackdb_closecursor(cursor); + return err; + default: + fatal(0, "getting prefs: %s", db_strerror(err)); + } /* Advance to the next track before the callback so that the callback * may safely delete the track */ err = cursor->c_get(cursor, &k, &d, DB_NEXT); - if((cberr = callback(track, data, u, tid))) { + if((cberr = callback(track, data, prefs, u, tid))) { err = cberr; break; } @@ -2146,14 +2189,20 @@ static int reap_rescan(ev_source attribute((unused)) *ev, return 0; } -void trackdb_rescan(ev_source *ev) { +/** @brief Initiate a rescan + * @param ev Event loop or 0 to block + * @param recheck 1 to recheck lengths, 0 to suppress check + */ +void trackdb_rescan(ev_source *ev, int recheck) { int w; if(rescan_pid != -1) { error(0, "rescan already underway"); return; } - rescan_pid = subprogram(ev, RESCAN, -1); + rescan_pid = subprogram(ev, -1, RESCAN, + recheck ? "--check" : "--no-check", + (char *)0); if(ev) { ev_child(ev, rescan_pid, 0, reap_rescan, 0); D(("started rescanner")); @@ -2588,7 +2637,7 @@ int trackdb_adduser(const char *user, WITH_TRANSACTION(create_user(user, password, rights, email, confirmation, tid, DB_NOOVERWRITE)); if(e) { - error(0, "cannot created user '%s' because they already exist", user); + error(0, "cannot create user '%s' because they already exist", user); return -1; } else { if(email)