Overview

Use watchPaths with a list of file system paths you want to watch as argument. It returns a stream of Event representing the file system events occurring under the watched paths.

Stream.mapM_ (putStrLn . showEvent) $ watchPaths [Array.fromList "dir"]

Event is an opaque type. Accessor functions (e.g. showEvent above) provided in this module are used to determine the attributes of the event.

Identical successive events may be coalesced into a single event.

Design notes

For reference documentation see:

We try to keep the macOS/Linux/Windows event handling APIs and defaults semantically and syntactically as close as possible.

BUGs

When testing on Linux Kernel version 5.3.0-53-generic #47-Ubuntu, the last event for the root path seems to be delayed until one more event occurs.

Differences between macOS and Linux APIs:

  1. macOS watch is based on the path provided to it, if the path is deleted and recreated it will still be watched, if the path moves to another path it won't be watched anymore. Whereas Linux watch is based on a handle to the path, if the path is deleted and recreated it won't be watched, if the path moves to another it can still be watched (though this is configurable).
  1. macOS watches the directory hierarchy recursively, Linux watches only one level of dir, recursive watch has to be built in user space by watching for create events and adding the new directories to the watch. Not sure how this will scale for too many paths.
  2. In macOS the path of the subject of the event is absolute, in Linux the path is the name of the object inside the dir being watched.
  3. On Linux watchPaths fails if a path does not exist, on macOS it does not fail.

Subscribing to events

Default configuration

data Config Source #

Watch configuration, used to specify the events of interest and the behavior of the watch.

Pre-release

Constructors

Config 

defaultConfig :: Config Source #

The default configuration settings are:

The tunable events enabled by default are:

  • setCreated True
  • setDeleted True
  • setMovedFrom True
  • setMovedTo True
  • setModified True

Pre-release

Watch Behavior

setRecursiveMode :: Bool -> Config -> Config Source #

Watch the whole directory tree recursively instead of watching just one level of directory.

default: False

Pre-release

setFollowSymLinks :: Bool -> Config -> Config Source #

If the pathname to be watched is a symbolic link then watch the target of the symbolic link instead of the symbolic link itself.

Note that the path location in the events is through the original symbolic link path rather than the resolved path.

default: True

Pre-release

setUnwatchMoved :: Bool -> Config -> Config Source #

If an object moves out of the directory being watched then stop watching it.

default: True

Pre-release

setOneShot :: Bool -> Config -> Config Source #

Watch the object only for one event and then remove it from the watch.

default: False

Pre-release

setOnlyDir :: Bool -> Config -> Config Source #

Watch the object only if it is a directory. This provides a race-free way to ensure that the watched object is a directory.

default: False

Pre-release

data WhenExists Source #

What to do if a watch already exists when openWatch or addToWatch is called for a path.

Pre-release

Constructors

AddIfExists

Do not set an existing setting to False only set to True

ReplaceIfExists

Replace the existing settings with new settings

FailIfExists

Fail the API

setWhenExists :: WhenExists -> Config -> Config Source #

When adding a new path to the watch, specify what to do if a watch already exists on that path.

default: FailIfExists

Pre-release

Events of Interest

Root Path Events

setRootDeleted :: Bool -> Config -> Config Source #

Report when the watched path itself gets deleted.

default: True

Pre-release

setRootMoved :: Bool -> Config -> Config Source #

Report when the watched root path itself gets renamed.

default: True

Pre-release

setRootPathEvents :: Bool -> Config -> Config Source #

Report when the watched root path itself gets deleted or renamed.

default: True

Pre-release

Item Level Metadata change

setAttrsModified :: Bool -> Config -> Config Source #

Report when the metadata e.g. owner, permission modes, modifications times of an object changes.

default: True

Pre-release

Item Level Access

setAccessed :: Bool -> Config -> Config Source #

Report when a file is accessed.

default: True

Pre-release

setOpened :: Bool -> Config -> Config Source #

Report when a file is opened.

default: True

Pre-release

setWriteClosed :: Bool -> Config -> Config Source #

Report when a file that was opened for writes is closed.

default: True

