copyrights in native sources
[idea/community.git] / native / fsNotifier / mac / fsnotifier.c
1 /*
2  * Copyright 2000-2009 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 #include <CoreServices/CoreServices.h>
18 #include <sys/mount.h>
19
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. 
25     //
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.
31 {
32     int                 err;
33     int                 fsCountOrig;
34     int                 fsCount;
35     struct statfs *     fsBuf;
36     bool                done;
37
38
39     fsBuf = NULL;
40     fsCount = 0;
41     
42     done = false;
43     do {
44         // Get the initial count.
45         err = 0;
46         fsCountOrig = getfsstat(NULL, 0, MNT_WAIT);
47         if (fsCountOrig < 0) {
48             err = errno;
49         }
50         
51         // Allocate a buffer for fsCountOrig + 1 items.
52         if (err == 0) {
53             if (fsBuf != NULL) {
54                 free(fsBuf);
55             }
56             fsBuf = malloc((fsCountOrig + 1) * sizeof(*fsBuf));
57             if (fsBuf == NULL) {
58                 err = ENOMEM;
59             }
60         }
61         
62         // Get the list.  
63         if (err == 0) {
64             fsCount = getfsstat(fsBuf, (int) ((fsCountOrig + 1) * sizeof(*fsBuf)), MNT_WAIT);
65             if (fsCount < 0) {
66                 err = errno;
67             }
68         }
69         
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).
72         if (err == 0) {
73             if (fsCount <= fsCountOrig) {
74                 done = true;
75             }
76         }
77     } while ( (err == 0) && ! done );
78
79     int i;
80     int mountCounts = 0;
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");
85             }
86             printf("%s\n", fsBuf[i].f_mntonname);
87             mountCounts++;
88         }
89     }
90
91     if (mountCounts > 0) {
92       printf("#\n");
93       fflush(stdout);
94     }
95
96     free(fsBuf);
97     fsBuf = NULL;
98     
99     return err;
100 }
101
102 void callback(ConstFSEventStreamRef streamRef,
103               void *clientCallBackInfo,
104               size_t numEvents,
105               void *eventPaths,
106               const FSEventStreamEventFlags eventFlags[],
107               const FSEventStreamEventId eventIds[]) {
108     char **paths = eventPaths;
109  
110     int i;
111     for (i=0; i<numEvents; i++) {
112       FSEventStreamEventFlags flags = eventFlags[i];
113       if (flags == kFSEventStreamEventFlagMount || flags == kFSEventStreamEventFlagUnmount) {
114         ReportMountedFileSystems();
115       }
116       else if ((flags & kFSEventStreamEventFlagMustScanSubDirs) != 0) {
117         printf("RECDIRTY\n");
118         printf("%s\n", paths[i]);
119       }
120       else if (eventFlags[i] != kFSEventStreamEventFlagNone) {
121         printf("RESET\n");
122       }
123       else {
124         printf("DIRTY\n");
125         printf("%s\n", paths[i]);
126       }
127     }
128
129     fflush(stdout);
130 }
131
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];
134
135 static void parseRoots() {
136     while (TRUE) {
137      fscanf(stdin, "%s", command);
138      if (strcmp(command, "#") == 0 || feof(stdin)) break;
139     }
140 }
141
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;
146
147     FSEventStreamRef stream;
148     CFAbsoluteTime latency = 0.3; /* Latency in seconds */
149  
150     // Create the stream, passing in a callback,
151     stream = FSEventStreamCreate(NULL,
152         &callback,
153         callbackInfo,
154         pathsToWatch,
155         kFSEventStreamEventIdSinceNow,
156         latency,
157         kFSEventStreamCreateFlagNoDefer
158     );
159
160     FSEventStreamScheduleWithRunLoop(stream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
161     FSEventStreamStart(stream);
162
163     CFRunLoopRun();
164     return NULL;
165 }
166
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) {
170       printf("GIVEUP\n");
171       return 1;
172     }
173
174     ReportMountedFileSystems();
175
176     pthread_t thread_id;
177     int rc = pthread_create(&thread_id, NULL, event_processing_thread, NULL);
178
179     if (rc != 0) {
180       // Give up if cannot create a thread.
181       printf("GIVEUP\n");
182       exit(1);
183     }
184
185     while (TRUE) {
186       fscanf(stdin, "%s", command); 
187       if (strcmp(command, "EXIT") == 0 || feof(stdin)) exit(0);
188       if (strcmp(command, "ROOTS") == 0) parseRoots();
189     }
190  
191     return 0;
192 }