rb-fsevent 0.9.4 → 0.9.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +12 -15
- data/Rakefile +1 -1
- data/bin/fsevent_watch +0 -0
- data/ext/fsevent_watch/FSEventsFix.c +626 -0
- data/ext/fsevent_watch/FSEventsFix.h +105 -0
- data/ext/fsevent_watch/main.c +20 -2
- data/ext/rakefile.rb +7 -8
- data/lib/rb-fsevent/version.rb +1 -1
- metadata +14 -13
- data/bin/fsevent_watch_10_5 +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b45289d17ae392fc4603c728ddd2e516b7eddde1
|
4
|
+
data.tar.gz: 1a06cb592031e5d9537bbce90de4958ea5d4529d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 89c095ca2fff6ac7f62ce06d71888fb9813ee34bbda312ec257ec1dc68fdaf0f61d7aed880a72f25f3f52235965e8840860f1a47279e465569f42db46e2f4a56
|
7
|
+
data.tar.gz: 9e6f1a6133b31fedabac98ae393ca7ad6bb0116ecf4913ea42f56b05d35f86f9f4ee6bb3c5c9a3a06d53dcf90f315e37e780e4c81c1eec4c70da471f2f1a156e
|
data/README.md
CHANGED
@@ -7,8 +7,14 @@ Very simple & usable Mac OSX FSEvents API
|
|
7
7
|
|
8
8
|
* RubyCocoa not required!
|
9
9
|
* Signals are working (really)
|
10
|
-
* Tested on MRI
|
11
|
-
* Tested on 10.
|
10
|
+
* Tested on MRI 2.1, RBX 2.5, JRuby
|
11
|
+
* Tested on 10.10
|
12
|
+
|
13
|
+
## HFS+ filename corruption bug
|
14
|
+
|
15
|
+
There is a _very_ long-standing (since 2011) OSX bug where sometimes the filename metadata for HFS+ filesystems will get corrupted, resulting in some APIs returning one case for a file, and other APIs returning another. This corruption is not currently fixed by their tools, though Apple has been made aware of the issue and are working on it (as of may 2015). The result is that sometimes, _for no visible reason to the user_, fsevents would simply not work. As of rb-fsevent 0.9.5 this issue is properly detected and an insanely hacky (but effective) workaround is used that replaces the system `realpath()` with a custom implementation that should always return the same value as the kernel reporting (thus fixing fsevents).
|
16
|
+
|
17
|
+
Please note that this doesn't repair the underlying issue on disk. Other apps and libraries using fsevents will continue to break with no warning. There may be other issues unrelated to fsevents.
|
12
18
|
|
13
19
|
## Install
|
14
20
|
|
@@ -16,9 +22,9 @@ Very simple & usable Mac OSX FSEvents API
|
|
16
22
|
|
17
23
|
### re-compilation
|
18
24
|
|
19
|
-
rb-fsevent comes with a pre-compiled fsevent\_watch binary supporting x86\_64
|
25
|
+
rb-fsevent comes with a pre-compiled fsevent\_watch binary supporting x86\_64 on 10.9 and above. The binary is codesigned with my (Travis Tilley) Developer ID as an extra precaution when distributing pre-compiled code and contains an embedded plist describing its build environment. This should be sufficient for most users, but if you need to use rb-fsevent on 10.8 or lower then recompilation is necessary. This can be done by entering the installed gem's ext directory and running:
|
20
26
|
|
21
|
-
MACOSX_DEPLOYMENT_TARGET="10.
|
27
|
+
MACOSX_DEPLOYMENT_TARGET="10.7" rake replace_exe
|
22
28
|
|
23
29
|
The following ENV vars are recognized:
|
24
30
|
|
@@ -216,10 +222,6 @@ If the gem is re-compiled with the environment variable FWDEBUG set, then fseven
|
|
216
222
|
[etc]
|
217
223
|
|
218
224
|
|
219
|
-
## Note about FFI
|
220
|
-
|
221
|
-
rb-fsevent doesn't use [ruby-ffi](http://github.com/ffi/ffi) anymore because it sadly doesn't allow for catching Signals. You can still see the code in the [ffi branch](http://github.com/thibaudgg/rb-fsevent/tree/ffi).
|
222
|
-
|
223
225
|
## Development
|
224
226
|
|
225
227
|
* Source hosted at [GitHub](http://github.com/thibaudgg/rb-fsevent)
|
@@ -231,15 +233,10 @@ Pull requests are quite welcome! Please ensure that your commits are in a topic
|
|
231
233
|
|
232
234
|
The list of tested targets is currently:
|
233
235
|
|
234
|
-
%w[
|
235
|
-
|
236
|
-
## Donations
|
237
|
-
|
238
|
-
rb-fsevent is truly free software. The license is quite liberal (you don't even have to contribute back your changes). If, however, you'd like to donate as a way of showing support for the project and its continued development:
|
236
|
+
%w[2.2.2 2.3.0-dev rbx-2.5.5 jruby-1.7.9]
|
239
237
|
|
240
|
-
[![Donate Bitcoins](https://d2o7j92jk8qjiw.cloudfront.net/assets/buttons/donation_small-2d08f8cd93c98acf496e0411cc6a5262.png)](https://coinbase.com/checkouts/5233986321e2217499bd6ef91f679aa4?c=rb-fsevent)
|
241
|
-
|
242
238
|
## Authors
|
243
239
|
|
244
240
|
* [Travis Tilley](http://github.com/ttilley)
|
245
241
|
* [Thibaud Guillaume-Gentil](http://github.com/thibaudgg)
|
242
|
+
* [Andrey Tarantsov](https://github.com/andreyvit)
|
data/Rakefile
CHANGED
@@ -9,7 +9,7 @@ task :default => :spec
|
|
9
9
|
namespace(:spec) do
|
10
10
|
desc "Run all specs on multiple ruby versions"
|
11
11
|
task(:portability) do
|
12
|
-
versions = %w[
|
12
|
+
versions = %w[2.2.2 2.3.0-dev rbx-2.5.5 jruby-1.7.9]
|
13
13
|
versions.each do |version|
|
14
14
|
# system <<-BASH
|
15
15
|
# bash -c 'source ~/.rvm/scripts/rvm;
|
data/bin/fsevent_watch
CHANGED
Binary file
|
@@ -0,0 +1,626 @@
|
|
1
|
+
/*
|
2
|
+
* FSEventsFix
|
3
|
+
*
|
4
|
+
* Resolves a long-standing bug in realpath() that prevents FSEvents API from
|
5
|
+
* monitoring certain folders on a wide range of OS X released (10.6-10.10 at least).
|
6
|
+
*
|
7
|
+
* The underlying issue is that for some folders, realpath() call starts returning
|
8
|
+
* a path with incorrect casing (e.g. "/users/smt" instead of "/Users/smt").
|
9
|
+
* FSEvents is case-sensitive and calls realpath() on the paths you pass in, so
|
10
|
+
* an incorrect value returned by realpath() prevents FSEvents from seeing any
|
11
|
+
* change events.
|
12
|
+
*
|
13
|
+
* See the discussion at https://github.com/thibaudgg/rb-fsevent/issues/10 about
|
14
|
+
* the history of this bug and how this library came to exist.
|
15
|
+
*
|
16
|
+
* This library uses Facebook's fishhook to replace a custom implementation of
|
17
|
+
* realpath in place of the system realpath; FSEvents will then invoke our custom
|
18
|
+
* implementation (which does not screw up the names) and will thus work correctly.
|
19
|
+
*
|
20
|
+
* Our implementation of realpath is based on the open-source implementation from
|
21
|
+
* OS X 10.10, with a single change applied (enclosed in "BEGIN WORKAROUND FOR
|
22
|
+
* OS X BUG" ... "END WORKAROUND FOR OS X BUG").
|
23
|
+
*
|
24
|
+
* Include FSEventsFix.{h,c} into your project and call FSEventsFixInstall().
|
25
|
+
*
|
26
|
+
* It is recommended that you install FSEventsFix on demand, using FSEventsFixIsBroken
|
27
|
+
* to check if the folder you're about to pass to FSEventStreamCreate needs the fix.
|
28
|
+
* Note that the fix must be applied before calling FSEventStreamCreate.
|
29
|
+
*
|
30
|
+
* FSEventsFixIsBroken requires a path that uses the correct case for all folder names,
|
31
|
+
* i.e. a path provided by the system APIs or constructed from folder names provided
|
32
|
+
* by the directory enumeration APIs.
|
33
|
+
*
|
34
|
+
* Copyright (c) 2015 Andrey Tarantsov <andrey@tarantsov.com>
|
35
|
+
* Copyright (c) 2003 Constantin S. Svintsoff <kostik@iclub.nsu.ru>
|
36
|
+
*
|
37
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
38
|
+
* of this software and associated documentation files (the "Software"), to deal
|
39
|
+
* in the Software without restriction, including without limitation the rights
|
40
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
41
|
+
* copies of the Software, and to permit persons to whom the Software is
|
42
|
+
* furnished to do so, subject to the following conditions:
|
43
|
+
*
|
44
|
+
* The above copyright notice and this permission notice shall be included in
|
45
|
+
* all copies or substantial portions of the Software.
|
46
|
+
*
|
47
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
48
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
49
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
50
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
51
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
52
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
53
|
+
* THE SOFTWARE.
|
54
|
+
*
|
55
|
+
* Based on a realpath implementation from Apple libc 498.1.7, taken from
|
56
|
+
* http://www.opensource.apple.com/source/Libc/Libc-498.1.7/stdlib/FreeBSD/realpath.c
|
57
|
+
* and provided under the following license:
|
58
|
+
*
|
59
|
+
* Copyright (c) 2003 Constantin S. Svintsoff <kostik@iclub.nsu.ru>
|
60
|
+
*
|
61
|
+
* Redistribution and use in source and binary forms, with or without
|
62
|
+
* modification, are permitted provided that the following conditions
|
63
|
+
* are met:
|
64
|
+
* 1. Redistributions of source code must retain the above copyright
|
65
|
+
* notice, this list of conditions and the following disclaimer.
|
66
|
+
* 2. Redistributions in binary form must reproduce the above copyright
|
67
|
+
* notice, this list of conditions and the following disclaimer in the
|
68
|
+
* documentation and/or other materials provided with the distribution.
|
69
|
+
* 3. The names of the authors may not be used to endorse or promote
|
70
|
+
* products derived from this software without specific prior written
|
71
|
+
* permission.
|
72
|
+
*
|
73
|
+
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
74
|
+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
75
|
+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
76
|
+
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
77
|
+
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
78
|
+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
79
|
+
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
80
|
+
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
81
|
+
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
82
|
+
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
83
|
+
* SUCH DAMAGE.
|
84
|
+
*/
|
85
|
+
|
86
|
+
|
87
|
+
#include "FSEventsFix.h"
|
88
|
+
|
89
|
+
#include <dispatch/dispatch.h>
|
90
|
+
#include <stddef.h>
|
91
|
+
#include <stdint.h>
|
92
|
+
#include <stdbool.h>
|
93
|
+
#include <string.h>
|
94
|
+
#include <stdio.h>
|
95
|
+
#include <stdlib.h>
|
96
|
+
#include <stdarg.h>
|
97
|
+
#include <ctype.h>
|
98
|
+
#include <dlfcn.h>
|
99
|
+
|
100
|
+
|
101
|
+
const char *const FSEventsFixVersionString = "0.11.0";
|
102
|
+
|
103
|
+
|
104
|
+
#pragma mark - Forward declarations
|
105
|
+
|
106
|
+
static char *(*orig_realpath)(const char *restrict file_name, char resolved_name[PATH_MAX]);
|
107
|
+
static char *CFURL_realpath(const char *restrict file_name, char resolved_name[PATH_MAX]);
|
108
|
+
static char *FSEventsFix_realpath_wrapper(const char *restrict src, char *restrict dst);
|
109
|
+
|
110
|
+
static void _FSEventsFixHookInstall();
|
111
|
+
static void _FSEventsFixHookUninstall();
|
112
|
+
|
113
|
+
|
114
|
+
#pragma mark - Internal state
|
115
|
+
|
116
|
+
static dispatch_queue_t g_queue = NULL;
|
117
|
+
|
118
|
+
static int64_t g_enable_refcount = 0;
|
119
|
+
|
120
|
+
static bool g_in_self_test = false;
|
121
|
+
static bool g_hook_operational = false;
|
122
|
+
|
123
|
+
static void(^g_logging_block)(FSEventsFixMessageType type, const char *message);
|
124
|
+
static FSEventsFixDebugOptions g_debug_opt = 0;
|
125
|
+
|
126
|
+
typedef struct {
|
127
|
+
char *name;
|
128
|
+
void *replacement;
|
129
|
+
void *original;
|
130
|
+
uint hooked_symbols;
|
131
|
+
} rebinding_t;
|
132
|
+
|
133
|
+
static rebinding_t g_rebindings[] = {
|
134
|
+
{ "_realpath$DARWIN_EXTSN", (void *) &FSEventsFix_realpath_wrapper, (void *) &realpath, 0 }
|
135
|
+
};
|
136
|
+
static const uint g_rebindings_nel = sizeof(g_rebindings) / sizeof(g_rebindings[0]);
|
137
|
+
|
138
|
+
|
139
|
+
#pragma mark - Logging
|
140
|
+
|
141
|
+
static void _FSEventsFixLog(FSEventsFixMessageType type, const char *__restrict fmt, ...) __attribute__((__format__ (__printf__, 2, 3)));
|
142
|
+
|
143
|
+
static void _FSEventsFixLog(FSEventsFixMessageType type, const char *__restrict fmt, ...) {
|
144
|
+
if (g_logging_block) {
|
145
|
+
char *message = NULL;
|
146
|
+
va_list va;
|
147
|
+
va_start(va, fmt);
|
148
|
+
vasprintf(&message, fmt, va);
|
149
|
+
va_end(va);
|
150
|
+
|
151
|
+
if (message) {
|
152
|
+
if (!!(g_debug_opt & FSEventsFixDebugOptionLogToStderr)) {
|
153
|
+
fprintf(stderr, "FSEventsFix: %s\n", message);
|
154
|
+
}
|
155
|
+
if (g_logging_block) {
|
156
|
+
g_logging_block(type, message);
|
157
|
+
}
|
158
|
+
free(message);
|
159
|
+
}
|
160
|
+
}
|
161
|
+
}
|
162
|
+
|
163
|
+
|
164
|
+
#pragma mark - API
|
165
|
+
|
166
|
+
void _FSEventsFixInitialize() {
|
167
|
+
static dispatch_once_t onceToken;
|
168
|
+
dispatch_once(&onceToken, ^{
|
169
|
+
g_queue = dispatch_queue_create("FSEventsFix", DISPATCH_QUEUE_SERIAL);
|
170
|
+
});
|
171
|
+
}
|
172
|
+
|
173
|
+
void FSEventsFixConfigure(FSEventsFixDebugOptions debugOptions, void(^loggingBlock)(FSEventsFixMessageType severity, const char *message)) {
|
174
|
+
_FSEventsFixInitialize();
|
175
|
+
loggingBlock = Block_copy(loggingBlock);
|
176
|
+
dispatch_sync(g_queue, ^{
|
177
|
+
g_debug_opt = debugOptions;
|
178
|
+
g_logging_block = loggingBlock;
|
179
|
+
});
|
180
|
+
}
|
181
|
+
|
182
|
+
// Must be called from the private serial queue.
|
183
|
+
void _FSEventsFixSelfTest() {
|
184
|
+
g_in_self_test = true;
|
185
|
+
g_hook_operational = false;
|
186
|
+
static char result[1024];
|
187
|
+
realpath("/Etc/__!FSEventsFixSelfTest!__", result);
|
188
|
+
g_in_self_test = false;
|
189
|
+
}
|
190
|
+
|
191
|
+
void FSEventsFixEnable() {
|
192
|
+
_FSEventsFixInitialize();
|
193
|
+
dispatch_sync(g_queue, ^{
|
194
|
+
if (++g_enable_refcount == 1) {
|
195
|
+
orig_realpath = dlsym(RTLD_DEFAULT, "realpath");
|
196
|
+
_FSEventsFixHookInstall();
|
197
|
+
_FSEventsFixSelfTest();
|
198
|
+
if (g_hook_operational) {
|
199
|
+
_FSEventsFixLog(FSEventsFixMessageTypeStatusChange, "Enabled");
|
200
|
+
} else {
|
201
|
+
_FSEventsFixLog(FSEventsFixMessageTypeFatalError, "Failed to enable (hook not called)");
|
202
|
+
}
|
203
|
+
}
|
204
|
+
});
|
205
|
+
}
|
206
|
+
|
207
|
+
void FSEventsFixDisable() {
|
208
|
+
_FSEventsFixInitialize();
|
209
|
+
dispatch_sync(g_queue, ^{
|
210
|
+
if (g_enable_refcount == 0) {
|
211
|
+
abort();
|
212
|
+
}
|
213
|
+
if (--g_enable_refcount == 0) {
|
214
|
+
_FSEventsFixHookUninstall();
|
215
|
+
_FSEventsFixSelfTest();
|
216
|
+
if (!g_hook_operational) {
|
217
|
+
_FSEventsFixLog(FSEventsFixMessageTypeStatusChange, "Disabled");
|
218
|
+
} else {
|
219
|
+
_FSEventsFixLog(FSEventsFixMessageTypeFatalError, "Failed to disable (hook still called)");
|
220
|
+
}
|
221
|
+
}
|
222
|
+
});
|
223
|
+
}
|
224
|
+
|
225
|
+
bool FSEventsFixIsOperational() {
|
226
|
+
_FSEventsFixInitialize();
|
227
|
+
__block bool result = false;
|
228
|
+
dispatch_sync(g_queue, ^{
|
229
|
+
result = g_hook_operational;
|
230
|
+
});
|
231
|
+
return result;
|
232
|
+
}
|
233
|
+
|
234
|
+
bool _FSEventsFixIsBroken_noresolve(const char *resolved) {
|
235
|
+
if (!!(g_debug_opt & FSEventsFixDebugOptionSimulateBroken)) {
|
236
|
+
if (strstr(resolved, FSEventsFixSimulatedBrokenFolderMarker)) {
|
237
|
+
return true;
|
238
|
+
}
|
239
|
+
}
|
240
|
+
|
241
|
+
char *reresolved = realpath(resolved, NULL);
|
242
|
+
if (reresolved) {
|
243
|
+
bool broken = (0 != strcmp(resolved, reresolved));
|
244
|
+
free(reresolved);
|
245
|
+
return broken;
|
246
|
+
} else {
|
247
|
+
return true;
|
248
|
+
}
|
249
|
+
}
|
250
|
+
|
251
|
+
bool FSEventsFixIsBroken(const char *path) {
|
252
|
+
char *resolved = CFURL_realpath(path, NULL);
|
253
|
+
if (!resolved) {
|
254
|
+
return true;
|
255
|
+
}
|
256
|
+
bool broken = _FSEventsFixIsBroken_noresolve(resolved);
|
257
|
+
free(resolved);
|
258
|
+
return broken;
|
259
|
+
}
|
260
|
+
|
261
|
+
char *FSEventsFixCopyRootBrokenFolderPath(const char *inpath) {
|
262
|
+
if (!FSEventsFixIsBroken(inpath)) {
|
263
|
+
return NULL;
|
264
|
+
}
|
265
|
+
|
266
|
+
// get a mutable copy of an absolute path
|
267
|
+
char *path = CFURL_realpath(inpath, NULL);
|
268
|
+
if (!path) {
|
269
|
+
return NULL;
|
270
|
+
}
|
271
|
+
|
272
|
+
for (;;) {
|
273
|
+
char *sep = strrchr(path, '/');
|
274
|
+
if ((sep == NULL) || (sep == path)) {
|
275
|
+
break;
|
276
|
+
}
|
277
|
+
*sep = 0;
|
278
|
+
if (!_FSEventsFixIsBroken_noresolve(path)) {
|
279
|
+
*sep = '/';
|
280
|
+
break;
|
281
|
+
}
|
282
|
+
}
|
283
|
+
|
284
|
+
return path;
|
285
|
+
}
|
286
|
+
|
287
|
+
static void _FSEventsFixAttemptRepair(const char *folder) {
|
288
|
+
int rv = rename(folder, folder);
|
289
|
+
|
290
|
+
if (!!(g_debug_opt & FSEventsFixDebugOptionSimulateRepair)) {
|
291
|
+
const char *pos = strstr(folder, FSEventsFixSimulatedBrokenFolderMarker);
|
292
|
+
if (pos) {
|
293
|
+
char *fixed = strdup(folder);
|
294
|
+
fixed[pos - folder] = 0;
|
295
|
+
strcat(fixed, pos + strlen(FSEventsFixSimulatedBrokenFolderMarker));
|
296
|
+
|
297
|
+
rv = rename(folder, fixed);
|
298
|
+
free(fixed);
|
299
|
+
}
|
300
|
+
}
|
301
|
+
|
302
|
+
if (rv != 0) {
|
303
|
+
if (errno == EPERM) {
|
304
|
+
_FSEventsFixLog(FSEventsFixMessageTypeResult, "Permission error when trying to repair '%s'", folder);
|
305
|
+
} else {
|
306
|
+
_FSEventsFixLog(FSEventsFixMessageTypeExpectedFailure, "Unknown error when trying to repair '%s': errno = %d", folder, errno);
|
307
|
+
}
|
308
|
+
}
|
309
|
+
}
|
310
|
+
|
311
|
+
FSEventsFixRepairStatus FSEventsFixRepairIfNeeded(const char *inpath) {
|
312
|
+
char *root = FSEventsFixCopyRootBrokenFolderPath(inpath);
|
313
|
+
if (root == NULL) {
|
314
|
+
return FSEventsFixRepairStatusNotBroken;
|
315
|
+
}
|
316
|
+
|
317
|
+
for (;;) {
|
318
|
+
_FSEventsFixAttemptRepair(root);
|
319
|
+
char *newRoot = FSEventsFixCopyRootBrokenFolderPath(inpath);
|
320
|
+
if (newRoot == NULL) {
|
321
|
+
_FSEventsFixLog(FSEventsFixMessageTypeResult, "Repaired '%s' in '%s'", root, inpath);
|
322
|
+
free(root);
|
323
|
+
return FSEventsFixRepairStatusRepaired;
|
324
|
+
}
|
325
|
+
if (0 == strcmp(root, newRoot)) {
|
326
|
+
_FSEventsFixLog(FSEventsFixMessageTypeResult, "Failed to repair '%s' in '%s'", root, inpath);
|
327
|
+
free(root);
|
328
|
+
free(newRoot);
|
329
|
+
return FSEventsFixRepairStatusFailed;
|
330
|
+
}
|
331
|
+
_FSEventsFixLog(FSEventsFixMessageTypeResult, "Partial success, repaired '%s' in '%s'", root, inpath);
|
332
|
+
free(root);
|
333
|
+
root = newRoot;
|
334
|
+
}
|
335
|
+
}
|
336
|
+
|
337
|
+
|
338
|
+
#pragma mark - FSEventsFix realpath wrapper
|
339
|
+
|
340
|
+
static char *FSEventsFix_realpath_wrapper(const char * __restrict src, char * __restrict dst) {
|
341
|
+
if (g_in_self_test) {
|
342
|
+
if (strstr(src, "__!FSEventsFixSelfTest!__")) {
|
343
|
+
g_hook_operational = true;
|
344
|
+
}
|
345
|
+
}
|
346
|
+
|
347
|
+
// CFURL_realpath doesn't support putting where resolution failed into the
|
348
|
+
// dst buffer, so we call the original realpath here first and if it gets a
|
349
|
+
// result, replace that with the output of CFURL_realpath. that way all the
|
350
|
+
// features of the original realpath are available.
|
351
|
+
char *rv = NULL;
|
352
|
+
char *orv = orig_realpath(src, dst);
|
353
|
+
if (orv != NULL) { rv = CFURL_realpath(src, dst); }
|
354
|
+
|
355
|
+
if (!!(g_debug_opt & FSEventsFixDebugOptionLogCalls)) {
|
356
|
+
char *result = rv ?: dst;
|
357
|
+
_FSEventsFixLog(FSEventsFixMessageTypeCall, "realpath(%s) => %s\n", src, result);
|
358
|
+
}
|
359
|
+
|
360
|
+
if (!!(g_debug_opt & FSEventsFixDebugOptionUppercaseReturn)) {
|
361
|
+
char *result = rv ?: dst;
|
362
|
+
if (result) {
|
363
|
+
for (char *pch = result; *pch; ++pch) {
|
364
|
+
*pch = (char)toupper(*pch);
|
365
|
+
}
|
366
|
+
}
|
367
|
+
}
|
368
|
+
|
369
|
+
return rv;
|
370
|
+
}
|
371
|
+
|
372
|
+
|
373
|
+
#pragma mark - realpath
|
374
|
+
|
375
|
+
// naive implementation of realpath on top of CFURL
|
376
|
+
// NOTE: doesn't quite support the full range of errno results one would
|
377
|
+
// expect here, in part because some of these functions just return a boolean,
|
378
|
+
// and in part because i'm not dealing with messy CFErrorRef objects and
|
379
|
+
// attempting to translate those to sane errno values.
|
380
|
+
// NOTE: the OSX realpath will return _where_ resolution failed in resolved_name
|
381
|
+
// if passed in and return NULL. we can't properly support that extension here
|
382
|
+
// since the resolution happens entirely behind the scenes to us in CFURL.
|
383
|
+
static char* CFURL_realpath(const char *file_name, char resolved_name[PATH_MAX])
|
384
|
+
{
|
385
|
+
char* resolved;
|
386
|
+
CFURLRef url1;
|
387
|
+
CFURLRef url2;
|
388
|
+
CFStringRef path;
|
389
|
+
|
390
|
+
if (file_name == NULL) {
|
391
|
+
errno = EINVAL;
|
392
|
+
return (NULL);
|
393
|
+
}
|
394
|
+
|
395
|
+
#if __DARWIN_UNIX03
|
396
|
+
if (*file_name == 0) {
|
397
|
+
errno = ENOENT;
|
398
|
+
return (NULL);
|
399
|
+
}
|
400
|
+
#endif
|
401
|
+
|
402
|
+
// create a buffer to store our result if we weren't passed one
|
403
|
+
if (!resolved_name) {
|
404
|
+
if ((resolved = malloc(PATH_MAX)) == NULL) return (NULL);
|
405
|
+
} else {
|
406
|
+
resolved = resolved_name;
|
407
|
+
}
|
408
|
+
|
409
|
+
url1 = CFURLCreateFromFileSystemRepresentation(NULL, (const UInt8*)file_name, (CFIndex)strlen(file_name), false);
|
410
|
+
if (url1 == NULL) { goto error_return; }
|
411
|
+
|
412
|
+
url2 = CFURLCopyAbsoluteURL(url1);
|
413
|
+
CFRelease(url1);
|
414
|
+
if (url2 == NULL) { goto error_return; }
|
415
|
+
|
416
|
+
url1 = CFURLCreateFileReferenceURL(NULL, url2, NULL);
|
417
|
+
CFRelease(url2);
|
418
|
+
if (url1 == NULL) { goto error_return; }
|
419
|
+
|
420
|
+
// if there are multiple hard links to the original path, this may end up
|
421
|
+
// being _completely_ different from what was intended
|
422
|
+
url2 = CFURLCreateFilePathURL(NULL, url1, NULL);
|
423
|
+
CFRelease(url1);
|
424
|
+
if (url2 == NULL) { goto error_return; }
|
425
|
+
|
426
|
+
path = CFURLCopyFileSystemPath(url2, kCFURLPOSIXPathStyle);
|
427
|
+
CFRelease(url2);
|
428
|
+
if (path == NULL) { goto error_return; }
|
429
|
+
|
430
|
+
bool success = CFStringGetCString(path, resolved, PATH_MAX, kCFStringEncodingUTF8);
|
431
|
+
CFRelease(path);
|
432
|
+
if (!success) { goto error_return; }
|
433
|
+
|
434
|
+
return resolved;
|
435
|
+
|
436
|
+
error_return:
|
437
|
+
if (!resolved_name) {
|
438
|
+
// we weren't passed in an output buffer and created our own. free it
|
439
|
+
int e = errno;
|
440
|
+
free(resolved);
|
441
|
+
errno = e;
|
442
|
+
}
|
443
|
+
return (NULL);
|
444
|
+
}
|
445
|
+
|
446
|
+
|
447
|
+
#pragma mark - fishhook
|
448
|
+
|
449
|
+
// Copyright (c) 2013, Facebook, Inc.
|
450
|
+
// All rights reserved.
|
451
|
+
// Redistribution and use in source and binary forms, with or without
|
452
|
+
// modification, are permitted provided that the following conditions are met:
|
453
|
+
// * Redistributions of source code must retain the above copyright notice,
|
454
|
+
// this list of conditions and the following disclaimer.
|
455
|
+
// * Redistributions in binary form must reproduce the above copyright notice,
|
456
|
+
// this list of conditions and the following disclaimer in the documentation
|
457
|
+
// and/or other materials provided with the distribution.
|
458
|
+
// * Neither the name Facebook nor the names of its contributors may be used to
|
459
|
+
// endorse or promote products derived from this software without specific
|
460
|
+
// prior written permission.
|
461
|
+
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
462
|
+
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
463
|
+
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
464
|
+
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
465
|
+
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
466
|
+
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
467
|
+
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
468
|
+
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
469
|
+
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
470
|
+
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
471
|
+
|
472
|
+
#import <stdlib.h>
|
473
|
+
#import <string.h>
|
474
|
+
#import <sys/types.h>
|
475
|
+
#import <mach-o/dyld.h>
|
476
|
+
#import <mach-o/loader.h>
|
477
|
+
#import <mach-o/nlist.h>
|
478
|
+
|
479
|
+
#ifdef __LP64__
|
480
|
+
typedef struct mach_header_64 mach_header_t;
|
481
|
+
typedef struct segment_command_64 segment_command_t;
|
482
|
+
typedef struct section_64 section_t;
|
483
|
+
typedef struct nlist_64 nlist_t;
|
484
|
+
#define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT_64
|
485
|
+
#else
|
486
|
+
typedef struct mach_header mach_header_t;
|
487
|
+
typedef struct segment_command segment_command_t;
|
488
|
+
typedef struct section section_t;
|
489
|
+
typedef struct nlist nlist_t;
|
490
|
+
#define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT
|
491
|
+
#endif
|
492
|
+
|
493
|
+
static volatile bool g_hook_installed = false;
|
494
|
+
|
495
|
+
static void _FSEventsFixHookUpdateSection(section_t *section, intptr_t slide, nlist_t *symtab, char *strtab, uint32_t *indirect_symtab)
|
496
|
+
{
|
497
|
+
uint32_t *indirect_symbol_indices = indirect_symtab + section->reserved1;
|
498
|
+
void **indirect_symbol_bindings = (void **)((uintptr_t)slide + section->addr);
|
499
|
+
for (uint i = 0; i < section->size / sizeof(void *); i++) {
|
500
|
+
uint32_t symtab_index = indirect_symbol_indices[i];
|
501
|
+
if (symtab_index == INDIRECT_SYMBOL_ABS || symtab_index == INDIRECT_SYMBOL_LOCAL ||
|
502
|
+
symtab_index == (INDIRECT_SYMBOL_LOCAL | INDIRECT_SYMBOL_ABS)) {
|
503
|
+
continue;
|
504
|
+
}
|
505
|
+
uint32_t strtab_offset = symtab[symtab_index].n_un.n_strx;
|
506
|
+
char *symbol_name = strtab + strtab_offset;
|
507
|
+
for (rebinding_t *cur = g_rebindings, *end = g_rebindings + g_rebindings_nel; cur < end; ++cur) {
|
508
|
+
if (strcmp(symbol_name, cur->name) == 0) {
|
509
|
+
if (g_hook_installed) {
|
510
|
+
if (indirect_symbol_bindings[i] != cur->replacement) {
|
511
|
+
indirect_symbol_bindings[i] = cur->replacement;
|
512
|
+
++cur->hooked_symbols;
|
513
|
+
}
|
514
|
+
} else if (cur->original != NULL) {
|
515
|
+
if (indirect_symbol_bindings[i] == cur->replacement) {
|
516
|
+
indirect_symbol_bindings[i] = cur->original;
|
517
|
+
if (cur->hooked_symbols > 0) {
|
518
|
+
--cur->hooked_symbols;
|
519
|
+
}
|
520
|
+
}
|
521
|
+
}
|
522
|
+
goto symbol_loop;
|
523
|
+
}
|
524
|
+
}
|
525
|
+
symbol_loop:;
|
526
|
+
}
|
527
|
+
}
|
528
|
+
|
529
|
+
static void _FSEventsFixHookUpdateImage(const struct mach_header *header, intptr_t slide) {
|
530
|
+
Dl_info info;
|
531
|
+
if (dladdr(header, &info) == 0) {
|
532
|
+
return;
|
533
|
+
}
|
534
|
+
|
535
|
+
segment_command_t *cur_seg_cmd;
|
536
|
+
segment_command_t *linkedit_segment = NULL;
|
537
|
+
struct symtab_command* symtab_cmd = NULL;
|
538
|
+
struct dysymtab_command* dysymtab_cmd = NULL;
|
539
|
+
|
540
|
+
uintptr_t cur = (uintptr_t)header + sizeof(mach_header_t);
|
541
|
+
for (uint i = 0; i < header->ncmds; i++, cur += cur_seg_cmd->cmdsize) {
|
542
|
+
cur_seg_cmd = (segment_command_t *)cur;
|
543
|
+
if (cur_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) {
|
544
|
+
if (strcmp(cur_seg_cmd->segname, SEG_LINKEDIT) == 0) {
|
545
|
+
linkedit_segment = cur_seg_cmd;
|
546
|
+
}
|
547
|
+
} else if (cur_seg_cmd->cmd == LC_SYMTAB) {
|
548
|
+
symtab_cmd = (struct symtab_command*)cur_seg_cmd;
|
549
|
+
} else if (cur_seg_cmd->cmd == LC_DYSYMTAB) {
|
550
|
+
dysymtab_cmd = (struct dysymtab_command*)cur_seg_cmd;
|
551
|
+
}
|
552
|
+
}
|
553
|
+
|
554
|
+
if (!symtab_cmd || !dysymtab_cmd || !linkedit_segment ||
|
555
|
+
!dysymtab_cmd->nindirectsyms) {
|
556
|
+
return;
|
557
|
+
}
|
558
|
+
|
559
|
+
// Find base symbol/string table addresses
|
560
|
+
uintptr_t linkedit_base = (uintptr_t)slide + linkedit_segment->vmaddr - linkedit_segment->fileoff;
|
561
|
+
nlist_t *symtab = (nlist_t *)(linkedit_base + symtab_cmd->symoff);
|
562
|
+
char *strtab = (char *)(linkedit_base + symtab_cmd->stroff);
|
563
|
+
|
564
|
+
// Get indirect symbol table (array of uint32_t indices into symbol table)
|
565
|
+
uint32_t *indirect_symtab = (uint32_t *)(linkedit_base + dysymtab_cmd->indirectsymoff);
|
566
|
+
|
567
|
+
cur = (uintptr_t)header + sizeof(mach_header_t);
|
568
|
+
for (uint i = 0; i < header->ncmds; i++, cur += cur_seg_cmd->cmdsize) {
|
569
|
+
cur_seg_cmd = (segment_command_t *)cur;
|
570
|
+
if (cur_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) {
|
571
|
+
if (strcmp(cur_seg_cmd->segname, SEG_DATA) != 0) {
|
572
|
+
continue;
|
573
|
+
}
|
574
|
+
for (uint j = 0; j < cur_seg_cmd->nsects; j++) {
|
575
|
+
section_t *sect =
|
576
|
+
(section_t *)(cur + sizeof(segment_command_t)) + j;
|
577
|
+
if ((sect->flags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS) {
|
578
|
+
_FSEventsFixHookUpdateSection(sect, slide, symtab, strtab, indirect_symtab);
|
579
|
+
}
|
580
|
+
if ((sect->flags & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS) {
|
581
|
+
_FSEventsFixHookUpdateSection(sect, slide, symtab, strtab, indirect_symtab);
|
582
|
+
}
|
583
|
+
}
|
584
|
+
}
|
585
|
+
}
|
586
|
+
}
|
587
|
+
|
588
|
+
static void _FSEventsFixHookSaveOriginals() {
|
589
|
+
for (rebinding_t *cur = g_rebindings, *end = g_rebindings + g_rebindings_nel; cur < end; ++cur) {
|
590
|
+
void *original = cur->original = dlsym(RTLD_DEFAULT, cur->name+1);
|
591
|
+
if (!original) {
|
592
|
+
const char *error = dlerror();
|
593
|
+
_FSEventsFixLog(FSEventsFixMessageTypeFatalError, "Cannot find symbol %s, dlsym says: %s\n", cur->name, error);
|
594
|
+
}
|
595
|
+
}
|
596
|
+
}
|
597
|
+
|
598
|
+
static void _FSEventsFixHookUpdate() {
|
599
|
+
uint32_t c = _dyld_image_count();
|
600
|
+
for (uint32_t i = 0; i < c; i++) {
|
601
|
+
_FSEventsFixHookUpdateImage(_dyld_get_image_header(i), _dyld_get_image_vmaddr_slide(i));
|
602
|
+
}
|
603
|
+
}
|
604
|
+
|
605
|
+
static void _FSEventsFixHookInstall() {
|
606
|
+
static bool first_rebinding_done = false;
|
607
|
+
|
608
|
+
if (!g_hook_installed) {
|
609
|
+
g_hook_installed = true;
|
610
|
+
|
611
|
+
if (!first_rebinding_done) {
|
612
|
+
first_rebinding_done = true;
|
613
|
+
_FSEventsFixHookSaveOriginals();
|
614
|
+
_dyld_register_func_for_add_image(_FSEventsFixHookUpdateImage);
|
615
|
+
} else {
|
616
|
+
_FSEventsFixHookUpdate();
|
617
|
+
}
|
618
|
+
}
|
619
|
+
}
|
620
|
+
|
621
|
+
static void _FSEventsFixHookUninstall() {
|
622
|
+
if (g_hook_installed) {
|
623
|
+
g_hook_installed = false;
|
624
|
+
_FSEventsFixHookUpdate();
|
625
|
+
}
|
626
|
+
}
|
@@ -0,0 +1,105 @@
|
|
1
|
+
/*
|
2
|
+
* FSEventsFix
|
3
|
+
*
|
4
|
+
* Works around a long-standing bug in realpath() that prevents FSEvents API from
|
5
|
+
* monitoring certain folders on a wide range of OS X releases (10.6-10.10 at least).
|
6
|
+
*
|
7
|
+
* The underlying issue is that for some folders, realpath() call starts returning
|
8
|
+
* a path with incorrect casing (e.g. "/users/smt" instead of "/Users/smt").
|
9
|
+
* FSEvents is case-sensitive and calls realpath() on the paths you pass in, so
|
10
|
+
* an incorrect value returned by realpath() prevents FSEvents from seeing any
|
11
|
+
* change events.
|
12
|
+
*
|
13
|
+
* See the discussion at https://github.com/thibaudgg/rb-fsevent/issues/10 about
|
14
|
+
* the history of this bug and how this library came to exist.
|
15
|
+
*
|
16
|
+
* This library uses Facebook's fishhook to replace a custom implementation of
|
17
|
+
* realpath in place of the system realpath; FSEvents will then invoke our custom
|
18
|
+
* implementation (which does not screw up the names) and will thus work correctly.
|
19
|
+
*
|
20
|
+
* Our implementation of realpath is based on the open-source implementation from
|
21
|
+
* OS X 10.10, with a single change applied (enclosed in "BEGIN WORKAROUND FOR
|
22
|
+
* OS X BUG" ... "END WORKAROUND FOR OS X BUG").
|
23
|
+
*
|
24
|
+
* Include FSEventsFix.{h,c} into your project and call FSEventsFixInstall().
|
25
|
+
*
|
26
|
+
* It is recommended that you install FSEventsFix on demand, using FSEventsFixIsBroken
|
27
|
+
* to check if the folder you're about to pass to FSEventStreamCreate needs the fix.
|
28
|
+
* Note that the fix must be applied before calling FSEventStreamCreate.
|
29
|
+
*
|
30
|
+
* FSEventsFixIsBroken requires a path that uses the correct case for all folder names,
|
31
|
+
* i.e. a path provided by the system APIs or constructed from folder names provided
|
32
|
+
* by the directory enumeration APIs.
|
33
|
+
*
|
34
|
+
* See .c file for license & copyrights, but basically this is available under a mix
|
35
|
+
* of MIT and BSD licenses.
|
36
|
+
*/
|
37
|
+
|
38
|
+
#ifndef __FSEventsFix__
|
39
|
+
#define __FSEventsFix__
|
40
|
+
|
41
|
+
#include <CoreFoundation/CoreFoundation.h>
|
42
|
+
|
43
|
+
/// A library version string (e.g. 1.2.3) for displaying and logging purposes
|
44
|
+
extern const char *const FSEventsFixVersionString;
|
45
|
+
|
46
|
+
/// See FSEventsFixDebugOptionSimulateBroken
|
47
|
+
#define FSEventsFixSimulatedBrokenFolderMarker "__!FSEventsBroken!__"
|
48
|
+
|
49
|
+
typedef CF_OPTIONS(unsigned, FSEventsFixDebugOptions) {
|
50
|
+
/// Always return an uppercase string from realpath
|
51
|
+
FSEventsFixDebugOptionUppercaseReturn = 0x01,
|
52
|
+
|
53
|
+
/// Log all calls to realpath using the logger configured via FSEventsFixConfigure
|
54
|
+
FSEventsFixDebugOptionLogCalls = 0x02,
|
55
|
+
|
56
|
+
/// In addition to the logging block (if any), log everything to stderr
|
57
|
+
FSEventsFixDebugOptionLogToStderr = 0x08,
|
58
|
+
|
59
|
+
/// Report paths containing FSEventsFixSimulatedBrokenFolderMarker as broken
|
60
|
+
FSEventsFixDebugOptionSimulateBroken = 0x10,
|
61
|
+
|
62
|
+
/// Repair paths containing FSEventsFixSimulatedBrokenFolderMarker by renaming them
|
63
|
+
FSEventsFixDebugOptionSimulateRepair = 0x20,
|
64
|
+
};
|
65
|
+
|
66
|
+
typedef CF_ENUM(int, FSEventsFixMessageType) {
|
67
|
+
/// Call logging requested via FSEventsFixDebugOptionLogCalls
|
68
|
+
FSEventsFixMessageTypeCall,
|
69
|
+
|
70
|
+
/// Results of actions like repair, and other pretty verbose, but notable, stuff.
|
71
|
+
FSEventsFixMessageTypeResult,
|
72
|
+
|
73
|
+
/// Enabled/disabled status change
|
74
|
+
FSEventsFixMessageTypeStatusChange,
|
75
|
+
|
76
|
+
/// Expected failure (treat as a warning)
|
77
|
+
FSEventsFixMessageTypeExpectedFailure,
|
78
|
+
|
79
|
+
/// Severe failure that most likely means that the library won't work
|
80
|
+
FSEventsFixMessageTypeFatalError
|
81
|
+
};
|
82
|
+
|
83
|
+
typedef CF_ENUM(int, FSEventsFixRepairStatus) {
|
84
|
+
FSEventsFixRepairStatusNotBroken,
|
85
|
+
FSEventsFixRepairStatusRepaired,
|
86
|
+
FSEventsFixRepairStatusFailed,
|
87
|
+
};
|
88
|
+
|
89
|
+
/// Note that the logging block can be called on any dispatch queue.
|
90
|
+
void FSEventsFixConfigure(FSEventsFixDebugOptions debugOptions, void(^loggingBlock)(FSEventsFixMessageType type, const char *message));
|
91
|
+
|
92
|
+
void FSEventsFixEnable();
|
93
|
+
void FSEventsFixDisable();
|
94
|
+
|
95
|
+
bool FSEventsFixIsOperational();
|
96
|
+
|
97
|
+
bool FSEventsFixIsBroken(const char *path);
|
98
|
+
|
99
|
+
/// If the path is broken, returns a string identifying the root broken folder,
|
100
|
+
/// otherwise, returns NULL. You need to free() the returned string.
|
101
|
+
char *FSEventsFixCopyRootBrokenFolderPath(const char *path);
|
102
|
+
|
103
|
+
FSEventsFixRepairStatus FSEventsFixRepairIfNeeded(const char *path);
|
104
|
+
|
105
|
+
#endif
|
data/ext/fsevent_watch/main.c
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
#include "common.h"
|
2
2
|
#include "cli.h"
|
3
|
+
#include "FSEventsFix.h"
|
3
4
|
|
4
5
|
// TODO: set on fire. cli.{h,c} handle both parsing and defaults, so there's
|
5
6
|
// no need to set those here. also, in order to scope metadata by path,
|
@@ -34,7 +35,7 @@ static void callback(FSEventStreamRef streamRef,
|
|
34
35
|
void* eventPaths,
|
35
36
|
const FSEventStreamEventFlags eventFlags[],
|
36
37
|
const FSEventStreamEventId eventIds[]);
|
37
|
-
|
38
|
+
static bool needs_fsevents_fix = false;
|
38
39
|
|
39
40
|
// Resolve a path and append it to the CLI settings structure
|
40
41
|
// The FSEvents API will, internally, resolve paths using a similar scheme.
|
@@ -126,9 +127,18 @@ static void append_path(const char* path)
|
|
126
127
|
#endif
|
127
128
|
|
128
129
|
CFStringRef cfPath = CFURLCopyFileSystemPath(placeholder, kCFURLPOSIXPathStyle);
|
130
|
+
CFRelease(placeholder);
|
131
|
+
|
132
|
+
char cPath[PATH_MAX];
|
133
|
+
if (CFStringGetCString(cfPath, cPath, PATH_MAX, kCFStringEncodingUTF8)) {
|
134
|
+
FSEventsFixRepairStatus status = FSEventsFixRepairIfNeeded(cPath);
|
135
|
+
if (status == FSEventsFixRepairStatusFailed) {
|
136
|
+
needs_fsevents_fix = true;
|
137
|
+
}
|
138
|
+
}
|
139
|
+
|
129
140
|
CFArrayAppendValue(config.paths, cfPath);
|
130
141
|
CFRelease(cfPath);
|
131
|
-
CFRelease(placeholder);
|
132
142
|
|
133
143
|
#else
|
134
144
|
|
@@ -462,6 +472,10 @@ int main(int argc, const char* argv[])
|
|
462
472
|
{
|
463
473
|
parse_cli_settings(argc, argv);
|
464
474
|
|
475
|
+
if (needs_fsevents_fix) {
|
476
|
+
FSEventsFixEnable();
|
477
|
+
}
|
478
|
+
|
465
479
|
FSEventStreamContext context = {0, NULL, NULL, NULL, NULL};
|
466
480
|
FSEventStreamRef stream;
|
467
481
|
stream = FSEventStreamCreate(kCFAllocatorDefault,
|
@@ -477,6 +491,10 @@ int main(int argc, const char* argv[])
|
|
477
491
|
fprintf(stderr, "\n");
|
478
492
|
#endif
|
479
493
|
|
494
|
+
if (needs_fsevents_fix) {
|
495
|
+
FSEventsFixDisable();
|
496
|
+
}
|
497
|
+
|
480
498
|
FSEventStreamScheduleWithRunLoop(stream,
|
481
499
|
CFRunLoopGetCurrent(),
|
482
500
|
kCFRunLoopDefaultMode);
|
data/ext/rakefile.rb
CHANGED
@@ -8,7 +8,7 @@ require 'rake/clean'
|
|
8
8
|
raise "unable to find xcodebuild" unless system('which', 'xcodebuild')
|
9
9
|
|
10
10
|
|
11
|
-
FSEVENT_WATCH_EXE_VERSION = '0.1.
|
11
|
+
FSEVENT_WATCH_EXE_VERSION = '0.1.4'
|
12
12
|
|
13
13
|
$this_dir = Pathname.new(__FILE__).dirname.expand_path
|
14
14
|
$final_exe = $this_dir.parent.join('bin/fsevent_watch')
|
@@ -22,11 +22,11 @@ OBJ = SRC.map {|s| $obj_dir.join("#{s.basename('.c')}.o")}
|
|
22
22
|
$now = DateTime.now.xmlschema rescue Time.now.xmlschema
|
23
23
|
|
24
24
|
$CC = ENV['CC'] || `which clang || which gcc`.strip
|
25
|
-
$CFLAGS = ENV['CFLAGS'] || '-fconstant-cfstrings -
|
26
|
-
$ARCHFLAGS = ENV['ARCHFLAGS'] || '-arch x86_64
|
27
|
-
$DEFINES = "-DNS_BUILD_32_LIKE_64 -DNS_BLOCK_ASSERTIONS -
|
25
|
+
$CFLAGS = ENV['CFLAGS'] || '-fconstant-cfstrings -fasm-blocks -fstrict-aliasing -Wall'
|
26
|
+
$ARCHFLAGS = ENV['ARCHFLAGS'] || '-arch x86_64'
|
27
|
+
$DEFINES = "-DNS_BUILD_32_LIKE_64 -DNS_BLOCK_ASSERTIONS -DPROJECT_VERSION=#{FSEVENT_WATCH_EXE_VERSION}"
|
28
28
|
|
29
|
-
$GCC_C_LANGUAGE_STANDARD = ENV['GCC_C_LANGUAGE_STANDARD'] || '
|
29
|
+
$GCC_C_LANGUAGE_STANDARD = ENV['GCC_C_LANGUAGE_STANDARD'] || 'gnu11'
|
30
30
|
|
31
31
|
# generic developer id name so it'll match correctly for anyone who has only
|
32
32
|
# one developer id in their keychain (not that I expect anyone else to bother)
|
@@ -72,7 +72,7 @@ end
|
|
72
72
|
|
73
73
|
task :release => :sw_vers do
|
74
74
|
$DEFINES = "-DNDEBUG #{$DEFINES}"
|
75
|
-
$CFLAGS = "#{$CFLAGS} -
|
75
|
+
$CFLAGS = "#{$CFLAGS} -Ofast"
|
76
76
|
end
|
77
77
|
|
78
78
|
desc 'configure build type depending on whether ENV var FWDEBUG is set'
|
@@ -148,7 +148,7 @@ file $obj_dir.join('Info.plist').to_s => [$obj_dir.to_s, :setup_env] do
|
|
148
148
|
key['CFBundleDisplayName']
|
149
149
|
string['FSEvent Watch CLI']
|
150
150
|
key['NSHumanReadableCopyright']
|
151
|
-
string['Copyright (C) 2011-
|
151
|
+
string['Copyright (C) 2011-2015 Travis Tilley']
|
152
152
|
|
153
153
|
key['CFBundleVersion']
|
154
154
|
string["#{FSEVENT_WATCH_EXE_VERSION}"]
|
@@ -222,4 +222,3 @@ task :replace_exe => :build do
|
|
222
222
|
end
|
223
223
|
|
224
224
|
task :default => [:replace_exe, :clean]
|
225
|
-
|
data/lib/rb-fsevent/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rb-fsevent
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Thibaud Guillaume-Gentil
|
@@ -9,48 +9,48 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2015-05-20 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
requirements:
|
18
|
-
- -
|
18
|
+
- - ~>
|
19
19
|
- !ruby/object:Gem::Version
|
20
20
|
version: '1.0'
|
21
21
|
type: :development
|
22
22
|
prerelease: false
|
23
23
|
version_requirements: !ruby/object:Gem::Requirement
|
24
24
|
requirements:
|
25
|
-
- -
|
25
|
+
- - ~>
|
26
26
|
- !ruby/object:Gem::Version
|
27
27
|
version: '1.0'
|
28
28
|
- !ruby/object:Gem::Dependency
|
29
29
|
name: rspec
|
30
30
|
requirement: !ruby/object:Gem::Requirement
|
31
31
|
requirements:
|
32
|
-
- -
|
32
|
+
- - ~>
|
33
33
|
- !ruby/object:Gem::Version
|
34
34
|
version: '2.11'
|
35
35
|
type: :development
|
36
36
|
prerelease: false
|
37
37
|
version_requirements: !ruby/object:Gem::Requirement
|
38
38
|
requirements:
|
39
|
-
- -
|
39
|
+
- - ~>
|
40
40
|
- !ruby/object:Gem::Version
|
41
41
|
version: '2.11'
|
42
42
|
- !ruby/object:Gem::Dependency
|
43
43
|
name: guard-rspec
|
44
44
|
requirement: !ruby/object:Gem::Requirement
|
45
45
|
requirements:
|
46
|
-
- -
|
46
|
+
- - ~>
|
47
47
|
- !ruby/object:Gem::Version
|
48
48
|
version: '4.2'
|
49
49
|
type: :development
|
50
50
|
prerelease: false
|
51
51
|
version_requirements: !ruby/object:Gem::Requirement
|
52
52
|
requirements:
|
53
|
-
- -
|
53
|
+
- - ~>
|
54
54
|
- !ruby/object:Gem::Version
|
55
55
|
version: '4.2'
|
56
56
|
description: FSEvents API with Signals catching (without RubyCocoa)
|
@@ -61,15 +61,16 @@ executables: []
|
|
61
61
|
extensions: []
|
62
62
|
extra_rdoc_files: []
|
63
63
|
files:
|
64
|
-
-
|
64
|
+
- .gitignore
|
65
65
|
- Gemfile
|
66
66
|
- Guardfile
|
67
67
|
- LICENSE.txt
|
68
68
|
- README.md
|
69
69
|
- Rakefile
|
70
70
|
- bin/fsevent_watch
|
71
|
-
- bin/fsevent_watch_10_5
|
72
71
|
- ext/LICENSE
|
72
|
+
- ext/fsevent_watch/FSEventsFix.c
|
73
|
+
- ext/fsevent_watch/FSEventsFix.h
|
73
74
|
- ext/fsevent_watch/TSICTString.c
|
74
75
|
- ext/fsevent_watch/TSICTString.h
|
75
76
|
- ext/fsevent_watch/cli.c
|
@@ -99,17 +100,17 @@ require_paths:
|
|
99
100
|
- lib
|
100
101
|
required_ruby_version: !ruby/object:Gem::Requirement
|
101
102
|
requirements:
|
102
|
-
- -
|
103
|
+
- - '>='
|
103
104
|
- !ruby/object:Gem::Version
|
104
105
|
version: '0'
|
105
106
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
106
107
|
requirements:
|
107
|
-
- -
|
108
|
+
- - '>='
|
108
109
|
- !ruby/object:Gem::Version
|
109
110
|
version: '0'
|
110
111
|
requirements: []
|
111
112
|
rubyforge_project:
|
112
|
-
rubygems_version: 2.
|
113
|
+
rubygems_version: 2.0.14
|
113
114
|
signing_key:
|
114
115
|
specification_version: 4
|
115
116
|
summary: Very simple & usable FSEvents API
|
data/bin/fsevent_watch_10_5
DELETED
Binary file
|