rb-fsevent 0.9.2 → 0.11.1

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.
Files changed (39) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +13 -0
  3. data/Gemfile +3 -0
  4. data/Guardfile +8 -0
  5. data/{LICENSE → LICENSE.txt} +3 -1
  6. data/README.md +260 -0
  7. data/Rakefile +33 -0
  8. data/bin/fsevent_watch +0 -0
  9. data/ext/{fsevent_watch/LICENSE → LICENSE} +1 -1
  10. data/ext/fsevent_watch/FSEventsFix.c +626 -0
  11. data/ext/fsevent_watch/FSEventsFix.h +105 -0
  12. data/ext/fsevent_watch/{fsevent_watch/TSICTString.c → TSICTString.c} +34 -55
  13. data/ext/fsevent_watch/{fsevent_watch/TSICTString.h → TSICTString.h} +0 -0
  14. data/ext/fsevent_watch/{fsevent_watch/cli.c → cli.c} +48 -7
  15. data/ext/fsevent_watch/{fsevent_watch/cli.h → cli.h} +3 -3
  16. data/ext/fsevent_watch/{fsevent_watch/common.h → common.h} +1 -13
  17. data/ext/fsevent_watch/compat.c +41 -0
  18. data/ext/fsevent_watch/compat.h +100 -0
  19. data/ext/fsevent_watch/defines.h +42 -0
  20. data/ext/fsevent_watch/{fsevent_watch/main.c → main.c} +101 -62
  21. data/ext/fsevent_watch/signal_handlers.c +66 -0
  22. data/ext/fsevent_watch/signal_handlers.h +16 -0
  23. data/ext/rakefile.rb +225 -41
  24. data/lib/otnetstring.rb +85 -0
  25. data/lib/rb-fsevent/fsevent.rb +53 -7
  26. data/lib/rb-fsevent/version.rb +3 -1
  27. data/lib/rb-fsevent.rb +2 -1
  28. data/rb-fsevent.gemspec +26 -0
  29. metadata +53 -56
  30. data/README.rdoc +0 -255
  31. data/ext/fsevent_watch/Info.plist +0 -38
  32. data/ext/fsevent_watch/fsevent_watch/compat.c +0 -20
  33. data/ext/fsevent_watch/fsevent_watch/compat.h +0 -40
  34. data/ext/fsevent_watch/fsevent_watch.xcodeproj/project.pbxproj +0 -254
  35. data/ext/fsevent_watch/xcconfig/Common.xcconfig +0 -82
  36. data/ext/fsevent_watch/xcconfig/Debug.xcconfig +0 -19
  37. data/ext/fsevent_watch/xcconfig/Release.xcconfig +0 -23
  38. data/ext/fsevent_watch/xcconfig/fsevent_watch.xcconfig +0 -17
  39. data/ext/rb-fsevent.xcconfig +0 -33
@@ -0,0 +1,42 @@
1
+ #ifndef fsevent_watch_defines_h
2
+ #define fsevent_watch_defines_h
3
+
4
+ #define _str(s) #s
5
+ #define _xstr(s) _str(s)
6
+
7
+ #define COMPILED_AT __DATE__ " " __TIME__
8
+
9
+ #if defined (__clang__)
10
+ #define COMPILER "clang " __clang_version__
11
+ #elif defined (__GNUC__)
12
+ #define COMPILER "gcc " __VERSION__
13
+ #else
14
+ #define COMPILER "unknown"
15
+ #endif
16
+
17
+ #if defined(__ppc__)
18
+ #define TARGET_CPU "ppc"
19
+ #elif defined(__ppc64__)
20
+ #define TARGET_CPU "ppc64"
21
+ #elif defined(__i386__)
22
+ #define TARGET_CPU "i386"
23
+ #elif defined(__x86_64__)
24
+ #define TARGET_CPU "x86_64"
25
+ #elif defined(__arm64__)
26
+ #define TARGET_CPU "arm64"
27
+ #else
28
+ #define TARGET_CPU "unknown"
29
+ #endif
30
+
31
+ #define FLAG_CHECK(flags, flag) ((flags) & (flag))
32
+
33
+ #define FPRINTF_FLAG_CHECK(flags, flag, msg, fd) \
34
+ do { \
35
+ if (FLAG_CHECK(flags, flag)) { \
36
+ fprintf(fd, "%s", msg "\n"); } } \
37
+ while (0)
38
+
39
+ #define FLAG_CHECK_STDERR(flags, flag, msg) \
40
+ FPRINTF_FLAG_CHECK(flags, flag, msg, stderr)
41
+
42
+ #endif /* fsevent_watch_defines_h */
@@ -1,5 +1,7 @@
1
1
  #include "common.h"
