* This file is getting in desparate need of splitting up...
*/
-#include <config.h>
-#include "types.h"
+#include "common.h"
-#include <string.h>
-#include <stdio.h>
#include <db.h>
#include <sys/socket.h>
#include <pcre.h>
-#include <assert.h>
#include <unistd.h>
#include <errno.h>
#include <stddef.h>
#include <sys/resource.h>
#include <time.h>
#include <arpa/inet.h>
-#include <sys/wait.h>
#include <dirent.h>
#include <sys/stat.h>
#include <gcrypt.h>
*/
DB *trackdb_noticeddb; /* when track noticed */
+/** @brief The schedule database
+ *
+ * - Keys are ID strings, generated at random
+ * - Values are encoded key-value pairs
+ * - There can be more than one value per key
+ * - Data cannot be reconstructed
+ *
+ * See @ref server/schedule.c for further information.
+ */
+DB *trackdb_scheduledb;
+
/** @brief The user database
* - Keys are usernames
* - Values are encoded key-value pairs
trackdb_globaldb = open_db("global.db", 0, DB_HASH, dbflags, 0666);
trackdb_noticeddb = open_db("noticed.db",
DB_DUPSORT, DB_BTREE, dbflags, 0666);
+ trackdb_scheduledb = open_db("schedule.db", 0, DB_HASH, dbflags, 0666);
if(!trackdb_existing_database) {
/* Stash the database version */
char buf[32];
fatal(0, "error closing global.db: %s", db_strerror(err));
if((err = trackdb_noticeddb->close(trackdb_noticeddb, 0)))
fatal(0, "error closing noticed.db: %s", db_strerror(err));
+ if((err = trackdb_scheduledb->close(trackdb_scheduledb, 0)))
+ fatal(0, "error closing schedule.db: %s", db_strerror(err));
if((err = trackdb_usersdb->close(trackdb_usersdb, 0)))
fatal(0, "error closing users.db: %s", db_strerror(err));
trackdb_tracksdb = trackdb_searchdb = trackdb_prefsdb = 0;
char *ptr;
int err;
size_t l, last_dir_len = 0;
- char *last_dir = 0, *track, *alias;
+ char *last_dir = 0, *track;
struct kvp *p;
dl = strlen(dir);
if((err = trackdb_getdata(trackdb_prefsdb,
track, &p, tid)) == DB_LOCK_DEADLOCK)
goto deadlocked;
+ /* There's an awkward question here...
+ *
+ * If a track shares a directory with its alias then we could
+ * do one of three things:
+ * - report both. Looks ridiculuous in most UIs.
+ * - report just the alias. Remarkably inconvenient to write
+ * UI code for!
+ * - report just the real name. Ugly if the UI doesn't prettify
+ * names via the name parts.
+ */
+#if 1
+ /* If this file is an alias for a track in the same directory then we
+ * skip it */
+ struct kvp *t = kvp_urldecode(d.data, d.size);
+ const char *alias_target = kvp_get(t, "_alias_for");
+ if(!(alias_target
+ && !strcmp(d_dirname(alias_target),
+ d_dirname(track))))
+ if(track_matches(dl, k.data, k.size, re))
+ vector_append(v, track);
+#else
/* if this file has an alias in the same directory then we skip it */
+ char *alias;
if((err = compute_alias(&alias, track, p, tid)))
goto deadlocked;
if(!(alias && !strcmp(d_dirname(alias), d_dirname(track))))
if(track_matches(dl, k.data, k.size, re))
vector_append(v, track);
+#endif
}
}
err = cursor->c_get(cursor, &k, &d, DB_NEXT);
/* trackdb_rescan ************************************************************/
+/** @brief Node in the list of rescan-complete callbacks */
+struct rescanned_node {
+ struct rescanned_node *next;
+ void (*rescanned)(void *ru);
+ void *ru;
+};
+
+/** @brief List of rescan-complete callbacks */
+static struct rescanned_node *rescanned_list;
+
+/** @brief Add a rescan completion callback */
+void trackdb_add_rescanned(void (*rescanned)(void *ru),
+ void *ru) {
+ if(rescanned) {
+ struct rescanned_node *n = xmalloc(sizeof *n);
+ n->next = rescanned_list;
+ n->rescanned = rescanned;
+ n->ru = ru;
+ rescanned_list = n;
+ }
+}
+
/* called when the rescanner terminates */
static int reap_rescan(ev_source attribute((unused)) *ev,
pid_t pid,
/* Our cache of file lookups is out of date now */
cache_clean(&cache_files_type);
eventlog("rescanned", (char *)0);
+ /* Call rescanned callbacks */
+ while(rescanned_list) {
+ void (*rescanned)(void *u_) = rescanned_list->rescanned;
+ void *ru = rescanned_list->ru;
+
+ rescanned_list = rescanned_list->next;
+ rescanned(ru);
+ }
return 0;
}
/** @brief Initiate a rescan
* @param ev Event loop or 0 to block
* @param recheck 1 to recheck lengths, 0 to suppress check
+ * @param rescanned Called on completion (if not NULL)
+ * @param u Passed to @p rescanned
*/
-void trackdb_rescan(ev_source *ev, int recheck) {
+void trackdb_rescan(ev_source *ev, int recheck,
+ void (*rescanned)(void *ru),
+ void *ru) {
int w;
if(rescan_pid != -1) {
+ trackdb_add_rescanned(rescanned, ru);
error(0, "rescan already underway");
return;
}
rescan_pid = subprogram(ev, -1, RESCAN,
recheck ? "--check" : "--no-check",
(char *)0);
+ trackdb_add_rescanned(rescanned, ru);
if(ev) {
ev_child(ev, rescan_pid, 0, reap_rescan, 0);
D(("started rescanner"));
return 1;
}
+/** @brief Return true if a rescan is underway */
+int trackdb_rescan_underway(void) {
+ return rescan_pid != -1;
+}
+
/* global prefs **************************************************************/
void trackdb_set_global(const char *name,