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.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/Gemfile +3 -0
- data/Guardfile +8 -0
- data/{LICENSE → LICENSE.txt} +3 -1
- data/README.md +260 -0
- data/Rakefile +33 -0
- data/bin/fsevent_watch +0 -0
- data/ext/{fsevent_watch/LICENSE → LICENSE} +1 -1
- data/ext/fsevent_watch/FSEventsFix.c +626 -0
- data/ext/fsevent_watch/FSEventsFix.h +105 -0
- data/ext/fsevent_watch/{fsevent_watch/TSICTString.c → TSICTString.c} +34 -55
- data/ext/fsevent_watch/{fsevent_watch/TSICTString.h → TSICTString.h} +0 -0
- data/ext/fsevent_watch/{fsevent_watch/cli.c → cli.c} +48 -7
- data/ext/fsevent_watch/{fsevent_watch/cli.h → cli.h} +3 -3
- data/ext/fsevent_watch/{fsevent_watch/common.h → common.h} +1 -13
- data/ext/fsevent_watch/compat.c +41 -0
- data/ext/fsevent_watch/compat.h +100 -0
- data/ext/fsevent_watch/defines.h +42 -0
- data/ext/fsevent_watch/{fsevent_watch/main.c → main.c} +101 -62
- data/ext/fsevent_watch/signal_handlers.c +66 -0
- data/ext/fsevent_watch/signal_handlers.h +16 -0
- data/ext/rakefile.rb +225 -41
- data/lib/otnetstring.rb +85 -0
- data/lib/rb-fsevent/fsevent.rb +53 -7
- data/lib/rb-fsevent/version.rb +3 -1
- data/lib/rb-fsevent.rb +2 -1
- data/rb-fsevent.gemspec +26 -0
- metadata +53 -56
- data/README.rdoc +0 -255
- data/ext/fsevent_watch/Info.plist +0 -38
- data/ext/fsevent_watch/fsevent_watch/compat.c +0 -20
- data/ext/fsevent_watch/fsevent_watch/compat.h +0 -40
- data/ext/fsevent_watch/fsevent_watch.xcodeproj/project.pbxproj +0 -254
- data/ext/fsevent_watch/xcconfig/Common.xcconfig +0 -82
- data/ext/fsevent_watch/xcconfig/Debug.xcconfig +0 -19
- data/ext/fsevent_watch/xcconfig/Release.xcconfig +0 -23
- data/ext/fsevent_watch/xcconfig/fsevent_watch.xcconfig +0 -17
- 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
|
-
|
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
|
-
|
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
|