]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
libmount: (monitor) make mnt_monitor_next_changed() usable for epoll version too
authorKarel Zak <kzak@redhat.com>
Tue, 6 Jan 2015 15:18:52 +0000 (16:18 +0100)
committerKarel Zak <kzak@redhat.com>
Tue, 6 Jan 2015 15:19:02 +0000 (16:19 +0100)
Signed-off-by: Karel Zak <kzak@redhat.com>
libmount/src/monitor.c

index 2ec2510ed31b1e69a25f8be63d94f04a48432cab..f0d91d9311ee23efe0934fbd1a705de12e37b5e6 100644 (file)
@@ -336,7 +336,8 @@ int mnt_monitor_enable_userspace(struct libmnt_monitor *mn, int enable, const ch
        if (mnt_has_regular_mtab(NULL, NULL))
                return -ENOSYS;
 
-       filename = mnt_get_utab_path();         /* /run/mount/utab */
+       if (!filename)
+               filename = mnt_get_utab_path();         /* /run/mount/utab */
        if (!filename) {
                DBG(MONITOR, ul_debugobj(mn, "failed to get userspace mount table path"));
                return -EINVAL;
@@ -451,9 +452,22 @@ int mnt_monitor_enable_kernel(struct libmnt_monitor *mn, int enable)
        if (!me)
                goto err;
 
+       /* If you want to use epoll FD in another epoll then top level
+        * epoll_wait() will drain all events from low-level FD if the
+        * low-level FD is not added with EPOLLIN. It means without EPOLLIN it
+        * it's impossible to detect which low-level FD has been active.
+        *
+        * Unfortunately, use EPOLLIN for mountinfo is tricky because in this
+        * case kernel returns events all time (we don't read from the FD).
+        * The solution is to use also edge-triggered (EPOLLET) flag, then
+        * kernel generate events on moutinfo changes only. The disadvantage is
+        * that we have to drain initial event generated by EPOLLIN after
+        * epoll_ctl(ADD). See monitor_modify_epoll().
+        */
+       me->events = EPOLLPRI | EPOLLIN | EPOLLET;
+
        me->type = MNT_MONITOR_TYPE_KERNEL;
        me->opers = &kernel_opers;
-       me->events = EPOLLPRI;
        me->path = strdup(_PATH_PROC_MOUNTINFO);
        if (!me->path)
                goto err;
@@ -493,7 +507,11 @@ static int monitor_modify_epoll(struct libmnt_monitor *mn,
                        if (errno != EEXIST)
                                goto err;
                }
-
+               if (me->events & (EPOLLIN | EPOLLET)) {
+                       /* Drain initial events generated for /proc/self/mountinfo */
+                       struct epoll_event events[1];
+                       while (epoll_wait(mn->fd, events, 1, 0) > 0);
+               }
        } else if (me->fd) {
                DBG(MONITOR, ul_debugobj(mn, " remove fd=%d (for %s)", me->fd, me->path));
                if (epoll_ctl(mn->fd, EPOLL_CTL_DEL, me->fd, NULL) < 0) {
@@ -554,14 +572,10 @@ int mnt_monitor_close_fd(struct libmnt_monitor *mn)
  * @mn: monitor
  *
  * The file descriptor is associated with all monitored files and it's usable
- * for example for epoll.
- *
- * Note that if you want to use the @fd in your epoll then you will get only
- * notification, but mnt_monitor_next_changed() does not work in this case. You
- * have to call mnt_monitor_event_cleanup() after each event if you do not use
- * mnt_monitor_next_changed().
+ * for example for epoll. You have to call mnt_monitor_event_cleanup() or
+ * mnt_monitor_next_changed() after each event.
  *
- * Returns: >=0 on success, <0 on error
+ * Returns: >=0 (fd) on success, <0 on error
  */
 int mnt_monitor_get_fd(struct libmnt_monitor *mn)
 {
@@ -664,7 +678,7 @@ static struct monitor_entry *get_changed(struct libmnt_monitor *mn)
  * @filename: returns changed file (optional argument)
  * @type: returns MNT_MONITOR_TYPE_* (optional argument)
  *
- * The function does not wait and it's designed to provide details about chnages.
+ * The function does not wait and it's designed to provide details about changes.
  *
  * Returns: 0 on success, 1 no change, <0 on error
  */
@@ -819,6 +833,7 @@ int test_epoll(struct libmnt_test *ts, int argc, char *argv[])
 
        printf("waiting for changes...\n");
        do {
+               const char *filename = NULL;
                struct epoll_event events[1];
                int n = epoll_wait(efd, events, 1, -1);
 
@@ -830,9 +845,9 @@ int test_epoll(struct libmnt_test *ts, int argc, char *argv[])
                if (n == 0 || events[0].data.fd != fd)
                        continue;
 
-               printf(" change detected\n");
-               mnt_monitor_event_cleanup(mn);
-
+               printf(" top-level FD active\n");
+               while (mnt_monitor_next_changed(mn, &filename, NULL) == 0)
+                       printf("  %s: change detected\n", filename);
        } while (1);
 
        rc = 0;