summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libmount/src/monitor.c148
1 files changed, 75 insertions, 73 deletions
diff --git a/libmount/src/monitor.c b/libmount/src/monitor.c
index 15e34f013..cf91f32c3 100644
--- a/libmount/src/monitor.c
+++ b/libmount/src/monitor.c
@@ -46,7 +46,7 @@ struct libmnt_monitor {
struct monitor_opers {
int (*op_get_fd)(struct libmnt_monitor *, struct monitor_entry *);
int (*op_close_fd)(struct libmnt_monitor *, struct monitor_entry *);
- int (*op_verify_change)(struct libmnt_monitor *, struct monitor_entry *);
+ void (*op_event_cleanup)(struct libmnt_monitor *, struct monitor_entry *);
};
static int monitor_modify_epoll(struct libmnt_monitor *mn,
@@ -199,8 +199,9 @@ static int userspace_monitor_close_fd(struct libmnt_monitor *mn,
static int userspace_monitor_get_fd(struct libmnt_monitor *mn,
struct monitor_entry *me)
{
- int wd, rc;
+ int wd, rc, dummy_fd;
char *dirname, *sep;
+ char *filename = NULL;
assert(mn);
assert(me);
@@ -220,72 +221,65 @@ static int userspace_monitor_get_fd(struct libmnt_monitor *mn,
rc = mkdir(dirname, S_IWUSR|
S_IRUSR|S_IRGRP|S_IROTH|
S_IXUSR|S_IXGRP|S_IXOTH);
+
+ if (sep && sep > dirname)
+ *(sep - 1) = '/'; /* set '/' back to the path */
+
if (rc && errno != EEXIST)
goto err;
+ /*
+ * libmount uses rename(2) to atomically update utab/mtab, monitor
+ * rename changes is too tricky. It seems better to monitor utab
+ * lockfile close.
+ */
+ if (asprintf(&filename, "%s.lock", me->path) <= 0)
+ goto err;
+
+ /* make sure the file exists */
+ dummy_fd = open(filename, O_RDONLY|O_CREAT|O_CLOEXEC,
+ S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH);
+ if (dummy_fd < 0)
+ goto err;
+ close(dummy_fd);
+
/* initialize inotify stuff */
me->fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
if (me->fd < 0)
goto err;
- /*
- * libmount uses rename(2) to atomically update utab/mtab, the finame
- * change is possible to detect by IN_MOVE_TO inotify event.
- */
- wd = inotify_add_watch(me->fd, dirname, IN_MOVED_TO);
+
+ wd = inotify_add_watch(me->fd, filename, IN_CLOSE_NOWRITE);
if (wd < 0)
goto err;
- if (sep && sep > dirname)
- *(sep - 1) = '/'; /* set '/' back to the path */
-
+ free(filename);
return me->fd;
err:
rc = -errno;
+ free(filename);
+ if (me->fd)
+ close(me->fd);
+ me->fd = -1;
DBG(MONITOR, ul_debugobj(mn, "failed to create userspace monitor [rc=%d]", rc));
return rc;
}
-
-
/*
- * For 'utab' changes we have to monitor all /run/mount directory, this
- * function is necessary to avoid false positives.
+ * drain inotify buffer
*/
-static int userspace_monitor_verify_change(struct libmnt_monitor *mn,
- struct monitor_entry *me)
+static void userspace_event_cleanup(struct libmnt_monitor *mn,
+ struct monitor_entry *me)
{
- char wanted[NAME_MAX + 1];
char buf[sizeof(struct inotify_event) + NAME_MAX + 1];
- struct inotify_event *event;
- char *p;
- ssize_t r;
- int rc = 0;
- DBG(MONITOR, ul_debugobj(mn, "checking fd=%d for userspace changes", me->fd));
-
- p = strrchr(me->path, '/');
- if (!p)
- p = me->path;
- else
- p++;
- strncpy(wanted, p, sizeof(wanted) - 1);
- wanted[sizeof(wanted) - 1] = '\0';
- rc = 0;
+ if (!me || me->fd < 0)
+ return;
- while ((r = read(me->fd, buf, sizeof(buf))) > 0) {
- for (p = buf; p < buf + r; ) {
- event = (struct inotify_event *) p;
+ DBG(MONITOR, ul_debugobj(mn, "drain userspace monitor inotify"));
- if (strcmp(event->name, wanted) == 0)
- rc = 1;
- p += sizeof(struct inotify_event) + event->len;
- }
- if (rc)
- break;
- }
-
- return rc;
+ /* the 'fd' is non-blocking */
+ while (read(me->fd, buf, sizeof(buf)) > 0);
}
/*
@@ -294,7 +288,7 @@ static int userspace_monitor_verify_change(struct libmnt_monitor *mn,
static const struct monitor_opers userspace_opers = {
.op_get_fd = userspace_monitor_get_fd,
.op_close_fd = userspace_monitor_close_fd,
- .op_verify_change = userspace_monitor_verify_change
+ .op_event_cleanup = userspace_event_cleanup
};
/**
@@ -600,8 +594,9 @@ err:
* @mn: monitor
* @timeout: number of milliseconds, -1 block indefinitely, 0 return immediately
*
- * Waits for the next change, after the change it's recommended to verify the
- * change by mnt_monitor_next_change() or mnt_monitor_is_changed().
+ * Waits for the next change, after the event it's recommended to use
+ * mnt_monitor_next_change() to get more details about the change or
+ * at least mnt_monitor_event_cleanup().
*
* Returns: 1 success (something changed), 0 timeout, <0 error.
*/
@@ -632,6 +627,9 @@ int mnt_monitor_wait(struct libmnt_monitor *mn, int timeout)
return -EINVAL;
me->changed = 1;
+ if (me->opers->op_event_cleanup != NULL)
+ me->opers->op_event_cleanup(mn, me);
+
return 1; /* success */
}
@@ -658,25 +656,6 @@ static struct monitor_entry *get_changed(struct libmnt_monitor *mn)
* The function does not wait and it's designed to provide details and to avoid
* false positives after detected activity on monitor file descriptor.
*
- * The function is mutually exclusive to mnt_monitor_is_changed().
- *
- * Example:
- * <informalexample>
- * <programlisting>
- * fd = mnt_monitor_get_fd(mn);
- *
- * ... <add 'fd' to your epoll> ...
- *
- * do {
- * if (epoll_wait(efd, events, maxevents, -1) <= 0)
- * break;
- *
- * while (mnt_monitor_next_change(mn, &filename, NULL) == 0)
- * printf("%s changed\n", filename);
- * } while (1);
- * </programlisting>
- * </informalexample>
- *
* Returns: 0 on success, 1 no change, <0 on error
*/
int mnt_monitor_next_changed(struct libmnt_monitor *mn,
@@ -688,12 +667,12 @@ int mnt_monitor_next_changed(struct libmnt_monitor *mn,
if (!mn || mn->fd < 0)
return -EINVAL;
-again:
+
/*
* if we previously called epoll_wait() (e.g. mnt_monitor_waith()) then
* info about unread change is already stored in monitor_entry.
*
- * We we get nothing, then ask kernel.
+ * If we get nothing, then ask kernel.
*/
me = get_changed(mn);
if (!me) {
@@ -715,18 +694,14 @@ again:
me = (struct monitor_entry *) events[0].data.ptr;
if (!me)
return -EINVAL;
+ if (me->opers->op_event_cleanup != NULL)
+ me->opers->op_event_cleanup(mn, me);
break;
} while (1);
}
me->changed = 0;
- if (me->opers->op_verify_change != NULL &&
- me->opers->op_verify_change(mn, me) != 1) {
- DBG(MONITOR, ul_debugobj(mn, " *** false positive"));
- goto again;
- }
-
if (filename)
*filename = me->path;
if (type)
@@ -736,6 +711,31 @@ again:
return 0; /* success */
}
+/**
+ * mnt_monitor_event_cleanup:
+ * @mn: monitor
+ *
+ * This function cleanups (drain) internal buffers. It's necessary to call
+ * this function after event if you do not call mnt_monitor_next_change().
+ *
+ * Returns: 0 on success, 1 no change, <0 on error
+ */
+int mnt_monitor_event_cleanup(struct libmnt_monitor *mn)
+{
+ struct monitor_entry *me;
+ struct libmnt_iter itr;
+
+ if (!mn || mn->fd < 0)
+ return -EINVAL;
+
+ mnt_reset_iter(&itr, MNT_ITER_FORWARD);
+ while (monitor_next_entry(mn, &itr, &me) == 0) {
+ if (me->opers->op_event_cleanup != NULL)
+ me->opers->op_event_cleanup(mn, me);
+ }
+ return 0;
+}
+
#ifdef TEST_PROGRAM
static struct libmnt_monitor *create_test_monitor(int argc, char *argv[])
@@ -821,6 +821,8 @@ int test_epoll(struct libmnt_test *ts, int argc, char *argv[])
continue;
printf(" change detected\n");
+ mnt_monitor_event_cleanup(mn);
+
} while (1);
rc = 0;