Example #1
0
/** Spawn a thread to read from the pipe connected to the specified fd.
 * Returns a Future that will hold a string with the entire output from
 * that stream. */
Future<w_string> ChildProcess::readPipe(int fd) {
  auto it = pipes_.find(fd);
  if (it == pipes_.end()) {
    return makeFuture(w_string(nullptr));
  }

  auto p = std::make_shared<Promise<w_string>>();
  std::thread thr([this, fd, p] {
    std::string result;
    try {
      auto& pipe = pipes_[fd];
      while (true) {
        char buf[4096];
        auto x = read(pipe->read.fd(), buf, sizeof(buf));
        if (x == 0) {
          // all done
          break;
        }
        if (x == -1) {
          p->setException(std::make_exception_ptr(std::system_error(
              errno, std::generic_category(), "reading from child process")));
          return;
        }
        result.append(buf, x);
      }
      p->setValue(w_string(result.data(), result.size()));
    } catch (const std::exception& exc) {
      p->setException(std::current_exception());
    }
  });

  thr.detach();
  return p->getFuture();
}
Example #2
0
w_string w_string::pathCat(std::initializer_list<w_string_piece> elems) {
  uint32_t length = 0;
  w_string_t *s;
  char *buf;

  for (auto &p : elems) {
    length += p.size() + 1;
  }

  s = (w_string_t*)(new char[sizeof(*s) + length]);
  new (s) watchman_string();

  s->refcnt = 1;
  buf = (char *)(s + 1);
  s->buf = buf;

  for (auto &p : elems) {
    if (p.size() == 0) {
      // Skip empty strings
      continue;
    }
    if (buf != s->buf) {
      *buf = '/';
      ++buf;
    }
    memcpy(buf, p.data(), p.size());
    buf += p.size();
  }
  *buf = 0;
  s->len = buf - s->buf;

  return w_string(s, false);
}
SavedStateInterface::SavedStateResult
LocalSavedStateInterface::getMostRecentSavedStateImpl(
    w_string_piece lookupCommitId) const {
  auto commitIds =
      scm_->getCommitsPriorToAndIncluding(lookupCommitId, maxCommits_);
  for (auto& commitId : commitIds) {
    auto path = getLocalPath(commitId);
    // We could return a path that no longer exists if the path is removed
    // (for example by saved state GC) after we check that the path exists
    // here, but before the client reads the state. We've explicitly chosen to
    // return the state without additional safety guarantees, and leave it to
    // the client to ensure GC happens only after states are no longer likely
    // to be used.
    if (w_path_exists(path.c_str())) {
      log(DBG, "Found saved state for commit ", commitId, "\n");
      SavedStateInterface::SavedStateResult result;
      result.commitId = commitId;
      result.savedStateInfo =
          json_object({{"local-path", w_string_to_json(path)},
                       {"commit-id", w_string_to_json(commitId)}});
      return result;
    }
  }
  SavedStateInterface::SavedStateResult result;
  result.commitId = w_string();
  result.savedStateInfo = json_object(
      {{"error", w_string_to_json("No suitable saved state found")}});
  return result;
}
Example #4
0
w_string w_string::asNullTerminated() const {
  if (w_string_is_null_terminated(str_)) {
    return *this;
  }

  return w_string(str_->buf, str_->len, str_->type);
}
Example #5
0
void test_pointers() {
  bool foo = true;
  char lowerBuf[20];

  auto str = w_string::build(&foo);
  snprintf(
      lowerBuf, sizeof(lowerBuf), "0x%" PRIx64, (uint64_t)(uintptr_t)(&foo));
  ok(str.size() == strlen_uint32(lowerBuf),
     "reasonable seeming bool pointer len, got %" PRIu32
     " vs expected %" PRIu32,
     str.size(),
     strlen_uint32(lowerBuf));
  ok(str.size() == strlen_uint32(str.c_str()),
     "string is really nul terminated, size %" PRIu32
     " strlen of c_str %" PRIu32,
     str.size(),
     strlen_uint32(str.c_str()));
  ok(!strcmp(lowerBuf, str.c_str()),
     "bool pointer rendered right hex value sprintf->%s, str->%s",
     lowerBuf,
     str.c_str());

  str = w_string::build(nullptr);
  ok(str.size() > 0, "nullptr has reasonable size: %" PRIsize_t, str.size());
  ok(str == w_string("0x0"), "nullptr looks right %s", str.c_str());

  void* zero = 0;
  ok(w_string::build(zero) == "0x0", "zero pointer looks right");
}
Example #6
0
void test_strings() {
  {
    auto hello = w_string::build("hello");
    ok(hello == w_string("hello"), "hello");
    ok(hello.size() == 5, "there are 5 chars in hello");
    ok(!strcmp("hello", hello.c_str()),
       "looks nul terminated `%s` %" PRIu32,
       hello.c_str(),
       strlen_uint32(hello.c_str()));
  }

  {
    w_string_piece piece("hello");
    ok(piece.size() == 5, "piece has 5 char size");
    auto hello = w_string::build(piece);
    ok(hello.size() == 5, "hello has 5 char size");
    ok(!strcmp("hello", hello.c_str()), "looks nul terminated");
  }

  {
    char foo[] = "foo";
    auto str = w_string::build(foo);
    ok(str.size() == 3, "foo has 3 char size");
    ok(!strcmp("foo", foo), "foo matches");
  }
}
Example #7
0
w_string realPath(const char *path) {
  auto options = OpenFileHandleOptions::queryFileInfo();
  // Follow symlinks, because that's really the point of this function
  options.followSymlinks = 1;
  options.strictNameChecks = 0;

#ifdef _WIN32
  // Special cases for cwd
  w_string_piece pathPiece(path);
  // On Windows, "" is used to refer to the CWD.
  // We also allow using "." for parity with unix, even though that
  // doesn't generally work for that purpose on windows.
  // This allows `watchman watch-project .` to succeeed on windows.
  if (pathPiece.size() == 0 || pathPiece == ".") {
    std::wstring wchar;
    wchar.resize(WATCHMAN_NAME_MAX);
    auto len = GetCurrentDirectoryW(wchar.size(), &wchar[0]);
    auto err = GetLastError();
    if (len == 0) {
      throw std::system_error(err, std::system_category(),
                              "GetCurrentDirectoryW");
    }
    // Assumption: that the OS maintains the CWD in canonical form
    return w_string(wchar.data(), len);
  }
#endif

  auto handle = openFileHandle(path, options);
  return handle.getOpenedPath();
}
Example #8
0
w_string w_string::vprintf(const char* format, va_list args) {
  w_string_t *s;
  int len;
  char *buf;
  va_list args_copy;

  va_copy(args_copy, args);
  // Get the length needed
  len = vsnprintf(nullptr, 0, format, args_copy);
  va_end(args_copy);

  s = (w_string_t*)(new char[sizeof(*s) + len + 1]);
  if (!s) {
    perror("no memory available");
    abort();
  }

  new (s) watchman_string();

  s->refcnt = 1;
  s->len = len;
  buf = (char*)(s + 1);
  vsnprintf(buf, len + 1, format, args);
  s->buf = buf;

  return w_string(s, false);
}
Example #9
0
void add_strings(struct watchman_ignore *ignore, const char **strings,
                 uint32_t num_strings, bool is_vcs_ignore) {
  uint32_t i;
  for (i = 0; i < num_strings; i++) {
    ignore->add(w_string(strings[i], W_STRING_UNICODE), is_vcs_ignore);
  }
}
Example #10
0
void test_double() {
  auto str = w_string::build(5.5);
  char buf[16];
  snprintf(buf, sizeof(buf), "%f", 5.5);
  ok(str.size() == 8, "size is %" PRIsize_t, str.size());
  ok(!strcmp(str.c_str(), buf), "str.c_str=%s, buf=%s", str.c_str(), buf);
  ok(str == w_string("5.500000"), "double looks good '%s'", str.c_str());
}
Example #11
0
void write_string(const char* s, WFILE* p)
{
    int len = (int)strlen(s);
    w_byte(TYPE_STRING, p);
    w_long((long)len, p);
    w_string(s, len, p);
    //log_message(s,p->r);
}
Example #12
0
char *
ACE_NS_String::char_rep (void) const
{
  ACE_TRACE ("ACE_NS_String::char_rep");
  ACE_NS_WString w_string (this->rep_,
                           (this->len_ / sizeof (ACE_WCHAR_T)) - 1);
  return w_string.char_rep ();
}
Example #13
0
QSize StringToSize(const QString &a_string)
{
    QString w_string(a_string);
    removeWithespaceRef(w_string);
    w_string.remove("Size(");
    w_string.remove(")");
    int c_index = w_string.indexOf("|");
    return QSize(w_string.left(c_index).toInt(), w_string.right(w_string.size() - 1 - c_index).toInt());
}
w_string LocalSavedStateInterface::getLocalPath(w_string_piece commitId) const {
  w_string filename;
  if (!projectMetadata_) {
    filename = w_string::build(commitId);
  } else {
    filename = w_string::build(commitId, w_string("_"), projectMetadata_);
  }
  return w_string::pathCat({localStoragePath_, project_, filename});
}
Example #15
0
void	CInifile::w_s64			( LPCSTR S, LPCSTR L, s64				V, LPCSTR comment )
{
    string128			temp;
#ifndef _EDITOR
    _i64toa_s			(V, temp, sizeof(temp), 10);
#else
    _i64toa				(V, temp, 10);
#endif
    w_string			(S,L,temp,comment);
}
Example #16
0
void test_suffix() {
  ok(!w_string("").suffix(), "empty string suffix");
  ok(w_string(".").suffix() == w_string(""), "only one dot suffix");
  ok(w_string("endwithdot.").suffix() == w_string(""), "end with dot");
  ok(!w_string("nosuffix").suffix(), "no suffix");
  ok(w_string(".beginwithdot").suffix() == w_string("beginwithdot"),
     "begin with dot");
  ok(w_string("MainActivity.java").suffix() == w_string("java"), "java suffix");

  std::string longName(128, 'a');
  auto str = w_string::build(".", longName.c_str());
  ok(!str.suffix(), "too long suffix");

  std::string nearlongName(127, 'a');
  str = w_string::build("I am not long enough.", nearlongName.c_str());
  ok(str.suffix().size() == 127, "nearly too long suffix");

  // 255 is the longest suffix among some systems
  std::string toolongName(255, 'a');
  str = w_string::build(".", toolongName.c_str());
  ok(!str.suffix(), "too long suffix");
}
Example #17
0
w_string w_dir_path_cat_str(
    const struct watchman_dir* dir,
    w_string_piece extra) {
  uint32_t length = 0;
  const struct watchman_dir* d;
  w_string_t *s;
  char *buf, *end;

  if (extra.size()) {
    length = extra.size() + 1 /* separator */;
  }
  for (d = dir; d; d = d->parent) {
    length += d->name.size() + 1 /* separator OR final NUL terminator */;
  }

  s = (w_string_t*)(new char[sizeof(*s) + length]);
  new (s) watchman_string();

  s->refcnt = 1;
  s->len = length - 1;
  buf = (char *)(s + 1);
  end = buf + s->len;

  *end = 0;
  if (extra.size()) {
    end -= extra.size();
    memcpy(end, extra.data(), extra.size());
  }
  for (d = dir; d; d = d->parent) {
    if (d != dir || (extra.size())) {
      --end;
      *end = '/';
    }
    end -= d->name.size();
    memcpy(end, d->name.data(), d->name.size());
  }

  s->buf = buf;
  return w_string(s, false);
}
Example #18
0
w_string readSymbolicLink(const char* path) {
#ifndef _WIN32
  std::string result;

  // Speculatively assume that this is large enough to read the
  // symlink text.  This helps to avoid an extra lstat call.
  result.resize(256);

  for (int retry = 0; retry < 2; ++retry) {
    auto len = readlink(path, &result[0], result.size());
    if (len < 0) {
      throw std::system_error(
          errno, std::generic_category(), "readlink for readSymbolicLink");
    }
    if (size_t(len) < result.size()) {
      return w_string(result.data(), len);
    }

    // Truncated read; we need to figure out the right size to use
    struct stat st;
    if (lstat(path, &st)) {
      throw std::system_error(
          errno, std::generic_category(), "lstat for readSymbolicLink");
    }

    result.resize(st.st_size + 1, 0);
  }

  throw std::system_error(
      E2BIG,
      std::generic_category(),
      "readlink for readSymbolicLink: symlink changed while reading it");
#else
  return openFileHandle(path, OpenFileHandleOptions::queryFileInfo())
      .readSymbolicLink();
#endif
}
Example #19
0
w_string w_string_piece::asLowerCase(w_string_type_t stringType) const {
  char* buf;
  w_string_t* s;

  /* need to make a lowercase version */
  s = (w_string_t*)(new char[sizeof(*s) + size() + 1]);
  new (s) watchman_string();

  s->refcnt = 1;
  s->len = size();
  buf = (char*)(s + 1);
  s->buf = buf;
  s->type = stringType;

  auto cursor = s_;
  while (cursor < e_) {
    *buf = (char)tolower((uint8_t)*cursor);
    ++cursor;
    ++buf;
  }
  *buf = 0;

  return w_string(s, false);
}
Example #20
0
void	CInifile::w_fvector4	( LPCSTR S, LPCSTR L, const Fvector4&	V, LPCSTR comment )
{
    string128 temp;
    sprintf_s		(temp,sizeof(temp),"%f,%f,%f,%f", V.x, V.y, V.z, V.w);
    w_string	(S,L,temp,comment);
}
Example #21
0
std::pair<w_string, w_string> ChildProcess::pollingCommunicate(
    pipeWriteCallback writeCallback) {
  std::unordered_map<int, std::string> outputs;

  for (auto& it : pipes_) {
    if (it.first != STDIN_FILENO) {
      // We only want output streams here
      continue;
    }
    watchman::log(
        watchman::DBG, "Setting up output buffer for fd ", it.first, "\n");
    outputs.emplace(std::make_pair(it.first, ""));
  }

  std::vector<pollfd> pfds;
  std::unordered_map<int, int> revmap;
  pfds.reserve(pipes_.size());
  revmap.reserve(pipes_.size());

  while (!pipes_.empty()) {
    revmap.clear();
    pfds.clear();

    watchman::log(
        watchman::DBG, "Setting up pollfds for ", pipes_.size(), " fds\n");

    for (auto& it : pipes_) {
      pollfd pfd;
      if (it.first == STDIN_FILENO) {
        pfd.fd = it.second->write.fd();
        pfd.events = POLLOUT;
      } else {
        pfd.fd = it.second->read.fd();
        pfd.events = POLLIN;
      }
      pfds.emplace_back(std::move(pfd));
      revmap[pfd.fd] = it.first;
    }

    int r;
    do {
      watchman::log(watchman::DBG, "waiting for ", pfds.size(), " fds\n");
      r = ::poll(pfds.data(), pfds.size(), -1);
    } while (r == -1 && errno == EINTR);
    if (r == -1) {
      watchman::log(watchman::ERR, "poll error\n");
      throw std::system_error(errno, std::generic_category(), "poll");
    }

    for (auto& pfd : pfds) {
      watchman::log(
          watchman::DBG,
          "fd ",
          pfd.fd,
          " revmap to ",
          revmap[pfd.fd],
          " has events ",
          pfd.revents,
          "\n");
      if ((pfd.revents & (POLLHUP | POLLIN)) &&
          revmap[pfd.fd] != STDIN_FILENO) {
        watchman::log(
            watchman::DBG,
            "fd ",
            pfd.fd,
            " rev=",
            revmap[pfd.fd],
            " is readable\n");
        char buf[BUFSIZ];
        auto l = ::read(pfd.fd, buf, sizeof(buf));
        if (l == -1 && (errno == EAGAIN || errno == EINTR)) {
          watchman::log(
              watchman::DBG,
              "fd ",
              pfd.fd,
              " rev=",
              revmap[pfd.fd],
              " read give EAGAIN\n");
          continue;
        }
        if (l == -1) {
          int err = errno;
          watchman::log(
              watchman::ERR,
              "failed to read from pipe fd ",
              pfd.fd,
              " err ",
              strerror(err),
              "\n");
          throw std::system_error(
              err, std::generic_category(), "reading from child process");
        }
        watchman::log(
            watchman::DBG,
            "fd ",
            pfd.fd,
            " rev=",
            revmap[pfd.fd],
            " read ",
            l,
            " bytes\n");
        if (l == 0) {
          // Stream is done; close it out.
          pipes_.erase(revmap[pfd.fd]);
          continue;
        }
        outputs[revmap[pfd.fd]].append(buf, l);
      }

      if ((pfd.revents & POLLOUT) && revmap[pfd.fd] == STDIN_FILENO &&
          writeCallback(pipes_.at(revmap[pfd.fd])->write)) {
        // We should close it
        watchman::log(
            watchman::DBG,
            "fd ",
            pfd.fd,
            " rev ",
            revmap[pfd.fd],
            " writer says to close\n");
        pipes_.erase(revmap[pfd.fd]);
        continue;
      }

      if (pfd.revents & (POLLHUP | POLLERR)) {
        // Something wrong with it, so close it
        pipes_.erase(revmap[pfd.fd]);
        watchman::log(
            watchman::DBG,
            "fd ",
            pfd.fd,
            " rev ",
            revmap[pfd.fd],
            " error status, so closing\n");
        continue;
      }
    }

    watchman::log(watchman::DBG, "remaining pipes ", pipes_.size(), "\n");
  }

  auto optBuffer = [&](int fd) -> w_string {
    auto it = outputs.find(fd);
    if (it == outputs.end()) {
      watchman::log(watchman::DBG, "communicate fd ", fd, " nullptr\n");
      return nullptr;
    }
    watchman::log(
        watchman::DBG, "communicate fd ", fd, " gives ", it->second, "\n");
    return w_string(it->second.data(), it->second.size());
  };

  return std::make_pair(optBuffer(STDOUT_FILENO), optBuffer(STDERR_FILENO));
}
Example #22
0
void	CInifile::w_s32			( LPCSTR S, LPCSTR L, s32				V, LPCSTR comment )
{
    string128 temp;
    sprintf_s		(temp,sizeof(temp),"%d",V);
    w_string	(S,L,temp,comment);
}
Example #23
0
w_string FileDescriptor::readSymbolicLink() const {
#ifndef _WIN32
  struct stat st;
  if (fstat(fd_, &st)) {
    throw std::system_error(
        errno, std::generic_category(), "fstat for readSymbolicLink");
  }
  std::string result;
  result.resize(st.st_size + 1, 0);

#ifdef __linux__
  // Linux 2.6.39 and later provide this interface
  auto atlen = readlinkat(fd_, "", &result[0], result.size());
  if (atlen == int(result.size())) {
    // It's longer than we expected; TOCTOU detected!
    throw std::system_error(
        ENAMETOOLONG, std::generic_category(),
        "readlinkat: link contents grew while examining file");
  }
  if (atlen >= 0) {
    return w_string(result.data(), atlen);
  }
  // if we get ENOTDIR back then we're probably on an older linux and
  // should fall back to the technique used below.
  if (errno != ENOTDIR) {
    throw std::system_error(
        errno, std::generic_category(), "readlinkat for readSymbolicLink");
  }
#endif

  auto myName = getOpenedPath();
  auto len = readlink(myName.c_str(), &result[0], result.size());
  if (len == int(result.size())) {
    // It's longer than we expected; TOCTOU detected!
    throw std::system_error(
        ENAMETOOLONG, std::generic_category(),
        "readlink: link contents grew while examining file");
  }
  if (len >= 0) {
    return w_string(result.data(), len);
  }

  throw std::system_error(
      errno, std::generic_category(), "readlink for readSymbolicLink");
#else // _WIN32
  DWORD len = 64 * 1024;
  auto buf = malloc(len);
  if (!buf) {
    throw std::bad_alloc();
  }
  SCOPE_EXIT {
    free(buf);
  };
  WCHAR* target;
  USHORT targetlen;

  auto result = DeviceIoControl(
      (HANDLE)fd_,
      FSCTL_GET_REPARSE_POINT,
      nullptr,
      0,
      buf,
      len,
      &len,
      nullptr);

  // We only give one retry; if the size changed again already, we'll
  // have another pending notify from the OS to go look at it again
  // later, and it's totally fine to give up here for now.
  if (!result && GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
    free(buf);
    buf = malloc(len);
    if (!buf) {
      throw std::bad_alloc();
    }

    result = DeviceIoControl(
        (HANDLE)fd_,
        FSCTL_GET_REPARSE_POINT,
        nullptr,
        0,
        buf,
        len,
        &len,
        nullptr);
  }

  if (!result) {
    throw std::system_error(
        GetLastError(), std::system_category(), "FSCTL_GET_REPARSE_POINT");
  }

  auto rep = reinterpret_cast<REPARSE_DATA_BUFFER*>(buf);

  switch (rep->ReparseTag) {
    case IO_REPARSE_TAG_SYMLINK:
      target = rep->SymbolicLinkReparseBuffer.PathBuffer +
          (rep->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR));
      targetlen =
          rep->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(WCHAR);
      break;

    case IO_REPARSE_TAG_MOUNT_POINT:
      target = rep->MountPointReparseBuffer.PathBuffer +
          (rep->MountPointReparseBuffer.SubstituteNameOffset / sizeof(WCHAR));
      targetlen =
          rep->MountPointReparseBuffer.SubstituteNameLength / sizeof(WCHAR);
      break;
    default:
      throw std::system_error(
          ENOSYS, std::generic_category(), "Unsupported ReparseTag");
  }

  return w_string(target, targetlen);
