# Gamin the File Alteration Monitor

Gamin is a file and directory monitoring system defined to be a subset of the FAM (File Alteration Monitor) system. This is a service provided by a library which allows to detect when a file or a directory has been modified.

## Overview

Gamin is a file and directory monitoring system defined to be a subset of the FAM (File Alteration Monitor) system.

The main goals of the project are:

1. minimize the security model of FAM, the daemon runs under the user account, it is compatible with SELinux
2. simplify the code base, dropping some of the most exotic feature of FAM
3. provide an API and ABI compatible replacement for FAM
4. try to fix some other issues like resource consumption

Gamin also serves as an interface to test the inotify mechanism to improve the existing dnotify monitoring interface present in the Linux kernel.

At this point Gamin is fairly tied to Linux, portability is not a primary goal at this stage but if you have portability patches they are welcome.

From an historical point of view, gamin builds from the marmot project authored by James Willcox and Corey Bowers and then heavilly modified to turn it into a minimalist FAM replacement (Francophones will appreciate the filiation from fam to marmot and gamin.)

This library is available under the terms of the GNU LIBRARY GENERAL PUBLIC LICENSE, and a copy of it should be found in the source under the COPYING file.

## Using gamin

Basically it is exactly like for using the fam interface. From a programmer point of view this is the same API, and one can make use of the existing FAM documentation.

## Configuration

