fsnotifier self diagnostics
authorRoman Shevchenko <roman.shevchenko@jetbrains.com>
Wed, 21 Apr 2010 14:16:20 +0000 (18:16 +0400)
committerRoman Shevchenko <roman.shevchenko@jetbrains.com>
Wed, 21 Apr 2010 14:17:08 +0000 (18:17 +0400)
native/fsNotifier/linux/fsnotifier
native/fsNotifier/linux/fsnotifier.h
native/fsNotifier/linux/inotify.c
native/fsNotifier/linux/main.c

index 3651a1326a35eeff6a4c0c144ab6b70d01a67095..ace4619f3eb414803e9b7430838cc04000b5c090 100755 (executable)
Binary files a/native/fsNotifier/linux/fsnotifier and b/native/fsNotifier/linux/fsnotifier differ
index 1023f183bf1980c4c33452eb2be628dc0872e294..0ee04cd12639569ec49c893a5148a9061a79b687 100644 (file)
 #include <stdio.h>
 
 
+// logging
+void userlog(int priority, const char* format, ...);
+
+
 // variable-length array
 typedef struct __array array;
 
index ae799e2624893991742e2a8b7f9cec7ef9bba948..b3306e5c64ed03a36a7af295e7f958987bfaef97 100644 (file)
@@ -32,7 +32,7 @@
 
 #define DEFAULT_SUBDIR_COUNT 5
 
