Copyright | (c) 2020 Composewell Technologies |
---|---|
License | BSD-3-Clause |
Maintainer | streamly@composewell.com |
Stability | pre-release |
Portability | GHC |
Safe Haskell | None |
Language | Haskell2010 |
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.fromCString# "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:
- 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).
- 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.
- 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.
- On Linux
watchPaths
fails if a path does not exist, on macOS it does not fail.
Synopsis
- data Config
- data Toggle
- defaultConfig :: Config
- setFollowSymLinks :: Toggle -> Config -> Config
- setUnwatchMoved :: Toggle -> Config -> Config
- setOneShot :: Toggle -> Config -> Config
- setOnlyDir :: Toggle -> Config -> Config
- data WhenExists
- setWhenExists :: WhenExists -> Config -> Config
- setRootDeleted :: Toggle -> Config -> Config
- setRootMoved :: Toggle -> Config -> Config
- setMetadataChanged :: Toggle -> Config -> Config
- setAccessed :: Toggle -> Config -> Config
- setOpened :: Toggle -> Config -> Config
- setWriteClosed :: Toggle -> Config -> Config
- setNonWriteClosed :: Toggle -> Config -> Config
- setCreated :: Toggle -> Config -> Config
- setDeleted :: Toggle -> Config -> Config
- setMovedFrom :: Toggle -> Config -> Config
- setMovedTo :: Toggle -> Config -> Config
- setModified :: Toggle -> Config -> Config
- setAllEvents :: Toggle -> Config -> Config
- watchPathsWith :: (Config -> Config) -> NonEmpty (Array Word8) -> SerialT IO Event
- watchPaths :: NonEmpty (Array Word8) -> SerialT IO Event
- watchTreesWith :: (Config -> Config) -> NonEmpty (Array Word8) -> SerialT IO Event
- watchTrees :: NonEmpty (Array Word8) -> SerialT IO Event
- addToWatch :: Config -> Watch -> Array Word8 -> Array Word8 -> IO ()
- removeFromWatch :: Watch -> Array Word8 -> IO ()
- data Event = Event {
- eventWd :: CInt
- eventFlags :: Word32
- eventCookie :: Word32
- eventRelPath :: Array Word8
- eventMap :: IntMap (Array Word8, Array Word8)
- getRoot :: Event -> Array Word8
- getRelPath :: Event -> Array Word8
- getAbsPath :: Event -> Array Word8
- getCookie :: Event -> Cookie
- isOverflow :: Event -> Bool
- isRootUnwatched :: Event -> Bool
- isRootDeleted :: Event -> Bool
- isRootMoved :: Event -> Bool
- isRootUnmounted :: Event -> Bool
- isMetadataChanged :: Event -> Bool
- isAccessed :: Event -> Bool
- isOpened :: Event -> Bool
- isWriteClosed :: Event -> Bool
- isNonWriteClosed :: Event -> Bool
- isCreated :: Event -> Bool
- isDeleted :: Event -> Bool
- isMovedFrom :: Event -> Bool
- isMovedTo :: Event -> Bool
- isModified :: Event -> Bool
- isDir :: Event -> Bool
- showEvent :: Event -> String
Subscribing to events
Default configuration
Watch configuration, used to specify the events of interest and the behavior of the watch.
Pre-release
defaultConfig :: Config Source #
The default is:
setFollowSymLinks
On
setUnwatchMoved
On
setOneShot
Off
setOnlyDir
Off
setWhenExists
AddIfExists
setAllEvents
On
Pre-release
Watch Behavior
setFollowSymLinks :: Toggle -> 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.
default: On
Pre-release
setUnwatchMoved :: Toggle -> Config -> Config Source #
If an object moves out of the directory being watched then stop watching it.
default: On
Pre-release
setOneShot :: Toggle -> Config -> Config Source #
Watch the object only for one event and then remove it from the watch.
default: Off
Pre-release
setOnlyDir :: Toggle -> 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: Off
Pre-release
data WhenExists Source #
What to do if a watch already exists when openWatch
or addToWatch
is
called for a path.
Pre-release
AddIfExists | |
ReplaceIfExists | Replace the existing settings with new settings |
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 Level Events
setRootDeleted :: Toggle -> Config -> Config Source #
Report when the watched path itself gets deleted.
default: On
Pre-release
setRootMoved :: Toggle -> Config -> Config Source #
Report when the watched root path itself gets renamed.
default: On
Pre-release
Item Level Metadata change
setMetadataChanged :: Toggle -> Config -> Config Source #
Report when the metadata e.g. owner, permission modes, modifications times of an object changes.
default: On
Pre-release
Item Level Access
setAccessed :: Toggle -> Config -> Config Source #
Report when a file is accessed.
default: On
Pre-release
setWriteClosed :: Toggle -> Config -> Config Source #
Report when a file that was opened for writes is closed.
default: On
Pre-release
setNonWriteClosed :: Toggle -> Config -> Config Source #
Report when a file that was opened for not writing is closed.
default: On
Pre-release
Item CRUD events
setCreated :: Toggle -> Config -> Config Source #
Report when a file is created.
default: On
Pre-release
setDeleted :: Toggle -> Config -> Config Source #
Report when a file is deleted.
default: On
Pre-release
setMovedFrom :: Toggle -> Config -> Config Source #
Report the source of a move.
default: On
Pre-release
setModified :: Toggle -> Config -> Config Source #
Report when a file is modified.
default: On
Pre-release
Watch APIs
watchPathsWith :: (Config -> Config) -> NonEmpty (Array Word8) -> SerialT 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 the path is a directory, only the
files and directories directly under the watched directory are monitored,
contents of subdirectories are not monitored. Monitoring starts from the
current time onwards. The paths are specified as "/" separated Array
of
Word8
.
watchPathsWith (setFollowSymLinks
On .setUnwatchMoved
Off) [Array.fromCString# "dir"#]
Pre-release
watchTreesWith :: (Config -> Config) -> NonEmpty (Array Word8) -> SerialT 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 the path is a directory, the
whole directory tree under it is watched recursively. Monitoring starts from
the current time onwards.
Note that recrusive 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.
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.
Pre-release
watchTrees :: NonEmpty (Array Word8) -> SerialT IO Event Source #
Like watchTreesWith
but uses the defaultConfig
options.
watchTrees = watchTreesWith id
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
An Event generated by the file system. Use the accessor functions to examine the event.
Pre-release
Event | |
|
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. The path is a "/" separated array of bytes.
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
Exception Conditions
isOverflow :: 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
Root Level Events
isRootUnwatched :: Event -> Bool Source #
A path was removed from the watch explicitly using removeFromWatch
or
automatically (file was deleted, or filesystem was unmounted).
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.
Occurs only for a watched path
Pre-release
isRootMoved :: Event -> Bool Source #
Watched file/directory was itself moved within the file system.
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
isMetadataChanged :: 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 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
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