From 2a10b70b1cdbd55e9f2c26ed52dcff5255cd6e96 Mon Sep 17 00:00:00 2001 Message-Id: <2a10b70b1cdbd55e9f2c26ed52dcff5255cd6e96.1714217258.git.mdw@distorted.org.uk> From: Mark Wooding Date: Tue, 2 Oct 2007 18:03:01 +0100 Subject: [PATCH] server half of noticed.db Organization: Straylight/Edgeware From: Richard Kettlewell --- clients/disorder.c | 23 +++++++++ doc/disorder_config.5.in | 4 ++ doc/disorder_protocol.5.in | 4 ++ lib/client.c | 16 ++++++ lib/client.h | 5 ++ lib/configuration.c | 2 + lib/configuration.h | 3 ++ lib/eclient.c | 19 +++++++ lib/eclient.h | 4 ++ server/rescan.c | 7 +++ server/server.c | 17 +++++++ server/trackdb-int.h | 2 + server/trackdb.c | 100 +++++++++++++++++++++++++++++++++++-- server/trackdb.h | 2 + 14 files changed, 205 insertions(+), 3 deletions(-) diff --git a/clients/disorder.c b/clients/disorder.c index eae5a8f..5ccb625 100644 --- a/clients/disorder.c +++ b/clients/disorder.c @@ -35,6 +35,7 @@ #include #include #include +#include #include "configuration.h" #include "syscalls.h" @@ -378,6 +379,26 @@ static void cf_unset_global(disorder_client *c, char **argv) { if(disorder_unset_global(c, argv[0])) exit(EXIT_FAILURE); } +static int isarg_integer(const char *s) { + if(!*s) return 0; + while(*s) { + if(!isdigit((unsigned char)*s)) + return 0; + ++s; + } + return 1; +} + +static void cf_new(disorder_client *c, + char **argv) { + char **vec; + + if(disorder_new_tracks(c, &vec, 0, argv[0] ? atoi(argv[0]) : 0)) + exit(EXIT_FAILURE); + while(*vec) + xprintf("%s\n", nullcheck(utf82mb(*vec++))); +} + static const struct command { const char *name; int min, max; @@ -415,6 +436,8 @@ static const struct command { "Copy event log to stdout" }, { "move", 2, 2, cf_move, 0, "TRACK DELTA", "Move a track in the queue" }, + { "new", 0, 1, cf_new, isarg_integer, "[MAX]", + "Get the most recently added MAX tracks" }, { "part", 3, 3, cf_part, 0, "TRACK CONTEXT PART", "Find a track name part" }, { "pause", 0, 0, cf_pause, 0, "", diff --git a/doc/disorder_config.5.in b/doc/disorder_config.5.in index efb7a30..9009624 100644 --- a/doc/disorder_config.5.in +++ b/doc/disorder_config.5.in @@ -268,6 +268,10 @@ is not massively CPU intensive by today's standards but depends on reasonably timely scheduling. If you have limited CPU then it might help to set this to a small negative value. The default is 0. .TP +.B noticed_history +The maximum days that a track can survive in the database of newly added +tracks. The default is 31. +.TP .B player \fIPATTERN\fR \fIMODULE\fR [\fIOPTIONS.. [\fB--\fR]] \fIARGS\fR... Specifies the player for files matching the glob \fIPATTERN\fR. \fIMODULE\fR specifies which plugin module to use. diff --git a/doc/disorder_protocol.5.in b/doc/disorder_protocol.5.in index 60390f2..100e746 100644 --- a/doc/disorder_protocol.5.in +++ b/doc/disorder_protocol.5.in @@ -109,6 +109,10 @@ the queue. If \fITARGET\fR is listed in the ID list then the tracks are moved to just after the first non-listed track before it, or to the head if there is no such track. .TP +.B new \fR[\fIMAX\fR] +Sends the most recently added \fIMAX\fR tracks in a response body. If the +argument is ommitted, all recently added tracks are listed. +.TP .B nop Do nothing. Used by .BR disobedience (1) diff --git a/lib/client.c b/lib/client.c index b012841..d68d9d7 100644 --- a/lib/client.c +++ b/lib/client.c @@ -600,6 +600,22 @@ int disorder_tags(disorder_client *c, return disorder_simple_list(c, vecp, nvecp, "tags", (char *)0); } +/** @brief Get recentl added tracks + * @param c Client + * @param vecp Where to store pointer to list + * @param nvecp Where to store count + * @param max Maximum tracks to fetch, or 0 for all available + * @return 0 on success, non-0 on error + */ +int disorder_new_tracks(disorder_client *c, + char ***vecp, int *nvecp, + int max) { + char limit[32]; + + sprintf(limit, "%d", max); + return disorder_simple_list(c, vecp, nvecp, "new", limit, (char *)0); +} + int disorder_set_global(disorder_client *c, const char *key, const char *value) { return disorder_simple(c, 0, "set-global", key, value, (char *)0); diff --git a/lib/client.h b/lib/client.h index 433e81c..a7d0ff9 100644 --- a/lib/client.h +++ b/lib/client.h @@ -181,6 +181,11 @@ int disorder_unset_global(disorder_client *c, const char *key); int disorder_get_global(disorder_client *c, const char *key, char **valuep); /* get/unset/set global prefs */ +int disorder_new_tracks(disorder_client *c, + char ***vecp, int *nvecp, + int max); +/* get new tracks */ + #endif /* CLIENT_H */ /* diff --git a/lib/configuration.c b/lib/configuration.c index 69b8bb7..f75845c 100644 --- a/lib/configuration.c +++ b/lib/configuration.c @@ -857,6 +857,7 @@ static const struct conf conf[] = { { C(nice_rescan), &type_integer, validate_non_negative }, { C(nice_server), &type_integer, validate_any }, { C(nice_speaker), &type_integer, validate_any }, + { C(noticed_history), &type_integer, validate_positive }, { C(password), &type_string, validate_any }, { C(player), &type_stringlist_accum, validate_player }, { C(plugins), &type_string_accum, validate_isdir }, @@ -992,6 +993,7 @@ static struct config *config_default(void) { c->speaker_backend = -1; c->multicast_ttl = 1; c->authorization_algorithm = xstrdup("sha1"); + c->noticed_history = 31; return c; } diff --git a/lib/configuration.h b/lib/configuration.h index a2033c2..476a917 100644 --- a/lib/configuration.h +++ b/lib/configuration.h @@ -114,6 +114,9 @@ struct config { /** @brief Maximum number of recent tracks to record in history */ long history; + /** @brief Expiry limit for noticed.db */ + long noticed_history; + /** @brief Trusted users */ struct stringlist trust; diff --git a/lib/eclient.c b/lib/eclient.c index cefa5e3..2d57d73 100644 --- a/lib/eclient.c +++ b/lib/eclient.c @@ -1156,6 +1156,25 @@ int disorder_eclient_nop(disorder_eclient *c, "nop", (char *)0); } +/** @brief Get the last @p max added tracks + * @param c Client + * @param completed Called with list + * @param max Number of tracks to get, 0 for all + * @param v Passed to @p completed + * + * The first track in the list is the most recently added. + */ +int disorder_eclient_new_tracks(disorder_eclient *c, + disorder_eclient_list_response *completed, + int max, + void *v) { + char limit[32]; + + sprintf(limit, "%d", max); + return simple(c, list_response_opcallback, (void (*)())completed, v, + "new", limit, (char *)0); +} + /* Log clients ***************************************************************/ /** @brief Monitor the server log diff --git a/lib/eclient.h b/lib/eclient.h index a81cfe4..47feee3 100644 --- a/lib/eclient.h +++ b/lib/eclient.h @@ -316,6 +316,10 @@ int disorder_eclient_nop(disorder_eclient *c, disorder_eclient_no_response *completed, void *v); +int disorder_eclient_new_tracks(disorder_eclient *c, + disorder_eclient_list_response *completed, + int max, + void *v); #endif /* diff --git a/server/rescan.c b/server/rescan.c index 8a29300..45f5a65 100644 --- a/server/rescan.c +++ b/server/rescan.c @@ -303,6 +303,11 @@ static void do_all(void (*fn)(const struct collection *c)) { */ } +/** @brief Expire noticed.db */ +static void expire_noticed(void) { + error(0, "expire_noticed not implemented yet TODO"); +} + int main(int argc, char **argv) { int n; struct sigaction sa; @@ -340,6 +345,8 @@ int main(int argc, char **argv) { do_all(rescan_collection); /* Check that every track still exists */ recheck_collection(0); + /* Expire noticed.db */ + expire_noticed(); } else { /* Rescan specified collections */ diff --git a/server/server.c b/server/server.c index 6ee11c9..c5fb010 100644 --- a/server/server.c +++ b/server/server.c @@ -914,6 +914,22 @@ static int c_nop(struct conn *c, return 1; } +static int c_new(struct conn *c, + char **vec, + int nvec) { + char **tracks = trackdb_new(0, nvec > 0 ? atoi(vec[0]) : INT_MAX); + + sink_printf(ev_writer_sink(c->w), "253 New track list follows\n"); + while(*tracks) { + sink_printf(ev_writer_sink(c->w), "%s%s\n", + **tracks == '.' ? "." : "", *tracks); + ++tracks; + } + sink_writes(ev_writer_sink(c->w), ".\n"); + return 1; /* completed */ + +} + #define C_AUTH 0001 /* must be authenticated */ #define C_TRUSTED 0002 /* must be trusted user */ @@ -937,6 +953,7 @@ static const struct command { { "log", 0, 0, c_log, C_AUTH }, { "move", 2, 2, c_move, C_AUTH }, { "moveafter", 1, INT_MAX, c_moveafter, C_AUTH }, + { "new", 0, 1, c_new, C_AUTH }, { "nop", 0, 0, c_nop, C_AUTH }, { "part", 3, 3, c_part, C_AUTH }, { "pause", 0, 0, c_pause, C_AUTH }, diff --git a/server/trackdb-int.h b/server/trackdb-int.h index 81cd261..1ba890b 100644 --- a/server/trackdb-int.h +++ b/server/trackdb-int.h @@ -26,6 +26,7 @@ extern DB_ENV *trackdb_env; extern DB *trackdb_tracksdb; extern DB *trackdb_prefsdb; extern DB *trackdb_searchdb; +extern DB *trackdb_noticeddb; DBC *trackdb_opencursor(DB *db, DB_TXN *tid); /* open a transaction */ @@ -87,6 +88,7 @@ int trackdb_scan(const char *root, * EINTR to cancel the scan. */ /* fill KEY in with S, returns KEY */ + static inline DBT *make_key(DBT *key, const char *s) { memset(key, 0, sizeof *key); key->data = (void *)s; diff --git a/server/trackdb.c b/server/trackdb.c index fabb203..07fdd28 100644 --- a/server/trackdb.c +++ b/server/trackdb.c @@ -33,6 +33,7 @@ #include #include #include +#include #include "event.h" #include "mem.h" @@ -65,6 +66,9 @@ static int trackdb_alltags_tid(DB_TXN *tid, char ***taglistp); static int trackdb_get_global_tid(const char *name, DB_TXN *tid, const char **rp); +static char **trackdb_new_tid(int *ntracksp, + int maxtracks, + DB_TXN *tid); const struct cache_type cache_files_type = { 86400 }; unsigned long cache_files_hits, cache_files_misses; @@ -78,6 +82,7 @@ DB *trackdb_prefsdb; /* preferences */ DB *trackdb_searchdb; /* the search database */ DB *trackdb_tagsdb; /* the tags database */ DB *trackdb_globaldb; /* global preferences */ +DB *trackdb_noticeddb; /* when track noticed */ static pid_t db_deadlock_pid = -1; /* deadlock manager PID */ static pid_t rescan_pid = -1; /* rescanner PID */ static int initialized, opened; /* state */ @@ -247,6 +252,8 @@ void trackdb_open(void) { DB_DUP|DB_DUPSORT, DB_HASH, DB_CREATE, 0666); trackdb_prefsdb = open_db("prefs.db", 0, DB_HASH, DB_CREATE, 0666); trackdb_globaldb = open_db("global.db", 0, DB_HASH, DB_CREATE, 0666); + trackdb_noticeddb = open_db("noticed.db", + DB_DUPSORT, DB_BTREE, DB_CREATE, 0666); D(("opened databases")); } @@ -267,6 +274,8 @@ void trackdb_close(void) { fatal(0, "error closing prefs.db: %s", db_strerror(err)); if((err = trackdb_globaldb->close(trackdb_globaldb, 0))) 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)); trackdb_tracksdb = trackdb_searchdb = trackdb_prefsdb = 0; trackdb_tagsdb = trackdb_globaldb = 0; D(("closed databases")); @@ -673,7 +682,9 @@ done: /* trackdb_notice() **********************************************************/ -/* notice a track */ +/** @brief notice a possiby new track + * @return @c DB_NOTFOUND if new, 0 if already known + */ int trackdb_notice(const char *track, const char *path) { int err; @@ -691,6 +702,9 @@ int trackdb_notice(const char *track, return err; } +/** @brief notice a possiby new track + * @return @c DB_NOTFOUND if new, 0 if already known, @c DB_LOCK_DEADLOCK also + */ int trackdb_notice_tid(const char *track, const char *path, DB_TXN *tid) { @@ -698,13 +712,13 @@ int trackdb_notice_tid(const char *track, struct kvp *t, *a, *p; int t_changed, ret; char *alias, **w; - + /* notice whether the tracks.db entry changes */ t_changed = 0; /* get any existing tracks entry */ if((err = gettrackdata(track, &t, &p, 0, 0, tid)) == DB_LOCK_DEADLOCK) return err; - ret = err; + ret = err; /* 0 or DB_NOTFOUND */ /* this is a real track */ t_changed += kvp_set(&t, "_alias_for", 0); t_changed += kvp_set(&t, "_path", path); @@ -731,6 +745,24 @@ int trackdb_notice_tid(const char *track, /* only store the tracks.db entry if it has changed */ if(t_changed && (err = trackdb_putdata(trackdb_tracksdb, track, t, tid, 0))) return err; + if(ret == DB_NOTFOUND) { + uint32_t timestamp[2]; + time_t now; + DBT key, data; + + time(&now); + timestamp[0] = htonl((uint64_t)now >> 32); + timestamp[1] = htonl((uint32_t)now); + memset(&key, 0, sizeof key); + key.data = timestamp; + key.size = sizeof timestamp; + switch(err = trackdb_noticeddb->put(trackdb_noticeddb, tid, &key, + make_key(&data, track), 0)) { + case 0: break; + case DB_LOCK_DEADLOCK: return err; + default: fatal(0, "error updating noticed.db: %s", db_strerror(err)); + } + } return ret; } @@ -1826,6 +1858,68 @@ static int trackdb_get_global_tid(const char *name, } } +/** @brief Retrieve the most recently added tracks + * @param ntracksp Where to put count, or 0 + * @param maxtracks Maximum number of tracks to retrieve + * @return null-terminated array of track names + * + * The most recently added track is first in the array. + */ +char **trackdb_new(int *ntracksp, + int maxtracks) { + DB_TXN *tid; + char **tracks; + + for(;;) { + tid = trackdb_begin_transaction(); + tracks = trackdb_new_tid(ntracksp, maxtracks, tid); + if(tracks) + break; + trackdb_abort_transaction(tid); + } + trackdb_commit_transaction(tid); + return tracks; +} + +/** @brief Retrieve the most recently added tracks + * @param ntracksp Where to put count, or 0 + * @param maxtracks Maximum number of tracks to retrieve, or 0 for all + * @param tid Transaction ID + * @return null-terminated array of track names, or NULL on deadlock + * + * The most recently added track is first in the array. + */ +static char **trackdb_new_tid(int *ntracksp, + int maxtracks, + DB_TXN *tid) { + DBC *c; + DBT k, d; + int err = 0; + struct vector tracks[1]; + + vector_init(tracks); + c = trackdb_opencursor(trackdb_noticeddb, tid); + while((maxtracks <= 0 || tracks->nvec < maxtracks) + && !(err = c->c_get(c, prepare_data(&k), prepare_data(&d), DB_PREV))) + vector_append(tracks, xstrndup(d.data, d.size)); + switch(err) { + case 0: /* hit maxtracks */ + case DB_NOTFOUND: /* ran out of tracks */ + break; + case DB_LOCK_DEADLOCK: + trackdb_closecursor(c); + return 0; + default: + fatal(0, "error reading noticed.db: %s", db_strerror(err)); + } + if((err = trackdb_closecursor(c))) + return 0; /* deadlock */ + vector_terminate(tracks); + if(ntracksp) + *ntracksp = tracks->nvec; + return tracks->vec; +} + /* tidying up ****************************************************************/ void trackdb_gc(void) { diff --git a/server/trackdb.h b/server/trackdb.h index d142f8a..3e7976a 100644 --- a/server/trackdb.h +++ b/server/trackdb.h @@ -117,6 +117,8 @@ void trackdb_set_global(const char *name, const char *trackdb_get_global(const char *name); /* get a global pref */ +char **trackdb_new(int *ntracksp, int maxtracks); + #endif /* TRACKDB_H */ /* -- [mdw]