X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/disorder/blobdiff_plain/45f99508335e76dd0b29a1787dbd0b9c684ca0e4..561d077df8d0494aeeaf3f30d847f489336cbfd6:/lib/trackdb.c diff --git a/lib/trackdb.c b/lib/trackdb.c index 042bbef..1b16445 100644 --- a/lib/trackdb.c +++ b/lib/trackdb.c @@ -82,8 +82,14 @@ int trackdb_existing_database; /* setup and teardown ********************************************************/ -static const char *home; /* home had better not change */ -DB_ENV *trackdb_env; /* db environment */ +/** @brief Database home directory + * + * All database files live below here. It had better never change. + */ +static const char *home; + +/** @brief Database environment */ +DB_ENV *trackdb_env; /** @brief The tracks database * - Keys are UTF-8(NFC(unicode(path name))) @@ -164,23 +170,48 @@ DB *trackdb_usersdb; */ 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 Deadlock manager PID */ +static pid_t db_deadlock_pid = -1; + +/** @brief Rescanner PID */ +static pid_t rescan_pid = -1; + +/** @brief Set when the database environment exists */ +static int initialized; + +/** @brief Set when databases are open */ +static int opened; /** @brief Current stats subprocess PIDs */ static hash *stats_pids; +/** @brief PID of current random track chooser (disorder-choose) */ static pid_t choose_pid = -1; + +/** @brief Our end of pipe from disorder-choose */ static int choose_fd; + +/** @brief Callback to supply random track to */ static random_callback *choose_callback; + +/** @brief Accumulator for output from disorder-choose */ static struct dynstr choose_output; + +/** @brief Current completion status of disorder-choose + * A bitmap of @ref CHOOSE_READING and @ref CHOOSE_RUNNING. + */ static unsigned choose_complete; + +/* @brief Exit status from disorder-choose */ static int choose_status; + +/** @brief disorder-choose process is running */ #define CHOOSE_RUNNING 1 + +/** @brief disorder-choose pipe is still open */ #define CHOOSE_READING 2 -/* comparison function for keys */ +/** @brief Comparison function for filename-based keys */ static int compare(DB attribute((unused)) *db_, const DBT *a, const DBT *b) { return compare_path_raw(a->data, a->size, b->data, b->size); @@ -274,7 +305,7 @@ void trackdb_init(int flags) { D(("initialized database environment")); } -/* called when deadlock manager terminates */ +/** @brief Called when deadlock manager terminates */ static int reap_db_deadlock(ev_source attribute((unused)) *ev, pid_t attribute((unused)) pid, int status, @@ -289,6 +320,18 @@ static int reap_db_deadlock(ev_source attribute((unused)) *ev, return 0; } +/** @brief Start a subprogram + * @param ev Event loop + * @param outputfd File descriptor to redirect @c stdout to, or -1 + * @param prog Program name + * @param ... Arguments + * @return PID + * + * Starts a subprocess. Adds the following arguments: + * - @c --config to ensure the right config file is used + * - @c --debug or @c --no-debug to match debug settings + * - @c --syslog or @c --no-syslog to match log settings + */ static pid_t subprogram(ev_source *ev, int outputfd, const char *prog, ...) { pid_t pid; @@ -328,7 +371,11 @@ static pid_t subprogram(ev_source *ev, int outputfd, const char *prog, return pid; } -/* start deadlock manager */ +/** @brief Start deadlock manager + * @param ev Event loop + * + * Called from the main server (only). + */ void trackdb_master(ev_source *ev) { assert(db_deadlock_pid == -1); db_deadlock_pid = subprogram(ev, -1, DEADLOCK, (char *)0); @@ -336,6 +383,14 @@ void trackdb_master(ev_source *ev) { D(("started deadlock manager")); } +/** @brief Kill a subprocess and wait for it to terminate + * @param ev Event loop or NULL + * @param pid Process ID or -1 + * @param what Description of subprocess + * + * Used during trackdb_deinit(). This function blocks so don't use it for + * normal teardown as that will hang the server. + */ static void terminate_and_wait(ev_source *ev, pid_t pid, const char *what) { @@ -352,7 +407,9 @@ static void terminate_and_wait(ev_source *ev, ev_child_cancel(ev, pid); } -/* close environment */ +/** @brief Close database environment + * @param ev Event loop + */ void trackdb_deinit(ev_source *ev) { int err; @@ -384,7 +441,14 @@ void trackdb_deinit(ev_source *ev) { D(("deinitialized database environment")); } -/* open a specific database */ +/** @brief Open a specific database + * @param path Relative path to database + * @param dbflags Database flags: DB_DUP, DB_DUPSORT, etc + * @param dbtype Database type: DB_HASH, DB_BTREE, etc + * @param openflags Open flags: DB_RDONLY, DB_CREATE, etc + * @param mode Permission mask: usually 0666 + * @return Database handle + */ static DB *open_db(const char *path, u_int32_t dbflags, DBTYPE dbtype, @@ -520,7 +584,7 @@ void trackdb_open(int flags) { D(("opened databases")); } -/* close track databases */ +/** @brief Close track databases */ void trackdb_close(void) { int err; @@ -578,8 +642,14 @@ int trackdb_getdata(DB *db, } } -/* encode and store a database entry. Returns 0, DB_KEYEXIST or - * DB_LOCK_DEADLOCK. */ +/** @brief Encode and store a database entry + * @param db Database + * @param track Track name + * @param k List of key/value pairs to store + * @param tid Owning transaction + * @param flags DB flags e.g. DB_NOOVERWRITE + * @return 0, DB_KEYEXIST or DB_LOCK_DEADLOCK + */ int trackdb_putdata(DB *db, const char *track, const struct kvp *k, @@ -625,7 +695,11 @@ int trackdb_delkey(DB *db, } } -/* open a database cursor */ +/** @brief Open a database cursor + * @param db Database + * @param tid Owning transaction + * @return Cursor + */ DBC *trackdb_opencursor(DB *db, DB_TXN *tid) { int err; DBC *c; @@ -637,7 +711,10 @@ DBC *trackdb_opencursor(DB *db, DB_TXN *tid) { return c; } -/* close a database cursor; returns 0 or DB_LOCK_DEADLOCK */ +/** @brief Close a database cursor + * @param c Cursor + * @return 0 or DB_LOCK_DEADLOCK + */ int trackdb_closecursor(DBC *c) { int err; @@ -653,7 +730,16 @@ int trackdb_closecursor(DBC *c) { } } -/* delete a (key,data) pair. Returns 0, DB_NOTFOUND or DB_LOCK_DEADLOCK. */ +/** @brief Delete a key/data pair + * @param db Database + * @param word Key + * @param track Data + * @param tid Owning transaction + * @return 0, DB_NOTFOUND or DB_LOCK_DEADLOCK + * + * Used by the search and tags databases, hence the odd parameter names. + * See also register_word(). + */ int trackdb_delkeydata(DB *db, const char *word, const char *track, @@ -691,7 +777,9 @@ int trackdb_delkeydata(DB *db, return err; } -/* start a transaction */ +/** @brief Start a transaction + * @return Transaction + */ DB_TXN *trackdb_begin_transaction(void) { DB_TXN *tid; int err; @@ -701,7 +789,11 @@ DB_TXN *trackdb_begin_transaction(void) { return tid; } -/* abort transaction */ +/** @brief Abort transaction + * @param tid Transaction (or NULL) + * + * If @p tid is NULL then nothing happens. + */ void trackdb_abort_transaction(DB_TXN *tid) { int err; @@ -710,7 +802,9 @@ void trackdb_abort_transaction(DB_TXN *tid) { fatal(0, "tid->abort: %s", db_strerror(err)); } -/* commit transaction */ +/** @brief Commit transaction + * @param tid Transaction (must not be NULL) + */ void trackdb_commit_transaction(DB_TXN *tid) { int err; @@ -720,12 +814,26 @@ void trackdb_commit_transaction(DB_TXN *tid) { /* search/tags shared code ***************************************************/ -/* comparison function used by dedupe() */ +/** @brief Comparison function used by dedupe() + * @param a Pointer to first key + * @param b Pointer to second key + * @return -1, 0 or 1 + * + * Passed to qsort(). + */ static int wordcmp(const void *a, const void *b) { return strcmp(*(const char **)a, *(const char **)b); } -/* sort and de-dupe VEC */ +/** @brief Sort and de-duplicate @p vec + * @param vec Vector to sort + * @param nvec Length of @p vec + * @return @p vec + * + * The returned vector is NULL-terminated, and there must be room for this NULL + * even if there are no duplicates (i.e. it must have more than @p nvec + * elements.) + */ static char **dedupe(char **vec, int nvec) { int m, n; @@ -741,7 +849,17 @@ static char **dedupe(char **vec, int nvec) { return vec; } -/* update a key/track database. Returns 0 or DB_DEADLOCK. */ +/** @brief Store a key/data pair + * @param db Database + * @param what Description + * @param track Data + * @param word Key + * @param tid Owning transaction + * @return 0 or DB_DEADLOCK + * + * Used by the search and tags databases, hence the odd parameter names. + * See also trackdb_delkeydata(). + */ static int register_word(DB *db, const char *what, const char *track, const char *word, DB_TXN *tid) { @@ -763,13 +881,22 @@ static int register_word(DB *db, const char *what, /* search primitives *********************************************************/ -/* return true iff NAME is a trackname_display_ pref */ +/** @brief Return true iff @p name is a trackname_display_ pref + * @param name Preference name + * @return Non-zero iff @p name is a trackname_display_ pref + */ static int is_display_pref(const char *name) { static const char prefix[] = "trackname_display_"; return !strncmp(name, prefix, (sizeof prefix) - 1); } -/** @brief Word_Break property tailor that treats underscores as spaces */ +/** @brief Word_Break property tailor that treats underscores as spaces + * @param c Code point + * @return Tailored property or -1 to use standard value + * + * Passed to utf32_word_split() when splitting a track name into words. + * See word_split() and @ref unicode_property_tailor. + */ static int tailor_underscore_Word_Break_Other(uint32_t c) { switch(c) { default: @@ -795,7 +922,20 @@ static size_t remove_combining_chars(uint32_t *s, size_t ns) { return t - start; } -/** @brief Normalize and split a string using a given tailoring */ +/** @brief Normalize and split a string using a given tailoring + * @param v Where to store words from string + * @param s Input string + * @param pt Word_Break property tailor, or NULL + * + * The output words will be: + * - case-folded + * - have any combination characters stripped + * - not include any word break code points (as tailored) + * + * Used by track_to_words(), with @p pt set to @ref + * tailor_underscore_Word_Break_Other, and by normalize_tag() with no + * tailoring. + */ static void word_split(struct vector *v, const char *s, unicode_property_tailor *pt) { @@ -851,7 +991,11 @@ static char *normalize_tag(const char *s, size_t ns) { return d->vec; } -/* compute the words of a track name */ +/** @brief Compute the words of a track name + * @param track Track name + * @param p Preferences (for display prefs) + * @return NULL-terminated, de-duplicated list or words + */ static char **track_to_words(const char *track, const struct kvp *p) { struct vector v; @@ -869,7 +1013,10 @@ static char **track_to_words(const char *track, return dedupe(v.vec, v.nvec); } -/* return nonzero iff WORD is a stopword */ +/** @brief Test for a stopword + * @param word Word + * @return Non-zero if @p word is a stopword + */ static int stopword(const char *word) { int n; @@ -879,7 +1026,12 @@ static int stopword(const char *word) { return n < config->stopword.n; } -/* record that WORD appears in TRACK. Returns 0 or DB_LOCK_DEADLOCK. */ +/** @brief Register a search term + * @param track Track name + * @param word A word that appears in the name of @p track + * @param tid Owning transaction + * @return 0 or DB_LOCK_DEADLOCK + */ static int register_search_word(const char *track, const char *word, DB_TXN *tid) { if(stopword(word)) return 0; @@ -888,7 +1040,13 @@ static int register_search_word(const char *track, const char *word, /* Tags **********************************************************************/ -/* Return nonzero if C is a valid tag character */ +/** @brief Test for tag characters + * @param c Character + * @return Non-zero if @p c is a tag character + * + * The current rule is that commas and the control characters 0-31 are not + * allowed but anything else is permitted. This is arguably a bit loose. + */ static int tagchar(int c) { switch(c) { case ',': @@ -898,7 +1056,12 @@ static int tagchar(int c) { } } -/* Parse and de-dupe a tag list. If S=0 then assumes "". */ +/** @brief Parse a tag list + * @param s Tag list or NULL (equivalent to "") + * @return Parsed tag list + * + * The tags will be normalized (as per normalize_tag()) and de-duplicated. + */ char **parsetags(const char *s) { const char *t; struct vector v; @@ -927,7 +1090,12 @@ char **parsetags(const char *s) { return dedupe(v.vec, v.nvec); } -/* Record that TRACK has TAG. Returns 0 or DB_LOCK_DEADLOCK. */ +/** @brief Register a tag + * @param track Track name + * @param tag Tag name + * @param tid Owning transaction + * @return 0 or DB_LOCK_DEADLOCK + */ static int register_tag(const char *track, const char *tag, DB_TXN *tid) { return register_word(trackdb_tagsdb, "tags", track, tag, tid); }