#include #include #include #include #include #include #include #include #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; for (i = 0; i < CleanStringLength(cs); i++) s[i] = CleanStringCharacters(cs)[i]; s[i] = 0; 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(); if (fd < 0) return 0; fcntl(fd, IN_NONBLOCK); 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_); *re_watch = inotify_add_watch(fd, fname, mask); free(fname); *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); } /** * 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, 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) { char buf[4096] __attribute__((aligned(__alignof__(struct inotify_event)))); const struct inotify_event *ev; ssize_t len; char *ptr; struct pollfd pfd = {fd, POLLIN, 0}; int poll_n; char *wds_ptr = CleanStringCharacters(wds_string); char *masks_ptr = CleanStringCharacters(masks_string); char *names_ptr = CleanStringCharacters(names_string); CleanStringLength(wds_string) = 0; CleanStringLength(masks_string) = 0; CleanStringLength(names_string) = 0; *re_ok = 0; *re_fd = fd; *re_wds = (CleanString) &empty_string; *re_masks = (CleanString) &empty_string; *re_fnames = (CleanString) &empty_string; for (;;) { poll_n = poll(&pfd, 1, 0); if (poll_n < 0) { return; } else if (poll_n == 0) { break; } len = read(fd, buf, sizeof buf); if (len == -1 && errno != EAGAIN) { return; } if (len <= 0) { break; } for (ptr = buf; ptr < buf + len; ptr += sizeof(struct inotify_event) + ev->len) { ev = (const struct inotify_event*) ptr; memcpy(masks_ptr, &ev->mask, 4); masks_ptr += 4; CleanStringLength(masks_string) += 4; memcpy(wds_ptr, &ev->wd, sizeof(int)); wds_ptr += sizeof(int); CleanStringLength(wds_string) += sizeof(int); int len = strlen(ev->name); memcpy(names_ptr, &ev->name, len); names_ptr += len + 1; *(names_ptr - 1) = '\00'; CleanStringLength(names_string) += len + 1; } } *re_wds = (CleanString) wds_string; *re_masks = (CleanString) masks_string; *re_fnames = (CleanString) names_string; *re_ok = 1; }