on e856425 help sign in
242 lines 8.6 kB history raw
1-- |
2-- Module : Streamly.FileSystem.Event
3-- Copyright : (c) 2020 Composewell Technologies
4-- License : BSD-3-Clause
5-- Maintainer : streamly@composewell.com
6-- Stability : pre-release
7-- Portability : GHC
8--
9-- File system event notification API portable across Linux, macOS and Windows
10-- platforms.
11--
12-- Note that recursive directory tree watch does not work reliably on Linux
13-- (see notes in the Linux module), therefore, recursive watch API is not
14-- provided in this module. However, you can use it from the platform specific
15-- modules.
16--
17-- For platform specific APIs please see the following modules:
18--
19-- * "Streamly.Internal.FileSystem.Event.Darwin"
20-- * "Streamly.Internal.FileSystem.Event.Linux"
21-- * "Streamly.Internal.FileSystem.Event.Windows"
23module Streamly.Internal.FileSystem.Event
24 (
25 -- * Creating a Watch
27 watch
28 -- , watchRecursive
30 -- * Handling Events
31 , Event
32 , getAbsPath
34 -- ** Item CRUD events
35 , isCreated
36 , isDeleted
37 , isMoved
38 , isModified
40 -- ** Exception Conditions
41 , isEventsLost
43 -- ** Debugging
44 , showEvent
45 )
46where
48import Data.List.NonEmpty (NonEmpty)
49import Data.Word (Word8)
50import Streamly.Internal.Data.Array.Unboxed (Array)
51import Streamly.Internal.Data.Stream (Stream)
53#if defined(CABAL_OS_DARWIN)
54import Streamly.Internal.FileSystem.Event.Darwin (Event)
55import qualified Streamly.Internal.FileSystem.Event.Darwin as Event
56#elif defined(CABAL_OS_LINUX)
57import Streamly.Internal.FileSystem.Event.Linux (Event)
58import qualified Streamly.Internal.FileSystem.Event.Linux as Event
59#elif defined(CABAL_OS_WINDOWS)
60import Streamly.Internal.FileSystem.Event.Windows (Event)
61import qualified Streamly.Internal.FileSystem.Event.Windows as Event
62#else
63#error "FS Events not supported on this platform"
64#endif
66-- XXX Ensure that defaultConfig is the same for all platforms.
67--
68-- XXX Ensure that equivalent events are generated on all platforms when same
69-- operations are done. Document any differences in behavior on different
70-- platforms. Any events that are not common to all platforms should be
71-- filtered out.
72--
73-- XXX Only need to set common event types for macOS/Linux/Windows
74-- XXX write a test, that generates all types of events that can possibly
75-- occur on each platform. Then create a common test that only checks
76-- for the common subset of those.
78-------------------------------------------------------------------------------
79-- Creating a watch
80-------------------------------------------------------------------------------
82-- macOS and Windows seem to do a path level watch i.e. they watch the path
83-- rather than the underlying inode, possibly they can map the inode back to a
84-- path?. On Linux we cannot keep watching a watch root if the inode it is
85-- pointing to has moved. However, the inode continues to be watched in this
86-- case instead of the path, which is not the case for macOS.
87--
88-- To make the behavior same for all platforms, for the Linux case we will have
89-- to stop watching the inode if it moves from under the path
90-- (setUnwatchMoved). And on macOS we can stop watching a path if the inode
91-- under it changes i.e. just filter out events for that path.
92--
93-- Also, to make the behavior of Linux same as macOS we need to set
94-- setFollowSymLinks on Linux.
96-- XXX Verify all the cases mentioned below using test cases.
97-- XXX Make it fail if the watch root is a file. It works on macOS and Linux
98-- but does not work on Windows.
100-- | Start monitoring a list of directories or symbolic links to directories
101-- for file system events. Monitoring starts from the current time onwards.
102-- The paths are specified as UTF-8 encoded 'Array' of 'Word8'.
103--
104-- If a watch root is a symbolic link then the target of the link is watched.
105-- Fails if the watched path does not exist. If the user does not have
106-- permissions (read and execute?) on the watch root then no events are
107-- generated. No events are generated if the watch root itself is renamed or
108-- deleted.
109--
110-- This API watches for changes in the watch root directory only, any changes
111-- in the subdirectories of the watch root are not watched. However, on macOS
112-- the watch is always recursive, but do not rely on that behavior, it may
113-- change without notice in future. If you want to use recursive watch please
114-- use platform specific modules.
115--
116-- /Pre-release/
117--
118watch :: NonEmpty (Array Word8) -> Stream IO Event
119#if defined(CABAL_OS_DARWIN)
120watch = Event.watchRecursive
121#else
122watch = Event.watch
123#endif
125-- | Like 'watch' except that if a watched path is a directory the whole
126-- directory tree under it is watched recursively.
127--
128-- On Linux 'watchRecursive' may be more expensive than 'watch'.
129--
130-- /Pre-release/
131--
132_watchRecursive :: NonEmpty (Array Word8) -> Stream IO Event
133_watchRecursive = Event.watchRecursive
135-------------------------------------------------------------------------------
136-- Handling Events
137-------------------------------------------------------------------------------
139-- XXX Ensure that on all paltforms the path has same conventions. That is do
140-- not have a trailing path separator on one platfrom and not on another.
141--
142-- XXX We should use getRelPath instead so that the behavior when the root is a
143-- symlink becomes platform independent.
144--
145-- | Get the absolute path of the file system object for which the event is
146-- generated. The path is a UTF-8 encoded array of bytes.
147--
148-- When the watch root is a symlink the behavior is different on different
149-- platforms:
150--
151-- * On Linux and Windows, the absolute path returned is via the original
152-- symlink.
153--
154-- * On macOS the absolute path returned is via the real path of the root after
155-- resolving the symlink.
156--
157-- This API is subject to removal in future, to be replaced by a platform
158-- independent @getRelPath@.
159--
160-- /Pre-release/
161--
162getAbsPath :: Event -> Array Word8
163getAbsPath = Event.getAbsPath
165-- | Determine whether the event indicates creation of an object within the
166-- monitored path. This event is generated when any file system object is
167-- created.
168--
169-- For hard links the behavior is different on different operating systems. On
170-- macOS hard linking does not generate a create event, it generates an
171-- 'isInodeAttrsChanged' event on the directory instead (see the Darwin
172-- module). On Linux and Windows hard linking generates a create event.
173--
174-- /Pre-release/
175--
176isCreated :: Event -> Bool
177isCreated = Event.isCreated
179-- XXX To make the behavior consistent, can we block the event on watch root on
180-- macOS?
181--
182-- | Determine whether the event indicates deletion of an object within the
183-- monitored path. On Linux and Windows hard link deletion generates a delete
184-- event.
185--
186-- On Linux and Windows, this event does not occur when the watch root itself
187-- is deleted. On macOS it occurs on deleting the watch root when it is not a
188-- symbolic link.
189--
190-- See also 'isRootDeleted' event for Linux.
191--
192-- /Pre-release/
193--
194isDeleted :: Event -> Bool
195isDeleted = Event.isDeleted
197-- | Determine whether the event indicates rename of an object within the
198-- monitored path. This event is generated when an object is renamed within the
199-- watched directory or if it is moved out of or in the watched directory.
200-- Moving hard links is no different than other types of objects.
201--
202-- /Pre-release/
203--
204isMoved :: Event -> Bool
205isMoved = Event.isMoved
207-- XXX Make the Windows behavior consistent with other platforms by removing
208-- the event from directories.
209--
210-- | Determine whether the event indicates modification of an object within the
211-- monitored path. This event is generated on file modification on all
212-- platforms.
213--
214-- On Linux and macOS this event is never generated for directories. On
215-- Windows (in recursive watch mode) this event is generated for directories as
216-- well when an object is created in or deleted from the directory.
217--
218-- /Pre-release/
219--
220isModified :: Event -> Bool
221isModified = Event.isModified
223-- | An event that indicates that some events before this may have been lost,
224-- therefore, we need to take some recovery action.
225--
226-- /Pre-release/
227--
228isEventsLost :: Event -> Bool
229isEventsLost = Event.isEventsLost
231-------------------------------------------------------------------------------
232-- Debugging
233-------------------------------------------------------------------------------
235-- | Convert an 'Event' record to a String representation. Note that the output
236-- of this function may be different on different platforms because it may
237-- contain platform specific details.
238--
239-- /Internal/
240--
241showEvent :: Event -> String
242showEvent = Event.showEvent