#endif
}
Example #24
0
w_string FileDescriptor::getOpenedPath() const {
#if defined(F_GETPATH)
  // macOS.  The kernel interface only allows MAXPATHLEN
  char buf[MAXPATHLEN + 1];
  if (fcntl(fd_, F_GETPATH, buf) == -1) {
    throw std::system_error(errno, std::generic_category(),
                            "fcntl for getOpenedPath");
  }
  return w_string(buf);
#elif defined(__linux__) || defined(__sun)
  char procpath[1024];
#if defined(__linux__)
  snprintf(procpath, sizeof(procpath), "/proc/%d/fd/%d", getpid(), fd_);
#elif defined(__sun)
  snprintf(procpath, sizeof(procpath), "/proc/%d/path/%d", getpid(), fd_);
#endif

  // Avoid an extra stat by speculatively attempting to read into
  // a reasonably sized buffer.
  char buf[WATCHMAN_NAME_MAX];
  auto len = readlink(procpath, buf, sizeof(buf));
  if (len == sizeof(buf)) {
    len = -1;
    // We need to stat it to discover the required length
    errno = ENAMETOOLONG;
  }

  if (len >= 0) {
    return w_string(buf, len);
  }

  if (errno == ENOENT) {
    // For this path to not exist must mean that /proc is not mounted.
    // Report this with an actionable message
    throw std::system_error(ENOSYS, std::generic_category(),
                            "getOpenedPath: need /proc to be mounted!");
  }

  if (errno != ENAMETOOLONG) {
    throw std::system_error(errno, std::generic_category(),
                            "readlink for getOpenedPath");
  }

  // Figure out how much space we need
  struct stat st;
  if (fstat(fd_, &st)) {
    throw std::system_error(errno, std::generic_category(),
                            "fstat for getOpenedPath");
  }
  std::string result;
  result.resize(st.st_size + 1, 0);

  len = readlink(procpath, &result[0], result.size());
  if (len == int(result.size())) {
    // It's longer than we expected; TOCTOU detected!
    throw std::system_error(
        ENAMETOOLONG, std::generic_category(),
        "readlinkat: link contents grew while examining file");
  }
  if (len >= 0) {
    return w_string(&result[0], len);
  }

  throw std::system_error(errno, std::generic_category(),
                          "readlink for getOpenedPath");
#elif defined(_WIN32)
  std::wstring wchar;
  wchar.resize(WATCHMAN_NAME_MAX);
  auto len = GetFinalPathNameByHandleW(
      (HANDLE)fd_,
      &wchar[0],
      wchar.size(),
      FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
  auto err = GetLastError();

  if (len >= wchar.size()) {
    // Grow it
    wchar.resize(len);
    len = GetFinalPathNameByHandleW(
        (HANDLE)fd_, &wchar[0], len, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
    err = GetLastError();
  }

  if (len == 0) {
    throw std::system_error(
        GetLastError(), std::system_category(), "GetFinalPathNameByHandleW");
  }

  return w_string(wchar.data(), len);
#else
  throw std::system_error(ENOSYS, std::generic_category(),
                          "getOpenedPath not implemented on this platform");
#endif
}
Example #25
0
static fse_stream* fse_stream_make(
    const std::shared_ptr<w_root_t>& root,
    FSEventStreamEventId since,
    w_string& failure_reason) {
  FSEventStreamContext ctx;
  CFMutableArrayRef parray = nullptr;
  CFStringRef cpath = nullptr;
  double latency;
  struct stat st;
  auto watcher = watcherFromRoot(root);

  struct fse_stream* fse_stream = new struct fse_stream(root, since);

  // Each device has an optional journal maintained by fseventsd that keeps
  // track of the change events.  The journal may not be available if the
  // filesystem was mounted read-only.  The journal has an associated UUID
  // to track the version of the data.  In some cases the journal can become
  // invalidated and it will have a new UUID generated.  This can happen
  // if the EventId rolls over.
  // We need to lookup up the UUID for the associated path and use that to
  // help decide whether we can use a value of `since` other than SinceNow.
  if (stat(root->root_path.c_str(), &st)) {
    failure_reason = w_string::printf(
        "failed to stat(%s): %s\n", root->root_path.c_str(), strerror(errno));
    goto fail;
  }

  // Obtain the UUID for the device associated with the root
  fse_stream->uuid = FSEventsCopyUUIDForDevice(st.st_dev);
  if (since != kFSEventStreamEventIdSinceNow) {
    CFUUIDBytes a, b;

    if (!fse_stream->uuid) {
      // If there is no UUID available and we want to use an event offset,
      // we fail: a nullptr UUID means that the journal is not available.
      failure_reason = w_string::printf(
          "fsevents journal is not available for dev_t=%d\n", st.st_dev);
      goto fail;
    }
    // Compare the UUID with that of the current stream
    if (!watcher->stream->uuid) {
      failure_reason = w_string(
          "fsevents journal was not available for prior stream",
          W_STRING_UNICODE);
      goto fail;
    }

    a = CFUUIDGetUUIDBytes(fse_stream->uuid);
    b = CFUUIDGetUUIDBytes(watcher->stream->uuid);

    if (memcmp(&a, &b, sizeof(a)) != 0) {
      failure_reason =
          w_string("fsevents journal UUID is different", W_STRING_UNICODE);
      goto fail;
    }
  }

  memset(&ctx, 0, sizeof(ctx));
  ctx.info = fse_stream;

  parray = CFArrayCreateMutable(nullptr, 0, &kCFTypeArrayCallBacks);
  if (!parray) {
    failure_reason = w_string("CFArrayCreateMutable failed", W_STRING_UNICODE);
    goto fail;
  }

  cpath = CFStringCreateWithBytes(
      nullptr,
      (const UInt8*)root->root_path.data(),
      root->root_path.size(),
      kCFStringEncodingUTF8,
      false);
  if (!cpath) {
    failure_reason =
        w_string("CFStringCreateWithBytes failed", W_STRING_UNICODE);
    goto fail;
  }

  CFArrayAppendValue(parray, cpath);

  latency = root->config.getDouble("fsevents_latency", 0.01),
  w_log(
      W_LOG_DBG,
      "FSEventStreamCreate for path %s with latency %f seconds\n",
      root->root_path.c_str(),
      latency);

  fse_stream->stream = FSEventStreamCreate(
      nullptr,
      fse_callback,
      &ctx,
      parray,
      since,
      latency,
      kFSEventStreamCreateFlagNoDefer | kFSEventStreamCreateFlagWatchRoot |
          kFSEventStreamCreateFlagFileEvents);

  if (!fse_stream->stream) {
    failure_reason = w_string("FSEventStreamCreate failed", W_STRING_UNICODE);
    goto fail;
  }

  FSEventStreamScheduleWithRunLoop(fse_stream->stream,
      CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);

#ifdef HAVE_FSEVENTSTREAMSETEXCLUSIONPATHS
  if (!root->ignore.dirs_vec.empty() &&
      root->config.getBool("_use_fsevents_exclusions", true)) {
    CFMutableArrayRef ignarray;
    size_t i, nitems = std::min(root->ignore.dirs_vec.size(), MAX_EXCLUSIONS);

    ignarray = CFArrayCreateMutable(nullptr, 0, &kCFTypeArrayCallBacks);
    if (!ignarray) {
      failure_reason =
          w_string("CFArrayCreateMutable failed", W_STRING_UNICODE);
      goto fail;
    }

    for (i = 0; i < nitems; ++i) {
      const auto& path = root->ignore.dirs_vec[i];
      CFStringRef ignpath;

      ignpath = CFStringCreateWithBytes(
          nullptr,
          (const UInt8*)path.data(),
          path.size(),
          kCFStringEncodingUTF8,
          false);

      if (!ignpath) {
        failure_reason =
            w_string("CFStringCreateWithBytes failed", W_STRING_UNICODE);
        CFRelease(ignarray);
        goto fail;
      }

      CFArrayAppendValue(ignarray, ignpath);
      CFRelease(ignpath);
    }

    if (!FSEventStreamSetExclusionPaths(fse_stream->stream, ignarray)) {
      failure_reason =
          w_string("FSEventStreamSetExclusionPaths failed", W_STRING_UNICODE);
      CFRelease(ignarray);
      goto fail;
    }

    CFRelease(ignarray);
  }
#endif

out:
  if (parray) {
    CFRelease(parray);
  }
  if (cpath) {
    CFRelease(cpath);
  }

  return fse_stream;

fail:
  delete fse_stream;
  fse_stream = nullptr;
  goto out;
}
Example #26
0
void	CInifile::w_fcolor		( LPCSTR S, LPCSTR L, const Fcolor&		V, LPCSTR comment )
{
    string128 temp;
    sprintf_s		(temp,sizeof(temp),"%f,%f,%f,%f", V.r, V.g, V.b, V.a);
    w_string	(S,L,temp,comment);
}
Example #27
0
void	CInifile::w_ivector3	( LPCSTR S, LPCSTR L, const Ivector3&	V, LPCSTR comment )
{
    string128 temp;
    sprintf_s		(temp,sizeof(temp),"%d,%d,%d", V.x, V.y, V.z);
    w_string	(S,L,temp,comment);
}
Example #28
0
void	CInifile::w_color		( LPCSTR S, LPCSTR L, u32				V, LPCSTR comment )
{
    string128 temp;
    sprintf_s		(temp,sizeof(temp),"%d,%d,%d,%d", color_get_R(V), color_get_G(V), color_get_B(V), color_get_A(V));
    w_string	(S,L,temp,comment);
}
Example #29
0
void	CInifile::w_float		( LPCSTR S, LPCSTR L, float				V, LPCSTR comment )
{
    string128 temp;
    sprintf_s		(temp,sizeof(temp),"%f",V);
    w_string	(S,L,temp,comment);
}
Example #30
0
void	CInifile::w_bool		( LPCSTR S, LPCSTR L, BOOL				V, LPCSTR comment )
{
    w_string	(S,L,V?"on":"off",comment);
}