mirror of
https://github.com/owntone/owntone-server.git
synced 2025-10-29 15:55:02 -04:00
Merge pull request #1918 from owntone/libmount1
[scan] Use libmount to detect filesystem mount events
This commit is contained in:
commit
f85a800644
@ -103,6 +103,9 @@ AC_SEARCH_LIBS([copy_file_range], [c],
|
|||||||
AC_SEARCH_LIBS([fcopyfile], [c],
|
AC_SEARCH_LIBS([fcopyfile], [c],
|
||||||
[AC_DEFINE([HAVE_FCOPYFILE], 1,
|
[AC_DEFINE([HAVE_FCOPYFILE], 1,
|
||||||
[Define to 1 if you have fcopyfile])])
|
[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([log10], [m])
|
||||||
AC_SEARCH_LIBS([lrint], [m])
|
AC_SEARCH_LIBS([lrint], [m])
|
||||||
|
|||||||
@ -88,6 +88,7 @@ owntone_SOURCES = main.c \
|
|||||||
library/filescanner.c library/filescanner.h \
|
library/filescanner.c library/filescanner.h \
|
||||||
library/filescanner_ffmpeg.c library/filescanner_playlist.c \
|
library/filescanner_ffmpeg.c library/filescanner_playlist.c \
|
||||||
library/filescanner_smartpl.c library/filescanner_itunes.c \
|
library/filescanner_smartpl.c library/filescanner_itunes.c \
|
||||||
|
library/filescanner_mountwatch.c \
|
||||||
library/rssscanner.c \
|
library/rssscanner.c \
|
||||||
library.c library.h \
|
library.c library.h \
|
||||||
$(MDNS_SRC) mdns.h \
|
$(MDNS_SRC) mdns.h \
|
||||||
|
|||||||
@ -115,6 +115,7 @@ struct stacked_dir {
|
|||||||
|
|
||||||
static int inofd;
|
static int inofd;
|
||||||
static struct event *inoev;
|
static struct event *inoev;
|
||||||
|
static struct event *mntev;
|
||||||
static struct deferred_pl *playlists;
|
static struct deferred_pl *playlists;
|
||||||
static struct stacked_dir *dirstack;
|
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);
|
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)
|
if (ie->mask & IN_MOVE_SELF)
|
||||||
{
|
{
|
||||||
/* A directory we know about, that got moved from a place
|
/* 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 inotify_event *ie;
|
||||||
struct watch_info wi;
|
struct watch_info wi;
|
||||||
|
struct stat sb;
|
||||||
uint8_t *buf;
|
uint8_t *buf;
|
||||||
uint8_t *ptr;
|
uint8_t *ptr;
|
||||||
char path[PATH_MAX];
|
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);
|
DPRINTF(E_DBG, L_SCAN, "%s deleted or backing filesystem unmounted!\n", wi.path);
|
||||||
|
|
||||||
db_watch_delete_bywd(ie->wd);
|
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';
|
path[0] = '\0';
|
||||||
@ -1614,28 +1617,77 @@ inotify_cb(int fd, short event, void *arg)
|
|||||||
event_add(inoev, NULL);
|
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 */
|
/* Thread: main & scan */
|
||||||
static int
|
static int
|
||||||
inofd_event_set(void)
|
inofd_event_set(void)
|
||||||
{
|
{
|
||||||
|
int mntfd;
|
||||||
|
|
||||||
inofd = inotify_init1(IN_CLOEXEC);
|
inofd = inotify_init1(IN_CLOEXEC);
|
||||||
if (inofd < 0)
|
if (inofd < 0)
|
||||||
{
|
{
|
||||||
DPRINTF(E_FATAL, L_SCAN, "Could not create inotify fd: %s\n", strerror(errno));
|
DPRINTF(E_FATAL, L_SCAN, "Could not create inotify fd: %s\n", strerror(errno));
|
||||||
|
|
||||||
return -1;
|
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);
|
// If mountwatch cannot be initialized we create mntev as a timer (that will
|
||||||
|
// never trigger), so that all the event_add() don't need a "if (mntev)" test.
|
||||||
|
mntfd = mountwatch_init();
|
||||||
|
if (mntfd >= 0)
|
||||||
|
CHECK_NULL(L_SCAN, mntev = event_new(evbase_lib, mntfd, EV_READ, mount_cb, NULL));
|
||||||
|
else
|
||||||
|
CHECK_NULL(L_SCAN, mntev = evtimer_new(evbase_lib, mount_cb, NULL));
|
||||||
|
|
||||||
#ifndef __linux__
|
#ifndef __linux__
|
||||||
deferred_inoev = evtimer_new(evbase_lib, inotify_deferred_cb, NULL);
|
CHECK_NULL(L_SCAN, 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;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -1648,6 +1700,9 @@ inofd_event_unset(void)
|
|||||||
#ifndef __linux__
|
#ifndef __linux__
|
||||||
event_free(deferred_inoev);
|
event_free(deferred_inoev);
|
||||||
#endif
|
#endif
|
||||||
|
event_free(mntev);
|
||||||
|
mountwatch_deinit();
|
||||||
|
|
||||||
event_free(inoev);
|
event_free(inoev);
|
||||||
close(inofd);
|
close(inofd);
|
||||||
}
|
}
|
||||||
@ -1674,6 +1729,7 @@ filescanner_initscan()
|
|||||||
{
|
{
|
||||||
/* Enable inotify */
|
/* Enable inotify */
|
||||||
event_add(inoev, NULL);
|
event_add(inoev, NULL);
|
||||||
|
event_add(mntev, NULL);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -1692,6 +1748,7 @@ filescanner_rescan()
|
|||||||
{
|
{
|
||||||
/* Enable inotify */
|
/* Enable inotify */
|
||||||
event_add(inoev, NULL);
|
event_add(inoev, NULL);
|
||||||
|
event_add(mntev, NULL);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -1710,6 +1767,7 @@ filescanner_metarescan()
|
|||||||
{
|
{
|
||||||
/* Enable inotify */
|
/* Enable inotify */
|
||||||
event_add(inoev, NULL);
|
event_add(inoev, NULL);
|
||||||
|
event_add(mntev, NULL);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -1727,6 +1785,7 @@ filescanner_fullrescan()
|
|||||||
{
|
{
|
||||||
/* Enable inotify */
|
/* Enable inotify */
|
||||||
event_add(inoev, NULL);
|
event_add(inoev, NULL);
|
||||||
|
event_add(mntev, NULL);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -2210,9 +2269,7 @@ filescanner_init(void)
|
|||||||
|
|
||||||
ret = inofd_event_set();
|
ret = inofd_event_set();
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
return -1;
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,13 @@
|
|||||||
|
|
||||||
#include "db.h"
|
#include "db.h"
|
||||||
|
|
||||||
|
enum mountwatch_event
|
||||||
|
{
|
||||||
|
MOUNTWATCH_ERR = -1,
|
||||||
|
MOUNTWATCH_NONE = 0,
|
||||||
|
MOUNTWATCH_MOUNT = 1,
|
||||||
|
MOUNTWATCH_UNMOUNT = 2,
|
||||||
|
};
|
||||||
|
|
||||||
/* --------------------------- Actual scanners ---------------------------- */
|
/* --------------------------- Actual scanners ---------------------------- */
|
||||||
|
|
||||||
@ -84,4 +91,13 @@ playlist_add(const char *path);
|
|||||||
int
|
int
|
||||||
write_metadata_ffmpeg(const struct media_file_info *mfi);
|
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__ */
|
#endif /* !__FILESCANNER_H__ */
|
||||||
|
|||||||
162
src/library/filescanner_mountwatch.c
Normal file
162
src/library/filescanner_mountwatch.c
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
#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_event_get(char **path)
|
||||||
|
{
|
||||||
|
*path = NULL;
|
||||||
|
return MOUNTWATCH_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
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