The stream's element is now the enum InotifyEvent, and the struct that describes a change to a watched item is FileSystemEvent. A queue overflow was an event with descriptor -1 and an empty path that every consumer had to know about; as a case, the compiler makes them handle it. The enum is also where failed watches of a growing tree will be reported, since no call site can catch them.
62 lines
3.1 KiB
Markdown
62 lines
3.1 KiB
Markdown
# Watching Directory Trees
|
|
|
|
Monitor an entire directory hierarchy for filesystem events.
|
|
|
|
## Overview
|
|
|
|
The Linux inotify API watches individual directories — it does not descend into subdirectories automatically. The ``Inotify/Inotify`` actor offers two convenience methods that handle the recursion for you.
|
|
|
|
### Recursive Watch
|
|
|
|
Call ``Inotify/Inotify/addRecursiveWatch(forDirectory:mask:)`` to walk the directory tree once and install a watch on every subdirectory that exists at the time of the call:
|
|
|
|
```swift
|
|
let inotify = try Inotify()
|
|
let descriptors = try await inotify.addRecursiveWatch(
|
|
forDirectory: "/home/user/project",
|
|
mask: [.create, .modify, .delete]
|
|
)
|
|
```
|
|
|
|
The returned array contains one watch descriptor per directory. Subdirectories created **after** this call are not covered.
|
|
|
|
### Automatic Subtree Watching
|
|
|
|
When you also want future subdirectories to be picked up, use ``Inotify/Inotify/addWatchWithAutomaticSubtreeWatching(forDirectory:mask:)`` instead:
|
|
|
|
```swift
|
|
let descriptors = try await inotify.addWatchWithAutomaticSubtreeWatching(
|
|
forDirectory: "/home/user/project",
|
|
mask: [.create, .modify, .delete]
|
|
)
|
|
```
|
|
|
|
Internally this listens for `CREATE` and `MOVED_TO` events carrying the ``InotifyEventMask/isDir`` flag and installs new watches with the same mask on the subdirectory and its subtree whenever one appears. Items that already exist inside such a subdirectory are reported with ``FileSystemEvent/synthesized`` set to `true`, since the kernel never produces events for them; a synthesized event may duplicate a kernel event for the same item.
|
|
|
|
When a directory is moved out of the watched tree, the watches on it and on its subdirectories are removed, so no events are reported under the stale path.
|
|
|
|
### Excluding Directories
|
|
|
|
When watching large trees you often want to skip certain subdirectories entirely — version-control metadata, build artefacts, dependency caches, and so on. Call ``Inotify/Inotify/exclude(names:)`` or ``Inotify/Inotify/exclude(patterns:)`` **before** adding a recursive or automatic-subtree watch:
|
|
|
|
```swift
|
|
let inotify = try Inotify()
|
|
await inotify.exclude(names: ".git", "node_modules", ".build")
|
|
await inotify.exclude(patterns: ".*", "*.tmp")
|
|
|
|
try await inotify.addWatchWithAutomaticSubtreeWatching(
|
|
forDirectory: "/home/user/project",
|
|
mask: .allEvents
|
|
)
|
|
```
|
|
|
|
Excluded names and patterns are matched against the last path component of each directory during resolution, against a directory that appears later before a watch is extended to it, and against every event, so you never receive events for excluded items. A pattern is matched the way the shell matches file names: `*` and `?` stand for any characters and `[…]` for a set of characters; a leading dot needs no special treatment.
|
|
|
|
### Choosing the Right Method
|
|
|
|
| Method | Covers existing subdirectories | Covers new subdirectories |
|
|
|--------|:----:|:----:|
|
|
| ``Inotify/Inotify/addWatch(path:mask:)`` | No | No |
|
|
| ``Inotify/Inotify/addRecursiveWatch(forDirectory:mask:)`` | Yes | No |
|
|
| ``Inotify/Inotify/addWatchWithAutomaticSubtreeWatching(forDirectory:mask:)`` | Yes | Yes |
|