entangler 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/entangler.rb ADDED
@@ -0,0 +1,23 @@
1
+ require 'entangler/version'
2
+ require 'entangler/entangled_file'
3
+
4
+ module Entangler
5
+ class << self
6
+ attr_accessor :executor
7
+
8
+ def run(base_dir, opts = {})
9
+ opts = {mode: 'master'}.merge(opts)
10
+
11
+ require 'entangler/executor/base'
12
+ if opts[:mode] == 'master'
13
+ require 'entangler/executor/master'
14
+ self.executor = Entangler::Executor::Master.new(base_dir, opts)
15
+ elsif opts[:mode] == 'slave'
16
+ require 'entangler/executor/slave'
17
+ self.executor = Entangler::Executor::Slave.new(base_dir, opts)
18
+ end
19
+
20
+ self.executor.run
21
+ end
22
+ end
23
+ end
Binary file
Binary file
@@ -0,0 +1,7 @@
1
+ To compile notify.c, run
2
+
3
+ clang -framework CoreFoundation -framework CoreServices -o notify notify.c
4
+
5
+ or (if you don't have clang)
6
+
7
+ gcc -framework CoreFoundation -framework CoreServices -o notify notify.c
@@ -0,0 +1,12 @@
1
+ The notify utility continiously prints changes in a specified directory in the following format:
2
+
3
+ M changed/path/1
4
+ M changed/path/2
5
+ -
6
+
7
+
8
+ Where M stands for "Modifed", and "-" is the indicator of end of changeset
9
+
10
+ Utility only returns specific directories that have changed, it does not print
11
+ the files/directories that have been modified: you need to determine them
12
+ yourself
@@ -0,0 +1,63 @@
1
+ #include <CoreServices/CoreServices.h>
2
+ #include <CoreFoundation/CoreFoundation.h>
3
+ #include <sys/stat.h>
4
+
5
+ static void printChangesFunc(ConstFSEventStreamRef streamRef, void *clientCallBackInfo, size_t numEvents, void *eventPaths, const FSEventStreamEventFlags eventFlags[], const FSEventStreamEventId eventIds[]) {
6
+ char **paths = eventPaths;
7
+ int i;
8
+ for (i = 0; i < numEvents; i++) {
9
+ printf("M %s\n", paths[i]);
10
+ }
11
+ printf("-\n");
12
+ fflush(stdout);
13
+ }
14
+
15
+ void initFSEvents(const char *path) {
16
+
17
+ /* Define variables and create a CFArray object containing
18
+ CFString objects containing paths to watch.
19
+ */
20
+
21
+ CFStringRef mypath = CFStringCreateWithCString(NULL, path, kCFStringEncodingUTF8);
22
+ CFArrayRef pathsToWatch = CFArrayCreate(NULL, (const void **)&mypath, 1, NULL);
23
+ void *callbackInfo = NULL; // could put stream-specific data here.
24
+ FSEventStreamRef stream;
25
+ CFAbsoluteTime latency = 0.1; /* Latency in seconds */
26
+
27
+ /* Create the stream, passing in a callback */
28
+ stream = FSEventStreamCreate(NULL,
29
+ &printChangesFunc,
30
+ callbackInfo,
31
+ pathsToWatch,
32
+ kFSEventStreamEventIdSinceNow, /* Or a previous event ID */
33
+ latency,
34
+ kFSEventStreamCreateFlagNone /* Flags explained in reference */
35
+ );
36
+
37
+ CFRelease(pathsToWatch);
38
+ CFRelease(mypath);
39
+
40
+ /* Create the stream before calling this. */
41
+ FSEventStreamScheduleWithRunLoop(stream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
42
+ FSEventStreamStart(stream);
43
+ }
44
+
45
+ int main (int argc, const char * argv[]) {
46
+
47
+ if(argc != 2) {
48
+ printf("Usage: %s <path>\n", argv[0]);
49
+ return 1;
50
+ }
51
+
52
+ struct stat tmp;
53
+
54
+ if(stat(argv[1], &tmp) != 0) {
55
+ perror("Invalid path");
56
+ return 2;
57
+ }
58
+
59
+ initFSEvents(argv[1]);
60
+ CFRunLoopRun();
61
+
62
+ return 0;
63
+ }
@@ -0,0 +1,4 @@
1
+ notify.c was built using dietlibc to be as small as possible (using "bin-i386/diet gcc -static -o notify notify.c").
2
+ If you want to compile it without dietlibc, you can just do
3
+
4
+ gcc -o notify notify.c
@@ -0,0 +1,18 @@
1
+ The notify utility continiously prints changes in a specified directory in the following format:
2
+
3
+ M changed/path/1
4
+ M changed/path/2
5
+ -
6
+
7
+ Where M stands for "Modifed", and "-" is the indicator of end of changeset
8
+
9
+ Utility only returns specific directories that have changed, it does not print
10
+ the files/directories that have been modified: you need to determine them
11
+ yourself.
12
+
13
+ Linux-specific notes:
14
+
15
+ 1. You might need to adjust '/proc/sys/fs/inotify/max_user_watches' to allow more directories to be watched
16
+ 2. If you have a lot of changes you might also want to increase queue size in /proc/sys/fs/inotify/max_queued_events
17
+ 3. Watched queue can overflow and notify utility can run out of watched directories.
18
+ When you get exit code 3, you need to restart the daemon and re-process all directories you are interested in.
@@ -0,0 +1,296 @@
1
+ #include <stdio.h>
2
+ #include <stdlib.h>
3
+ #include <sys/types.h>
4
+ #include <sys/stat.h>
5
+ #include <sys/inotify.h>
6
+ #include <unistd.h>
7
+ #include <dirent.h>
8
+ #include <errno.h>
9
+ #include <stdlib.h>
10
+ #include <fcntl.h>
11
+ #include <sys/ioctl.h>
12
+
13
+ /* gcc should be able to optimize strlen() for constant strings */
14
+ #define PRINT(str) write(1, str, strlen(str))
15
+ #define ERROR(str) write(2, str, strlen(str))
16
+
17
+ #define EVENT_MASK (IN_CLOSE_WRITE|IN_CREATE|IN_DELETE|IN_DELETE_SELF|IN_MODIFY|IN_MOVE_SELF\
18
+ |IN_MOVED_FROM|IN_MOVED_TO|IN_DONT_FOLLOW|IN_ONLYDIR|IN_ATTRIB)
19
+
20
+ #ifdef DEBUG
21
+ #define DEBUG_PRINT printf
22
+ #else
23
+ #define DEBUG_PRINT(...) /* printf actually adds about 7k to binary size :) */
24
+ #endif
25
+
26
+ typedef struct {
27
+ int wd;
28
+ int parent_wd;
29
+ char *name;
30
+ } _watchstruct;
31
+
32
+ char events_buf[PATH_MAX + sizeof(struct inotify_event) + 1];
33
+ static _watchstruct *watches;
34
+ int ifd = 0, max_watches;
35
+ char *watch_dir;
36
+
37
+ /* make directory path for watch descriptor (recursively) */
38
+ static void wd_path(int wd, char *path)
39
+ {
40
+ if (wd == 0) {
41
+ strcpy(path, watch_dir);
42
+ strcat(path, "/");
43
+ return;
44
+ }
45
+
46
+ if (wd < 0 || !watches[wd].name) {
47
+ DEBUG_PRINT("Recusive %d, %x\n", wd, watches[wd].name);
48
+ ERROR("Memory corrupted: asked path of deleted event\n");
49
+ exit(1);
50
+ }
51
+
52
+ wd_path(watches[wd].parent_wd, path);
53
+ if (watches[wd].name[0] == 0) return;
54
+
55
+ strcat(path, watches[wd].name);
56
+ strcat(path, "/");
57
+ }
58
+
59
+ static int add_dir_watch(int parent_wd, char *dir, char *dir_name, int no_print)
60
+ {
61
+ int wd = inotify_add_watch(ifd, dir, EVENT_MASK);
62
+ if (wd < 0) {
63
+ ERROR("Cannot add watch to '");
64
+ ERROR(dir);
65
+ ERROR("' using inotify: ");
66
+ if (errno == ENOSPC) {
67
+ ERROR("too many watches\nYou can increase number of user watches using /proc/sys/fs/inotify/max_user_watches");
68
+ } else {
69
+ ERROR(strerror(errno));
70
+ }
71
+ ERROR("\n");
72
+ if (errno != EACCES && errno != ENOENT) exit(1);
73
+ return wd;
74
+ }
75
+
76
+ if (wd >= max_watches) {
77
+ ERROR("\nToo many events; restart required to prevent watch descriptor overflow.\n");
78
+ exit(3);
79
+ }
80
+
81
+ dir_name = strdup(dir_name);
82
+ if (!dir_name) {
83
+ ERROR("Cannot strdup(dir_name)\n");
84
+ exit(1);
85
+ }
86
+
87
+ watches[wd].wd = wd;
88
+ watches[wd].parent_wd = parent_wd;
89
+ if (watches[wd].name) free(watches[wd].name);
90
+ watches[wd].name = dir_name;
91
+
92
+ if (!no_print) {
93
+ PRINT("M ");
94
+ PRINT(dir);
95
+ PRINT("\n");
96
+ }
97
+
98
+ return wd;
99
+ }
100
+
101
+ static void add_dir(int dir_wd, char *dir, int errors_fatal, int no_print)
102
+ {
103
+ char path[PATH_MAX + 1];
104
+ DIR *dh = opendir(dir);
105
+ struct dirent *ent;
106
+ struct stat st;
107
+ int dirl = strlen(dir), n = sizeof(path) - 1 - dirl, had_errors = 0, wd;
108
+
109
+ if (dirl > sizeof(path) - 3) {
110
+ ERROR("Too long path (not watched): ");
111
+ ERROR(dir);
112
+ ERROR("\n");
113
+
114
+ if (errors_fatal) exit(1);
115
+ return;
116
+ }
117
+
118
+ if (!dh) {
119
+ ERROR("Cannot opendir(");
120
+ ERROR(dir);
121
+ ERROR("): ");
122
+ ERROR(strerror(errno));
123
+ ERROR("\n");
124
+
125
+ if (errors_fatal) exit(1);
126
+ return;
127
+ }
128
+
129
+ strcpy(path, dir);
130
+
131
+ while ((ent = readdir(dh)) != NULL) {
132
+ if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..") || !strcmp(ent->d_name, ".unrealsync")) continue;
133
+
134
+ path[dirl] = '/';
135
+ path[dirl + 1] = 0;
136
+ strncat(path + dirl, ent->d_name, n);
137
+ path[sizeof(path) - 1] = 0;
138
+ if (lstat(path, &st)) {
139
+ ERROR("Cannot lstat(");
140
+ ERROR(path);
141
+ ERROR("): ");
142
+ ERROR(strerror(errno));
143
+ ERROR("\n");
144
+ had_errors = 1;
145
+ continue;
146
+ }
147
+
148
+ if (S_ISDIR(st.st_mode)) {
149
+ wd = add_dir_watch(dir_wd, path, ent->d_name, no_print);
150
+ if (wd < 0) continue;
151
+ add_dir(wd, path, errors_fatal, no_print);
152
+ }
153
+ }
154
+
155
+ closedir(dh);
156
+
157
+ if (errors_fatal && had_errors) exit(1);
158
+ }
159
+
160
+ void debug_print_mask(uint32_t mask)
161
+ {
162
+ if (mask & IN_DELETE_SELF) DEBUG_PRINT("IN_DELETE_SELF ");
163
+ if (mask & IN_MOVE_SELF) DEBUG_PRINT("IN_MOVE_SELF ");
164
+ if (mask & IN_MOVED_FROM) DEBUG_PRINT("IN_MOVED_FROM ");
165
+ if (mask & IN_MOVED_TO) DEBUG_PRINT("IN_MOVED_TO ");
166
+ if (mask & IN_CLOSE_WRITE) DEBUG_PRINT("IN_CLOSE_WRITE ");
167
+ if (mask & IN_MODIFY) DEBUG_PRINT("IN_MODIFY ");
168
+ if (mask & IN_IGNORED) DEBUG_PRINT("IN_IGNORED ");
169
+ if (mask & IN_ISDIR) DEBUG_PRINT("IN_ISDIR ");
170
+ if (mask & IN_Q_OVERFLOW) DEBUG_PRINT("IN_Q_OVERFLOW ");
171
+ if (mask & IN_UNMOUNT) DEBUG_PRINT("IN_UNMOUNT ");
172
+ if (mask & IN_CREATE) DEBUG_PRINT("IN_CREATE ");
173
+ }
174
+
175
+ static int do_watch(int max_watches)
176
+ {
177
+ struct inotify_event *ev = (struct inotify_event*)events_buf;
178
+ ssize_t n = 0, wd;
179
+ char path[PATH_MAX + 1];
180
+
181
+ watches = (_watchstruct*) calloc(max_watches, sizeof(_watchstruct));
182
+ if (!watches) {
183
+ ERROR("Cannot allocate memory\n");
184
+ exit(1);
185
+ }
186
+
187
+ DEBUG_PRINT("Doing initial watches setup\n");
188
+
189
+ ifd = inotify_init();
190
+ if (ifd == -1) {
191
+ perror("Cannot init inotify");
192
+ exit(1);
193
+ }
194
+
195
+ wd = add_dir_watch(0, watch_dir, "", 1);
196
+ if (wd < 0) {
197
+ ERROR("Cannot add dir watch\n");
198
+ exit(1);
199
+ }
200
+ add_dir(wd, watch_dir, 1, 1);
201
+
202
+ while ((n = read(ifd, events_buf, sizeof(events_buf))) > 0) {
203
+ ev = (struct inotify_event*)events_buf;
204
+ while (n > 0) {
205
+ if (ev->mask & IN_Q_OVERFLOW) {
206
+ ERROR("Queue overflow, restart needed\n");
207
+ exit(3);
208
+ }
209
+
210
+ if (ev->mask & IN_IGNORED) {
211
+ free(watches[ev->wd].name);
212
+ watches[ev->wd].parent_wd = -1;
213
+ watches[ev->wd].name = NULL;
214
+ goto loop_end;
215
+ }
216
+
217
+ wd_path(ev->wd, path);
218
+ PRINT("M ");
219
+ PRINT(path);
220
+ PRINT("\n");
221
+ #ifdef DEBUG
222
+ if (ev->len) {
223
+ DEBUG_PRINT(" | ");
224
+ DEBUG_PRINT("%s", ev->name);
225
+ }
226
+ DEBUG_PRINT(" | ");
227
+ debug_print_mask(ev->mask);
228
+ #endif
229
+
230
+ if ((ev->mask & IN_DELETE) || (ev->mask & IN_MOVED_FROM)) {
231
+ goto loop_end;
232
+ }
233
+
234
+ if (ev->mask & IN_ISDIR) {
235
+ if (ev->len + strlen(path) > sizeof(path) - 1) {
236
+ ERROR("Too deep directory: ");
237
+ ERROR(path);
238
+ ERROR(ev->name);
239
+ ERROR("\n");
240
+ goto loop_end;
241
+ }
242
+ strcat(path, ev->name);
243
+ wd = add_dir_watch(ev->wd, path, ev->name, 0);
244
+ if (wd < 0) goto loop_end;
245
+ add_dir(ev->wd, path, 0, 0);
246
+ }
247
+
248
+ loop_end:
249
+ n -= sizeof(struct inotify_event) + ev->len;
250
+ ev = (struct inotify_event*) ((char*)ev + sizeof(struct inotify_event) + ev->len);
251
+ }
252
+
253
+ PRINT("-\n");
254
+ }
255
+
256
+ perror("Cannot read() inotify queue");
257
+ exit(1);
258
+ }
259
+
260
+ int main(int argc, char *argv[])
261
+ {
262
+ int fd, n;
263
+ char buf[12];
264
+
265
+ if (argc != 2) {
266
+ ERROR("Usage: notify <dir>\n");
267
+ return 1;
268
+ }
269
+
270
+ fd = open("/proc/sys/fs/inotify/max_user_watches", O_RDONLY);
271
+ if (fd < 0) {
272
+ perror("Cannot open /proc/sys/fs/inotify/max_user_watches");
273
+ return 1;
274
+ }
275
+
276
+ if ( (n = read(fd, buf, sizeof(buf) - 1)) < 0) {
277
+ perror("Cannot read() /proc/sys/fs/inotify/max_user_watches");
278
+ return 1;
279
+ }
280
+
281
+ buf[n] = 0;
282
+ max_watches = atoi(buf) * 2;
283
+ if (max_watches <= 0) {
284
+ ERROR("Incorrect number of watches: ");
285
+ ERROR(buf);
286
+ ERROR("\n");
287
+ return 1;
288
+ } else {
289
+ DEBUG_PRINT("Max watches: %d\n", max_watches);
290
+ }
291
+
292
+ watch_dir = argv[1];
293
+ do_watch(max_watches);
294
+
295
+ return 0;
296
+ }
@@ -0,0 +1,11 @@
1
+ The utility has been built using the following command under Ubuntu Linux 11.10 32-bit:
2
+
3
+ g++ -static -s -I. *.cpp /usr/lib/libpopt.a -o notify
4
+
5
+ Or, to achieve the minimal size of the binary:
6
+
7
+ g++ -static -s -Os -ffunction-sections -fdata-sections -Wl,--gc-sections -I. *.cpp /usr/lib/libpopt.a -o notify
8
+
9
+ You might need to change path to libpopt to whatever location it is on your system.
10
+ Libpopt is the only dependency aside from g++ if you want to build the utility.
11
+ It comes pre-built statically with realsync, so it will most probably just work
@@ -0,0 +1,14 @@
1
+ This is a daemon that notifies about filesystem changes in the following form:
2
+
3
+ M /path/to/changed/file
4
+ M /path/to/other/changed/file
5
+ -
6
+
7
+ The daemon is based on kfsmd-0.3.3 and tuned a little bit to be more realtime
8
+ The original kfsmd-0.3.3 could be found at http://sourceforge.net/projects/witme/files/kfsmd/
9
+ The preferred way to use this daemon is to run the following:
10
+
11
+ ./notify watch /path/to/watch | uniq
12
+
13
+ You can get rid of "| uniq", but in this case you will get multiple lines per each file
14
+ for each type of event (e.g. OPEN/WRITE/CLOSE)
@@ -0,0 +1,165 @@
1
+ /******************************************************************************
2
+ *******************************************************************************
3
+ *******************************************************************************
4
+
5
+
6
+ kernel-filesystem-monitor-daemon-cat
7
+ Copyright (C) 2005 Ben Martin
8
+
9
+ This program is free software; you can redistribute it and/or modify
10
+ it under the terms of the GNU General Public License as published by
11
+ the Free Software Foundation; either version 2 of the License, or
12
+ (at your option) any later version.
13
+
14
+ This program is distributed in the hope that it will be useful,
15
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
+ GNU General Public License for more details.
18
+
19
+ You should have received a copy of the GNU General Public License
20
+ along with this program; if not, write to the Free Software
21
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22
+
23
+ For more details see the COPYING file in the root directory of this
24
+ distribution.
25
+
26
+ $Id: kernel-filesystem-monitor-daemon-cat.cpp,v 1.3 2008/05/25 21:30:52 ben Exp $
27
+
28
+ *******************************************************************************
29
+ *******************************************************************************
30
+ ******************************************************************************/
31
+
32
+ #include <iostream>
33
+ #include "kernel-filesystem-monitor-daemon.hh"
34
+ #include <stdlib.h>
35
+
36
+ using namespace std;
37
+
38
+
39
+ const char* PROGRAM_NAME = "kernel-filesystem-monitor-cat";
40
+
41
+ void usage(poptContext optCon, int exitcode, char *error, char *addl)
42
+ {
43
+ poptPrintUsage(optCon, stderr, 0);
44
+ if (error) fprintf(stderr, "%s: %s0", error, addl);
45
+ exit(exitcode);
46
+ }
47
+
48
+ /********************************************************************************/
49
+ /********************************************************************************/
50
+ /********************************************************************************/
51
+ /********************************************************************************/
52
+ /********************************************************************************/
53
+ /********************************************************************************/
54
+
55
+
56
+
57
+ class KernelFileSystemMonitorDaemonCat
58
+ :
59
+ public KernelFileSystemMonitorDaemon
60
+ {
61
+ protected:
62
+
63
+ virtual void handle_event( struct inotify_event *pevent, time_t tt );
64
+ virtual void Closedown();
65
+ virtual void setupWorkingDirToPersistentDirIDMapping( long wd, const string& earl );
66
+
67
+ void
68
+ event_batch_start( time_t tt )
69
+ {}
70
+
71
+ void
72
+ event_batch_end( time_t tt )
73
+ {}
74
+
75
+
76
+ public:
77
+
78
+ string homedir;
79
+
80
+ KernelFileSystemMonitorDaemonCat()
81
+ {}
82
+ };
83
+
84
+
85
+ void
86
+ KernelFileSystemMonitorDaemonCat::setupWorkingDirToPersistentDirIDMapping(
87
+ long wd, const string& earl )
88
+ {
89
+ }
90
+
91
+ void
92
+ KernelFileSystemMonitorDaemonCat::handle_event( struct inotify_event *pevent, time_t tt )
93
+ {
94
+ print_event( pevent );
95
+ fflush(stdout);
96
+ }
97
+
98
+
99
+
100
+ void
101
+ KernelFileSystemMonitorDaemonCat::Closedown()
102
+ {
103
+ }
104
+
105
+ int main( int argc, char** argv )
106
+ {
107
+ unsigned long RunInForground = 0;
108
+ const char* homedir_CSTR = 0;
109
+
110
+ KernelFileSystemMonitorDaemonCat* daemon
111
+ = new KernelFileSystemMonitorDaemonCat();
112
+
113
+ struct poptOption optionsTable[] =
114
+ {
115
+ { "forground", 'F', POPT_ARG_NONE, &RunInForground, 0,
116
+ "Don't run the daemon in the background", "" },
117
+
118
+ { "homedir", 'H', POPT_ARG_STRING, &homedir_CSTR, 0,
119
+ "Home directory for user doing the monitoring", "" },
120
+
121
+ { 0, 0, POPT_ARG_INCLUDE_TABLE, daemon->getPopTable(), \
122
+ 0, "generic kfsmd daemon options:", 0 },
123
+
124
+ { "verbose", 'v', POPT_ARG_NONE, &Verbose, 0,
125
+ "output more info", "" },
126
+
127
+
128
+ POPT_AUTOHELP
129
+ POPT_TABLEEND
130
+ };
131
+ poptContext optCon;
132
+
133
+ optCon = poptGetContext(PROGRAM_NAME, argc, (const char**)argv, optionsTable, 0);
134
+ poptSetOtherOptionHelp(optCon, "[OPTIONS]* [IGNOREPFX URL]* [WATCH URL]+ ...");
135
+
136
+
137
+ /* Now do options processing */
138
+ char c=-1;
139
+ while ((c = poptGetNextOpt(optCon)) >= 0)
140
+ {
141
+ }
142
+
143
+ daemon->ParseWatchOptions( optCon );
144
+ daemon->setRunInForground( true );
145
+ daemon->setupSignalHandlers();
146
+
147
+ daemon->homedir = getHomeDir( homedir_CSTR );
148
+
149
+ try
150
+ {
151
+ if( Verbose )
152
+ cerr << "setting up watches" << endl;
153
+ daemon->setupWatches();
154
+ if( Verbose )
155
+ cerr << "calling run" << endl;
156
+ return daemon->run();
157
+ }
158
+ catch( exception& e )
159
+ {
160
+ cerr << "Exiting due to error reason:" << e.what() << endl;
161
+ }
162
+
163
+ return 0;
164
+ }
165
+