Monitoring filesystems with inotify

inotify is a Linux kernel subsystem which allows user land applications to watch for filesystem events, such as files being modified. inotify has been part of the Linux kernel since version 2.6.13, and therefore should already be available in modern Linux distributions.

inotify-tools

A quick way to get started with inotify is by installing inotify-tools. On CentOS 7 inotify-tools can be installed from EPEL:

yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
yum install inotify-tools

Once the package is installed you should be able to use inotifywatch and inotifywait. By default inotifywait will run until a inode event matching the arguments passed to it occurs. For example:

inotifywait --event delete_self /tmp/foobar

The command above will wait until /tmp/foobar is deleted, the output of the command will look similar to the following:

$ inotifywait --event delete_self /tmp/foobar
Setting up watches.
Watches established.
/tmp/foobar DELETE_SELF
$

The inotifywatch command works in a similar way, except it counts events and produces a summary. For example the command below will watch /etc/passwd and /etc/shadow for 60 seconds:

inotifywatch -t 60 /etc/passwd /etc/shadow

The output of the command will be similar to the following:

$ inotifywatch -t 60 /etc/passwd /etc/shadow
Establishing watches...
Finished establishing watches, now collecting statistics.
total  access  attrib  close_write  close_nowrite  open  delete_self  filename
16     5       1       1            3              4     1            /etc/passwd
7      2       1       1            0              1     1            /etc/shadow
$

Automatically running Make

A fairly common sequence when programming is running through the following steps:

  1. Modify code
  2. Re-compile
  3. Test code

A simple way to automate this is to poll the filesystem for changes, this can be done with a script similar to the following:

while true; do
    if find src/ -mmin 1 > /dev/null; do
        make
    else
        sleep 10
    fi
done

However this has two main drawbacks, you're constantly having to check files for changes, and if you're unlucky you might have to wait the entire interval between polling, 10 seconds in the example above, before make recompiles the code.

To get around this inotifywait can be used to monitor the source files for changes:

while true; do

    # Wait for changes in the src/ directory
    inotifywait \
      --recursive \
      --event create \
      --event modify \
      --exclude '.swp$' \
      src/

    # Run make after a new change
    make
done

Note: --exclude is used to prevent inotifiywait monitoring swap files written by Vim.

Processing inbound files

Another common use for inotify is watching input directories for new files. For example you could set up a script to automatically create thumbnails for new images:

#!/bin/sh
#
# Generate thumbnails for new JPG files.
#
WATCH_DIR=/var/images

inotifywait \
  --monitor \
  --recursive \
  --event close_write \
  --exclude '.*_thumb.jpg$' \
  --format='%w%f' "${WATCH_DIR}" | while read new_file
do
  mime_type="$(file --brief --mime-type "$new_file")"
  if [ "$mime_type" = 'image/jpeg' ]; then
     thumb_name="$(echo "$new_file" | sed 's/\.[a-z]*$//')_thumb.jpg"

     echo "Creating: ${thumb_name}"
     convert "$new_file"  -resize 200 -strip "$thumb_name"
  else
     echo "Skipping ${new_file}, incorrect mime type (${mime_type})"
  fi
done

When the script above is run it will start watching /var/images for new JPG files. Files can then be copied to the directory, e.g.:

$ scp cat.jpg  host.example.com:/var/images
user@host.example.com's password:

The script will detect the new file and make the corresponding thumbnail image (cat_thumb.jpg):

$ generate_thumbnails.sh
Setting up watches.  Beware: since -r was given, this may take a while!
Watches established.
Creating: /var/images/cat_thumb.jpg