Monitoring mount point changes via /proc/mounts
Asked Answered
R

4

11

According proc manual, one can monitor for mount point changes in linux system by opening "/proc/mounts", and adding the file descriptor to read fd_set in select() call.

Following piece of code works on Ubuntu 9.04, and not in Ubuntu 10.04 (with 2.6.32 linux kernel):

int mfd = open("/proc/mounts", O_RDONLY, 0);

fd_set rfds;
struct timeval tv;
int rv;

FD_ZERO(&rfds);
FD_SET(mfd, &rfds);
tv.tv_sec = 5;
tv.tv_usec = 0;

int changes = 0;
while ((rv = select(mfd+1, &rfds, NULL, NULL, &tv)) >= 0) {
    if (FD_ISSET(mfd, &rfds)) {
        fprintf(stdout, "Mount points changed. %d.\n", changes++);
    }

    FD_ZERO(&rfds);
    FD_SET(mfd, &rfds);
    tv.tv_sec = 5;
    tv.tv_usec = 0;

    if (changes > 10) {
        exit(EXIT_FAILURE);
    }
}

Compilable snippet.

The file descriptor is always readable in one machine, and hence it keeps popping up in the select call. Even there are no changes in mounts.

Am I missing something here?

Thanks in advance for any help!

man 5 proc:

/proc/[pid]/mounts (since Linux 2.4.19)

This is a list of all the file systems currently mounted in the process's mount namespace. The format of this file is documented in fstab(5). Since kernel version 2.6.15, this file is pollable: after opening the file for reading, a change in this file (i.e., a file system mount or unmount) causes select(2) to mark the file descriptor as readable, and poll(2) and epoll_wait(2) mark the file as having an error condition.

Russell answered 21/2, 2011 at 20:16 Comment(1)
strace output on the failing machine. gist.github.com/837605#gistcomment-21716Russell
E
15

There was a bugfix in linux kernel describing that behavior:

SUSv3 says "Regular files shall always poll TRUE for reading and writing". see http://www.opengroup.org/onlinepubs/009695399/functions/poll.html

So, you have to use poll with POLLPRI | POLLERR flags. Something like this:


     int mfd = open("/proc/mounts", O_RDONLY, 0);
     struct pollfd pfd;
     int rv;

     int changes = 0;
     pfd.fd = mfd;
     pfd.events = POLLERR | POLLPRI;
     pfd.revents = 0;
     while ((rv = poll(&pfd, 1, 5)) >= 0) {
          if (pfd.revents & POLLERR) {
               fprintf(stdout, "Mount points changed. %d.\n", changes++);
          }

          pfd.revents = 0;
          if (changes > 10) {
               exit(EXIT_FAILURE);
          }
     }

Extenuate answered 21/2, 2011 at 23:47 Comment(2)
I would also like to point out for people that are slow like myself, that you can also use "select(mfd+1, NULL, NULL, &rfds, &tv)" like that in the question's code and it will work also. (3rd arg is for "exceptional" condition). It took me a while to realize this :)Epidermis
This is a very old post and POLLPRI is not required on modern GNU/Linux systems.Cutright
C
4

The documentation that you pointed to is incorrect. To wait on mount changes using select(), the /proc/mounts or /proc/pid/mounts file descriptor should set be in exceptfds, not readfds. Just swap the 2nd and 4th arguments in your program. File descriptors associated with regular files are required by POSIX to always be readable.

Cerebral answered 21/2, 2011 at 23:36 Comment(1)
You are right. exceptfds also does the trick. The documentation, I quoted, is from the machine I was running in. I guess the proc manual has to be changed.Russell
W
1

To complement the main example posted on question, here's another example using GLib and GIO libraries, to listen to mount changes by monitoring /proc/self/mountinfo:

/* Compile with:
 * gcc -g -O0 `pkg-config --cflags --libs gio-2.0` -o test test.c
 */

#include <glib.h>
#include <gio/gio.h>

static gboolean
proc_mounts_changed (GIOChannel   *channel,
                     GIOCondition  cond,
                     gpointer      user_data)
{
  if (cond & G_IO_ERR)
    {
      g_message ("MOUNTS CHANGED!"); 
    }
  return TRUE;
}

int
main (int argc, char *argv[])
{
  GIOChannel *proc_mounts_channel;
  GSource *proc_mounts_watch_source;
  GError *error = NULL;
  GMainLoop *loop;

  proc_mounts_channel = g_io_channel_new_file ("/proc/self/mountinfo", "r", &error);
  if (proc_mounts_channel == NULL)
    {
      g_warning ("Error creating IO channel for %s: %s (%s, %d)", "/proc/self/mountinfo",
               error->message, g_quark_to_string (error->domain), error->code);
      g_error_free (error);
      return error->code;
    }

  proc_mounts_watch_source = g_io_create_watch (proc_mounts_channel, G_IO_ERR);
  g_source_set_callback (proc_mounts_watch_source,
                         (GSourceFunc) proc_mounts_changed,
                         NULL, NULL);
  g_source_attach (proc_mounts_watch_source,
                   g_main_context_get_thread_default ());
  g_source_unref (proc_mounts_watch_source);
  g_io_channel_unref (proc_mounts_channel);

  loop = g_main_loop_new (NULL, FALSE);

  /* Run the main loop, program can be ended with CTRL+C */
  g_main_loop_run (loop);
  g_main_loop_unref (loop);

  return 0;
}
Wehner answered 17/9, 2017 at 9:9 Comment(0)
A
-1

If you do

ls -l /proc/mounts

You will see that time is keep on changing that means mount data is being constantly updated even though not much is changed. So looks like the current method of monitoring mounts is not workable.

Araldo answered 21/2, 2011 at 20:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.