*/
DB *trackdb_usersdb;
+/** @brief The playlists database
+ * - Keys are playlist names
+ * - Values are encoded key-value pairs
+ * - Data is user data and cannot be reconstructed
+ */
+DB *trackdb_playlistsdb;
+
static pid_t db_deadlock_pid = -1; /* deadlock manager PID */
static pid_t rescan_pid = -1; /* rescanner PID */
static int initialized, opened; /* state */
+/** @brief Current stats subprocess PIDs */
+static hash *stats_pids;
+
/* comparison function for keys */
static int compare(DB attribute((unused)) *db_,
const DBT *a, const DBT *b) {
}
/* close environment */
-void trackdb_deinit(void) {
+void trackdb_deinit(ev_source *ev) {
int err;
/* sanity checks */
/* wait for the rescanner to finish */
while(waitpid(rescan_pid, &err, 0) == -1 && errno == EINTR)
;
+ if(ev)
+ ev_child_cancel(ev, rescan_pid);
+ rescan_pid = -1;
+ }
+
+ if(stats_pids) {
+ char **ks = hash_keys(stats_pids);
+
+ while(*ks) {
+ pid_t pid = atoi(*ks++);
+ if(kill(pid, SIGTERM) < 0)
+ fatal(errno, "error killing stats subprocess");
+ while(waitpid(pid, &err, 0) == -1 && errno == EINTR)
+ ;
+ if(ev)
+ ev_child_cancel(ev, pid);
+ pid = -1;
+ }
}
- /* TODO kill any stats subprocesses */
-
- /* 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;
+ if(db_deadlock_pid != -1) {
+ /* shut down the deadlock manager */
+ if(kill(db_deadlock_pid, SIGTERM) < 0)
+ fatal(errno, "error killing deadlock manager");
+ while(waitpid(db_deadlock_pid, &err, 0) == -1 && errno == EINTR)
+ ;
+ if(ev)
+ ev_child_cancel(ev, db_deadlock_pid);
+ db_deadlock_pid = -1;
+ }
D(("deinitialized database environment"));
}
trackdb_noticeddb = open_db("noticed.db",
DB_DUPSORT, DB_BTREE, dbflags, 0666);
trackdb_scheduledb = open_db("schedule.db", 0, DB_HASH, dbflags, 0666);
+ trackdb_playlistsdb = open_db("playlists.db", 0, DB_HASH, dbflags, 0666);
if(!trackdb_existing_database && !(flags & TRACKDB_READ_ONLY)) {
/* Stash the database version */
char buf[32];
CLOSE("noticed.db", trackdb_noticeddb);
CLOSE("schedule.db", trackdb_scheduledb);
CLOSE("users.db", trackdb_usersdb);
+ CLOSE("playlists.db", trackdb_playlistsdb);
D(("closed databases"));
}
/* this is a real track */
t_changed += kvp_set(&t, "_alias_for", 0);
t_changed += kvp_set(&t, "_path", path);
- time(&now);
+ xtime(&now);
if(ret == DB_NOTFOUND) {
/* It's a new track; record the time */
byte_xasprintf(¬iced, "%lld", (long long)now);
}
static int stats_finished(ev_source attribute((unused)) *ev,
- pid_t attribute((unused)) pid,
+ pid_t pid,
int status,
const struct rusage attribute((unused)) *rusage,
void *u) {
if(status)
error(0, "disorder-stats %s", wstat(status));
stats_complete(d);
+ char *k;
+ byte_xasprintf(&k, "%lu", (unsigned long)pid);
+ hash_remove(stats_pids, k);
return 0;
}
if(!ev_reader_new(ev, p[0], stats_read, stats_error, d,
"disorder-stats reader"))
fatal(0, "ev_reader_new for disorder-stats reader failed");
+ /* Remember the PID */
+ if(!stats_pids)
+ stats_pids = hash_new(1);
+ char *k;
+ byte_xasprintf(&k, "%lu", (unsigned long)pid);
+ hash_add(stats_pids, k, "", HASH_INSERT);
}
/** @brief Parse a track name part preference
* Currently we only allow the letters and digits in ASCII. We could be more
* liberal than this but it is a nice simple test. It is critical that
* semicolons are never allowed.
+ *
+ * NB also used by playlist_parse_name() to validate playlist names!
*/
-static int valid_username(const char *user) {
+int valid_username(const char *user) {
if(!*user)
return 0;
while(*user) {
kvp_set(&k, "email", email);
if(confirmation)
kvp_set(&k, "confirmation", confirmation);
- snprintf(s, sizeof s, "%jd", (intmax_t)time(0));
+ snprintf(s, sizeof s, "%jd", (intmax_t)xtime(0));
kvp_set(&k, "created", s);
return trackdb_putdata(trackdb_usersdb, user, k, tid, flags);
}