By default gamin should work without needing any configuration, but sometimes using the kernel notification APIs doesn't work or lead to troubles (for example when trying to unmount device). By default gamin revert to using polling for all paths matching `/mnt/*` or `/media/*` on Linux. This may be overriden or extended by the one of the three config files `/etc/gamin/gaminrc`, `$HOME/.gaminrc`, `/etc/gamin/mandatory_gaminrc` (note that prior to 0.1.4 only `$HOME/.gaminrc` was used and fsset was not implemented). Here is an example of such a configuration file:

    # configuration for gamin
    # Can be used to override the default behaviour.
    # notify filepath(s) : indicate to use kernel notification
    # poll filepath(s)   : indicate to use polling instead
    # fsset fsname method poll_limit : indicate what method of notification for the filesystem
    #                                  kernel - use the kernel for notification
    #                                  poll - use polling for notification
    #                                  none - don't use any notification
    #
    #                                  the poll_limit is the number of seconds
    #                                  that must pass before a resource is polled again.
    #                                  It is optional, and if it is not present the previous
    #                                  value will be used or the default.

    notify /mnt/local* /mnt/pictures* # use kernel notification on these paths
    poll /temp/*                      # use poll notification on these paths
    fsset nfs poll 10                 # use polling on nfs mounts and poll once every 10 seconds

The configuration file accepts only 3 types of command:

- notify : to express that kernel monitoring should be used for matching paths
- poll: to express that polling should be used for matching paths
- fsset: to control what notification method is used on a filesystem type

The three config files are loaded in this order:

- `/etc/gamin/gaminrc`
- `~/.gaminrc`
- `/etc/gamin/mandatory_gaminrc`

The mandatory config file allows the system administrator to override any potentially dangerous preferences set by the user.

When checking a path to guess whether polling or kernel notification should be used, gamin checks first the user provided rules in their declaration order within the configuration file and then check the predefined rules. This way the first declaration for `/mnt/local*` in the example override the default one for `/mnt/*` .

If gamin is not told to use poll on a particular path, it will then try and decide based on the filesystem the path is located on

Caveat: separators in the config file should be spaces, not tabs.

## News

### 0.1.9: Jul 27 2007

- Bug fixes: enable polling when using inotify this fixes support for NFS partitions (Alexander Larsson), do not run idle handler if not needed reduce wakeups (Alexander Larsson), handling of failure of inotify initialization (Robert Clark), force poll support if compiled without inotify and dnotify (Ray Strode)
- Portability: patches to allow compiling again on Hurd and kFreeBSD (Michael Banck), patches to ease compilation on OS X (Brendan Cully)

### 0.1.8: Oct 31 2006

- Build and portability fixes: handle sys/inotify.h, Python detection (Joseph Sacco), FreeBSD portability fix (Loïc Minier/Petr Salinger), out of tree build and install (Loïc Minier), fix build with Sun's compiler (James Andrewartha)
- Bug fixes: pkg-config cflags (Claudio Fontana), debug signal hookup fix (Bastien Nocera), client filedescriptor close trouble (Alexander Larsson), low filedescriptor handling in the server (Alexander Larsson), buffer on connection reset cleanup (Ariel T. Glenn), inotify new backend fixes (Alexander Larsson)
- Improvements: update the inotify backend to the version used by gnome-vfs (John McCutchan), minimize the timeouts usage to not wake up apps unduely (Alexander Larsson)

### 0.1.7: Oct 27 2005

- Portability fixes (Diego Pettenò, Sjoerd Simons, Daichi Kawahata)
- A small memory leak fix (Christopher Aillon)
- Fixes for gam_server segfaults (Ed Catmur, DV)
- One last patch for Python detection

### 0.1.6: Sep 8 2005

- Large revamp of the inotify back-end (John McCutchan)
- Code reorganization, changes for polling (John McCutchan)
- Portability patches for NetBSD (Johnny Lam)
- Fix compilation without inotify
- environment variable GAM_TEST_DNOTIFY to force dnotify on inotify kernels (John McCutchan)
- Inotify race conditions fixes (John McCutchan)
- Removal of some asserts resulting in crashes
- Applied some testing patches (TomPh)
- Fixed a memory leak on inotify back-end
- Python detection fix

### 0.1.5: Aug 9 2005

- Improvement of configuration, system wide configuration files and per filesystem type default (John McCutchan)
- Rewrite of the inotify back-end, reduce resources usage, tuning in case of busy resources (John McCutchan)
- Documentation updates
- Changes to compile inotify back-end on various architectures
- Debugging output improvements

### 0.1.3: Aug 2 2005

- Fix to compile on older gcc versions (Jean-Yves Lefort)
- Inotify back-end changes and optimizations (John McCutchan)
- Debug ouput cleanup, pid and process name reports (John McCutchan)
- Dropped kernel monitor bugfix (John McCutchan)
- Removed the old glist copy used for debugging
- Maintain mounted filesystems knowledge, and per fstype preferences (John McCutchan)

### 0.1.2: Jul 13 2005

- More patches from John McCutchan for the inotify back-end, inotify is now in Linus's kernel tree, a patch for the new kernel API was needed.
- Lot of cleanup patches from Neal H. Walfield affecting most of the server code.
- Fixed an authentication problem.

### 0.1.1: Jun 10 2005

- Bug fixes: gamin_data_conn_event (Mark McLoughlin), crash from bug #303932 (Frederic Crozat), Inotify and mounted media #171201 (John McCutchan), monitoring multiple files in busy directories #159748 (mounted media did not show up on Desktop), write may not be atomic (Neal H. Walfield), Monitoring a directory when it is a file.
- Portability to Hurd/Mach and various code cleanups (Neal H. Walfield)
- Added support for ~ as user home alias in .gaminrc

### 0.1.0: May 12 2005

- Close inherited file descriptors on exec of gam_server
- Cancelling a monitor send back a FAMAcknowledge based on patch by Christophe Saout
- Fixed for big files > 2GB
- Bug when monitoring a non existing directory
- Make client side thread safe (Nicholas Miell base patch)
- Unreadable directory fixes (Nickolay Shmyrev)
- A lot more regression tests, more debugging support and code cleanups.
- Better flow control handling
- Updated to latest inotify version: 0.23-6 (John McCutchan, Todor Penev, Andrei Lahun, Daniel Drake)
- MacOSX/BSDs portability using kqueue (Joe Marcus Clarke, Fred Leason)

### 0.0.26: Mar 15 2005

- Fixed an include problem showing up with gcc4
- A couple of documentation typo fixes
- fixed the crash on failed tree assert bug #150471 based on patch from Dean Brettle
- removed an incompatibility with SGI FAM #149822

### 0.0.25: Mar 1 2005

- Fix a configure problem reported by Martin Schlemmer
- Fix the /media/* and /mnt/* mount blocking problems from 0.0.24
- Fix the monitoring of directory using poll and not kernel

### 0.0.24: Feb 18 2005

- more documentation
- lot of serious bug fixes including Gnome Desktop refresh bug
- extending the framework for more debug (configure --enable-debug-api)
- extending the python bindings for watching the same resource multiple times and adding debug framework support
- growing the regression tests a lot based on python bindings
- inotify-0.19 patch from John McCutchan
- renamed python private module to _gamin to follow Python PEP 8

### 0.0.23: Feb 8 2005

- memory corruption fix from Mark on the client side.
- extending the protocol and API to allow skipping Exists and EndExists events to avoid deadlock on reconnect or when they are not used (all gam_servers should be killed after an update to 0.0.23).

### 0.0.22: Jan 31 2005

- Added Python regression test, and a python disconnect() operation.
- Applied patches from John McCutchan to fix inotify 0.18 backend.
- 3 bugs fixed.

### 0.0.21: Jan 26 2005

- Added Python bindings.
- Applied patch from Philipp Zabel to switch to inotify 0.18 protocol. This won't work with older inotify kernel versions.
- Doc fix (Mark McLoughlin).
- Removal of compilation warnings.

### 0.0.20: Jan 6 2005

- Frederic Crozat seems to have found the list corruption bug and provided a patch.
- Frederic Crozat fixed the poll only mode.

### 0.0.19: Dec 3 2004

- still chasing the loop bug, made another pass at checking GList, added own copy with memory poisonning of GList implementation.
- fixed a compile issue when compiling without debug
- Another internal bugfix.

### 0.0.18: Nov 26 2004

- still chasing the loop bug, checked and cleaned up all GList use
- patch from markmc to minimize load on busy apps

### 0.0.16: Oct 20 2004

- chasing #132354, lot of debugging, checking and testing and a bit of refactoring

### 0.0.15: Oct 16 2004

- workaround to detect loops and avoid the nasty effects, see RedHat bug #132354

### 0.0.14: Oct 3 2004

- Found and fixed the annoying bug where update were not received should fix bugs ##132429, #133665 and #134413
- new mechanism to debug on-the-fly by sending SIGUSR2 to client or server
- Added documentation about internals

### 0.0.14: Oct 3 2004

- Found and fixed the annoying bug where update were not received should fix bugs ##132429, #133665 and #134413 in Red Hat bugzilla
- new mechanism to debug on-the-fly by sending SIGUSR2 to client or server
- Added documentation about internals

### 0.0.13: Oct 1 2004

- applied portability fixes
- hardened the code while chasing a segfault

### 0.0.12: Sep 30 2004

- potential fix for a hard to reproduce looping problem.

### 0.0.11: Sep 27 2004

- inotify support compiled in by default
- fix ABI FAM compatibility problems #133162
- update to the latest version of inotify

### 0.0.10: Sep 21 2004

- fixed API/ABI incompatibility between FAM and gamin about FAMErrno and FamErrlist
- added support for a per-user configuration file $HOME/.gaminrc
- applied a portability patch from Michael Banck
- extended the documentation

### 0.0.9: Sep 1 2004

- fix konqueror crash Red Hat bug #130967
- statically excludes /mnt//* /media//* from kernel monitoring to avoid umount problems

### 0.0.8: Aug 26 2004

- inotify backend patch (Martin Schlemmer)
- documentation
- fixed 0.0.7 crashes
- fixed the kernel/poll detection code which was buggy

### 0.0.7: Aug 24 2004

- increased the test suite
- activate poll and kernel monitoring (dnotify) simultaneously
- fix monitoring of resources initially missing
- fix load problem due to dnotify on very busy resources

### 0.0.6: Aug 19 2004

- fix monitoring of file resources
- moved gam_server to $libexec since it doesn't need to be started from the command line usually
- test and fixes on x86_64
- removal all thread dependancies and conditional code

## Downloads

You can download gamin from the GNOME project pages, either as sources, and binaries or source RPMs.

## Python bindings

Starting with release 0.0.21, gamin comes with Python bindings. Use "import gamin" to load the module. It exports the main class "WatchMonitor" which handle one connection to the gam_server. Once an instance of the class has been created you can use the watch_directory and watch_file methods to register objects to monitors, and provide the callback to be called when events are generated. Like the FAM API, the python binding API is passive, i.e. one need to monitor the file descriptor provided with the get_fd() method to detect events, and call handle_one_event() (blocking) or handle_events() (non-blocking) to process incoming events and get the callbacks appropriately. You can also use the event_pending() method to detect if handle_one_event() would block or not.
Since a short example is worth a thousand words, here is a small session from the python shell:

    >>> import gamin
    >>>
    >>> def callback(path, event):
    ...     print "Got callback: %s, %s" % (path, event)
    ...
    >>> mon = gamin.WatchMonitor()
    >>> mon.watch_directory(".", callback)
    <gamin.WatchObject instance at 0xb7f7b56c>
    >>> mon.event_pending()
    1
    >>> mon.handle_one_event()
    Got callback: /u/veillard/temp, 8
    1
    >>> mon.handle_events()
    Got callback: bar1, 8
    Got callback: foo1, 8
    Got callback: /u/veillard/temp, 9
    3
    >>> mon.stop_watch(".")
    >>> del mon

The corresponding standalone code follows:

    #!/usr/bin/env python

    import gamin
    import time

    def callback(path, event):
        print "Got callback: %s, %s" % (path, event)

    mon = gamin.WatchMonitor()
    mon.watch_directory(".", callback)
    time.sleep(1)
    ret = mon.event_pending()
    if ret > 0:
        ret = mon.handle_one_event()
        ret = mon.handle_events()
    mon.stop_watch(".")
    del mon

Note the addition of the sleep timeout, it is needed because due to the round trip between the client and the gam_server, events may not be immediately available after the monitor creation to the client.

## Developers informations

The CVS base is in the GNOME project CVS base at cvs.gnome.org, the module name is gamin. See the Gnome CVS Tools page for more information on using CVS.

We expect bug reports to be entered in GNOME bugzilla or in Red Hat bugzilla.

## Contacts

The best way to contact the developers is to use the mailing-list gamin-list@gnome.org.

Before reporting a bug please double-check:

- that you are using the latest version
- that the bug hasn't been reported previously in GNOME or Red Hat bugzillas.

Then the best way is to log the bug in one of the bugzilla or join the list and post there if you think that some of gamin design or code should be changed. If you can provide a testgam scenario reproducing the problem this would really help getting it debugged and fixed, see examples in tests/scenario/ .

## FAQ

This will be created as user feedback is provided.

## Debugging Gamin

- Debugging support in gamin
- Debugging a running program
- Debugging and testing client

### Debugging support in gamin:

Both the client and server side, if compiled with debug support accept an environment variable GAM_DEBUG which is set will make them report debugging informations to stdout.

Usually for debugging you also want to use a dedicated server process so setting the GAM_CLIENT_ID environment allows to ensure this. Usually one also want to keep control over the server lifetime and not have it exit automatically after 30 seconds without connection, there is a command line flag --notimeout to gam_server for this.

A typical example of a debugging session using 2 shells would be:

    shell1: export GAM_DEBUG=
    shell1: gam_server --notimeout test

to run the server in debug mode using the ID "test"

    shell2: export GAM_DEBUG=
    shell2: export GAM_CLIENT_ID=test
    shell2: gamin_client

to run the client in a verbose session. It is perfectly possible to also run the client under a debugger, for the server it works too except the dnotify kernel interface uses a signal SIG33 which is trapped by gdb. To avoid this use the handle gdb instruction:

    (gdb) handle SIG33 nostop
    Signal        Stop      Print   Pass to program Description
    SIG33         No        Yes     Yes             Real-time event 33
    (gdb)

even better add it to your $HOME/.gdbinit .

### Debugging a running program:

Both the gam_server and client of the gamin library can get switched dynamically to a debug mode by sending them a signal SIGUSR2. In that case the program or library switches to verbose debugging and outputs the traces to a new file /tmp/gamin_debug_XXXXXX . Sending the signal again to the application or the server should switch off debugging.

### Debugging and testing client:

A debugging client program called testgam is also available in the tests subdirectory. It allow to use the interface and monitor the flow of event received. Here is an example of a session:

    paphio:~/gamin/tests -> export GAMIN_DEBUG_SERVER="../server/gam_server"
    paphio:~/gamin/tests -> ./testgam -
    > connect test
    connected to test
    >

The environment variable can be used to specify the path to the server to run, then testgam is launched in interactive mode (argument - ) and the program is asked to connect to the server for session test (the server will be started on-demand by the library if needed).

    > mkdir temp
    mkdir temp
    > mkfile temp/foo
    mkfile temp/foo
    > mondir temp
    mondir temp 0
    >
    1: /u/veillard/gamin/tests/temp Exists: NULL
    1: foo Exists: NULL
    1: /u/veillard/gamin/tests/temp EndExist: NULL
    > mkfile temp/bar
    mkfile temp/bar
    >
    1: bar Created: NULL
    > rmfile temp/foo
    rmfile temp/foo
    >
    1: foo Deleted: NULL
    >

In this example a new directory is created with a file in it and then monitored, then the directory content is modified. The testgam program also poll and report events coming from the server as they arrive.

## Security

While gamin still use a server to provide the service (ideally if the kernel had a proper interface a library only implementation should be doable and possibly better), it tries to avoid security hazard associated to contacting an external server process:

- the server runs under the same privilege level as the client, by running under the uid, no root or superuser access is involved, this is checked by both side using kernel support for the checking
- when possible (e.g. on Linux) the socket used to communicate is not mapped at the filesystem level to avoid risks related to opening a real file, if the kernel doesn't allow this a per user directory holding the socket is used and appropriate rights are checked.
- to limit DoS attacks done by continuously modifying a monitored resource, the daemon will switch back monitoring of very busy resources to polling with generation of events only once per second.

Here is the process used to acquire and create the sockets:

### If there is abstract socket support:

Use the filename "\0/tmp/fam-$USER-$GAM_CLIENT_ID". They are not mapped on the filesystem, no attack is possible that way. The client and the server checks on the first '\0' byte received from the socket that the other side is running under the same UID.

### If there is no abstract socket support:

On the server side:

     start:
      try to create /tmp/fam-$USER using mkdir('/tmp/fam-$USER', 007)
      if error:
          make a stat() on it
          if doesn't exist:
              return failure to create
          if user is not getuid() or mode is not 007 or type is not dir:
              try to unlink()
              if error:
                  exit with error.
              if success:
                  goto start:

      do the socket()/bind() on /tmp/fam-$USER/fam-$GAM_CLIENT_ID

On the client side:

      make a stat on /tmp/fam-$USER
      if doesn't exist:
          return failure to create should start the server
      if user is not getuid() or mode is not 007 or type is not dir:
          try to unlink()
          if error:
              exit with error.
          if success:
              return failure should start the server
      make a stat on /tmp/fam-$USER/fam-$GAM_CLIENT_ID
      if doesn't exist:
          return failure to create should start the server
      if user is not getuid() or type is not socket:
          try to unlink()
          if error:
              exit with error.
          if success:
              return failure should start the server

      do the socket()/connect() on /tmp/fam-$USER/fam-$GAM_CLIENT_ID

The client and the server checks on the first '\0' byte received that the other side is of the same UID.

## Internals

gamin uses a client server model, this is in a large measure justified by the inappropriate dnotify kernel API way to signal modification events to an application but also to share kernel signal when multiple application monitors the same resource. It also allow to fine tune and filters event flow in the daemon, potentially minimizing resource consumption in applications.

Internally the gam_server maintain various data structures:

- A tree of monitored nodes, using both GamNode structure containing specific informations (like the path, the subscriptions list for that node, monitoring data and whether it's a directory), the tree itself is based on a GNode N-ary tree.
- Per connection data (GamConnData) one per connected socket. The data includes state, connection informations (file descriptor, Glib I/O Channel, pid of the application), and potential unprocessed yet data request from the socket.
- Listener information, keeps the list of subscriptions for the connected application, this could probably be merged in the connection data.
- For each path monitored by an application, a specific GamSubscription structure is maintained. The subscriptions are referenced both from the monitored nodes tree and from the listeners.

## Differences from FAM

### Differences

gamin should be binary and source code compatible with FAM 2.6.8. However there are some differences and at least one significant extension.

The differences are in term of implementation:

- No system wide server, instead it relies on per user server, if needed started on demand by the fam/gamin library.
- The functions FAMSuspendMonitor(), FAMResumeMonitor() and FAMMonitorCollection() are not implemented. They all raise problem of accumulating unbounded state on the server side and better handled at the client level if needed.
- FAMErrno is provided, but the values may not match their FAM counterparts, similary FamErrlist[] error messages are different.
- No NFS support based on specific RPC and server, instead gamin monitors only the state as reported locally by the kernel, not that locally done changes on NFS or AFS filesystems are reported on Linux which is the main criteria when having user home directories on such filesystems.

### Extension(s)

We tried to limit changes in gamin but a number of features were deemed more important than sticking to the exact same set than FAM:

- Support for inotify on Linux if compiled in the kernel, this override most of the deficiencies related to dnotify.
- A lot of debugging support was added both for client and server see the specific page on the matter.
- The possibility to block Exist/EndExists callbacks when monitoring directories based on a new API, see below

FAM when monitoring a directory, immediately send a set of events listing the files found in that directory, this ends up with an EndExists events. However when monitoring hierarchy it's usually far more efficient and simple to do the scanning on the client side and ignore those directory listing events from the FAM server (the only drawback is a potential mismatch of the directory content between the FAM server and the client). In such a case, all those events are not only superfluous but they are also dangerous since they can lead to a congested pipe to the client which is just scanning directories and not listening to FAM. To that intent we added in gamin 0.0.23 a new API disabling the Exists/EndExists sequences when watching directories for a given FAMConnection:

    int FAMNoExists(FAMConnection *fc)

and with the Python bindings:

    WatchMonitor.no_exists()

This feature is also used when the client reconnect to the server after a connection loss or if the server died.

Calling it changes the protocol as described below, directory monitoring from that call will only get mutation events and not the initial lists: