io-watch 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data/ext/Makefile +31 -0
- data/ext/bin/io-watch +0 -0
- data/ext/configure +77 -0
- data/ext/io/watch/fsevent.c +107 -0
- data/ext/io/watch/fsevent.o +0 -0
- data/ext/io/watch/inotify.c +229 -0
- data/ext/io/watch/watch.h +13 -0
- data/ext/io/watch.c +45 -0
- data/ext/io/watch.o +0 -0
- data/ext/io-watch +0 -0
- data/lib/io/watch/monitor.rb +65 -0
- data/lib/io/watch/version.rb +10 -0
- data/lib/io/watch.rb +15 -0
- data/license.md +21 -0
- data/readme.md +29 -0
- data.tar.gz.sig +0 -0
- metadata +89 -0
- metadata.gz.sig +0 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 28d2d87eebbc0c7ba0750330239bdd1d00c8195ff8a93121ba13a8368b681730
|
4
|
+
data.tar.gz: 8528b998df129ca0da1f4ff7cbf3627721608bf4491ac7da41fb3a8b381d65cf
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7896b82cab31023f395aeb3bf9a588e31291b8d5ed2030f818377977e15dfb431a7ec80af2717ee252bff134816aea367c2099084747c09af16af62c58c174fe
|
7
|
+
data.tar.gz: e9f8f8a987490ace634ddd18eea1514657e12b0f945219063442750c16e53dc1f59e95b91c0ecb356e06ec863d2661a601069e611fe6ad3503a739737a01a2bf
|
checksums.yaml.gz.sig
ADDED
Binary file
|
data/ext/Makefile
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
TARGET = io-watch
|
2
|
+
CC = cc
|
3
|
+
CFLAGS = -Wall -Wextra
|
4
|
+
LDFLAGS = -framework CoreServices
|
5
|
+
SOURCES = io/watch.c io/watch/fsevent.c
|
6
|
+
OBJECTS = $(SOURCES:.c=.o)
|
7
|
+
PREFIX = /Users/samuel/Developer/socketry/io-watch/ext
|
8
|
+
|
9
|
+
all: $(TARGET)
|
10
|
+
|
11
|
+
$(TARGET): $(OBJECTS)
|
12
|
+
$(CC) $(LDFLAGS) -o $@ $^
|
13
|
+
|
14
|
+
# To create object files
|
15
|
+
%.o: %.c
|
16
|
+
$(CC) $(CFLAGS) -c $< -o $@
|
17
|
+
|
18
|
+
# Clean Target
|
19
|
+
clean:
|
20
|
+
rm -f $(TARGET) $(OBJECTS)
|
21
|
+
|
22
|
+
install: $(TARGET)
|
23
|
+
@echo "Installing $(TARGET) to $(PREFIX)/bin"
|
24
|
+
mkdir -p $(PREFIX)/bin
|
25
|
+
install -m 755 $(TARGET) $(PREFIX)/bin
|
26
|
+
|
27
|
+
uninstall:
|
28
|
+
@echo "Removing $(TARGET) from $(PREFIX)/bin"
|
29
|
+
rm -f $(PREFIX)/bin/$(TARGET)
|
30
|
+
|
31
|
+
.PHONY: all clean install uninstall
|
data/ext/bin/io-watch
ADDED
Binary file
|
data/ext/configure
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
|
3
|
+
PREFIX=$(pwd)
|
4
|
+
LDFLAGS=""
|
5
|
+
SOURCES=""
|
6
|
+
CC="${CC:-cc}"
|
7
|
+
|
8
|
+
# Parse command line arguments
|
9
|
+
for arg in "$@"; do
|
10
|
+
case $arg in
|
11
|
+
--prefix=*)
|
12
|
+
PREFIX="${arg#*=}"
|
13
|
+
shift
|
14
|
+
;;
|
15
|
+
*)
|
16
|
+
echo "Unknown option: $arg"
|
17
|
+
exit 1
|
18
|
+
;;
|
19
|
+
esac
|
20
|
+
done
|
21
|
+
|
22
|
+
OS="$(uname)"
|
23
|
+
echo "Detected OS: $OS"
|
24
|
+
|
25
|
+
# Generate Makefile based on the platform
|
26
|
+
case "$OS" in
|
27
|
+
Darwin)
|
28
|
+
echo "Using FSEvents (macOS)"
|
29
|
+
LDFLAGS="-framework CoreServices"
|
30
|
+
SOURCES="io/watch.c io/watch/fsevent.c"
|
31
|
+
;;
|
32
|
+
Linux)
|
33
|
+
echo "Using inotify (Linux)"
|
34
|
+
SOURCES="io/watch.c io/watch/inotify.c"
|
35
|
+
;;
|
36
|
+
*)
|
37
|
+
echo "Unsupported platform: $OS"
|
38
|
+
exit 1
|
39
|
+
;;
|
40
|
+
esac
|
41
|
+
|
42
|
+
# Create the Makefile
|
43
|
+
cat > Makefile <<EOF
|
44
|
+
TARGET = io-watch
|
45
|
+
CC = $CC
|
46
|
+
CFLAGS = -Wall -Wextra
|
47
|
+
LDFLAGS = $LDFLAGS
|
48
|
+
SOURCES = $SOURCES
|
49
|
+
OBJECTS = \$(SOURCES:.c=.o)
|
50
|
+
PREFIX = $PREFIX
|
51
|
+
|
52
|
+
all: \$(TARGET)
|
53
|
+
|
54
|
+
\$(TARGET): \$(OBJECTS)
|
55
|
+
\$(CC) \$(LDFLAGS) -o \$@ \$^
|
56
|
+
|
57
|
+
# To create object files
|
58
|
+
%.o: %.c
|
59
|
+
\$(CC) \$(CFLAGS) -c \$< -o \$@
|
60
|
+
|
61
|
+
# Clean Target
|
62
|
+
clean:
|
63
|
+
rm -f \$(TARGET) \$(OBJECTS)
|
64
|
+
|
65
|
+
install: \$(TARGET)
|
66
|
+
@echo "Installing \$(TARGET) to \$(PREFIX)/bin"
|
67
|
+
mkdir -p \$(PREFIX)/bin
|
68
|
+
install -m 755 \$(TARGET) \$(PREFIX)/bin
|
69
|
+
|
70
|
+
uninstall:
|
71
|
+
@echo "Removing \$(TARGET) from \$(PREFIX)/bin"
|
72
|
+
rm -f \$(PREFIX)/bin/\$(TARGET)
|
73
|
+
|
74
|
+
.PHONY: all clean install uninstall
|
75
|
+
EOF
|
76
|
+
|
77
|
+
echo "Configuration complete. Run 'make' to build the project."
|
@@ -0,0 +1,107 @@
|
|
1
|
+
#include "watch.h"
|
2
|
+
|
3
|
+
#include <CoreServices/CoreServices.h>
|
4
|
+
#include <dispatch/dispatch.h>
|
5
|
+
#include <stdio.h>
|
6
|
+
|
7
|
+
enum {
|
8
|
+
DEBUG = 0,
|
9
|
+
};
|
10
|
+
|
11
|
+
static
|
12
|
+
int IO_Watch_path_prefix(const char *path, const char *prefix) {
|
13
|
+
size_t path_size = strlen(path);
|
14
|
+
size_t prefix_size = strlen(prefix);
|
15
|
+
|
16
|
+
if (path_size < prefix_size) {
|
17
|
+
return 0;
|
18
|
+
}
|
19
|
+
|
20
|
+
return strncmp(path, prefix, prefix_size) == 0;
|
21
|
+
}
|
22
|
+
|
23
|
+
static
|
24
|
+
ssize_t IO_Watch_find_path(struct IO_Watch *watch, const char *path) {
|
25
|
+
size_t index = 0;
|
26
|
+
|
27
|
+
while (index < watch->size) {
|
28
|
+
if (IO_Watch_path_prefix(path, watch->paths[index])) {
|
29
|
+
return index;
|
30
|
+
}
|
31
|
+
|
32
|
+
index += 1;
|
33
|
+
}
|
34
|
+
|
35
|
+
return -1;
|
36
|
+
}
|
37
|
+
|
38
|
+
// Function to handle filesystem events
|
39
|
+
void IO_Watch_FSEvent_callback(
|
40
|
+
ConstFSEventStreamRef streamRef,
|
41
|
+
void *context,
|
42
|
+
size_t numberOfEvents,
|
43
|
+
void *eventData,
|
44
|
+
const FSEventStreamEventFlags eventFlags[],
|
45
|
+
const FSEventStreamEventId eventIds[]) {
|
46
|
+
|
47
|
+
const char **eventPaths = (const char**)eventData;
|
48
|
+
struct IO_Watch *watch = context;
|
49
|
+
|
50
|
+
for (size_t i = 0; i < numberOfEvents; i++) {
|
51
|
+
if (DEBUG) fprintf(stderr, "Event: %s\n", eventPaths[i]);
|
52
|
+
|
53
|
+
// Find the index of the path in the paths array
|
54
|
+
ssize_t index = IO_Watch_find_path(watch, eventPaths[i]);
|
55
|
+
|
56
|
+
if (index != -1) {
|
57
|
+
// Output event data as newline-delimited JSON
|
58
|
+
printf("{\"index\":%zu,\"flags\":%u,\"id\":%llu}\n", index, eventFlags[i], eventIds[i]);
|
59
|
+
} else {
|
60
|
+
fprintf(stderr, "Path not found in paths array: %s\n", eventPaths[i]);
|
61
|
+
}
|
62
|
+
}
|
63
|
+
|
64
|
+
fflush(stdout);
|
65
|
+
}
|
66
|
+
|
67
|
+
void IO_Watch_run(struct IO_Watch *watch) {
|
68
|
+
CFStringRef *pathsToWatch = malloc(sizeof(CFStringRef) * watch->size);
|
69
|
+
for (size_t i = 0; i < watch->size; i++) {
|
70
|
+
pathsToWatch[i] = CFStringCreateWithCString(NULL, watch->paths[i], kCFStringEncodingUTF8);
|
71
|
+
}
|
72
|
+
|
73
|
+
CFArrayRef pathsToWatchArray = CFArrayCreate(NULL, (const void **)pathsToWatch, watch->size, &kCFTypeArrayCallBacks);
|
74
|
+
|
75
|
+
FSEventStreamContext context = {0, watch, NULL, NULL, NULL};
|
76
|
+
|
77
|
+
FSEventStreamRef stream;
|
78
|
+
CFAbsoluteTime latency = watch->latency;
|
79
|
+
|
80
|
+
stream = FSEventStreamCreate(
|
81
|
+
NULL,
|
82
|
+
&IO_Watch_FSEvent_callback,
|
83
|
+
&context,
|
84
|
+
pathsToWatchArray,
|
85
|
+
kFSEventStreamEventIdSinceNow,
|
86
|
+
latency,
|
87
|
+
kFSEventStreamCreateFlagNone
|
88
|
+
);
|
89
|
+
|
90
|
+
dispatch_queue_t queue = dispatch_queue_create("com.example.fsevents.queue", NULL);
|
91
|
+
FSEventStreamSetDispatchQueue(stream, queue);
|
92
|
+
FSEventStreamStart(stream);
|
93
|
+
|
94
|
+
printf("{\"status\":\"started\"}\n");
|
95
|
+
fflush(stdout);
|
96
|
+
|
97
|
+
dispatch_main();
|
98
|
+
|
99
|
+
FSEventStreamStop(stream);
|
100
|
+
FSEventStreamInvalidate(stream);
|
101
|
+
FSEventStreamRelease(stream);
|
102
|
+
for (size_t i = 0; i < watch->size; i++) {
|
103
|
+
CFRelease(pathsToWatch[i]);
|
104
|
+
}
|
105
|
+
free(pathsToWatch);
|
106
|
+
CFRelease(pathsToWatchArray);
|
107
|
+
}
|
Binary file
|
@@ -0,0 +1,229 @@
|
|
1
|
+
#include "watch.h"
|
2
|
+
|
3
|
+
#include <sys/inotify.h>
|
4
|
+
#include <limits.h>
|
5
|
+
#include <stdio.h>
|
6
|
+
#include <stdlib.h>
|
7
|
+
#include <unistd.h>
|
8
|
+
#include <errno.h>
|
9
|
+
#include <dirent.h>
|
10
|
+
#include <string.h>
|
11
|
+
#include <sys/stat.h>
|
12
|
+
|
13
|
+
enum {
|
14
|
+
DEBUG = 0,
|
15
|
+
};
|
16
|
+
|
17
|
+
struct IO_Watch_Watch {
|
18
|
+
int watch_descriptor;
|
19
|
+
char *path;
|
20
|
+
int index;
|
21
|
+
};
|
22
|
+
|
23
|
+
struct IO_Watch_Watch_Array {
|
24
|
+
size_t size;
|
25
|
+
size_t capacity;
|
26
|
+
struct IO_Watch_Watch *watches;
|
27
|
+
};
|
28
|
+
|
29
|
+
#define BUFFER_SIZE (10 * (sizeof(struct inotify_event) + NAME_MAX + 1))
|
30
|
+
|
31
|
+
void IO_Watch_Watch_Array_initialize(struct IO_Watch_Watch_Array *array) {
|
32
|
+
array->size = 0;
|
33
|
+
array->capacity = 16;
|
34
|
+
array->watches = malloc(array->capacity * sizeof(struct IO_Watch_Watch));
|
35
|
+
if (!array->watches) {
|
36
|
+
perror("malloc");
|
37
|
+
exit(EXIT_FAILURE);
|
38
|
+
}
|
39
|
+
}
|
40
|
+
|
41
|
+
void IO_Watch_Watch_Array_resize(struct IO_Watch_Watch_Array *array) {
|
42
|
+
array->capacity *= 2;
|
43
|
+
array->watches = realloc(array->watches, array->capacity * sizeof(struct IO_Watch_Watch));
|
44
|
+
if (!array->watches) {
|
45
|
+
perror("realloc");
|
46
|
+
exit(EXIT_FAILURE);
|
47
|
+
}
|
48
|
+
}
|
49
|
+
|
50
|
+
void IO_Watch_Watch_Array_add(struct IO_Watch_Watch_Array *array, int watch_descriptor, char *path, int index) {
|
51
|
+
if (array->size == array->capacity) {
|
52
|
+
IO_Watch_Watch_Array_resize(array);
|
53
|
+
}
|
54
|
+
array->watches[array->size].watch_descriptor = watch_descriptor;
|
55
|
+
array->watches[array->size].path = path;
|
56
|
+
array->watches[array->size].index = index;
|
57
|
+
array->size++;
|
58
|
+
}
|
59
|
+
|
60
|
+
ssize_t IO_Watch_Watch_Array_find(struct IO_Watch_Watch_Array *array, int watch_descriptor) {
|
61
|
+
for (size_t i = 0; i < array->size; i++) {
|
62
|
+
if (array->watches[i].watch_descriptor == watch_descriptor) {
|
63
|
+
return i;
|
64
|
+
}
|
65
|
+
}
|
66
|
+
return -1;
|
67
|
+
}
|
68
|
+
|
69
|
+
void IO_Watch_Watch_Array_watch(int fd, struct IO_Watch_Watch_Array *watch_array, char *path, int index) {
|
70
|
+
int watch_descriptor = inotify_add_watch(fd, path, IN_ALL_EVENTS);
|
71
|
+
if (watch_descriptor == -1) {
|
72
|
+
perror("inotify_add_watch");
|
73
|
+
exit(EXIT_FAILURE);
|
74
|
+
}
|
75
|
+
|
76
|
+
IO_Watch_Watch_Array_add(watch_array, watch_descriptor, path, index);
|
77
|
+
|
78
|
+
if (DEBUG) fprintf(stderr, "Added watch: %s\n", path);
|
79
|
+
}
|
80
|
+
|
81
|
+
void IO_Watch_Watch_Array_scan(int fd, struct IO_Watch_Watch_Array *watch_array, const char *root, int index) {
|
82
|
+
DIR *dir = opendir(root);
|
83
|
+
if (!dir) {
|
84
|
+
perror("opendir");
|
85
|
+
return;
|
86
|
+
}
|
87
|
+
|
88
|
+
struct dirent *entry;
|
89
|
+
while ((entry = readdir(dir)) != NULL) {
|
90
|
+
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
|
91
|
+
continue;
|
92
|
+
}
|
93
|
+
|
94
|
+
size_t size = strlen(root) + 1 + strlen(entry->d_name) + 1;
|
95
|
+
char *path = malloc(size);
|
96
|
+
snprintf(path, size, "%s/%s", root, entry->d_name);
|
97
|
+
|
98
|
+
struct stat statbuf;
|
99
|
+
if (stat(path, &statbuf) == -1) {
|
100
|
+
perror("stat");
|
101
|
+
continue;
|
102
|
+
}
|
103
|
+
|
104
|
+
if (S_ISDIR(statbuf.st_mode)) {
|
105
|
+
IO_Watch_Watch_Array_watch(fd, watch_array, path, index);
|
106
|
+
IO_Watch_Watch_Array_scan(fd, watch_array, path, index);
|
107
|
+
} else {
|
108
|
+
free(path);
|
109
|
+
}
|
110
|
+
}
|
111
|
+
|
112
|
+
closedir(dir);
|
113
|
+
}
|
114
|
+
|
115
|
+
void IO_Watch_Watch_Array_add_subdirectory(int fd, struct IO_Watch_Watch_Array *watch_array, struct IO_Watch_Watch watch, const char *name) {
|
116
|
+
size_t size = strlen(watch.path) + 1 + strlen(name) + 1;
|
117
|
+
char *path = malloc(size);
|
118
|
+
snprintf(path, size, "%s/%s", watch.path, name);
|
119
|
+
|
120
|
+
IO_Watch_Watch_Array_watch(fd, watch_array, path, watch.index);
|
121
|
+
IO_Watch_Watch_Array_scan(fd, watch_array, path, watch.index);
|
122
|
+
}
|
123
|
+
|
124
|
+
void IO_Watch_Watch_Array_remove(int fd, struct IO_Watch_Watch_Array *watch_array, size_t index) {
|
125
|
+
struct IO_Watch_Watch watch = watch_array->watches[index];
|
126
|
+
|
127
|
+
if (DEBUG) fprintf(stderr, "Removing watch: %s\n", watch.path);
|
128
|
+
|
129
|
+
inotify_rm_watch(fd, watch.watch_descriptor);
|
130
|
+
free(watch.path);
|
131
|
+
|
132
|
+
// Replace the removed item with the last one.
|
133
|
+
watch_array->size--;
|
134
|
+
if (index < watch_array->size) {
|
135
|
+
watch_array->watches[index] = watch_array->watches[watch_array->size];
|
136
|
+
}
|
137
|
+
}
|
138
|
+
|
139
|
+
static
|
140
|
+
void IO_Watch_INotify_print_event(struct inotify_event *event) {
|
141
|
+
fprintf(stderr, "Event: wd=%d", event->wd);
|
142
|
+
|
143
|
+
uint32_t mask = event->mask;
|
144
|
+
if (mask & IN_ACCESS) fprintf(stderr, " ACCESS");
|
145
|
+
if (mask & IN_MODIFY) fprintf(stderr, " MODIFY");
|
146
|
+
if (mask & IN_ATTRIB) fprintf(stderr, " ATTRIB");
|
147
|
+
if (mask & IN_CLOSE_WRITE) fprintf(stderr, " CLOSE_WRITE");
|
148
|
+
if (mask & IN_CLOSE_NOWRITE) fprintf(stderr, " CLOSE_NOWRITE");
|
149
|
+
if (mask & IN_OPEN) fprintf(stderr, " OPEN");
|
150
|
+
if (mask & IN_MOVED_FROM) fprintf(stderr, " MOVED_FROM");
|
151
|
+
if (mask & IN_MOVED_TO) fprintf(stderr, " MOVED_TO");
|
152
|
+
if (mask & IN_CREATE) fprintf(stderr, " CREATE");
|
153
|
+
if (mask & IN_DELETE) fprintf(stderr, " DELETE");
|
154
|
+
if (mask & IN_DELETE_SELF) fprintf(stderr, " DELETE_SELF");
|
155
|
+
if (mask & IN_MOVE_SELF) fprintf(stderr, " MOVE_SELF");
|
156
|
+
if (mask & IN_UNMOUNT) fprintf(stderr, " UNMOUNT");
|
157
|
+
if (mask & IN_Q_OVERFLOW) fprintf(stderr, " Q_OVERFLOW");
|
158
|
+
if (mask & IN_IGNORED) fprintf(stderr, " IGNORED");
|
159
|
+
if (mask & IN_ISDIR) fprintf(stderr, " ISDIR");
|
160
|
+
if (mask & IN_ONESHOT) fprintf(stderr, " ONESHOT");
|
161
|
+
if (mask & IN_ALL_EVENTS) fprintf(stderr, " ALL_EVENTS");
|
162
|
+
|
163
|
+
if (event->len > 0) {
|
164
|
+
fprintf(stderr, " name=%s", event->name);
|
165
|
+
}
|
166
|
+
|
167
|
+
fprintf(stderr, "\n");
|
168
|
+
}
|
169
|
+
|
170
|
+
void IO_Watch_run(struct IO_Watch *watch) {
|
171
|
+
int fd = inotify_init1(IN_NONBLOCK);
|
172
|
+
if (fd == -1) {
|
173
|
+
perror("inotify_init1");
|
174
|
+
exit(EXIT_FAILURE);
|
175
|
+
}
|
176
|
+
|
177
|
+
struct IO_Watch_Watch_Array watch_array;
|
178
|
+
IO_Watch_Watch_Array_initialize(&watch_array);
|
179
|
+
|
180
|
+
for (size_t i = 0; i < watch->size; i++) {
|
181
|
+
char *path = strdup(watch->paths[i]);
|
182
|
+
|
183
|
+
IO_Watch_Watch_Array_watch(fd, &watch_array, path, i);
|
184
|
+
IO_Watch_Watch_Array_scan(fd, &watch_array, path, i);
|
185
|
+
}
|
186
|
+
|
187
|
+
printf("{\"status\":\"started\"}\n");
|
188
|
+
fflush(stdout);
|
189
|
+
|
190
|
+
char buffer[BUFFER_SIZE] __attribute__ ((aligned(8)));
|
191
|
+
|
192
|
+
while (1) {
|
193
|
+
ssize_t result = read(fd, buffer, BUFFER_SIZE);
|
194
|
+
if (result == -1 && errno != EAGAIN) {
|
195
|
+
perror("read");
|
196
|
+
exit(EXIT_FAILURE);
|
197
|
+
}
|
198
|
+
|
199
|
+
for (ssize_t offset = 0; offset < result;) {
|
200
|
+
struct inotify_event *event = (struct inotify_event *) &buffer[offset];
|
201
|
+
if (DEBUG) IO_Watch_INotify_print_event(event);
|
202
|
+
|
203
|
+
ssize_t index = IO_Watch_Watch_Array_find(&watch_array, event->wd);
|
204
|
+
|
205
|
+
if (index != -1) {
|
206
|
+
printf("{\"index\":%d,\"mask\":%u}\n", watch_array.watches[index].index, event->mask);
|
207
|
+
|
208
|
+
// If a new directory is created, add a watch for it
|
209
|
+
if (event->mask & IN_CREATE && event->mask & IN_ISDIR) {
|
210
|
+
IO_Watch_Watch_Array_add_subdirectory(fd, &watch_array, watch_array.watches[index], event->name);
|
211
|
+
} else if (event->mask & IN_IGNORED) {
|
212
|
+
IO_Watch_Watch_Array_remove(fd, &watch_array, index);
|
213
|
+
}
|
214
|
+
} else {
|
215
|
+
fprintf(stderr, "Watch descriptor not found: %d\n", event->wd);
|
216
|
+
}
|
217
|
+
|
218
|
+
offset += sizeof(struct inotify_event) + event->len;
|
219
|
+
}
|
220
|
+
fflush(stdout);
|
221
|
+
}
|
222
|
+
|
223
|
+
for (size_t i = 0; i < watch_array.size; i++) {
|
224
|
+
inotify_rm_watch(fd, watch_array.watches[i].watch_descriptor);
|
225
|
+
free(watch_array.watches[i].path);
|
226
|
+
}
|
227
|
+
close(fd);
|
228
|
+
free(watch_array.watches);
|
229
|
+
}
|
data/ext/io/watch.c
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
#include "watch/watch.h"
|
2
|
+
|
3
|
+
#include <stdio.h>
|
4
|
+
#include <stdlib.h>
|
5
|
+
#include <string.h>
|
6
|
+
|
7
|
+
enum {
|
8
|
+
DEBUG = 0,
|
9
|
+
};
|
10
|
+
|
11
|
+
int main(int argc, char **argv) {
|
12
|
+
if (argc < 2) {
|
13
|
+
fprintf(stderr, "Usage: %s <directory_to_watch> [<directory_to_watch> ...]\n", argv[0]);
|
14
|
+
return 1;
|
15
|
+
}
|
16
|
+
|
17
|
+
struct IO_Watch watch;
|
18
|
+
watch.latency = 0.1;
|
19
|
+
watch.size = argc - 1;
|
20
|
+
watch.paths = (const char **) &argv[1];
|
21
|
+
|
22
|
+
char * latency = getenv("IO_WATCH_LATENCY");
|
23
|
+
if (latency != NULL) {
|
24
|
+
watch.latency = atof(latency);
|
25
|
+
}
|
26
|
+
|
27
|
+
for (size_t i = 0; i < watch.size; i++) {
|
28
|
+
char *real_path = realpath(watch.paths[i], NULL);
|
29
|
+
if (real_path == NULL) {
|
30
|
+
fprintf(stderr, "Error: realpath failed for %s\n", watch.paths[i]);
|
31
|
+
return 1;
|
32
|
+
} else {
|
33
|
+
if (DEBUG) fprintf(stderr, "Watching: %s\n", real_path);
|
34
|
+
watch.paths[i] = real_path;
|
35
|
+
}
|
36
|
+
}
|
37
|
+
|
38
|
+
IO_Watch_run(&watch);
|
39
|
+
|
40
|
+
for (size_t i = 0; i < watch.size; i++) {
|
41
|
+
free((void *) watch.paths[i]);
|
42
|
+
}
|
43
|
+
|
44
|
+
return 0;
|
45
|
+
}
|
data/ext/io/watch.o
ADDED
Binary file
|
data/ext/io-watch
ADDED
Binary file
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Released under the MIT License.
|
4
|
+
# Copyright, 2024, by Samuel Williams.
|
5
|
+
|
6
|
+
require 'json'
|
7
|
+
|
8
|
+
class IO
|
9
|
+
module Watch
|
10
|
+
# Represents a list of roots to watch for changes.
|
11
|
+
class Monitor
|
12
|
+
def self.command_path
|
13
|
+
if extensions_path = Gem.loaded_specs['io-watch']&.extensions_dir
|
14
|
+
if File.exist?(extensions_path)
|
15
|
+
return File.join(extensions_path, 'io-watch')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
return File.join(__dir__, '../../../ext/bin/io-watch')
|
20
|
+
end
|
21
|
+
|
22
|
+
COMMAND_PATH = self.command_path
|
23
|
+
|
24
|
+
# Initialize the monitor with a list of roots to watch.
|
25
|
+
#
|
26
|
+
# The roots are the paths that will be watched for changes, recursively.
|
27
|
+
#
|
28
|
+
# @parameter roots [Array(String)] The list of root directories to watch. Changes to these directories, and their children, recursively, will be reported.
|
29
|
+
# @parameter latency [Float] The latency to use when watching the filesystem.
|
30
|
+
def initialize(roots, latency = 0.1)
|
31
|
+
@roots = roots
|
32
|
+
@latency = latency
|
33
|
+
end
|
34
|
+
|
35
|
+
# Run the monitor and yield events as they occur.
|
36
|
+
#
|
37
|
+
# The values yielded are hashes with at least the following:
|
38
|
+
# - `:root` - The root path that the event occurred in.
|
39
|
+
#
|
40
|
+
# There may be other platform specific keys present.
|
41
|
+
#
|
42
|
+
# @yields {|event| ...} Yielded for each event that occurs.
|
43
|
+
# @parameter event [Hash] The event that occurred.
|
44
|
+
def run
|
45
|
+
environment = {'IO_WATCH_LATENCY' => @latency.to_s}
|
46
|
+
IO.pipe do |input, output|
|
47
|
+
pid = Process.spawn(environment, self.class.command_path, *@roots, out: output)
|
48
|
+
output.close
|
49
|
+
|
50
|
+
input.each_line do |line|
|
51
|
+
event = JSON.parse(line, symbolize_names: true)
|
52
|
+
|
53
|
+
if index = event[:index]
|
54
|
+
event[:root] = @roots[index]
|
55
|
+
end
|
56
|
+
|
57
|
+
yield event
|
58
|
+
end
|
59
|
+
ensure
|
60
|
+
Process.kill('TERM', pid) if pid
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
data/lib/io/watch.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Released under the MIT License.
|
4
|
+
# Copyright, 2024, by Samuel Williams.
|
5
|
+
|
6
|
+
require_relative 'watch/version'
|
7
|
+
require_relative 'watch/monitor'
|
8
|
+
|
9
|
+
class IO
|
10
|
+
module Watch
|
11
|
+
def self.new(...)
|
12
|
+
Monitor.new(...)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/license.md
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# MIT License
|
2
|
+
|
3
|
+
Copyright, 2024, by Samuel Williams.
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/readme.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# IO::Watch
|
2
|
+
|
3
|
+
Monitor the file-system for changes.
|
4
|
+
|
5
|
+
[![Development Status](https://github.com/socketry/io-watch/workflows/Test/badge.svg)](https://github.com/socketry/io-watch/actions?workflow=Test)
|
6
|
+
|
7
|
+
## Usage
|
8
|
+
|
9
|
+
Please see the [project documentation](https://socketry.github.io/io-watch/) for more details.
|
10
|
+
|
11
|
+
- [Getting Started](https://socketry.github.io/io-watch/guides/getting-started/index) - This guide explains how to use the `io-watch` gem for watching files and directories for changes.
|
12
|
+
|
13
|
+
## Contributing
|
14
|
+
|
15
|
+
We welcome contributions to this project.
|
16
|
+
|
17
|
+
1. Fork it.
|
18
|
+
2. Create your feature branch (`git checkout -b my-new-feature`).
|
19
|
+
3. Commit your changes (`git commit -am 'Add some feature'`).
|
20
|
+
4. Push to the branch (`git push origin my-new-feature`).
|
21
|
+
5. Create new Pull Request.
|
22
|
+
|
23
|
+
### Developer Certificate of Origin
|
24
|
+
|
25
|
+
In order to protect users of this project, we require all contributions to comply with the [Developer Certificate of Origin](https://developercertificate.org/). This ensures that all contributions are properly licensed and attributed. All contributors must agree to this document for their contributions to be accepted.
|
26
|
+
|
27
|
+
### Community Guidelines
|
28
|
+
|
29
|
+
This project is best served by a collaborative and respectful environment. Treat each other professionally, respect differing viewpoints, and engage constructively. Harassment, discrimination, or harmful behavior is not tolerated. Communicate clearly, listen actively, and support one another. If any issues arise, please inform the project maintainers.
|
data.tar.gz.sig
ADDED
Binary file
|
metadata
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: io-watch
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Samuel Williams
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain:
|
11
|
+
- |
|
12
|
+
-----BEGIN CERTIFICATE-----
|
13
|
+
MIIE2DCCA0CgAwIBAgIBATANBgkqhkiG9w0BAQsFADBhMRgwFgYDVQQDDA9zYW11
|
14
|
+
ZWwud2lsbGlhbXMxHTAbBgoJkiaJk/IsZAEZFg1vcmlvbnRyYW5zZmVyMRIwEAYK
|
15
|
+
CZImiZPyLGQBGRYCY28xEjAQBgoJkiaJk/IsZAEZFgJuejAeFw0yMjA4MDYwNDUz
|
16
|
+
MjRaFw0zMjA4MDMwNDUzMjRaMGExGDAWBgNVBAMMD3NhbXVlbC53aWxsaWFtczEd
|
17
|
+
MBsGCgmSJomT8ixkARkWDW9yaW9udHJhbnNmZXIxEjAQBgoJkiaJk/IsZAEZFgJj
|
18
|
+
bzESMBAGCgmSJomT8ixkARkWAm56MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIB
|
19
|
+
igKCAYEAomvSopQXQ24+9DBB6I6jxRI2auu3VVb4nOjmmHq7XWM4u3HL+pni63X2
|
20
|
+
9qZdoq9xt7H+RPbwL28LDpDNflYQXoOhoVhQ37Pjn9YDjl8/4/9xa9+NUpl9XDIW
|
21
|
+
sGkaOY0eqsQm1pEWkHJr3zn/fxoKPZPfaJOglovdxf7dgsHz67Xgd/ka+Wo1YqoE
|
22
|
+
e5AUKRwUuvaUaumAKgPH+4E4oiLXI4T1Ff5Q7xxv6yXvHuYtlMHhYfgNn8iiW8WN
|
23
|
+
XibYXPNP7NtieSQqwR/xM6IRSoyXKuS+ZNGDPUUGk8RoiV/xvVN4LrVm9upSc0ss
|
24
|
+
RZ6qwOQmXCo/lLcDUxJAgG95cPw//sI00tZan75VgsGzSWAOdjQpFM0l4dxvKwHn
|
25
|
+
tUeT3ZsAgt0JnGqNm2Bkz81kG4A2hSyFZTFA8vZGhp+hz+8Q573tAR89y9YJBdYM
|
26
|
+
zp0FM4zwMNEUwgfRzv1tEVVUEXmoFCyhzonUUw4nE4CFu/sE3ffhjKcXcY//qiSW
|
27
|
+
xm4erY3XAgMBAAGjgZowgZcwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0O
|
28
|
+
BBYEFO9t7XWuFf2SKLmuijgqR4sGDlRsMC4GA1UdEQQnMCWBI3NhbXVlbC53aWxs
|
29
|
+
aWFtc0BvcmlvbnRyYW5zZmVyLmNvLm56MC4GA1UdEgQnMCWBI3NhbXVlbC53aWxs
|
30
|
+
aWFtc0BvcmlvbnRyYW5zZmVyLmNvLm56MA0GCSqGSIb3DQEBCwUAA4IBgQB5sxkE
|
31
|
+
cBsSYwK6fYpM+hA5B5yZY2+L0Z+27jF1pWGgbhPH8/FjjBLVn+VFok3CDpRqwXCl
|
32
|
+
xCO40JEkKdznNy2avOMra6PFiQyOE74kCtv7P+Fdc+FhgqI5lMon6tt9rNeXmnW/
|
33
|
+
c1NaMRdxy999hmRGzUSFjozcCwxpy/LwabxtdXwXgSay4mQ32EDjqR1TixS1+smp
|
34
|
+
8C/NCWgpIfzpHGJsjvmH2wAfKtTTqB9CVKLCWEnCHyCaRVuKkrKjqhYCdmMBqCws
|
35
|
+
JkxfQWC+jBVeG9ZtPhQgZpfhvh+6hMhraUYRQ6XGyvBqEUe+yo6DKIT3MtGE2+CP
|
36
|
+
eX9i9ZWBydWb8/rvmwmX2kkcBbX0hZS1rcR593hGc61JR6lvkGYQ2MYskBveyaxt
|
37
|
+
Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
|
38
|
+
voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
|
39
|
+
-----END CERTIFICATE-----
|
40
|
+
date: 2024-06-28 00:00:00.000000000 Z
|
41
|
+
dependencies: []
|
42
|
+
description:
|
43
|
+
email:
|
44
|
+
executables: []
|
45
|
+
extensions:
|
46
|
+
- ext/configure
|
47
|
+
extra_rdoc_files: []
|
48
|
+
files:
|
49
|
+
- ext/Makefile
|
50
|
+
- ext/bin/io-watch
|
51
|
+
- ext/configure
|
52
|
+
- ext/io-watch
|
53
|
+
- ext/io/watch.c
|
54
|
+
- ext/io/watch.o
|
55
|
+
- ext/io/watch/fsevent.c
|
56
|
+
- ext/io/watch/fsevent.o
|
57
|
+
- ext/io/watch/inotify.c
|
58
|
+
- ext/io/watch/watch.h
|
59
|
+
- lib/io/watch.rb
|
60
|
+
- lib/io/watch/monitor.rb
|
61
|
+
- lib/io/watch/version.rb
|
62
|
+
- license.md
|
63
|
+
- readme.md
|
64
|
+
homepage: https://github.com/socketry/io-watch
|
65
|
+
licenses:
|
66
|
+
- MIT
|
67
|
+
metadata:
|
68
|
+
documentation_uri: https://socketry.github.io/io-watch/
|
69
|
+
source_code_uri: https://github.com/socketry/io-watch.git
|
70
|
+
post_install_message:
|
71
|
+
rdoc_options: []
|
72
|
+
require_paths:
|
73
|
+
- lib
|
74
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
75
|
+
requirements:
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '3.1'
|
79
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
requirements: []
|
85
|
+
rubygems_version: 3.5.11
|
86
|
+
signing_key:
|
87
|
+
specification_version: 4
|
88
|
+
summary: A tool for watching changes to the filesystem.
|
89
|
+
test_files: []
|
metadata.gz.sig
ADDED
Binary file
|