moving native binaries to community
[idea/community.git] / native / fsNotifier / mac / fsnotifier.c
1 #include <CoreServices/CoreServices.h>
2 #include <sys/mount.h>
3
4 static int ReportMountedFileSystems()
5     // If fsBuf is too small to account for all volumes, getfsstat will 
6     // silently truncate the returned information.  Worse yet, it returns 
7     // the number of volumes it passed back, not the number of volumes present, 
8     // so you can't tell if the list was truncated. 
9     //
10     // So, in order to get an accurate snapshot of the volume list, I call 
11     // getfsstat with a NULL fsBuf to get a count (fsCountOrig), then allocate a 
12     // buffer that holds (fsCountOrig + 1) items, then call getfsstat again with 
13     // that buffer.  If the list was silently truncated, the second count (fsCount)
14     // will be (fsCountOrig + 1), and we loop to try again.
15 {
16     int                 err;
17     int                 fsCountOrig;
18     int                 fsCount;
19     struct statfs *     fsBuf;
20     bool                done;
21
22
23     fsBuf = NULL;
24     fsCount = 0;
25     
26     done = false;
27     do {
28         // Get the initial count.
29         err = 0;
30         fsCountOrig = getfsstat(NULL, 0, MNT_WAIT);
31         if (fsCountOrig < 0) {
32             err = errno;
33         }
34         
35         // Allocate a buffer for fsCountOrig + 1 items.
36         if (err == 0) {
37             if (fsBuf != NULL) {
38                 free(fsBuf);
39             }
40             fsBuf = malloc((fsCountOrig + 1) * sizeof(*fsBuf));
41             if (fsBuf == NULL) {
42                 err = ENOMEM;
43             }
44         }
45         
46         // Get the list.  
47         if (err == 0) {
48             fsCount = getfsstat(fsBuf, (int) ((fsCountOrig + 1) * sizeof(*fsBuf)), MNT_WAIT);
49             if (fsCount < 0) {
50                 err = errno;
51             }
52         }
53         
54         // We got the full list if the number of items returned by the kernel 
55         // is strictly less than the buffer that we allocated (fsCountOrig + 1).
56         if (err == 0) {
57             if (fsCount <= fsCountOrig) {
58                 done = true;
59             }
60         }
61     } while ( (err == 0) && ! done );
62
63     int i;
64     int mountCounts = 0;
65     for (i = 0; i < fsCount; i++) {
66         if ((fsBuf[i].f_flags & MNT_LOCAL) == 0 || (fsBuf[i].f_flags & MNT_JOURNALED) == 0) {
67             if (mountCounts == 0) {
68               printf("UNWATCHEABLE\n");
69             }
70             printf("%s\n", fsBuf[i].f_mntonname);
71             mountCounts++;
72         }
73     }
74
75     if (mountCounts > 0) {
76       printf("#\n");
77       fflush(stdout);
78     }
79
80     free(fsBuf);
81     fsBuf = NULL;
82     
83     return err;
84 }
85
86 void callback(ConstFSEventStreamRef streamRef,
87               void *clientCallBackInfo,
88               size_t numEvents,
89               void *eventPaths,
90               const FSEventStreamEventFlags eventFlags[],
91               const FSEventStreamEventId eventIds[]) {
92     char **paths = eventPaths;
93  
94     int i;
95     for (i=0; i<numEvents; i++) {
96       FSEventStreamEventFlags flags = eventFlags[i];
97       if (flags == kFSEventStreamEventFlagMount || flags == kFSEventStreamEventFlagUnmount) {
98         ReportMountedFileSystems();
99       }
100       else if ((flags & kFSEventStreamEventFlagMustScanSubDirs) != 0) {
101         printf("RECDIRTY\n");
102         printf("%s\n", paths[i]);
103       }
104       else if (eventFlags[i] != kFSEventStreamEventFlagNone) {
105         printf("RESET\n");
106       }
107       else {
108         printf("DIRTY\n");
109         printf("%s\n", paths[i]);
110       }
111     }
112
113     fflush(stdout);
114 }
115
116 // Static buffer for fscanf. All of the are being performed from a single thread, so it's thread safe.
117 static char command[2048];
118
119 static void parseRoots() {
120     while (TRUE) {
121      fscanf(stdin, "%s", command);
122      if (strcmp(command, "#") == 0 || feof(stdin)) break;
123     }
124 }
125
126 void *event_processing_thread(void *data) {
127     CFStringRef mypath = CFSTR("/");
128     CFArrayRef pathsToWatch = CFArrayCreate(NULL, (const void **)&mypath, 1, NULL);
129     void *callbackInfo = NULL;
130
131     FSEventStreamRef stream;
132     CFAbsoluteTime latency = 0.3; /* Latency in seconds */
133  
134     // Create the stream, passing in a callback,
135     stream = FSEventStreamCreate(NULL,
136         &callback,
137         callbackInfo,
138         pathsToWatch,
139         kFSEventStreamEventIdSinceNow,
140         latency,
141         kFSEventStreamCreateFlagNoDefer
142     );
143
144     FSEventStreamScheduleWithRunLoop(stream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
145     FSEventStreamStart(stream);
146
147     CFRunLoopRun();
148     return NULL;
149 }
150
151 int main (int argc, const char * argv[]) {
152     // Checking if necessary API is available (need MacOS X 10.5 or later).
153     if (FSEventStreamCreate == NULL) {
154       printf("GIVEUP\n");
155       return 1;
156     }
157
158     ReportMountedFileSystems();
159
160     pthread_t thread_id;
161     int rc = pthread_create(&thread_id, NULL, event_processing_thread, NULL);
162
163     if (rc != 0) {
164       // Give up if cannot create a thread.
165       printf("GIVEUP\n");
166       exit(1);
167     }
168
169     while (TRUE) {
170       fscanf(stdin, "%s", command); 
171       if (strcmp(command, "EXIT") == 0 || feof(stdin)) exit(0);
172       if (strcmp(command, "ROOTS") == 0) parseRoots();
173     }
174  
175     return 0;
176 }