2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include <CoreServices/CoreServices.h>
18 #include <sys/mount.h>
20 static int ReportMountedFileSystems()
21 // If fsBuf is too small to account for all volumes, getfsstat will
22 // silently truncate the returned information. Worse yet, it returns
23 // the number of volumes it passed back, not the number of volumes present,
24 // so you can't tell if the list was truncated.
26 // So, in order to get an accurate snapshot of the volume list, I call
27 // getfsstat with a NULL fsBuf to get a count (fsCountOrig), then allocate a
28 // buffer that holds (fsCountOrig + 1) items, then call getfsstat again with
29 // that buffer. If the list was silently truncated, the second count (fsCount)
30 // will be (fsCountOrig + 1), and we loop to try again.
35 struct statfs
* fsBuf
;
44 // Get the initial count.
46 fsCountOrig
= getfsstat(NULL
, 0, MNT_WAIT
);
47 if (fsCountOrig
< 0) {
51 // Allocate a buffer for fsCountOrig + 1 items.
56 fsBuf
= malloc((fsCountOrig
+ 1) * sizeof(*fsBuf
));
64 fsCount
= getfsstat(fsBuf
, (int) ((fsCountOrig
+ 1) * sizeof(*fsBuf
)), MNT_WAIT
);
70 // We got the full list if the number of items returned by the kernel
71 // is strictly less than the buffer that we allocated (fsCountOrig + 1).
73 if (fsCount
<= fsCountOrig
) {
77 } while ( (err
== 0) && ! done
);
81 for (i
= 0; i
< fsCount
; i
++) {
82 if ((fsBuf
[i
].f_flags
& MNT_LOCAL
) == 0 || (fsBuf
[i
].f_flags
& MNT_JOURNALED
) == 0) {
83 if (mountCounts
== 0) {
84 printf("UNWATCHEABLE\n");
86 printf("%s\n", fsBuf
[i
].f_mntonname
);
91 if (mountCounts
> 0) {
102 void callback(ConstFSEventStreamRef streamRef
,
103 void *clientCallBackInfo
,
106 const FSEventStreamEventFlags eventFlags
[],
107 const FSEventStreamEventId eventIds
[]) {
108 char **paths
= eventPaths
;
111 for (i
=0; i
<numEvents
; i
++) {
112 FSEventStreamEventFlags flags
= eventFlags
[i
];
113 if (flags
== kFSEventStreamEventFlagMount
|| flags
== kFSEventStreamEventFlagUnmount
) {
114 ReportMountedFileSystems();
116 else if ((flags
& kFSEventStreamEventFlagMustScanSubDirs
) != 0) {
117 printf("RECDIRTY\n");
118 printf("%s\n", paths
[i
]);
120 else if (eventFlags
[i
] != kFSEventStreamEventFlagNone
) {
125 printf("%s\n", paths
[i
]);
132 // Static buffer for fscanf. All of the are being performed from a single thread, so it's thread safe.
133 static char command
[2048];
135 static void parseRoots() {
137 fscanf(stdin
, "%s", command
);
138 if (strcmp(command
, "#") == 0 || feof(stdin
)) break;
142 void *event_processing_thread(void *data
) {
143 CFStringRef mypath
= CFSTR("/");
144 CFArrayRef pathsToWatch
= CFArrayCreate(NULL
, (const void **)&mypath
, 1, NULL
);
145 void *callbackInfo
= NULL
;
147 FSEventStreamRef stream
;
148 CFAbsoluteTime latency
= 0.3; /* Latency in seconds */
150 // Create the stream, passing in a callback,
151 stream
= FSEventStreamCreate(NULL
,
155 kFSEventStreamEventIdSinceNow
,
157 kFSEventStreamCreateFlagNoDefer
160 FSEventStreamScheduleWithRunLoop(stream
, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode
);
161 FSEventStreamStart(stream
);
167 int main (int argc
, const char * argv
[]) {
168 // Checking if necessary API is available (need MacOS X 10.5 or later).
169 if (FSEventStreamCreate
== NULL
) {
174 ReportMountedFileSystems();
177 int rc
= pthread_create(&thread_id
, NULL
, event_processing_thread
, NULL
);
180 // Give up if cannot create a thread.
186 fscanf(stdin
, "%s", command
);
187 if (strcmp(command
, "EXIT") == 0 || feof(stdin
)) exit(0);
188 if (strcmp(command
, "ROOTS") == 0) parseRoots();