Verified Commit 25cc1298 authored by Camil Staps's avatar Camil Staps 🚀

Documentation; inotify_is_event; timeout for polling

parent f2edfd50
......@@ -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 ***/
......@@ -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
......
......@@ -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
......
......@@ -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)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment