github.com/nycdavid/zeus@v0.0.0-20201208104106-9ba439429e03/vagrant/ext/inotify-wrapper/inotify-wrapper.cpp (about)

     1  #include <map>
     2  #include <string>
     3  
     4  #include <stdio.h>
     5  #include <stdlib.h>
     6  #include <string.h>
     7  #include <errno.h>
     8  #include <unistd.h>
     9  #include <sys/types.h>
    10  #include <sys/inotify.h>
    11  
    12  #include <errno.h>
    13  
    14  #define EVENT_SIZE (sizeof (struct inotify_event))
    15  #define EVENT_BUF_LEN (1024 * (EVENT_SIZE + 16))
    16  
    17  using namespace std;
    18  
    19  static int _inotify_fd;
    20  static map<int, string> _WatchedFiles;
    21  static map<string, bool> _FileIsWatched;
    22  
    23  // static int inotifyFlags = IN_ATTRIB | IN_MODIFY | IN_MOVE_SELF | IN_DELETE_SELF;
    24  static int inotifyFlags = IN_ATTRIB | IN_MODIFY | IN_MOVE_SELF | IN_DELETE_SELF;
    25  
    26  void maybeAddFileToWatchList(string file)
    27  {
    28    if (_FileIsWatched[file]) return;
    29  
    30    int wd = inotify_add_watch(_inotify_fd, file.c_str(), inotifyFlags);
    31    int attempts = 0;
    32    // Files are momentarily inaccessible when they are rewritten. I couldn't
    33    // find a good way to deal with this, so we poll 'deleted' files for 0.25s or so
    34    // to see if they reappear.
    35    while (wd == -1 && errno == ENOENT) {
    36      usleep(10000);
    37      wd = inotify_add_watch(_inotify_fd, file.c_str(), inotifyFlags);
    38      if (attempts++ == 25) break; // try for at most about a quarter of a second
    39    }
    40    if (wd != -1) {
    41      _WatchedFiles[wd] = file;
    42      _FileIsWatched[file] = true;
    43    }
    44  }
    45  
    46  // This essentially removes a file from the watchlist then
    47  // immediately re-adds it. This is because when a file is rewritten,
    48  // as so many editors love to do, the watchdescriptor no longer refers to
    49  // the file, so re must re-watch the path.
    50  void replaceFileInWatchList(int wd, string file)
    51  {
    52    _FileIsWatched.erase(file);
    53    _WatchedFiles.erase(wd);
    54    inotify_rm_watch(_inotify_fd, wd);
    55    maybeAddFileToWatchList(file);
    56  }
    57  
    58  void handleStdin()
    59  {
    60    char line[2048];
    61    if (fgets(line, sizeof(line), stdin) == NULL) return;
    62    line[strlen(line)-1] = 0;
    63  
    64    maybeAddFileToWatchList(string(line));
    65  }
    66  
    67  void handleInotify()
    68  {
    69    int length;
    70    int i = 0;
    71    char buffer[EVENT_BUF_LEN];
    72    string filename;
    73  
    74    length = read(_inotify_fd, buffer, EVENT_BUF_LEN);
    75    if (length < 0) return;
    76  
    77    while (i < length) {
    78      struct inotify_event *event = (struct inotify_event *) &buffer[i];
    79      string file = _WatchedFiles[event->wd];
    80      if (file != "") {
    81        printf("%s\n", file.c_str());
    82        fflush(stdout);
    83        replaceFileInWatchList(event->wd, file);
    84      }
    85  
    86      i += EVENT_SIZE + event->len;
    87    }
    88  }
    89  
    90  void go()
    91  {
    92    fd_set rfds;
    93    int retval;
    94  
    95    for (;;) {
    96      FD_ZERO(&rfds);
    97      FD_SET(0, &rfds);
    98      FD_SET(_inotify_fd, &rfds);
    99  
   100      retval = select(_inotify_fd+1, &rfds, NULL, NULL, NULL);
   101  
   102      if (retval == -1) {
   103        // perror("select");
   104      } else if (retval) {
   105        if(feof(stdin)) break;
   106        if (FD_ISSET(0, &rfds))           handleStdin();
   107        if (FD_ISSET(_inotify_fd, &rfds)) handleInotify();
   108      }
   109    }
   110  }
   111  
   112  
   113  int main(int argc, const char *argv[])
   114  {
   115    _inotify_fd = inotify_init();
   116    go();
   117  }