2
+ #include "signal_handlers.h"
2
3
  #include "cli.h"
4
+ #include "FSEventsFix.h"
3
5
 
4
6
  // TODO: set on fire. cli.{h,c} handle both parsing and defaults, so there's
5
7
  // no need to set those here. also, in order to scope metadata by path,
@@ -22,7 +24,7 @@ static struct {
22
24
  (double) 0.3,
23
25
  (CFOptionFlags) kFSEventStreamCreateFlagNone,
24
26
  NULL,
25
- kFSEventWatchOutputFormatClassic
27
+ kFSEventWatchOutputFormatOTNetstring
26
28
  };
27
29
 
28
30
  // Prototypes
@@ -34,7 +36,7 @@ static void callback(FSEventStreamRef streamRef,
34
36
  void* eventPaths,
35
37
  const FSEventStreamEventFlags eventFlags[],
36
38
  const FSEventStreamEventId eventIds[]);
37
-
39
+ static bool needs_fsevents_fix = false;
38
40
 
39
41
  // Resolve a path and append it to the CLI settings structure
40
42
  // The FSEvents API will, internally, resolve paths using a similar scheme.
@@ -47,48 +49,48 @@ static void append_path(const char* path)
47
49
  #endif
48
50
 
49
51
  #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
50
-
52
+
51
53
  #ifdef DEBUG
52
54
  fprintf(stderr, "compiled against 10.6+, using CFURLCreateFileReferenceURL\n");
53
55
  #endif
54
-
56
+
55
57
  CFURLRef url = CFURLCreateFromFileSystemRepresentation(NULL, (const UInt8*)path, (CFIndex)strlen(path), false);
56
58
  CFURLRef placeholder = CFURLCopyAbsoluteURL(url);
57
59
  CFRelease(url);
58
-
60
+
59
61
  CFMutableArrayRef imaginary = NULL;
60
-
62
+
61
63
  // if we don't have an existing url, spin until we get to a parent that
62
64
  // does exist, saving any imaginary components for appending back later
63
65
  while(!CFURLResourceIsReachable(placeholder, NULL)) {
64
66
  #ifdef DEBUG
65
67
  fprintf(stderr, "path does not exist\n");
66
68
  #endif
67
-
69
+
68
70
  CFStringRef child;
69
-
71
+
70
72
  if (imaginary == NULL) {
71
73
  imaginary = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
72
74
  }
73
-
75
+
74
76
  child = CFURLCopyLastPathComponent(placeholder);
75
77
  CFArrayInsertValueAtIndex(imaginary, 0, child);
76
78
  CFRelease(child);
77
-
79
+
78
80
  url = CFURLCreateCopyDeletingLastPathComponent(NULL, placeholder);
79
81
  CFRelease(placeholder);
80
82
  placeholder = url;
81
-
83
+
82
84
  #ifdef DEBUG
83
85
  fprintf(stderr, "parent: ");
84
86
  CFShow(placeholder);
85
87
  #endif
86
88
  }
87
-
89
+
88
90
  #ifdef DEBUG
89
91
  fprintf(stderr, "path exists\n");
90
92
  #endif
91
-
93
+
92
94
  // realpath() doesn't always return the correct case for a path, so this
93
95
  // is a funky workaround that converts a path into a (volId/inodeId) pair
94
96
  // and asks what the path should be for that. since it looks at the actual
@@ -98,12 +100,12 @@ static void append_path(const char* path)
98
100
  CFRelease(placeholder);
99
101
  placeholder = CFURLCreateFilePathURL(NULL, url, NULL);
100
102
  CFRelease(url);
101
-
103
+
102
104
  #ifdef DEBUG
103
105
  fprintf(stderr, "path resolved to: ");
104
106
  CFShow(placeholder);
105
107
  #endif
106
-
108
+
107
109
  // if we stripped off any imaginary path components, append them back on
108
110
  if (imaginary != NULL) {
109
111
  CFIndex count = CFArrayGetCount(imaginary);
@@ -119,30 +121,39 @@ static void append_path(const char* path)
119
121
  }
120
122
  CFRelease(imaginary);
121
123
  }
122
-
124
+
123
125
  #ifdef DEBUG
124
126
  fprintf(stderr, "result: ");
125
127
  CFShow(placeholder);
126
128
  #endif
127
-
129
+
128
130
  CFStringRef cfPath = CFURLCopyFileSystemPath(placeholder, kCFURLPOSIXPathStyle);
131
+ CFRelease(placeholder);
132
+
133
+ char cPath[PATH_MAX];
134
+ if (CFStringGetCString(cfPath, cPath, PATH_MAX, kCFStringEncodingUTF8)) {
135
+ FSEventsFixRepairStatus status = FSEventsFixRepairIfNeeded(cPath);
136
+ if (status == FSEventsFixRepairStatusFailed) {
137
+ needs_fsevents_fix = true;
138
+ }
139
+ }
140
+
129
141
  CFArrayAppendValue(config.paths, cfPath);
130
142
  CFRelease(cfPath);
131
- CFRelease(placeholder);
132
-
143
+
133
144
  #else
134
-
145
+
135
146
  #ifdef DEBUG
136
147
  fprintf(stderr, "compiled against 10.5, using realpath()\n");
137
148
  #endif
138
-
149
+
139
150
  char fullPath[PATH_MAX + 1];
140
-
151
+
141
152
  if (realpath(path, fullPath) == NULL) {
142
153
  #ifdef DEBUG
143
154
  fprintf(stderr, " realpath not directly resolvable from path\n");
144
155
  #endif
145
-
156
+
146
157
  if (path[0] != '/') {
147
158
  #ifdef DEBUG
148
159
  fprintf(stderr, " passed path is not absolute\n");
@@ -162,18 +173,18 @@ static void append_path(const char* path)
162
173
  strlcpy(fullPath, path, sizeof(fullPath));
163
174
  }
164
175
  }
165
-
176
+
166
177
  #ifdef DEBUG
167
178
  fprintf(stderr, " resolved path to: %s\n", fullPath);
168
179
  fprintf(stderr, "\n");
169
180
  #endif
170
-
181
+
171
182
  CFStringRef pathRef = CFStringCreateWithCString(kCFAllocatorDefault,
172
183
  fullPath,
173
184
  kCFStringEncodingUTF8);
174
185
  CFArrayAppendValue(config.paths, pathRef);
175
186
  CFRelease(pathRef);
176
-
187
+
177
188
  #endif
178
189
  }
179
190
 
@@ -234,6 +245,15 @@ static inline void parse_cli_settings(int argc, const char* argv[])
234
245
  }
235
246
  }
236
247
 
248
+ if (args_info.mark_self_flag) {
249
+ if ((osMajorVersion == 10) & (osMinorVersion >= 9)) {
250
+ config.flags |= kFSEventStreamCreateFlagMarkSelf;
251
+ } else {
252
+ fprintf(stderr, "MacOSX 10.9 or later required for --mark-self\n");
253
+ exit(EXIT_FAILURE);
254
+ }
255
+ }
256
+
237
257
  if (args_info.inputs_num == 0) {
238
258
  append_path(".");
239
259
  } else {
@@ -249,7 +269,7 @@ static inline void parse_cli_settings(int argc, const char* argv[])
249
269
  fprintf(stderr, "config.latency %f\n", config.latency);
250
270
 
251
271
  // STFU clang
252
- #if __LP64__
272
+ #if defined(__LP64__)
253
273
  fprintf(stderr, "config.flags %#.8x\n", config.flags);
254
274
  #else
255
275
  fprintf(stderr, "config.flags %#.8lx\n", config.flags);
@@ -330,18 +350,54 @@ static void tstring_output_format(size_t numEvents,
330
350
  false);
331
351
  CFDictionarySetValue(event, CFSTR("path"), path);
332
352
 
333
- CFNumberRef flags = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &eventFlags[i]);
334
- CFDictionarySetValue(event, CFSTR("flags"), flags);
335
-
336
353
  CFNumberRef ident = CFNumberCreate(kCFAllocatorDefault, kCFNumberLongLongType, &eventIds[i]);
337
354
  CFDictionarySetValue(event, CFSTR("id"), ident);
338
355
 
356
+ CFNumberRef cflags = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &eventFlags[i]);
357
+ CFDictionarySetValue(event, CFSTR("cflags"), cflags);
358
+
359
+ CFMutableArrayRef flags = CFArrayCreateMutable(kCFAllocatorDefault,
360
+ 0, &kCFTypeArrayCallBacks);
361
+
362
+ #define FLAG_ADD_NAME(flagsnum, flagnum, flagname, flagarray) \
363
+ do { \
364
+ if (FLAG_CHECK(flagsnum, flagnum)) { \
365
+ CFArrayAppendValue(flagarray, CFSTR(flagname)); } } \
366
+ while(0)
367
+
368
+ FLAG_ADD_NAME(eventFlags[i], kFSEventStreamEventFlagMustScanSubDirs, "MustScanSubDirs", flags);
369
+ FLAG_ADD_NAME(eventFlags[i], kFSEventStreamEventFlagUserDropped, "UserDropped", flags);
370
+ FLAG_ADD_NAME(eventFlags[i], kFSEventStreamEventFlagKernelDropped, "KernelDropped", flags);
371
+ FLAG_ADD_NAME(eventFlags[i], kFSEventStreamEventFlagEventIdsWrapped, "EventIdsWrapped", flags);
372
+ FLAG_ADD_NAME(eventFlags[i], kFSEventStreamEventFlagHistoryDone, "HistoryDone", flags);
373
+ FLAG_ADD_NAME(eventFlags[i], kFSEventStreamEventFlagRootChanged, "RootChanged", flags);
374
+ FLAG_ADD_NAME(eventFlags[i], kFSEventStreamEventFlagMount, "Mount", flags);
375
+ FLAG_ADD_NAME(eventFlags[i], kFSEventStreamEventFlagUnmount, "Unmount", flags);
376
+ FLAG_ADD_NAME(eventFlags[i], kFSEventStreamEventFlagItemCreated, "ItemCreated", flags);
377
+ FLAG_ADD_NAME(eventFlags[i], kFSEventStreamEventFlagItemRemoved, "ItemRemoved", flags);
378
+ FLAG_ADD_NAME(eventFlags[i], kFSEventStreamEventFlagItemInodeMetaMod, "ItemInodeMetaMod", flags);
379
+ FLAG_ADD_NAME(eventFlags[i], kFSEventStreamEventFlagItemRenamed, "ItemRenamed", flags);
380
+ FLAG_ADD_NAME(eventFlags[i], kFSEventStreamEventFlagItemModified, "ItemModified", flags);
381
+ FLAG_ADD_NAME(eventFlags[i], kFSEventStreamEventFlagItemFinderInfoMod, "ItemFinderInfoMod", flags);
382
+ FLAG_ADD_NAME(eventFlags[i], kFSEventStreamEventFlagItemChangeOwner, "ItemChangeOwner", flags);
383
+ FLAG_ADD_NAME(eventFlags[i], kFSEventStreamEventFlagItemXattrMod, "ItemXattrMod", flags);
384
+ FLAG_ADD_NAME(eventFlags[i], kFSEventStreamEventFlagItemIsFile, "ItemIsFile", flags);
385
+ FLAG_ADD_NAME(eventFlags[i], kFSEventStreamEventFlagItemIsDir, "ItemIsDir", flags);
386
+ FLAG_ADD_NAME(eventFlags[i], kFSEventStreamEventFlagItemIsSymlink, "ItemIsSymlink", flags);
387
+ FLAG_ADD_NAME(eventFlags[i], kFSEventStreamEventFlagOwnEvent, "OwnEvent", flags);
388
+ FLAG_ADD_NAME(eventFlags[i], kFSEventStreamEventFlagItemIsHardlink, "ItemIsHardLink", flags);
389
+ FLAG_ADD_NAME(eventFlags[i], kFSEventStreamEventFlagItemIsLastHardlink, "ItemIsLastHardLink", flags);
390
+
391
+ CFDictionarySetValue(event, CFSTR("flags"), flags);
392
+
393
+
339
394
  CFArrayAppendValue(events, event);