Pre-release

setNonWriteClosed :: Bool -> Config -> Config Source #

Report when a file that was opened for not writing is closed.

default: True

Pre-release

Item CRUD events

setCreated :: Bool -> Config -> Config Source #

Report when a file is created.

default: True

Pre-release

setDeleted :: Bool -> Config -> Config Source #

Report when a file is deleted.

default: True

Pre-release

setMovedFrom :: Bool -> Config -> Config Source #

Report the source of a move.

default: True

Pre-release

setMovedTo :: Bool -> Config -> Config Source #

Report the target of a move.

default: True

Pre-release

setModified :: Bool -> Config -> Config Source #

Report when a file is modified.

default: True

Pre-release

setAllEvents :: Bool -> Config -> Config Source #

Set all tunable events True or False. Equivalent to setting:

  • setRootDeleted
  • setRootMoved
  • setAttrsModified
  • setAccessed
  • setOpened
  • setWriteClosed
  • setNonWriteClosed
  • setCreated
  • setDeleted
  • setMovedFrom
  • setMovedTo
  • setModified

Pre-release

Watch APIs

watch :: NonEmpty (Array Word8) -> Stream IO Event Source #

Same as watchWith using defaultConfig and non-recursive mode.

>>> watch = watchWith id

Pre-release

watchRecursive :: NonEmpty (Array Word8) -> Stream IO Event Source #

Same as watchWith using defaultConfig and recursive mode.

>>> watchRecursive = watchWith (setRecursiveMode True)

See watchWith for pitfalls and bugs when using recursive watch on Linux.

Pre-release

watchWith :: (Config -> Config) -> NonEmpty (Array Word8) -> Stream IO Event Source #

Start monitoring a list of file system paths for file system events with the supplied configuration operation over the defaultConfig. The paths could be files or directories. When recursive mode is set and the path is a directory, the whole directory tree under it is watched recursively. Monitoring starts from the current time onwards. The paths are specified as UTF-8 encoded Array of Word8.

Non-existing Paths: the API fails if a watch is started on a non-exsting path.

Performance: Note that recursive watch on a large directory tree could be expensive. When starting a watch, the whole tree must be read and watches are started on each directory in the tree. The initial time to start the watch as well as the memory required is proportional to the number of directories in the tree.

Bugs: When new directories are created under the tree they are added to the watch on receiving the directory create event. However, the creation of a dir and adding a watch for it is not atomic. The implementation takes care of this and makes sure that watches are added for all directories. However, In the mean time, the directory may have received more events which may get lost. Handling of any such lost events is yet to be implemented.

See the Linux inotify man page for more details.

watchwith
     (setFollowSymLinks True . setUnwatchMoved False)
     [Array.fromList "dir"]

Pre-release

addToWatch :: Config -> Watch -> Array Word8 -> Array Word8 -> IO () Source #

addToWatch cfg watch root subpath adds subpath to the list of paths being monitored under root via the watch handle watch. root must be an absolute path and subpath must be relative to root.

Pre-release

removeFromWatch :: Watch -> Array Word8 -> IO () Source #

Remove an absolute root path from a Watch, if a path was moved after adding you need to provide the original path which was used to add the Watch.

Pre-release

Handling Events

data Event Source #

An Event generated by the file system. Use the accessor functions to examine the event.

Pre-release

getRoot :: Event -> Array Word8 Source #

Get the watch root corresponding to the Event.

Note that if a path was moved after adding to the watch, this will give the original path and not the new path after moving.

TBD: we can possibly update the watch root on a move self event.

Pre-release

getRelPath :: Event -> Array Word8 Source #

Get the file system object path for which the event is generated, relative to the watched root. The path is a "/" separated array of bytes.

Pre-release

getAbsPath :: Event -> Array Word8 Source #

Get the absolute file system object path for which the event is generated.

When the watch root is a symlink, the absolute path returned is via the original symlink and not through the resolved path.

Pre-release

getCookie :: Event -> Cookie Source #

Cookie is set when a rename occurs. The cookie value can be used to connect the isMovedFrom and isMovedTo events, if both the events belong to the same move operation then they will have the same cookie value.

