inotify_c.c 4.78 KB
Newer Older
Camil Staps's avatar
Camil Staps committed
1 2 3 4 5 6 7 8 9 10 11
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/inotify.h>
#include <unistd.h>

#include "Clean.h"

12 13 14 15
/**
 * Cast a CleanString to a char*
 * The result should be freed.
 */
Camil Staps's avatar
Camil Staps committed
16
char* clstocs(CleanString* cs) {
Camil Staps's avatar
Camil Staps committed
17 18 19 20 21 22
	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;
Camil Staps's avatar
Camil Staps committed
23 24
}

25
/** The empty string, as a CleanString */
Camil Staps's avatar
Camil Staps committed
26 27
static struct {int length; char chars[1]; } empty_string = {0,""};

28 29 30 31
/**
 * Get the errno. The parameter is ignored, it is just there because ccalls
 * need to have an argument.
 */
Camil Staps's avatar
Camil Staps committed
32 33 34 35
int clean_errno(int ignored) {
	return errno;
}

36 37 38
/**
 * Initialise an inotify file descriptor and change to NONBLOCK mode.
 */
Camil Staps's avatar
Camil Staps committed
39 40 41 42 43 44 45 46 47
int clean_inotify_init(int ignored) {
	int fd;
	fd = inotify_init();
	if (fd < 0)
		return 0;
	fcntl(fd, IN_NONBLOCK);
	return fd;
}

48 49 50 51 52 53 54 55 56
/**
 * 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)
 */
Camil Staps's avatar
Camil Staps committed
57 58
void clean_inotify_add_watch(int fd, CleanString* fname_, int mask,
		int *re_watch, int *re_fd) {
Camil Staps's avatar
Camil Staps committed
59 60 61 62 63 64
	char* fname = clstocs(fname_);
	*re_watch = inotify_add_watch(fd, fname, mask);
	free(fname);
	*re_fd = fd;
}

65 66 67 68 69 70 71 72
/**
 * 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)
 */
Camil Staps's avatar
Camil Staps committed
73 74 75 76 77
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);
}

78 79 80 81 82 83 84 85 86
/**
 * 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) {
Camil Staps's avatar
Camil Staps committed
87
	struct pollfd pfd = {fd, POLLIN, 0};
88
	*re_nrevents = poll(&pfd, 1, timeout);
Camil Staps's avatar
Camil Staps committed
89 90 91
	*re_fd = fd;
}

92 93 94 95
/**
 * CleanStrings that are returned from clean_inotify_check (so that we don't
 * have to malloc all the time.)
 */
Camil Staps's avatar
Camil Staps committed
96 97
static CleanStringVariable(wds_string, 1024);
static CleanStringVariable(masks_string, 1024);
98
static CleanStringVariable(names_string, 4096);
Camil Staps's avatar
Camil Staps committed
99

100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
/**
 * 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).
 */
Camil Staps's avatar
Camil Staps committed
115
void clean_inotify_check(int fd,
116 117
		int *re_ok, CleanString* re_wds, CleanString* re_masks,
		CleanString* re_fnames, int *re_fd) {
Camil Staps's avatar
Camil Staps committed
118 119 120 121 122 123 124 125 126 127
	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);
128
	char *names_ptr = CleanStringCharacters(names_string);
Camil Staps's avatar
Camil Staps committed
129 130
	CleanStringLength(wds_string) = 0;
	CleanStringLength(masks_string) = 0;
131
	CleanStringLength(names_string) = 0;
Camil Staps's avatar
Camil Staps committed
132 133 134 135 136 137

	*re_ok = 0;
	*re_fd = fd;

	*re_wds = (CleanString) &empty_string;
	*re_masks = (CleanString) &empty_string;
138
	*re_fnames = (CleanString) &empty_string;
Camil Staps's avatar
Camil Staps committed
139 140

	for (;;) {
141
		poll_n = poll(&pfd, 1, 0);
Camil Staps's avatar
Camil Staps committed
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
		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);
167 168 169 170 171 172

			int len = strlen(ev->name);
			memcpy(names_ptr, &ev->name, len);
			names_ptr += len + 1;
			*(names_ptr - 1) = '\00';
			CleanStringLength(names_string) += len + 1;
Camil Staps's avatar
Camil Staps committed
173 174 175 176 177
		}
	}

	*re_wds = (CleanString) wds_string;
	*re_masks = (CleanString) masks_string;
178
	*re_fnames = (CleanString) names_string;
Camil Staps's avatar
Camil Staps committed
179 180 181

	*re_ok = 1;
}