340
395
 
341
396
  CFRelease(event);
342
397
  CFRelease(path);
343
- CFRelease(flags);
344
398
  CFRelease(ident);
399
+ CFRelease(cflags);
400
+ CFRelease(flags);
345
401
  }
346
402
 
347
403
  CFMutableDictionaryRef meta = CFDictionaryCreateMutable(kCFAllocatorDefault,
@@ -382,7 +438,7 @@ static void callback(__attribute__((unused)) FSEventStreamRef streamRef,
382
438
  fprintf(stderr, " event ID: %llu\n", eventIds[i]);
383
439
 
384
440
  // STFU clang
385
- #if __LP64__
441
+ #if defined(__LP64__)
386
442
  fprintf(stderr, " event flags: %#.8x\n", eventFlags[i]);
387
443
  #else
388
444
  fprintf(stderr, " event flags: %#.8lx\n", eventFlags[i]);
@@ -426,7 +482,10 @@ static void callback(__attribute__((unused)) FSEventStreamRef streamRef,
426
482
  " Item is a directory");
427
483
  FLAG_CHECK_STDERR(eventFlags[i], kFSEventStreamEventFlagItemIsSymlink,
428
484
  " Item is a symbolic link");
429
-
485
+ FLAG_CHECK_STDERR(eventFlags[i], kFSEventStreamEventFlagItemIsHardlink,
486
+ " Item is a hard link");
487
+ FLAG_CHECK_STDERR(eventFlags[i], kFSEventStreamEventFlagItemIsLastHardlink,
488
+ " Item is the last hard link");
430
489
  fprintf(stderr, " event path: %s\n", paths[i]);
431
490
  fprintf(stderr, "\n");
432
491
  }
@@ -451,37 +510,13 @@ static void callback(__attribute__((unused)) FSEventStreamRef streamRef,
451
510
 
452
511
  int main(int argc, const char* argv[])
453
512
  {
454
- /*
455
- * a subprocess will initially inherit the process group of its parent. the
456
- * process group may have a control terminal associated with it, which would
457
- * be the first tty device opened by the group leader. typically the group
458
- * leader is your shell and the control terminal is your login device. a
459
- * subset of signals triggered on the control terminal are sent to all members
460
- * of the process group, in large part to facilitate sane and consistent
461
- * cleanup (ex: control terminal was closed).
462
- *
463
- * so why the overly descriptive lecture style comment?
464
- * 1. SIGINT and SIGQUIT are among the signals with this behavior
465
- * 2. a number of applications gank the above for their own use
466
- * 3. ruby's insanely useful "guard" is one of these applications
467
- * 4. despite having some level of understanding of POSIX signals and a few
468
- * of the scenarios that might cause problems, i learned this one only
469
- * after reading ruby 1.9's process.c
470
- * 5. if left completely undocumented, even slightly obscure bugfixes
471
- * may be removed as cruft by a future maintainer
472
- *
473
- * hindsight is 20/20 addition: if you're single-threaded and blocking on IO
474
- * with a subprocess, then handlers for deferrable signals might not get run
475
- * when you expect them to. In the case of Ruby 1.8, that means making use of
476
- * IO::select, which will preserve correct signal handling behavior.
477
- */
478
- if (setpgid(0,0) < 0) {
479
- fprintf(stderr, "Unable to set new process group.\n");
480
- return 1;
481
- }
482
-
513
+ install_signal_handlers();
483
514
  parse_cli_settings(argc, argv);
484
515
 
516
+ if (needs_fsevents_fix) {
517
+ FSEventsFixEnable();
518
+ }
519
+
485
520
  FSEventStreamContext context = {0, NULL, NULL, NULL, NULL};
486
521
  FSEventStreamRef stream;
487
522
  stream = FSEventStreamCreate(kCFAllocatorDefault,
@@ -497,6 +532,10 @@ int main(int argc, const char* argv[])
497
532
  fprintf(stderr, "\n");
498
533
  #endif
499
534
 
535
+ if (needs_fsevents_fix) {
536
+ FSEventsFixDisable();
537
+ }
538
+
500
539
  FSEventStreamScheduleWithRunLoop(stream,
501
540
  CFRunLoopGetCurrent(),
502
541
  kCFRunLoopDefaultMode);
@@ -0,0 +1,66 @@
1
+ #include "signal_handlers.h"
2
+ #include <fcntl.h>
3
+ #include <signal.h>
4
+ #include <stdio.h>
5
+ #include <stdlib.h>
6
+ #include <sys/time.h>
7
+ #include <unistd.h>
8
+
9
+
10
+ #define PPID_ALARM_INTERVAL 2 // send SIGALRM every this seconds
11
+
12
+
13
+ static pid_t orig_ppid;
14
+
15
+
16
+ static void signal_handler(int _) {
17
+ exit(EXIT_FAILURE);
18
+ }
19
+
20
+ static void check_ppid(void) {
21
+ if (getppid() != orig_ppid) {
22
+ exit(EXIT_FAILURE);
23
+ }
24
+ }
25
+
26
+ static void check_stdout_open(void) {
27
+ if (fcntl(STDOUT_FILENO, F_GETFD) < 0) {
28
+ exit(EXIT_FAILURE);
29
+ }
30
+ }
31
+
32
+ static void alarm_handler(int _) {
33
+ check_ppid();
34
+ check_stdout_open();
35
+ alarm(PPID_ALARM_INTERVAL);
36
+ signal(SIGALRM, alarm_handler);
37
+ }
38
+
39
+ static void die(const char *msg) {
40
+ fprintf(stderr, "\nFATAL: %s\n", msg);
41
+ abort();
42
+ }
43
+
44
+ static void install_signal_handler(int sig, void (*handler)(int)) {
45
+ if (signal(sig, handler) == SIG_ERR) {
46
+ die("Could not install signal handler");
47
+ }
48
+ }
49
+
50
+ void install_signal_handlers(void) {
51
+ // check pipe is still connected
52
+ check_stdout_open();
53
+
54
+ // watch getppid() every PPID_ALARM_INTERVAL seconds
55
+ orig_ppid = getppid();
56
+ if (orig_ppid <= 1) {
57
+ die("prematurely zombied");
58
+ }
59
+ install_signal_handler(SIGALRM, alarm_handler);
60
+ alarm(PPID_ALARM_INTERVAL);
61
+
62
+ // be sure to exit on SIGHUP, SIGPIPE
63
+ install_signal_handler(SIGHUP, signal_handler);
64
+ install_signal_handler(SIGPIPE, signal_handler);
65
+ }
66
+
@@ -0,0 +1,16 @@
1
+ /**
2
+ * @headerfile signal_handlers.h
3
+ * Signal handlers to stop the zombie hordes
4
+ *
5
+ * Catch and handle signals better so that we die faster like a good meat puppet.
6
+ */
7
+
8
+
9
+ #ifndef fsevent_watch_signal_handlers_h
10
+ #define fsevent_watch_signal_handlers_h
11
+
12
+
13
+ void install_signal_handlers(void);
14
+
15
+
16
+ #endif // fsevent_watch_signal_handlers_h