main.c 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. #include "common.h"
  2. #include "cli.h"
  3. #include <sys/types.h>
  4. #include <sys/sysctl.h>
  5. #include <stdio.h>
  6. // TODO: set on fire. cli.{h,c} handle both parsing and defaults, so there's
  7. // no need to set those here. also, in order to scope metadata by path,
  8. // each stream will need its own configuration... so this won't work as
  9. // a global any more. In the end the goal is to make the output format
  10. // able to declare not just that something happened and what flags were
  11. // attached, but what path it was watching that caused those events (so
  12. // that the path itself can be used for routing that information to the
  13. // relevant callback).
  14. //
  15. // Structure for storing metadata parsed from the commandline
  16. static struct {
  17. FSEventStreamEventId sinceWhen;
  18. CFTimeInterval latency;
  19. FSEventStreamCreateFlags flags;
  20. CFMutableArrayRef paths;
  21. int format;
  22. } config = {
  23. (UInt64) kFSEventStreamEventIdSinceNow,
  24. (double) 0.3,
  25. (CFOptionFlags) kFSEventStreamCreateFlagNone,
  26. NULL,
  27. 0
  28. };
  29. // Prototypes
  30. static void append_path(const char* path);
  31. static inline void parse_cli_settings(int argc, const char* argv[]);
  32. static void callback(FSEventStreamRef streamRef,
  33. void* clientCallBackInfo,
  34. size_t numEvents,
  35. void* eventPaths,
  36. const FSEventStreamEventFlags eventFlags[],
  37. const FSEventStreamEventId eventIds[]);
  38. static void append_path(const char* path)
  39. {
  40. CFStringRef pathRef = CFStringCreateWithCString(kCFAllocatorDefault,
  41. path,
  42. kCFStringEncodingUTF8);
  43. CFArrayAppendValue(config.paths, pathRef);
  44. CFRelease(pathRef);
  45. }
  46. // Parse commandline settings
  47. static inline void parse_cli_settings(int argc, const char* argv[])
  48. {
  49. // runtime os version detection
  50. char buf[25];
  51. size_t buflen = 25;
  52. sysctlbyname("kern.osproductversion", &buf, &buflen, NULL, 0);
  53. int osMajorVersion, osMinorVersion;
  54. int res = sscanf(buf, "%d.%d", &osMajorVersion, &osMinorVersion);
  55. if (res != 2)
  56. {
  57. osMajorVersion = osMinorVersion = 0;
  58. }
  59. if ((osMajorVersion == 10) & (osMinorVersion < 5)) {
  60. fprintf(stderr, "The FSEvents API is unavailable on this version of macos!\n");
  61. exit(EXIT_FAILURE);
  62. }
  63. struct cli_info args_info;
  64. cli_parser_init(&args_info);
  65. if (cli_parser(argc, argv, &args_info) != 0) {
  66. exit(EXIT_FAILURE);
  67. }
  68. config.paths = CFArrayCreateMutable(NULL,
  69. (CFIndex)0,
  70. &kCFTypeArrayCallBacks);
  71. config.sinceWhen = args_info.since_when_arg;
  72. config.latency = args_info.latency_arg;
  73. config.format = args_info.format_arg;
  74. if (args_info.no_defer_flag) {
  75. config.flags |= kFSEventStreamCreateFlagNoDefer;
  76. }
  77. if (args_info.watch_root_flag) {
  78. config.flags |= kFSEventStreamCreateFlagWatchRoot;
  79. }
  80. if (args_info.ignore_self_flag) {
  81. if ((osMajorVersion < 10) || ((osMajorVersion == 10) && (osMinorVersion >= 6))) {
  82. fprintf(stderr, "MacOSX 10.6 or later is required for --ignore-self\n");
  83. exit(EXIT_FAILURE);
  84. } else {
  85. config.flags |= kFSEventStreamCreateFlagIgnoreSelf;
  86. }
  87. }
  88. if (args_info.file_events_flag) {
  89. if ((osMajorVersion < 10) || ((osMajorVersion == 10) && (osMinorVersion < 7))) {
  90. fprintf(stderr, "MacOSX 10.7 or later required for --file-events\n");
  91. exit(EXIT_FAILURE);
  92. } else {
  93. config.flags |= kFSEventStreamCreateFlagFileEvents;
  94. }
  95. }
  96. if (args_info.mark_self_flag) {
  97. if ((osMajorVersion < 10) || ((osMajorVersion == 10) && (osMinorVersion < 9))) {
  98. fprintf(stderr, "MacOSX 10.9 or later required for --mark-self\n");
  99. exit(EXIT_FAILURE);
  100. } else {
  101. config.flags |= kFSEventStreamCreateFlagMarkSelf;
  102. }
  103. }
  104. if (args_info.inputs_num == 0) {
  105. append_path(".");
  106. } else {
  107. for (unsigned int i=0; i < args_info.inputs_num; ++i) {
  108. append_path(args_info.inputs[i]);
  109. }
  110. }
  111. cli_parser_free(&args_info);
  112. #ifdef DEBUG
  113. fprintf(stderr, "config.sinceWhen %llu\n", config.sinceWhen);
  114. fprintf(stderr, "config.latency %f\n", config.latency);
  115. fprintf(stderr, "config.flags %#.8x\n", config.flags);
  116. FLAG_CHECK_STDERR(config.flags, kFSEventStreamCreateFlagUseCFTypes,
  117. " Using CF instead of C types");
  118. FLAG_CHECK_STDERR(config.flags, kFSEventStreamCreateFlagNoDefer,
  119. " NoDefer latency modifier enabled");
  120. FLAG_CHECK_STDERR(config.flags, kFSEventStreamCreateFlagWatchRoot,
  121. " WatchRoot notifications enabled");
  122. FLAG_CHECK_STDERR(config.flags, kFSEventStreamCreateFlagIgnoreSelf,
  123. " IgnoreSelf enabled");
  124. FLAG_CHECK_STDERR(config.flags, kFSEventStreamCreateFlagFileEvents,
  125. " FileEvents enabled");
  126. fprintf(stderr, "config.paths\n");
  127. long numpaths = CFArrayGetCount(config.paths);
  128. for (long i = 0; i < numpaths; i++) {
  129. char path[PATH_MAX];
  130. CFStringGetCString(CFArrayGetValueAtIndex(config.paths, i),
  131. path,
  132. PATH_MAX,
  133. kCFStringEncodingUTF8);
  134. fprintf(stderr, " %s\n", path);
  135. }
  136. fprintf(stderr, "\n");
  137. #endif
  138. }
  139. static void callback(__attribute__((unused)) FSEventStreamRef streamRef,
  140. __attribute__((unused)) void* clientCallBackInfo,
  141. size_t numEvents,
  142. void* eventPaths,
  143. const FSEventStreamEventFlags eventFlags[],
  144. const FSEventStreamEventId eventIds[])
  145. {
  146. char** paths = eventPaths;
  147. char *buf = calloc(sizeof(FSEVENTSBITS), sizeof(char));
  148. for (size_t i = 0; i < numEvents; i++) {
  149. sprintb(buf, eventFlags[i], FSEVENTSBITS);
  150. printf("%llu\t%#.8x=[%s]\t%s\n", eventIds[i], eventFlags[i], buf, paths[i]);
  151. }
  152. fflush(stdout);
  153. free(buf);
  154. if (fcntl(STDIN_FILENO, F_GETFD) == -1) {
  155. CFRunLoopStop(CFRunLoopGetCurrent());
  156. }
  157. }
  158. static void stdin_callback(CFFileDescriptorRef fdref, CFOptionFlags callBackTypes, void *info)
  159. {
  160. char buf[1024];
  161. int nread;
  162. do {
  163. nread = read(STDIN_FILENO, buf, sizeof(buf));
  164. if (nread == -1 && errno == EAGAIN) {
  165. CFFileDescriptorEnableCallBacks(fdref, kCFFileDescriptorReadCallBack);
  166. return;
  167. } else if (nread == 0) {
  168. exit(1);
  169. return;
  170. }
  171. } while (nread > 0);
  172. }
  173. int main(int argc, const char* argv[])
  174. {
  175. parse_cli_settings(argc, argv);
  176. FSEventStreamContext context = {0, NULL, NULL, NULL, NULL};
  177. FSEventStreamRef stream;
  178. stream = FSEventStreamCreate(kCFAllocatorDefault,
  179. (FSEventStreamCallback)&callback,
  180. &context,
  181. config.paths,
  182. config.sinceWhen,
  183. config.latency,
  184. config.flags);
  185. #ifdef DEBUG
  186. FSEventStreamShow(stream);
  187. fprintf(stderr, "\n");
  188. #endif
  189. fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK);
  190. CFFileDescriptorRef fdref = CFFileDescriptorCreate(kCFAllocatorDefault, STDIN_FILENO, false, stdin_callback, NULL);
  191. CFFileDescriptorEnableCallBacks(fdref, kCFFileDescriptorReadCallBack);
  192. CFRunLoopSourceRef source = CFFileDescriptorCreateRunLoopSource(kCFAllocatorDefault, fdref, 0);
  193. CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
  194. CFRelease(source);
  195. FSEventStreamScheduleWithRunLoop(stream,
  196. CFRunLoopGetCurrent(),
  197. kCFRunLoopDefaultMode);
  198. FSEventStreamStart(stream);
  199. CFRunLoopRun();
  200. FSEventStreamFlushSync(stream);
  201. FSEventStreamStop(stream);
  202. return 0;
  203. }
  204. // vim: ts=2 sts=2 et sw=2