diff options
author | Camil Staps | 2016-05-21 14:04:49 +0200 |
---|---|---|
committer | Camil Staps | 2016-05-21 14:04:49 +0200 |
commit | 25cc129876ec0e556d409a0f50454eb400d3a54b (patch) | |
tree | 27dfb315f3caeeee1d08d9e7bcaf2b48695d2288 | |
parent | Also pass event->name through; inotify_loop_forever (diff) |
Documentation; inotify_is_event; timeout for polling
-rw-r--r-- | Inotify.dcl | 71 | ||||
-rw-r--r-- | Inotify.icl | 35 | ||||
-rw-r--r-- | inotify_c.c | 60 | ||||
-rw-r--r-- | test.icl | 23 | ||||
-rw-r--r-- | test.prj | 2 | ||||
-rw-r--r-- | test_reload.icl | 7 |
6 files changed, 167 insertions, 31 deletions
diff --git a/Inotify.dcl b/Inotify.dcl index 3811abc..c3a3e05 100644 --- a/Inotify.dcl +++ b/Inotify.dcl @@ -3,27 +3,84 @@ definition module Inotify from Data.Either import ::Either from Data.Maybe import ::Maybe +// Inotify file descriptor :: *Inotify st +// Inotify watch descriptor :: INWatch +// Inotify event mask :: INMask :== Int +// Inotify event :: INEvent :== Int + +// Inotify callback: event, maybe filename, state, world -> state, world :: INCallback st :== INEvent (Maybe String) st *World -> *(st, *World) (|-) infixl 6 :: (INMask INMask -> INMask) +/* Initialise an inotify file descriptor with some state */ inotify_init :: st -> Maybe *(Inotify st) + +/* Close an inotify file descriptor and get back the state */ inotify_close :: *(Inotify st) -> st -inotify_add_watch :: (INCallback st) !Int !String !*(Inotify st) - -> *(Either Int INWatch, *Inotify st) -inotify_rm_watch :: !INWatch !*(Inotify st) -> *(Bool, *Inotify st) +/** + * Add a watch on some file + * + * INCallback st: the callback for events + * INMask: a mask of events to watch for + * String: the filename + * Inotify st: the inotify file descriptor + * + * Either Int INWatch: either an error code or a watch descriptor + * Inotify st: the new file descriptor + */ +inotify_add_watch :: (INCallback st) !INMask !String !*(Inotify st) + -> *(!Either Int INWatch, !*Inotify st) + +/** + * Remove a watch + * + * INWatch: the watch to remove + * Inotify st: the inotify file descriptor + * + * Bool: success + * Inotify st: the new file descriptor + */ +inotify_rm_watch :: !INWatch !*(Inotify st) -> *(!Bool, !*Inotify st) + +/** + * Poll an inotify file descriptor; i.e. wait for new events + * + * Maybe Int timeout in milliseconds (Nothing for no timeout) + * + * Int the number of events + */ +inotify_poll :: !(Maybe Int) !*(Inotify st) -> *(!Int, !*Inotify st) -inotify_poll :: *(Inotify st) -> *Inotify st -inotify_check :: *(Inotify st) *World -> *(*Inotify st, *World) +/** + * Check for new events and call callbacks + */ +inotify_check :: !*(Inotify st) !*World -> *(!*Inotify st, !*World) -inotify_loop_forever :: *(Inotify st) *World -> *(*Inotify st, *World) +/** + * Check if an event matches a mask + */ +inotify_is_event :: INMask INEvent -> Bool + +/** + * Combination of inotify_poll and inotify_check that will return only if no + * events were given when a timeout occurred. + */ +inotify_loop_with_timeout :: !(Maybe Int) !*(Inotify st) !*World -> *(!*Inotify st, !*World) + +/** + * inotify_loop_with_timeout with Nothing as timeout (will never return) + */ +inotify_loop_forever :: !*(Inotify st) !*World -> *(!*Inotify st, !*World) + +/*** Begin inotify.h ***/ IN_ACCESS :== 0x00000001 // File was accessed IN_MODIFY :== 0x00000002 // File was modified @@ -52,3 +109,5 @@ IN_ALL_EVENTS :== (IN_ACCESS |- IN_MODIFY |- IN_ATTRIB |- IN_CLOSE_WRITE |- IN_CLOSE_NOWRITE |- IN_OPEN |- IN_MOVED_FROM |- IN_MOVED_TO |- IN_DELETE |- IN_CREATE |- IN_DELETE_SELF |- IN_MOVE_SELF) + +/*** End inotify.h ***/ diff --git a/Inotify.icl b/Inotify.icl index e676f03..205aba9 100644 --- a/Inotify.icl +++ b/Inotify.icl @@ -42,8 +42,8 @@ where ccall close "I:V:A" } -inotify_add_watch :: (INCallback st) !Int !String !*(Inotify st) - -> *(Either Int INWatch, *Inotify st) +inotify_add_watch :: (INCallback st) !INMask !String !*(Inotify st) + -> *(!Either Int INWatch, !*Inotify st) inotify_add_watch f mask fname inot=:{fd,watches} = let (w, fd`) = c_add_watch fd fname mask in ( if (w == -1) (Left errno) (Right w) @@ -55,7 +55,7 @@ where ccall clean_inotify_add_watch "ISI:VII" } -inotify_rm_watch :: !INWatch !*(Inotify st) -> *(Bool, *Inotify st) +inotify_rm_watch :: !INWatch !*(Inotify st) -> *(!Bool, !*Inotify st) inotify_rm_watch w inot=:{fd} = case c_inotify_rm_watch fd w of (0, fd`) = (True, {inot & fd=fd`}) (_, fd`) = (False, {inot & fd=fd`}) @@ -65,15 +65,17 @@ where ccall clean_inotify_rm_watch "II:VII" } -inotify_poll :: *(Inotify st) -> *Inotify st -inotify_poll inot=:{fd} = let (_,fd`) = c_poll fd in { inot & fd=fd` } +inotify_poll :: !(Maybe Int) !*(Inotify st) -> *(!Int, !*Inotify st) +inotify_poll mbTo inot=:{fd} = let (n,fd`)=c_poll fd to in (n, {inot & fd=fd`}) where - c_poll :: !*Int -> *(!Int, !*Int) - c_poll fd = code { - ccall clean_poll "I:VII" + to = if (isNothing mbTo) -1 (fromJust mbTo) + + c_poll :: !*Int !Int -> *(!Int, !*Int) + c_poll fd timeout = code { + ccall clean_poll "II:VII" } -inotify_check :: *(Inotify st) *World -> *(*Inotify st, *World) +inotify_check :: !*(Inotify st) !*World -> *(!*Inotify st, !*World) inotify_check inot=:{fd,watches,state} w # (ok, wds, masks, fnames, fd) = c_check fd inot = { inot & fd=fd } @@ -117,11 +119,18 @@ where ccall clean_inotify_check "I:VISSSI" } -inotify_loop_forever :: *(Inotify st) *World -> *(*Inotify st, *World) -inotify_loop_forever inot w - # inot = inotify_poll inot +inotify_is_event :: INMask INEvent -> Bool +inotify_is_event mask ev = ev bitand mask <> 0 + +inotify_loop_with_timeout :: !(Maybe Int) !*(Inotify st) !*World -> *(!*Inotify st, !*World) +inotify_loop_with_timeout to inot w + # (n,inot) = inotify_poll to inot + | n == 0 = (inot,w) # (inot,w) = inotify_check inot w - = inotify_loop_forever inot w + = inotify_loop_with_timeout to inot w + +inotify_loop_forever :: !*(Inotify st) !*World -> *(!*Inotify st, !*World) +inotify_loop_forever inot w = inotify_loop_with_timeout Nothing inot w errno :: Int errno = err 0 diff --git a/inotify_c.c b/inotify_c.c index c81f57d..e63ebfb 100644 --- a/inotify_c.c +++ b/inotify_c.c @@ -9,6 +9,10 @@ #include "Clean.h" +/** + * Cast a CleanString to a char* + * The result should be freed. + */ char* clstocs(CleanString* cs) { char* s = calloc(CleanStringLength(cs) + 1, 1); uint8_t i; @@ -18,12 +22,20 @@ char* clstocs(CleanString* cs) { return s; } +/** The empty string, as a CleanString */ static struct {int length; char chars[1]; } empty_string = {0,""}; +/** + * Get the errno. The parameter is ignored, it is just there because ccalls + * need to have an argument. + */ int clean_errno(int ignored) { return errno; } +/** + * Initialise an inotify file descriptor and change to NONBLOCK mode. + */ int clean_inotify_init(int ignored) { int fd; fd = inotify_init(); @@ -33,6 +45,15 @@ int clean_inotify_init(int ignored) { return fd; } +/** + * Add a watch on some file. + * + * fd The inotify file descriptor + * fname The file to watch + * mask A mask of events to watch on + * re_watch Will be set to the resulting watch descriptor + * re_fd Will be set to fd (needed for uniqueness) + */ void clean_inotify_add_watch(int fd, CleanString* fname_, int mask, int *re_watch, int *re_fd) { char* fname = clstocs(fname_); @@ -41,21 +62,56 @@ void clean_inotify_add_watch(int fd, CleanString* fname_, int mask, *re_fd = fd; } +/** + * Remove a watch descriptor. + * + * fd The inotify file descriptor + * watch The watch descriptor to remove + * re_code Will be set to the return code of inotify_rm_watch + * re_fd Will be set to fd (needed for uniqueness) + */ void clean_inotify_rm_watch(int fd, int watch, int *re_code, int *re_fd) { *re_fd = fd; *re_code = inotify_rm_watch(fd, watch); } -void clean_poll(int fd, int *re_nrevents, int *re_fd) { +/** + * Poll an inotify file descriptor + * + * fd The inotify file descriptor to poll + * timeout The timeout (negative for no timeout) + * re_nrevents Will be set to the number of polled events + * re_fd Will be set to fd (needed for uniqueness) + */ +void clean_poll(int fd, int timeout, int *re_nrevents, int *re_fd) { struct pollfd pfd = {fd, POLLIN, 0}; - *re_nrevents = poll(&pfd, 1, -1); + *re_nrevents = poll(&pfd, 1, timeout); *re_fd = fd; } +/** + * CleanStrings that are returned from clean_inotify_check (so that we don't + * have to malloc all the time.) + */ static CleanStringVariable(wds_string, 1024); static CleanStringVariable(masks_string, 1024); static CleanStringVariable(names_string, 4096); +/** + * Check for events on an inotify file descriptor. + * + * fd The inotify file descriptor + * re_ok Will be set to 1 on success, 0 on failure + * re_wds An array of ints, the watch descriptors that had events + * re_masks An array of ints, the events + * re_fnames A list of strings, the filenames of the events (may be empty) + * re_fd Will be set to fd (needed for uniqueness) + * + * re_wds, re_masks and re_fnames are hacks because ccall doesn't allow + * returning {#Int} or {#String}. The int arrays can be read by taking 4 chars + * at a time and casting that to an int. The string array can be read by + * splitting on \0 (since they are filenames, \0 cannot occur). + */ void clean_inotify_check(int fd, int *re_ok, CleanString* re_wds, CleanString* re_masks, CleanString* re_fnames, int *re_fd) { @@ -5,24 +5,35 @@ import Data.Either import Data.Maybe import Inotify +:: Void = Void + +/** + * test: Example usage of Inotify + * + * This will show all events on files file1 and file2. They should exist before + * starting this program. + * + * When nothing has happened for 10s, the program will exit. + */ + Start w -# (Just inot) = inotify_init 0 +# (Just inot) = inotify_init Void # (Right watch, inot) = inotify_add_watch (echo "file1") IN_ALL_EVENTS "file1" inot # (Right watch, inot) = inotify_add_watch (echo "file2") IN_ALL_EVENTS "file2" inot # (io,w) = stdio w -# io = io <<< "Do something with file1 or file2\n" +# io = io <<< "You have 10 seconds to do something with file1 or file2\n" # (ok,w) = fclose io w -# (inot, w) = inotify_loop_forever inot w +# (inot, w) = inotify_loop_with_timeout (Just 10000) inot w = inotify_close inot where - echo :: String INEvent (Maybe String) Int *World -> *(Int, *World) - echo fname ev f i w + echo :: String INEvent (Maybe String) Void *World -> *(Void, *World) + echo fname ev f _ w # (io,w) = stdio w # io = io <<< "EVENT: ["<<< fname <<<"; "<<< ev <<<"; "<<< f <<<"]\n" # (ok,w) = fclose io w - = (i, w) + = (Void, w) instance <<< (Maybe a) | <<< a where @@ -24,7 +24,7 @@ Global Time: False Stack: False Output - Output: ShowConstructors + Output: NoConsole Font: Monaco FontSize: 9 WriteStdErr: False diff --git a/test_reload.icl b/test_reload.icl index 052c4c9..6b71c8f 100644 --- a/test_reload.icl +++ b/test_reload.icl @@ -22,6 +22,7 @@ import Inotify * * Test it by running `make run_test_reload`, then editing `changeme` below and * running `make test_reload` to recompile. + * You can also use `touch test_reload`. */ my_name :== "test_reload" @@ -33,10 +34,10 @@ verbose :== False Start w # (io,w) = stdio w -# io = io <<< changeme <<< "\n" +# io = io <<< changeme <<< " (edit changeme and recompile)\n" # (ok,w) = fclose io w # (Just inot) = inotify_init Void -# (Right watch,inot) = inotify_add_watch reload IN_ALL_EVENTS my_dir inot +# (Right watch,inot) = inotify_add_watch reload IN_ATTRIB my_dir inot # (inot,w) = inotify_loop_forever inot w = inotify_close inot where @@ -45,7 +46,7 @@ where # w = echo (\f -> f <<< "event: " <<< ev <<< "; " <<< mbName) w | isNothing mbName = (Void, w) # (Just name) = mbName - | ev bitand IN_ATTRIB <> 0 && name == my_name + | inotify_is_event IN_ATTRIB ev && name == my_name # w = echo (\f -> f <<< "reloading...") w # w = exit 127 w = (Void, w) |