mirror of
https://github.com/owntone/owntone-server.git
synced 2025-10-29 07:45:04 -04:00
[scan] Use libmount to detect filesystem mount events
Resolves issue #1897
This commit is contained in:
parent
324b6eb61a
commit
e607019a1c
@ -103,6 +103,9 @@ AC_SEARCH_LIBS([copy_file_range], [c],
|
||||
AC_SEARCH_LIBS([fcopyfile], [c],
|
||||
[AC_DEFINE([HAVE_FCOPYFILE], 1,
|
||||
[Define to 1 if you have fcopyfile])])
|
||||
AC_SEARCH_LIBS([mnt_new_monitor], [mount],
|
||||
[AC_DEFINE([HAVE_LIBMOUNT], 1,
|
||||
[Define to 1 if you have libmount])])
|
||||
|
||||
AC_SEARCH_LIBS([log10], [m])
|
||||
AC_SEARCH_LIBS([lrint], [m])
|
||||
|
||||
@ -88,6 +88,7 @@ owntone_SOURCES = main.c \
|
||||
library/filescanner.c library/filescanner.h \
|
||||
library/filescanner_ffmpeg.c library/filescanner_playlist.c \
|
||||
library/filescanner_smartpl.c library/filescanner_itunes.c \
|
||||
library/filescanner_mountwatch.c \
|
||||
library/rssscanner.c \
|
||||
library.c library.h \
|
||||
$(MDNS_SRC) mdns.h \
|
||||
|
||||
@ -115,6 +115,7 @@ struct stacked_dir {
|
||||
|
||||
static int inofd;
|
||||
static struct event *inoev;
|
||||
static struct event *mntev;
|
||||
static struct deferred_pl *playlists;
|
||||
static struct stacked_dir *dirstack;
|
||||
|
||||
@ -1130,13 +1131,6 @@ process_inotify_dir(struct watch_info *wi, char *path, struct inotify_event *ie)
|
||||
|
||||
DPRINTF(E_DBG, L_SCAN, "Directory event: 0x%08x, cookie 0x%08x, wd %d\n", ie->mask, ie->cookie, wi->wd);
|
||||
|
||||
if (ie->mask & IN_UNMOUNT)
|
||||
{
|
||||
db_file_disable_bymatch(path, STRIP_NONE, 0);
|
||||
db_pl_disable_bymatch(path, STRIP_NONE, 0);
|
||||
db_directory_disable_bymatch(path, STRIP_NONE, 0);
|
||||
}
|
||||
|
||||
if (ie->mask & IN_MOVE_SELF)
|
||||
{
|
||||
/* A directory we know about, that got moved from a place
|
||||
@ -1508,6 +1502,7 @@ inotify_cb(int fd, short event, void *arg)
|
||||
{
|
||||
struct inotify_event *ie;
|
||||
struct watch_info wi;
|
||||
struct stat sb;
|
||||
uint8_t *buf;
|
||||
uint8_t *ptr;
|
||||
char path[PATH_MAX];
|
||||
@ -1565,8 +1560,16 @@ inotify_cb(int fd, short event, void *arg)
|
||||
DPRINTF(E_DBG, L_SCAN, "%s deleted or backing filesystem unmounted!\n", wi.path);
|
||||
|
||||
db_watch_delete_bywd(ie->wd);
|
||||
free_wi(&wi, 1);
|
||||
continue;
|
||||
|
||||
// Is the directory gone?
|
||||
if (! (lstat(wi.path, &sb) == 0 && S_ISDIR(sb.st_mode)))
|
||||
{
|
||||
free_wi(&wi, 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
// After an unmount event the mount point is a regular dir that we must process
|
||||
ie->mask |= IN_CREATE;
|
||||
}
|
||||
|
||||
path[0] = '\0';
|
||||
@ -1614,28 +1617,73 @@ inotify_cb(int fd, short event, void *arg)
|
||||
event_add(inoev, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
mount_cb(int fd, short event, void *arg)
|
||||
{
|
||||
struct watch_info wi = { 0 };
|
||||
char *path = NULL;
|
||||
enum mountwatch_event mev;
|
||||
int parent_id;
|
||||
int ret;
|
||||
|
||||
mev = mountwatch_event_get(&path);
|
||||
if (mev == MOUNTWATCH_ERR || mev == MOUNTWATCH_NONE)
|
||||
goto readd;
|
||||
|
||||
// Check if this is a location we are watching
|
||||
ret = db_watch_get_bypath(&wi, path);
|
||||
if (ret < 0)
|
||||
goto readd;
|
||||
|
||||
if (mev == MOUNTWATCH_MOUNT)
|
||||
{
|
||||
DPRINTF(E_DBG, L_SCAN, "MNT_MOUNT path %s\n", path);
|
||||
|
||||
// After a mount, the inotify watch we had on the mount point will no
|
||||
// longer be working, so remove.
|
||||
inotify_rm_watch(inofd, wi.wd);
|
||||
db_watch_delete_bywd(wi.wd);
|
||||
|
||||
// Adds watches, scans and sets disabled = 0
|
||||
parent_id = get_parent_dir_id(path);
|
||||
process_directories(path, parent_id, 0);
|
||||
}
|
||||
else if (mev == MOUNTWATCH_UNMOUNT)
|
||||
{
|
||||
DPRINTF(E_DBG, L_SCAN, "MNT_UNMOUNT path %s\n", path);
|
||||
|
||||
db_file_disable_bymatch(path, STRIP_NONE, 0);
|
||||
db_pl_disable_bymatch(path, STRIP_NONE, 0);
|
||||
db_directory_disable_bymatch(path, STRIP_NONE, 0);
|
||||
}
|
||||
|
||||
readd:
|
||||
free_wi(&wi, 1);
|
||||
free(path);
|
||||
event_add(mntev, NULL);
|
||||
}
|
||||
|
||||
/* Thread: main & scan */
|
||||
static int
|
||||
inofd_event_set(void)
|
||||
{
|
||||
int mntfd;
|
||||
|
||||
inofd = inotify_init1(IN_CLOEXEC);
|
||||
if (inofd < 0)
|
||||
{
|
||||
DPRINTF(E_FATAL, L_SCAN, "Could not create inotify fd: %s\n", strerror(errno));
|
||||
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
CHECK_NULL(L_SCAN, inoev = event_new(evbase_lib, inofd, EV_READ, inotify_cb, NULL));
|
||||
|
||||
inoev = event_new(evbase_lib, inofd, EV_READ, inotify_cb, NULL);
|
||||
mntfd = mountwatch_init();
|
||||
if (mntfd >= 0)
|
||||
CHECK_NULL(L_SCAN, mntev = event_new(evbase_lib, mntfd, EV_READ, mount_cb, NULL));
|
||||
|
||||
#ifndef __linux__
|
||||
deferred_inoev = evtimer_new(evbase_lib, inotify_deferred_cb, NULL);
|
||||
if (!deferred_inoev)
|
||||
{
|
||||
DPRINTF(E_LOG, L_SCAN, "Could not create deferred inotify event\n");
|
||||
|
||||
return -1;
|
||||
}
|
||||
CHECK_NULL(L_SCAN, deferred_inoev = evtimer_new(evbase_lib, inotify_deferred_cb, NULL));
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
@ -1648,6 +1696,9 @@ inofd_event_unset(void)
|
||||
#ifndef __linux__
|
||||
event_free(deferred_inoev);
|
||||
#endif
|
||||
event_free(mntev);
|
||||
mountwatch_deinit();
|
||||
|
||||
event_free(inoev);
|
||||
close(inofd);
|
||||
}
|
||||
@ -1674,6 +1725,7 @@ filescanner_initscan()
|
||||
{
|
||||
/* Enable inotify */
|
||||
event_add(inoev, NULL);
|
||||
event_add(mntev, NULL);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -1692,6 +1744,7 @@ filescanner_rescan()
|
||||
{
|
||||
/* Enable inotify */
|
||||
event_add(inoev, NULL);
|
||||
event_add(mntev, NULL);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -1710,6 +1763,7 @@ filescanner_metarescan()
|
||||
{
|
||||
/* Enable inotify */
|
||||
event_add(inoev, NULL);
|
||||
event_add(mntev, NULL);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -1727,6 +1781,7 @@ filescanner_fullrescan()
|
||||
{
|
||||
/* Enable inotify */
|
||||
event_add(inoev, NULL);
|
||||
event_add(mntev, NULL);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -2210,9 +2265,7 @@ filescanner_init(void)
|
||||
|
||||
ret = inofd_event_set();
|
||||
if (ret < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -4,6 +4,13 @@
|
||||
|
||||
#include "db.h"
|
||||
|
||||
enum mountwatch_event
|
||||
{
|
||||
MOUNTWATCH_ERR = -1,
|
||||
MOUNTWATCH_NONE = 0,
|
||||
MOUNTWATCH_MOUNT = 1,
|
||||
MOUNTWATCH_UNMOUNT = 2,
|
||||
};
|
||||
|
||||
/* --------------------------- Actual scanners ---------------------------- */
|
||||
|
||||
@ -84,4 +91,13 @@ playlist_add(const char *path);
|
||||
int
|
||||
write_metadata_ffmpeg(const struct media_file_info *mfi);
|
||||
|
||||
int
|
||||
mountwatch_event_get(char **path);
|
||||
|
||||
void
|
||||
mountwatch_deinit(void);
|
||||
|
||||
int
|
||||
mountwatch_init(void);
|
||||
|
||||
#endif /* !__FILESCANNER_H__ */
|
||||
|
||||
161
src/library/filescanner_mountwatch.c
Normal file
161
src/library/filescanner_mountwatch.c
Normal file
@ -0,0 +1,161 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include "filescanner.h"
|
||||
#include "config.h"
|
||||
#include "misc.h"
|
||||
#include "logger.h"
|
||||
|
||||
#ifdef HAVE_LIBMOUNT
|
||||
#include <libmount/libmount.h>
|
||||
|
||||
static struct libmnt_monitor *mountwatch_monitor;
|
||||
static struct libmnt_table *mountwatch_table;
|
||||
|
||||
// path is allocated if a change is found
|
||||
static int
|
||||
compare_tables(char **path, struct libmnt_table *old_tab, struct libmnt_table *new_tab)
|
||||
{
|
||||
struct libmnt_iter *iter;
|
||||
struct libmnt_fs *fs;
|
||||
const char *target;
|
||||
|
||||
*path = NULL;
|
||||
|
||||
CHECK_NULL(L_SCAN, iter = mnt_new_iter(MNT_ITER_FORWARD));
|
||||
|
||||
// Find new mounts (in new_tab but not in old_tab)
|
||||
mnt_reset_iter(iter, MNT_ITER_FORWARD);
|
||||
while (mnt_table_next_fs(new_tab, iter, &fs) == 0)
|
||||
{
|
||||
target = mnt_fs_get_target(fs);
|
||||
if (!target || mnt_table_find_target(old_tab, target, MNT_ITER_FORWARD))
|
||||
continue;
|
||||
|
||||
*path = strdup(target);
|
||||
return MOUNTWATCH_MOUNT;
|
||||
}
|
||||
|
||||
// Find removed mounts (in old_tab but not in new_tab)
|
||||
mnt_reset_iter(iter, MNT_ITER_FORWARD);
|
||||
while (mnt_table_next_fs(old_tab, iter, &fs) == 0)
|
||||
{
|
||||
target = mnt_fs_get_target(fs);
|
||||
if (!target || mnt_table_find_target(new_tab, target, MNT_ITER_FORWARD))
|
||||
continue;
|
||||
|
||||
*path = strdup(target);
|
||||
return MOUNTWATCH_UNMOUNT;
|
||||
}
|
||||
|
||||
mnt_free_iter(iter);
|
||||
return MOUNTWATCH_NONE;
|
||||
}
|
||||
|
||||
int
|
||||
mountwatch_event_get(char **path)
|
||||
{
|
||||
struct libmnt_table *newtable = NULL;
|
||||
enum mountwatch_event event;
|
||||
int ret;
|
||||
|
||||
*path = NULL;
|
||||
|
||||
ret = mnt_monitor_event_cleanup(mountwatch_monitor);
|
||||
if (ret < 0)
|
||||
{
|
||||
DPRINTF(E_WARN, L_SCAN, "Monitor cleanup failed: %s\n", strerror(-ret));
|
||||
goto error;
|
||||
}
|
||||
|
||||
CHECK_NULL(L_SCAN, newtable = mnt_new_table());
|
||||
|
||||
ret = mnt_table_parse_mtab(newtable, NULL);
|
||||
if (ret < 0)
|
||||
{
|
||||
DPRINTF(E_WARN, L_SCAN, "Failed to reload mount table: %s\n", strerror(-ret));
|
||||
goto error;
|
||||
}
|
||||
|
||||
event = compare_tables(path, mountwatch_table, newtable);
|
||||
|
||||
mnt_unref_table(mountwatch_table);
|
||||
mountwatch_table = newtable;
|
||||
|
||||
return event;
|
||||
|
||||
error:
|
||||
if (newtable)
|
||||
mnt_unref_table(newtable);
|
||||
|
||||
return MOUNTWATCH_ERR;
|
||||
}
|
||||
|
||||
void
|
||||
mountwatch_deinit(void)
|
||||
{
|
||||
mnt_unref_table(mountwatch_table);
|
||||
mountwatch_table = NULL;
|
||||
|
||||
mnt_unref_monitor(mountwatch_monitor);
|
||||
mountwatch_monitor = NULL;
|
||||
}
|
||||
|
||||
int
|
||||
mountwatch_init(void)
|
||||
{
|
||||
int ret, fd;
|
||||
|
||||
CHECK_NULL(L_SCAN, mountwatch_monitor = mnt_new_monitor());
|
||||
|
||||
ret = mnt_monitor_enable_kernel(mountwatch_monitor, 1);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
fd = mnt_monitor_get_fd(mountwatch_monitor);
|
||||
if (fd < 0)
|
||||
goto error;
|
||||
|
||||
mountwatch_table = mnt_new_table();
|
||||
if (!mountwatch_table)
|
||||
goto error;
|
||||
|
||||
ret = mnt_table_parse_mtab(mountwatch_table, NULL);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
return fd;
|
||||
|
||||
error:
|
||||
DPRINTF(E_LOG, L_SCAN, "Error initializing libmount, mount/unmount events won't be detected\n");
|
||||
mountwatch_deinit();
|
||||
errno = -ret; //TODO
|
||||
return -1;
|
||||
}
|
||||
#else
|
||||
int
|
||||
mountwatch_changed_get(char **path)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
void
|
||||
mountwatch_deinit(void)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int
|
||||
mountwatch_init(void)
|
||||
{
|
||||
DPRINTF(E_LOG, L_SCAN, "No libmount on this platform, mount/unmount events won't be detected\n");
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
Loading…
x
Reference in New Issue
Block a user