-#define CHECK_NULL(p) if (p == NULL)  { syslog(LOG_ERR, "out of memory"); return ERR_ABORT; }
+#define CHECK_NULL(p) if (p == NULL)  { userlog(LOG_ERR, "out of memory"); return ERR_ABORT; }
 
 typedef struct __watch_node {
   char* name;
@@ -55,13 +55,13 @@ static char event_buf[EVENT_BUF_LEN];
 static void read_watch_descriptors_count() {
   FILE* f = fopen(WATCH_COUNT_NAME, "r");
   if (f == NULL) {
-    syslog(LOG_ERR, "can't open %s: %s", WATCH_COUNT_NAME, strerror(errno));
+    userlog(LOG_ERR, "can't open %s: %s", WATCH_COUNT_NAME, strerror(errno));
     return;
   }
 
   char* str = read_line(f);
   if (str == NULL) {
-    syslog(LOG_ERR, "can't read from %s", WATCH_COUNT_NAME);
+    userlog(LOG_ERR, "can't read from %s", WATCH_COUNT_NAME);
   }
   else {
     watch_count = atoi(str);
@@ -74,10 +74,10 @@ static void read_watch_descriptors_count() {
 bool init_inotify() {
   inotify_fd = inotify_init();
   if (inotify_fd < 0) {
-    syslog(LOG_ERR, "inotify_init: %s", strerror(errno));
+    userlog(LOG_ERR, "inotify_init: %s", strerror(errno));
     return false;
   }
-  syslog(LOG_DEBUG, "inotify fd: %d", get_inotify_fd());
+  userlog(LOG_DEBUG, "inotify fd: %d", get_inotify_fd());
 
   read_watch_descriptors_count();
   if (watch_count <= 0) {
@@ -85,11 +85,11 @@ bool init_inotify() {
     inotify_fd = -1;
     return false;
   }
-  syslog(LOG_INFO, "inotify watch descriptors: %d", watch_count);
+  userlog(LOG_INFO, "inotify watch descriptors: %d", watch_count);
 
   watches = table_create(watch_count);
   if (watches == NULL) {
-    syslog(LOG_ERR, "out of memory");
+    userlog(LOG_ERR, "out of memory");
     close(inotify_fd);
     inotify_fd = -1;
     return false;
@@ -125,17 +125,17 @@ static int add_watch(const char* path, watch_node* parent) {
     if (errno == ENOSPC) {
       limit_reached = true;
     }
-    syslog(LOG_ERR, "inotify_add_watch(%s): %s", path, strerror(errno));
+    userlog(LOG_ERR, "inotify_add_watch(%s): %s", path, strerror(errno));
     return ERR_CONTINUE;
   }
   else {
-    syslog(LOG_DEBUG, "watching %s: %d", path, wd);
+    userlog(LOG_DEBUG, "watching %s: %d", path, wd);
   }
 
   watch_node* node = table_get(watches, wd);
   if (node != NULL) {
     if (node->wd != wd || strcmp(node->name, path) != 0) {
-      syslog(LOG_ERR, "table error: collision (new %d:%s, existing %d:%s)", wd, path, node->wd, node->name);
+      userlog(LOG_ERR, "table error: collision (new %d:%s, existing %d:%s)", wd, path, node->wd, node->name);
       return ERR_ABORT;
     }
 
@@ -160,7 +160,7 @@ static int add_watch(const char* path, watch_node* parent) {
   }
 
   if (table_put(watches, wd, node) == NULL) {
-    syslog(LOG_ERR, "table error: unable to put (%d:%s)", wd, path);
+    userlog(LOG_ERR, "table error: unable to put (%d:%s)", wd, path);
     return ERR_ABORT;
   }
 
@@ -174,10 +174,10 @@ static void rm_watch(int wd, bool update_parent) {
     return;
   }
 
-  syslog(LOG_DEBUG, "unwatching %s: %d (%p)", node->name, node->wd, node);
+  userlog(LOG_DEBUG, "unwatching %s: %d (%p)", node->name, node->wd, node);
 
   if (inotify_rm_watch(inotify_fd, node->wd) < 0) {
-    syslog(LOG_DEBUG, "inotify_rm_watch(%d:%s): %s", node->wd, node->name, strerror(errno));
+    userlog(LOG_DEBUG, "inotify_rm_watch(%d:%s): %s", node->wd, node->name, strerror(errno));
   }
 
   for (int i=0; i<array_size(node->kids); i++) {
@@ -221,7 +221,7 @@ static bool is_ignored(const char* path, array* ignores) {
       const char* ignore = array_get(ignores, i);
       int il = strlen(ignore);
       if (pl >= il && strncmp(path, ignore, il) == 0) {
-        syslog(LOG_DEBUG, "path %s is under unwatchable %s - ignoring", path, ignore);
+        userlog(LOG_DEBUG, "path %s is under unwatchable %s - ignoring", path, ignore);
         return true;
       }
     }
@@ -242,7 +242,7 @@ static int walk_tree(const char* path, watch_node* parent, array* ignores) {
     else if (errno == ENOTDIR) {  // flat root
       return add_watch(path, parent);
     }
-    syslog(LOG_ERR, "opendir(%s): %s", path, strerror(errno));
+    userlog(LOG_ERR, "opendir(%s): %s", path, strerror(errno));
     return ERR_CONTINUE;
   }
 
@@ -299,7 +299,7 @@ static bool process_inotify_event(struct inotify_event* event) {
     return true;
   }
 
-  syslog(LOG_DEBUG, "inotify: wd=%d mask=%d dir=%d name=%s",
+  userlog(LOG_DEBUG, "inotify: wd=%d mask=%d dir=%d name=%s",
       event->wd, event->mask & (~IN_ISDIR), (event->mask & IN_ISDIR) != 0, node->name);
 
   char path[PATH_MAX];
@@ -339,7 +339,7 @@ static bool process_inotify_event(struct inotify_event* event) {
 bool process_inotify_input() {
   size_t len = read(inotify_fd, event_buf, EVENT_BUF_LEN);
   if (len < 0) {
-    syslog(LOG_ERR, "read: %s", strerror(errno));
+    userlog(LOG_ERR, "read: %s", strerror(errno));
     return false;
   }
 
@@ -352,7 +352,7 @@ bool process_inotify_input() {
       continue;
     }
     if (event->mask & IN_Q_OVERFLOW) {
-      syslog(LOG_ERR, "event queue overflow");
+      userlog(LOG_ERR, "event queue overflow");
       continue;
     }
 
index ba498a7913bd5c2a40560cbb666932ac2da90101..192ce9288fae541a10c45e493951296461bc9b92 100644 (file)
@@ -17,7 +17,8 @@
 #include "fsnotifier.h"
 
 #include <errno.h>
-#include <stdio.h>
+#include <limits.h>
+#include <stdarg.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/inotify.h>
@@ -36,7 +37,8 @@
     "fsnotifier - IntelliJ IDEA companion program for watching and reporting file and directory structure modifications.\n\n" \
     "fsnotifier utilizes \"user\" facility of syslog(3) - messages usually can be found in /var/log/user.log.\n" \
     "Verbosity is regulated via " LOG_ENV " environment variable, possible values are: " \
-    LOG_ENV_DEBUG ", " LOG_ENV_INFO ", " LOG_ENV_WARNING ", " LOG_ENV_ERROR ", " LOG_ENV_OFF "; latter is the default.\n"
+    LOG_ENV_DEBUG ", " LOG_ENV_INFO ", " LOG_ENV_WARNING ", " LOG_ENV_ERROR ", " LOG_ENV_OFF "; latter is the default.\n\n" \
+    "Use \"fsnotifier --selftest\" to perform some self-diagnostics (output will be logged and printed to console).\n"
 
 #define INOTIFY_LIMIT_MSG \
     "The current <b>inotify</b>(7) watch limit of %d is too low. " \
@@ -51,9 +53,12 @@ static array* roots = NULL;
 
 static bool show_warning = true;
 
-#define CHECK_NULL(p) if (p == NULL)  { syslog(LOG_ERR, "out of memory"); return false; }
+static bool self_test = false;
+
+#define CHECK_NULL(p) if (p == NULL)  { userlog(LOG_ERR, "out of memory"); return false; }
 
 static void init_log();
+static void run_self_test();
 static void main_loop();
 static bool read_input();
 static bool update_roots(array* new_roots);
@@ -61,16 +66,27 @@ static void unregister_roots();
 static bool register_roots(array* new_roots, array* unwatchable);
 static bool unwatchable_mounts(array* mounts);
 static void inotify_callback(char* path, int event);
+static void output(const char* format, ...);
 
 
 int main(int argc, char** argv) {
-  if (argc == 2 && strcmp(argv[1], "--help") == 0) {
-    printf(USAGE_MSG);
-    return 0;
+  if (argc == 2) {
+    if (strcmp(argv[1], "--help") == 0) {
+      printf(USAGE_MSG);
+      return 0;
+    }
+    if (strcmp(argv[1], "--selftest") == 0) {
+      self_test = true;
+    }
   }
 
   init_log();
-  syslog(LOG_INFO, "started");
+  if (!self_test) {
+    userlog(LOG_INFO, "started");
+  }
+  else {
+    userlog(LOG_INFO, "started (self-test mode)");
+  }
 
   setvbuf(stdin, NULL, _IONBF, 0);
   setvbuf(stdout, NULL, _IONBF, 0);
@@ -78,7 +94,14 @@ int main(int argc, char** argv) {
   roots = array_create(20);
   if (init_inotify() && roots != NULL) {
     set_inotify_callback(&inotify_callback);
-    main_loop();
+
+    if (!self_test) {
+      main_loop();
+    }
+    else {
+      run_self_test();
+    }
+
     unregister_roots();
   }
   else {
@@ -87,7 +110,7 @@ int main(int argc, char** argv) {
   close_inotify();
   array_delete(roots);
 
-  syslog(LOG_INFO, "finished");
+  userlog(LOG_INFO, "finished");
   closelog();
 
   return 0;
@@ -104,6 +127,10 @@ static void init_log() {
     else if (strcmp(env_level, LOG_ENV_ERROR) == 0)  level = LOG_ERR;
   }
 
+  if (self_test) {
+    level = LOG_DEBUG;
+  }
+
   char ident[32];
   snprintf(ident, sizeof(ident), "fsnotifier[%d]", getpid());
   openlog(ident, 0, LOG_USER);
@@ -111,6 +138,33 @@ static void init_log() {
 }
 
 
+void userlog(int priority, const char* format, ...) {
+  va_list ap;
+  va_start(ap, format);
+
+  vsyslog(priority, format, ap);
+
+  if (self_test) {
+    printf("fsnotifier[%d]: ", getpid());
+    vprintf(format, ap);
+    printf("\n");
+  }
+
+  va_end(ap);
+}
+
+
+static void run_self_test() {
+  array* test_roots = array_create(1);
+  char* cwd = malloc(PATH_MAX);
+  if (getcwd(cwd, PATH_MAX) == NULL) {
+    strncpy(cwd, ".", PATH_MAX);
+  }
+  array_push(test_roots, cwd);
+  update_roots(test_roots);
+}
+
+
 static void main_loop() {
   int input_fd = fileno(stdin), inotify_fd = get_inotify_fd();
   int nfds = (inotify_fd > input_fd ? inotify_fd : input_fd) + 1;
@@ -122,7 +176,7 @@ static void main_loop() {
     FD_SET(input_fd, &rfds);
     FD_SET(inotify_fd, &rfds);
     if (select(nfds, &rfds, NULL, NULL, NULL) < 0) {
-      syslog(LOG_ERR, "select: %s", strerror(errno));
+      userlog(LOG_ERR, "select: %s", strerror(errno));
       go_on = false;
     }
     else if (FD_ISSET(input_fd, &rfds)) {
@@ -137,7 +191,7 @@ static void main_loop() {
 
 static bool read_input() {
   char* line = read_line(stdin);
-  syslog(LOG_DEBUG, "input: %s", (line ? line : "<null>"));
+  userlog(LOG_DEBUG, "input: %s", (line ? line : "<null>"));
 
   if (line == NULL || strcmp(line, "EXIT") == 0) {
     return false;
@@ -149,7 +203,7 @@ static bool read_input() {
 
     while (1) {
       line = read_line(stdin);
-      syslog(LOG_DEBUG, "input: %s", (line ? line : "<null>"));
+      userlog(LOG_DEBUG, "input: %s", (line ? line : "<null>"));
       if (line == NULL || strlen(line) == 0) {
         return false;
       }
@@ -174,15 +228,15 @@ static bool read_input() {
 
 
 static bool update_roots(array* new_roots) {
-  syslog(LOG_INFO, "updating roots (curr:%d, new:%d)", array_size(roots), array_size(new_roots));
+  userlog(LOG_INFO, "updating roots (curr:%d, new:%d)", array_size(roots), array_size(new_roots));
 
   unregister_roots();
   if (array_size(new_roots) == 0) {
     return true;
   }
   else if (array_size(new_roots) == 1 && strcmp(array_get(new_roots, 0), "/") == 0) {  // refuse to watch entire tree
-    printf("UNWATCHEABLE\n/\n#\n");
-    syslog(LOG_INFO, "unwatchable: /");
+    output("UNWATCHEABLE\n/\n#\n");
+    userlog(LOG_INFO, "unwatchable: /");
     array_delete_vs_data(new_roots);
     return true;
   }
@@ -198,13 +252,13 @@ static bool update_roots(array* new_roots) {
   }
 
   // todo: sort/optimize list
-  printf("UNWATCHEABLE\n");
+  output("UNWATCHEABLE\n");
   for (int i=0; i<array_size(unwatchable); i++) {
     char* s = array_get(unwatchable, i);
-    printf("%s\n", s);
-    syslog(LOG_INFO, "unwatchable: %s", s);
+    output("%s\n", s);
+    userlog(LOG_INFO, "unwatchable: %s", s);
   }
-  printf("#\n");
+  output("#\n");
 
   array_delete_vs_data(unwatchable);
   array_delete(new_roots);
@@ -216,7 +270,7 @@ static bool update_roots(array* new_roots) {
 static void unregister_roots() {
   watch_root* root;
   while ((root = array_pop(roots)) != NULL) {
-    syslog(LOG_INFO, "unregistering root: %s\n", root->name);
+    userlog(LOG_INFO, "unregistering root: %s", root->name);
     unwatch(root->id);
     free(root->name);
     free(root);
@@ -227,7 +281,7 @@ static void unregister_roots() {
 static bool register_roots(array* new_roots, array* unwatchable) {
   for (int i=0; i<array_size(new_roots); i++) {
     char* new_root = array_get(new_roots, i);
-    syslog(LOG_INFO, "registering root: %s\n", new_root);
+    userlog(LOG_INFO, "registering root: %s", new_root);
     int id = watch(new_root, unwatchable);
     if (id == ERR_ABORT) {
       return false;
@@ -242,8 +296,8 @@ static bool register_roots(array* new_roots, array* unwatchable) {
     else {
       if (show_warning && watch_limit_reached()) {
         int limit = get_watch_count();
-        syslog(LOG_WARNING, "watch limit (%d) reached", limit);
-        printf("MESSAGE\n" INOTIFY_LIMIT_MSG, limit);
+        userlog(LOG_WARNING, "watch limit (%d) reached", limit);
+        output("MESSAGE\n" INOTIFY_LIMIT_MSG, limit);
         show_warning = false;  // warn only once
       }
       CHECK_NULL(array_push(unwatchable, new_root));
@@ -267,19 +321,19 @@ static bool unwatchable_mounts(array* mounts) {
     mtab = fopen("/proc/mounts", "r");
   }
   if (mtab == NULL) {
-    syslog(LOG_ERR, "neither /etc/mtab nor /proc/mounts can be read");
+    userlog(LOG_ERR, "neither /etc/mtab nor /proc/mounts can be read");
     return false;
   }
 
   char* line;
   while ((line = read_line(mtab)) != NULL) {
-    syslog(LOG_DEBUG, "mtab: %s", line);
+    userlog(LOG_DEBUG, "mtab: %s", line);
     char* dev = strtok(line, MTAB_DELIMS);
     char* point = strtok(NULL, MTAB_DELIMS);
     char* fs = strtok(NULL, MTAB_DELIMS);
 
     if (dev == NULL || point == NULL || fs == NULL) {
-      syslog(LOG_ERR, "can't parse mount line");
+      userlog(LOG_ERR, "can't parse mount line");
       return false;
     }
 
@@ -295,32 +349,44 @@ static bool unwatchable_mounts(array* mounts) {
 
 static void inotify_callback(char* path, int event) {
   if (event & IN_CREATE || event & IN_MOVED_TO) {
-    printf("CREATE\n%s\n", path);
-    syslog(LOG_DEBUG, "CREATE: %s", path);
+    output("CREATE\n%s\n", path);
+    userlog(LOG_DEBUG, "CREATE: %s", path);
     return;
   }
 
   if (event & IN_MODIFY) {
-    printf("CHANGE\n%s\n", path);
-    syslog(LOG_DEBUG, "CHANGE: %s", path);
+    output("CHANGE\n%s\n", path);
+    userlog(LOG_DEBUG, "CHANGE: %s", path);
     return;
   }
 
   if (event & IN_ATTRIB) {
-    printf("STATS\n%s\n", path);
-    syslog(LOG_DEBUG, "STATS: %s", path);
+    output("STATS\n%s\n", path);
+    userlog(LOG_DEBUG, "STATS: %s", path);
     return;
   }
 
   if (event & IN_DELETE || event & IN_MOVED_FROM) {
-    printf("DELETE\n%s\n", path);
-    syslog(LOG_DEBUG, "DELETE: %s", path);
+    output("DELETE\n%s\n", path);
+    userlog(LOG_DEBUG, "DELETE: %s", path);
     return;
   }
 
   if (event & IN_UNMOUNT) {
-    printf("RESET\n");
-    syslog(LOG_DEBUG, "RESET");
+    output("RESET\n");
+    userlog(LOG_DEBUG, "RESET");
     return;
   }
 }
+
+
+static void output(const char* format, ...) {
+  if (self_test) {
+    return;
+  }
+
+  va_list ap;
+  va_start(ap, format);
+  vprintf(format, ap);
+  va_end(ap);
+}