Inotifywatch: continue watch after file rotation

inotifylogrotate

I'm trying to write a script that uses inotifywatch to watch for changes in a log file. If a specific message is written to the log file, it's supposed to trigger a certain function. The script currently exists in this basic form:

while inotifywait -e modify /var/log/auth.log
do
  alert=$(tail -n1 /var/log/auth.log | grep -E -o ".{0,7}password")
  if [[ $alert == "Failed password" ]]
  then
   echo "FAILURE" >> test.log
  elif [[ $alert == "cepted password" ]]
  then
   echo "LOGIN" >> test.log
  fi
done

Everything works fine – up until the point where the logfile watched by inotifywatch is rotated. Then it ceases to function. I presume that's because during rotation the watched file is renamed and no longer written to afterwards and a new file with the old name is created in its place which inotify has never been told to watch in the first place.

I tried to circumvent this by switching from inotifywatch to using tail -f but the same problem seems to apply there as well.

Now I realize this could probably be solved by creating a huge if-structure in which inotifywatch not only watches for modify, but also for file creation and restarts a watch for modification. But I like to keep things simple, so does anyone know if there's a simpler way? (And please, no, I don't want to use prefabricated solutions like fail2ban etc. – the interesting part for me is to create stuff like this myself with simple tools.)

Best Answer

inotify is used to monitor files an directory by their inode, not by their name. When the file is rotated, its content does not change any longer (except for a short period, until the daemons are reloaded so that they use the newly created log file)

AFAIK, tail -f uses the inotify system, so it won't help. But if you have a working solution with tail -f then use tail --follow=name (or tail -F) if this is supported by your version of tail (POSIX tail does not support this). tail will then monitor the file identified by its filename. Here is an excerpt of the man page:

With --follow (-f), tail defaults to following the file descriptor, which means that even if a tail'ed file is renamed, tail will con‐ tinue to track its end. This default behavior is not desirable when you really want to track the actual name of the file, not the file descriptor (e.g., log rotation). Use --follow=name in that case. That causes tail to track the named file in a way that accommodates renaming, removal and creation.

[update]

Example of use:

tail -n0 -F my_file.log \
| while read -r log_line; do
    do_something_with "$log_line"
done

Because of the pipe, the while loop executes in a sub-process, which may cause you troubles if you want to modify variables outside the loop. If you use bash, you may want to use this alternative syntax which does not have this undesirable effect (but is less readable):

while read -r log_line; do
    do_something_with "$log_line"
done < <(tail -n0 -F my_file.log)
Related Question