From 1bd11d28955ea29782669bf3d11f3980ec6279df Mon Sep 17 00:00:00 2001 From: ejurgensen Date: Mon, 9 Jun 2014 23:42:02 +0200 Subject: [PATCH 1/2] Fix so permission changes in the library are handled (issue #8) --- src/db.c | 53 +++++++++++++++------- src/db.h | 3 ++ src/filescanner.c | 110 ++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 132 insertions(+), 34 deletions(-) diff --git a/src/db.c b/src/db.c index 1c03fef7..12ed79e1 100644 --- a/src/db.c +++ b/src/db.c @@ -2470,7 +2470,7 @@ db_file_update(struct media_file_info *mfi) " year = %d, track = %d, total_tracks = %d, disc = %d, total_discs = %d, bpm = %d," \ " compilation = %d, artwork = %d, rating = %d, seek = %d, data_kind = %d, item_kind = %d," \ " description = %Q, time_modified = %" PRIi64 "," \ - " db_timestamp = %" PRIi64 ", sample_count = %" PRIi64 "," \ + " db_timestamp = %" PRIi64 ", disabled = %" PRIi64 ", sample_count = %" PRIi64 "," \ " codectype = %Q, idx = %d, has_video = %d," \ " bits_per_sample = %d, album_artist = TRIM(%Q)," \ " media_kind = %d, tv_series_name = TRIM(%Q), tv_episode_num_str = TRIM(%Q)," \ @@ -2500,7 +2500,7 @@ db_file_update(struct media_file_info *mfi) mfi->year, mfi->track, mfi->total_tracks, mfi->disc, mfi->total_discs, mfi->bpm, mfi->compilation, mfi->artwork, mfi->rating, mfi->seek, mfi->data_kind, mfi->item_kind, mfi->description, (int64_t)mfi->time_modified, - (int64_t)mfi->db_timestamp, mfi->sample_count, + (int64_t)mfi->db_timestamp, (int64_t)mfi->disabled, mfi->sample_count, mfi->codectype, mfi->index, mfi->has_video, mfi->bits_per_sample, mfi->album_artist, mfi->media_kind, mfi->tv_series_name, mfi->tv_episode_num_str, @@ -3170,7 +3170,7 @@ db_pl_add_item_byid(int plid, int fileid) int db_pl_update(char *title, char *path, int id) { -#define Q_TMPL "UPDATE playlists SET title = '%q', db_timestamp = %" PRIi64 ", path = '%q' WHERE id = %d;" +#define Q_TMPL "UPDATE playlists SET title = '%q', db_timestamp = %" PRIi64 ", disabled = 0, path = '%q' WHERE id = %d;" char *query; char *errmsg; int ret; @@ -3949,11 +3949,9 @@ db_watch_delete_bycookie(uint32_t cookie) #undef Q_TMPL } -int -db_watch_get_bywd(struct watch_info *wi) +static int +db_watch_get_byquery(struct watch_info *wi, char *query) { -#define Q_TMPL "SELECT * FROM inotify WHERE wd = %d;" - char *query; sqlite3_stmt *stmt; char **strval; char *cval; @@ -3963,13 +3961,6 @@ db_watch_get_bywd(struct watch_info *wi) int i; int ret; - query = sqlite3_mprintf(Q_TMPL, wi->wd); - if (!query) - { - DPRINTF(E_LOG, L_DB, "Out of memory for query string\n"); - return -1; - } - DPRINTF(E_DBG, L_DB, "Running query '%s'\n", query); ret = db_blocking_prepare_v2(query, -1, &stmt, NULL); @@ -3982,7 +3973,7 @@ db_watch_get_bywd(struct watch_info *wi) ret = db_blocking_step(stmt); if (ret != SQLITE_ROW) { - DPRINTF(E_LOG, L_DB, "Watch wd %d not found\n", wi->wd); + DPRINTF(E_WARN, L_DB, "Watch not found: '%s'\n", query); sqlite3_finalize(stmt); sqlite3_free(query); @@ -4041,7 +4032,39 @@ db_watch_get_bywd(struct watch_info *wi) sqlite3_free(query); return 0; +} +int +db_watch_get_bywd(struct watch_info *wi) +{ +#define Q_TMPL "SELECT * FROM inotify WHERE wd = %d;" + char *query; + + query = sqlite3_mprintf(Q_TMPL, wi->wd); + if (!query) + { + DPRINTF(E_LOG, L_DB, "Out of memory for query string\n"); + return -1; + } + + return db_watch_get_byquery(wi, query); +#undef Q_TMPL +} + +int +db_watch_get_bypath(struct watch_info *wi) +{ +#define Q_TMPL "SELECT * FROM inotify WHERE path = '%q';" + char *query; + + query = sqlite3_mprintf(Q_TMPL, wi->path); + if (!query) + { + DPRINTF(E_LOG, L_DB, "Out of memory for query string\n"); + return -1; + } + + return db_watch_get_byquery(wi, query); #undef Q_TMPL } diff --git a/src/db.h b/src/db.h index 790686bd..14e2462d 100644 --- a/src/db.h +++ b/src/db.h @@ -512,6 +512,9 @@ db_watch_delete_bycookie(uint32_t cookie); int db_watch_get_bywd(struct watch_info *wi); +int +db_watch_get_bypath(struct watch_info *wi); + void db_watch_mark_bypath(char *path, char *strip, uint32_t cookie); diff --git a/src/filescanner.c b/src/filescanner.c index 5a6e9248..a84bdd93 100644 --- a/src/filescanner.c +++ b/src/filescanner.c @@ -897,7 +897,7 @@ process_directory(char *path, int flags) #if defined(__linux__) /* Add inotify watch */ - wi.wd = inotify_add_watch(inofd, path, IN_CREATE | IN_DELETE | IN_CLOSE_WRITE | IN_MOVE | IN_DELETE | IN_MOVE_SELF); + wi.wd = inotify_add_watch(inofd, path, IN_ATTRIB | IN_CREATE | IN_DELETE | IN_CLOSE_WRITE | IN_MOVE | IN_DELETE | IN_MOVE_SELF); if (wi.wd < 0) { DPRINTF(E_WARN, L_SCAN, "Could not create inotify watch for %s: %s\n", path, strerror(errno)); @@ -1111,16 +1111,47 @@ filescanner(void *arg) #if defined(__linux__) +static int +watches_clear(uint32_t wd, char *path) +{ + struct watch_enum we; + uint32_t rm_wd; + int ret; + + inotify_rm_watch(inofd, wd); + db_watch_delete_bywd(wd); + + memset(&we, 0, sizeof(struct watch_enum)); + + we.match = path; + + ret = db_watch_enum_start(&we); + if (ret < 0) + return -1; + + while ((db_watch_enum_fetchwd(&we, &rm_wd) == 0) && (rm_wd)) + { + inotify_rm_watch(inofd, rm_wd); + } + + db_watch_enum_end(&we); + + db_watch_delete_bymatch(path); + + return 0; +} + /* Thread: scan */ static void process_inotify_dir(struct watch_info *wi, char *path, struct inotify_event *ie) { struct watch_enum we; uint32_t rm_wd; + char *s; int flags = 0; int ret; - DPRINTF(E_DBG, L_SCAN, "Directory event: 0x%x, cookie 0x%x, wd %d\n", ie->mask, ie->cookie, wi->wd); + DPRINTF(E_SPAM, L_SCAN, "Directory event: 0x%x, cookie 0x%x, wd %d\n", ie->mask, ie->cookie, wi->wd); if (ie->mask & IN_UNMOUNT) { @@ -1165,26 +1196,10 @@ process_inotify_dir(struct watch_info *wi, char *path, struct inotify_event *ie) * and we can't tell where it's going */ - inotify_rm_watch(inofd, ie->wd); - db_watch_delete_bywd(ie->wd); - - memset(&we, 0, sizeof(struct watch_enum)); - - we.match = path; - - ret = db_watch_enum_start(&we); + ret = watches_clear(ie->wd, path); if (ret < 0) return; - while ((db_watch_enum_fetchwd(&we, &rm_wd) == 0) && (rm_wd)) - { - inotify_rm_watch(inofd, rm_wd); - } - - db_watch_enum_end(&we); - - db_watch_delete_bymatch(path); - db_file_disable_bymatch(path, "", 0); db_pl_disable_bymatch(path, "", 0); } @@ -1213,6 +1228,40 @@ process_inotify_dir(struct watch_info *wi, char *path, struct inotify_event *ie) ie->mask |= IN_CREATE; } + if (ie->mask & IN_ATTRIB) + { + DPRINTF(E_DBG, L_SCAN, "Directory permissions changed (%s): %s\n", wi->path, path); + + // Find out if we are already watching the dir (ret will be 0) + s = wi->path; + wi->path = path; + ret = db_watch_get_bypath(wi); + if (ret == 0) + free(wi->path); + wi->path = s; + + if (euidaccess(path, (R_OK | X_OK)) < 0) + { + DPRINTF(E_LOG, L_SCAN, "Directory access to '%s' failed: %s\n", path, strerror(errno)); + + if (ret == 0) + watches_clear(wi->wd, path); + + db_file_disable_bymatch(path, "", 0); + db_pl_disable_bymatch(path, "", 0); + } + else if (ret < 0) + { + DPRINTF(E_LOG, L_SCAN, "Directory access to '%s' achieved\n", path); + + ie->mask |= IN_CREATE; + } + else + { + DPRINTF(E_INFO, L_SCAN, "Directory event, but '%s' already being watched\n", path); + } + } + if (ie->mask & IN_CREATE) { process_directories(path, flags); @@ -1237,6 +1286,7 @@ process_inotify_file(struct watch_info *wi, char *path, struct inotify_event *ie if (ie->mask & IN_DELETE) { DPRINTF(E_DBG, L_SCAN, "File deleted: %s\n", path); + db_file_delete_bypath(path); db_pl_delete_bypath(path); } @@ -1244,13 +1294,33 @@ process_inotify_file(struct watch_info *wi, char *path, struct inotify_event *ie if (ie->mask & IN_MOVED_FROM) { DPRINTF(E_DBG, L_SCAN, "File moved from: %s\n", path); + db_file_disable_bypath(path, path, ie->cookie); db_pl_disable_bypath(path, path, ie->cookie); } + if (ie->mask & IN_ATTRIB) + { + DPRINTF(E_DBG, L_SCAN, "File permissions changed: %s\n", path); + + if (euidaccess(path, R_OK) < 0) + { + DPRINTF(E_LOG, L_SCAN, "File access to '%s' failed: %s\n", path, strerror(errno)); + + db_file_delete_bypath(path);; + } + else if (db_file_id_bypath(path) <= 0) + { + DPRINTF(E_LOG, L_SCAN, "File access to '%s' achieved\n", path); + + ie->mask |= IN_CLOSE_WRITE; + } + } + if (ie->mask & IN_MOVED_TO) { DPRINTF(E_DBG, L_SCAN, "File moved to: %s\n", path); + ret = db_file_enable_bycookie(ie->cookie, path); if (ret <= 0) @@ -1268,6 +1338,7 @@ process_inotify_file(struct watch_info *wi, char *path, struct inotify_event *ie if (ie->mask & IN_CREATE) { DPRINTF(E_DBG, L_SCAN, "File created: %s\n", path); + ret = lstat(path, &sb); if (ret < 0) { @@ -1283,6 +1354,7 @@ process_inotify_file(struct watch_info *wi, char *path, struct inotify_event *ie if (ie->mask & IN_CLOSE_WRITE) { DPRINTF(E_DBG, L_SCAN, "File closed: %s\n", path); + ret = lstat(path, &sb); if (ret < 0) { From d993bc3750aadc1d58c5ba4acb6d2f482e74a63a Mon Sep 17 00:00:00 2001 From: ejurgensen Date: Tue, 10 Jun 2014 22:49:44 +0200 Subject: [PATCH 2/2] Defer loading Spotify playlists until bulk scan is complete (because of database locking issues) --- src/filescanner.c | 4 ++ src/spotify.c | 149 ++++++++++++++++++++++++---------------------- 2 files changed, 82 insertions(+), 71 deletions(-) diff --git a/src/filescanner.c b/src/filescanner.c index a84bdd93..aa887527 100644 --- a/src/filescanner.c +++ b/src/filescanner.c @@ -1093,6 +1093,10 @@ filescanner(void *arg) else bulk_scan(F_SCAN_BULK); +#ifdef HAVE_SPOTIFY_H + spotify_login(NULL); +#endif + if (!scan_exit) { /* Enable inotify */ diff --git a/src/spotify.c b/src/spotify.c index c848fe62..6fa978c0 100644 --- a/src/spotify.c +++ b/src/spotify.c @@ -501,21 +501,21 @@ spotify_track_save(int plid, sp_track *track) if (fptr_sp_track_get_availability(g_sess, track) != SP_TRACK_AVAILABILITY_AVAILABLE) { - DPRINTF(E_INFO, L_SPOTIFY, "Track not available for playback\n"); + DPRINTF(E_LOG, L_SPOTIFY, "Track not available for playback: '%s'\n", fptr_sp_track_name(track)); return 0; } link = fptr_sp_link_create_from_track(track, 0); if (!link) { - DPRINTF(E_LOG, L_SPOTIFY, "Could not create link for track\n"); + DPRINTF(E_LOG, L_SPOTIFY, "Could not create link for track: '%s'\n", fptr_sp_track_name(track)); return -1; } ret = fptr_sp_link_as_string(link, url, sizeof(url)); if (ret == sizeof(url)) { - DPRINTF(E_DBG, L_SPOTIFY, "Spotify link truncated: %s\n", url); + DPRINTF(E_DBG, L_SPOTIFY, "Spotify link truncated: '%s'\n", url); } fptr_sp_link_release(link); @@ -523,7 +523,7 @@ spotify_track_save(int plid, sp_track *track) ret = db_pl_add_item_bypath(plid, url); if (ret < 0) { - DPRINTF(E_LOG, L_SPOTIFY, "Could not save playlist item\n"); + DPRINTF(E_LOG, L_SPOTIFY, "Could not save playlist item: '%s'\n", url); return -1; } @@ -532,7 +532,7 @@ spotify_track_save(int plid, sp_track *track) ret = spotify_metadata_get(track, &mfi); if (ret < 0) { - DPRINTF(E_LOG, L_SPOTIFY, "Metadata missing (but track should be loaded?)\n"); + DPRINTF(E_LOG, L_SPOTIFY, "Metadata missing (but track should be loaded?): '%s'\n", fptr_sp_track_name(track)); free_mfi(&mfi, 1); return -1; } @@ -566,13 +566,13 @@ spotify_playlist_save(sp_playlist *pl) name = fptr_sp_playlist_name(pl); - DPRINTF(E_DBG, L_SPOTIFY, "Saving playlist: %s\n", name); + DPRINTF(E_INFO, L_SPOTIFY, "Saving playlist: '%s'\n", name); /* Save playlist (playlists table) */ link = fptr_sp_link_create_from_playlist(pl); if (!link) { - DPRINTF(E_LOG, L_SPOTIFY, "Could not create link for playlist (wait)\n"); + DPRINTF(E_LOG, L_SPOTIFY, "Could not create link for playlist (wait): '%s'\n", name); return -1; } @@ -583,14 +583,14 @@ spotify_playlist_save(sp_playlist *pl) } fptr_sp_link_release(link); - sleep(1); // Primitive way of preventing database locking (the mutex wasn't working) +// sleep(1); // Primitive way of preventing database locking (the mutex wasn't working) pli = db_pl_fetch_bypath(url); snprintf(title, sizeof(title), "[s] %s", name); if (pli) { - DPRINTF(E_DBG, L_SPOTIFY, "Playlist found (%s, link %s), updating\n", name, url); + DPRINTF(E_DBG, L_SPOTIFY, "Playlist found ('%s', link %s), updating\n", name, url); plid = pli->id; @@ -599,7 +599,7 @@ spotify_playlist_save(sp_playlist *pl) ret = db_pl_update(title, url, plid); if (ret < 0) { - DPRINTF(E_LOG, L_SPOTIFY, "Error updating playlist (%s, link %s)\n", name, url); + DPRINTF(E_LOG, L_SPOTIFY, "Error updating playlist ('%s', link %s)\n", name, url); return -1; } @@ -608,12 +608,12 @@ spotify_playlist_save(sp_playlist *pl) } else { - DPRINTF(E_DBG, L_SPOTIFY, "Adding playlist (%s, link %s)\n", name, url); + DPRINTF(E_DBG, L_SPOTIFY, "Adding playlist ('%s', link %s)\n", name, url); ret = db_pl_add(title, url, &plid); if ((ret < 0) || (plid < 1)) { - DPRINTF(E_LOG, L_SPOTIFY, "Error adding playlist (%s, link %s, ret %d, plid %d)\n", name, url, ret, plid); + DPRINTF(E_LOG, L_SPOTIFY, "Error adding playlist ('%s', link %s, ret %d, plid %d)\n", name, url, ret, plid); return -1; } } @@ -625,14 +625,14 @@ spotify_playlist_save(sp_playlist *pl) track = fptr_sp_playlist_track(pl, i); if (!track) { - DPRINTF(E_LOG, L_SPOTIFY, "Track %d in playlist %s (id %d) is invalid\n", i, name, plid); + DPRINTF(E_LOG, L_SPOTIFY, "Track %d in playlist '%s' (id %d) is invalid\n", i, name, plid); continue; } ret = spotify_track_save(plid, track); if (ret < 0) { - DPRINTF(E_LOG, L_SPOTIFY, "Error saving track %d to playlist %s (id %d)\n", i, name, plid); + DPRINTF(E_LOG, L_SPOTIFY, "Error saving track %d to playlist '%s' (id %d)\n", i, name, plid); continue; } } @@ -1648,47 +1648,38 @@ spotify_artwork_get(struct evbuffer *evbuf, char *path, int max_w, int max_h) return ret; } -/* Thread: filescanner */ -void -spotify_login(char *path) +static int +spotify_file_read(char *path, char **username, char **password) { - char buf[256]; FILE *fp; - char *username; - char *password; + char *u; + char *p; + char buf[256]; int len; - int ret; - sp_error err; - - if (!g_sess) - { - DPRINTF(E_LOG, L_SPOTIFY, "Can't login! No valid Spotify session.\n"); - return; - } fp = fopen(path, "rb"); if (!fp) { DPRINTF(E_LOG, L_SPOTIFY, "Could not open Spotify credentials file %s: %s\n", path, strerror(errno)); - return; + return -1; } - username = fgets(buf, sizeof(buf), fp); - if (!username) + u = fgets(buf, sizeof(buf), fp); + if (!u) { DPRINTF(E_LOG, L_SPOTIFY, "Empty Spotify credentials file %s\n", path); fclose(fp); - return; + return -1; } - len = strlen(username); + len = strlen(u); if (buf[len - 1] != '\n') { DPRINTF(E_LOG, L_SPOTIFY, "Invalid Spotify credentials file %s: username name too long or missing password\n", path); fclose(fp); - return; + return -1; } while (len) @@ -1707,29 +1698,29 @@ spotify_login(char *path) DPRINTF(E_LOG, L_SPOTIFY, "Invalid Spotify credentials file %s: empty line where username expected\n", path); fclose(fp); - return; + return -1; } - username = strdup(buf); - if (!username) + u = strdup(buf); + if (!u) { DPRINTF(E_LOG, L_SPOTIFY, "Out of memory for username while reading %s\n", path); fclose(fp); - return; + return -1; } - password = fgets(buf, sizeof(buf), fp); + p = fgets(buf, sizeof(buf), fp); fclose(fp); - if (!password) + if (!p) { DPRINTF(E_LOG, L_SPOTIFY, "Invalid Spotify credentials file %s: no password\n", path); - free(username); - return; + free(u); + return -1; } - len = strlen(password); + len = strlen(p); while (len) { @@ -1742,18 +1733,42 @@ spotify_login(char *path) break; } - password = strdup(buf); - if (!password) + p = strdup(buf); + if (!p) { DPRINTF(E_LOG, L_SPOTIFY, "Out of memory for password while reading %s\n", path); - free(username); + free(u); + return -1; + } + + DPRINTF(E_LOG, L_SPOTIFY, "Spotify credentials file OK, logging in with username %s\n", u); + + *username = u; + *password = p; + + return 0; +} + +/* Thread: filescanner */ +void +spotify_login(char *path) +{ + char *username; + char *password; + int ret; + sp_error err; + + if (!g_sess) + { + DPRINTF(E_LOG, L_SPOTIFY, "Can't login! No valid Spotify session.\n"); return; } + /* Log out if thread already running */ if (g_state != SPOTIFY_STATE_INACTIVE) { - DPRINTF(E_DBG, L_SPOTIFY, "Existing login terminating (state %d)\n", g_state); + DPRINTF(E_LOG, L_SPOTIFY, "Existing login terminating (state %d)\n", g_state); thread_exit(); @@ -1765,15 +1780,29 @@ spotify_login(char *path) } } - DPRINTF(E_DBG, L_SPOTIFY, "Spotify credentials file OK, logging in with username %s\n", username); + /* Log in */ + if (path) + { + ret = spotify_file_read(path, &username, &password); + if (ret < 0) + return; + + DPRINTF(E_INFO, L_SPOTIFY, "Logging into Spotify\n"); + err = fptr_sp_session_login(g_sess, username, password, 1, NULL); + } + else + { + DPRINTF(E_INFO, L_SPOTIFY, "Logging into Spotify\n"); + err = fptr_sp_session_relogin(g_sess); + } - err = fptr_sp_session_login(g_sess, username, password, 1, NULL); if (SP_ERROR_OK != err) { DPRINTF(E_LOG, L_SPOTIFY, "Could not login into Spotify: %s\n", fptr_sp_error_message(err)); return; } + /* Spawn thread */ ret = pthread_create(&tid_spotify, NULL, spotify, NULL); if (ret < 0) { @@ -1941,30 +1970,9 @@ spotify_init(void) pthread_mutex_init(&g_audio_fifo->mutex, NULL); pthread_cond_init(&g_audio_fifo->cond, NULL); - /* Log in and spawn thread */ - DPRINTF(E_DBG, L_SPOTIFY, "Logging into Spotify\n"); - err = fptr_sp_session_relogin(sp); - if (SP_ERROR_OK != err) - { - DPRINTF(E_LOG, L_SPOTIFY, "Could not login into Spotify: %s\n", fptr_sp_error_message(err)); - goto login_fail; - } - - ret = pthread_create(&tid_spotify, NULL, spotify, NULL); - if (ret < 0) - { - DPRINTF(E_LOG, L_SPOTIFY, "Could not spawn Spotify thread: %s\n", strerror(errno)); - goto thread_fail; - } - DPRINTF(E_DBG, L_SPOTIFY, "Spotify init complete\n"); return 0; - thread_fail: - pthread_cond_destroy(&g_audio_fifo->cond); - pthread_mutex_destroy(&g_audio_fifo->mutex); - free(g_audio_fifo); - audio_fifo_fail: fptr_sp_session_release(g_sess); g_sess = NULL; @@ -1992,7 +2000,6 @@ spotify_init(void) g_libhandle = NULL; libspotify_fail: - login_fail: return -1; }