Use INotify to watch a file with multiple symlinks
Asked Answered
P

1

6

So I setup some code to watch a config file for edits, which worked until I used VIM to edit the file, then I had to also watch the directory for renames and creations. Then I discovered that didn't catch renames higher in the path hierarchy. Then I looked into symlinks...gaaahhhh!

First setup a made up example showing one (of many) tricky symlink scenarios:

mkdir config1
touch config1/config
ln -s config1 machine1

mkdir config2
touch config2/config
ln -s config2 machine2

ln -s machine1 active

Now, given a filename like active/config that I want to watch, I can see how to get an inotify watch descriptor for:

config1/ -> watch active/ follow symlinks (watches inode for config1)
active/ -> watch active/ dont follow symlinks (watches inode for active symlink
active/config -> watch active/config (watches inode for config1/config)

How do I add a watch on the machine1 symlink? Do I need to find some way to manually walk each symlink adding watches for each along the way? How?

The purpose is to allow:

mkdir config3
touch config3/config
ln -s -f -n config3 machine1

And have inotify warn that active/config has been redirected. At the moment it looks like I'll have to add a watch for:

- target file inode
- every directory inode leading to the file (to detect moves/renames of directories)
- every symlink inode involved in reaching any of the above

There must be an easier way to just watch one file? Have I strayed from the path or is this really the way?

Polypus answered 27/2, 2015 at 16:13 Comment(3)
You are not watching "the one file" -- you are watching its full path, file included. That, plus inotify not being recursive, covers the first 2 points. Existence of symlinks mandates your last point.Limewater
Yes, I do understand that INotify must be set up to watch all the inodes involved in reaching the file of interest, but I'm unclear what the use case is that doesn't require doing what I've described? It's complex (I didn't even mention '..' and symlinks), easy to screw up (reference counting multiply encountered directories and symlinks), has lots of race conditions (watching a symlink while someone is changing it?), and everyone seems to need it. But there's not a library that does that? Huh.Polypus
With all its shortcomings, inotify is still a lot better than the dnotify it replaces (see, for example, lwn.net/Articles/604686). As for lack of libraries, you did not mention you were looking for them; libinotiftytools can slightly simplify your tasks (it does the syscalls for you), but I can't find anything that covers your exact use-case.Limewater
L
6

My answer is a straight "yes, you are doing it right".

After carefully reading the inotify syscall manpage, I cannot see any way of short of watching every step of a (possibly-symlinked) path-to-a-file in order to detect any and all changes to the full path.

This just seems to be the way inotify works: it only looks at specific files or folders, and it does not do recursion on its own. That, plus having to explicitly follow symlinks, seems to match your 3-step plan to the letter.

Selected quotes from the manpage:

The following further bits can be specified in mask when calling inotify_add_watch(2): IN_DONT_FOLLOW (since Linux 2.6.15)

Don't dereference pathname if it is a symbolic link.

[...]

Limitations and caveats

Inotify monitoring of directories is not recursive: to monitor subdirectories under a directory, additional watches must be created. This can take a significant amount time for large directory trees. [...]

This FAQ also lends support for your strategy re symlinks:

Q: What about the IN_ONLYDIR and IN_DONT_FOLLOW flags? IN_ONLYDIR ensures that the event occur only on a directory. If you create such watch on a file it will not emit events. IN_DONT_FOLLOW forbids following symbolic links (these ones will be monitored themselves and not the files they point to).

Limewater answered 9/3, 2015 at 17:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.