Pre-release

Root Level Events

isRootPathEvent :: Event -> Bool Source #

Determine whether the event indicates a change of path of the monitored object itself. Note that the object may become unreachable or deleted after a change of path.

Occurs only for a watched path

Pre-release

isRootUnwatched :: Event -> Bool Source #

A path was removed from the watch explicitly using removeFromWatch or automatically (file was deleted, or filesystem was unmounted).

Note that in recursive watch mode all the subdirectories are watch roots, therefore, they will all generate this event.

Occurs only for a watched path

Pre-release

isRootDeleted :: Event -> Bool Source #

Watched file/directory was itself deleted. (This event also occurs if an object is moved to another filesystem, since mv(1) in effect copies the file to the other filesystem and then deletes it from the original filesystem.) In addition, an isRootUnwatched event will subsequently be generated for the watch descriptor.

Note that in recursive watch mode all the subdirectories are watch roots, therefore, they will all generate this event.

Occurs only for a watched path

Pre-release

isRootMoved :: Event -> Bool Source #

Watched file/directory was itself moved within the file system.

Note that in recursive watch mode all the subdirectories are watch roots, therefore, they will all generate this event.

Occurs only for a watched path

Pre-release

isRootUnmounted :: Event -> Bool Source #

Filesystem containing watched object was unmounted. In addition, an isRootUnwatched event will subsequently be generated for the watch descriptor.

Occurs only for a watched path

Pre-release

Item Level Metadata change

isAttrsModified :: Event -> Bool Source #

Determine whether the event indicates inode metadata change for an object contained within the monitored path.

Metadata change may include, permissions (e.g., chmod(2)), timestamps (e.g., utimensat(2)), extended attributes (setxattr(2)), link count (since Linux 2.6.25; e.g., for the target of link(2) and for unlink(2)), and user/group ID (e.g., chown(2)).

Can occur for watched path or a file inside it

Pre-release

Item Level Access

isAccessed :: Event -> Bool Source #

File was accessed (e.g. read, execve).

Occurs only for a file inside the watched directory

Pre-release

isOpened :: Event -> Bool Source #

File or directory was opened.

Occurs only for a file inside the watched directory

Pre-release

isWriteClosed :: Event -> Bool Source #

File opened for writing was closed.

Occurs only for a file inside the watched directory

Pre-release

isNonWriteClosed :: Event -> Bool Source #

File or directory opened for read but not write was closed.

Can occur for watched path or a file inside it

Pre-release

Item Level CRUD events

isCreated :: Event -> Bool Source #

File/directory created in watched directory (e.g., open(2) O_CREAT, mkdir(2), link(2), symlink(2), bind(2) on a UNIX domain socket).

Occurs only for an object inside the watched directory

Pre-release

isDeleted :: Event -> Bool Source #

File/directory deleted from watched directory.

Occurs only for an object inside the watched directory

Pre-release

isMovedFrom :: Event -> Bool Source #

Generated for the original path when an object is moved from under a monitored directory.

Occurs only for an object inside the watched directory

Pre-release

isMovedTo :: Event -> Bool Source #

Generated for the new path when an object is moved under a monitored directory.

Occurs only for an object inside the watched directory

Pre-release

isMoved :: Event -> Bool Source #

Generated for a path that is moved from or moved to the monitored directory.

>>> isMoved ev = isMovedFrom ev || isMovedTo ev

Occurs only for an object inside the watched directory

Pre-release

isModified :: Event -> Bool Source #

Determine whether the event indicates modification of an object within the monitored path. This event is generated only for files and not directories.

Occurs only for an object inside the watched directory

Pre-release

Item Path info

isDir :: Event -> Bool Source #

Determine whether the event is for a directory path.

Pre-release

Exception Conditions

isEventsLost :: Event -> Bool Source #

Event queue overflowed (WD is invalid for this event) and we may have lost some events.. The user application must scan everything under the watched paths to know the current state.

Pre-release

Debugging

showEvent :: Event -> String Source #

Convert an Event record to a String representation.