Beispiel #1
0
static void accept_loop(FileDescriptor&& listenerDescriptor) {
  auto listener = w_stm_fdopen(std::move(listenerDescriptor));
  while (!stopping) {
    FileDescriptor client_fd;
    struct watchman_event_poll pfd[2];
    int bufsize;

#ifdef HAVE_LIBGIMLI_H
    if (hb) {
      gimli_heartbeat_set(hb, GIMLI_HB_RUNNING);
    }
#endif

    pfd[0].evt = listener->getEvents();
    pfd[1].evt = listener_thread_event.get();

    if (w_poll_events(pfd, 2, 60000) == 0) {
      if (stopping) {
        break;
      }
      // Timed out, or error.
      // Arrange to sanity check that we're working
      w_check_my_sock();
      continue;
    }

    if (stopping) {
      break;
    }

#ifdef HAVE_ACCEPT4
    client_fd = FileDescriptor(
        accept4(listener->getFileDescriptor().fd(), nullptr, 0, SOCK_CLOEXEC));
#else
    client_fd =
        FileDescriptor(accept(listener->getFileDescriptor().fd(), nullptr, 0));
#endif
    if (!client_fd) {
      continue;
    }
    client_fd.setCloExec();
    bufsize = WATCHMAN_IO_BUF_SIZE;
    setsockopt(
        client_fd.fd(),
        SOL_SOCKET,
        SO_SNDBUF,
        (void*)&bufsize,
        sizeof(bufsize));

    make_new_client(w_stm_fdopen(std::move(client_fd)));
  }
}
Beispiel #2
0
bool w_cmd_realpath_root(json_ref& args, char** errmsg) {
  const char *path;

  if (json_array_size(args) < 2) {
    ignore_result(asprintf(errmsg, "wrong number of arguments"));
    return false;
  }

  path = json_string_value(json_array_get(args, 1));
  if (!path) {
    ignore_result(asprintf(errmsg, "second argument must be a string"));
    return false;
  }

  try {
    auto resolved = realPath(path);
    args.array()[1] = w_string_to_json(resolved);
    return true;
  } catch (const std::exception &exc) {
    watchman::log(watchman::DBG, "w_cmd_realpath_root: path ", path,
                  " does not resolve: ", exc.what(), "\n");
    // We don't treat this as an error; the caller will subsequently
    // fail and perform their usual error handling
    return true;
  }
}
Beispiel #3
0
// If we are running under inetd-style supervision, call this function
// to move the inetd provided socket descriptor(s) to a new descriptor
// number and remember that we can just use these when we're starting
// up the listener.
void w_listener_prep_inetd() {
  if (listener_fd) {
    throw std::runtime_error(
        "w_listener_prep_inetd: listener_fd is already assigned");
  }

  listener_fd = FileDescriptor(dup(STDIN_FILENO), "dup(stdin) for listener");
}
Beispiel #4
0
static FileDescriptor create_pipe_server(const char* path) {
  return FileDescriptor(intptr_t(CreateNamedPipe(
      path,
      PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
      PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_REJECT_REMOTE_CLIENTS,
      PIPE_UNLIMITED_INSTANCES,
      WATCHMAN_IO_BUF_SIZE,
      512,
      0,
      nullptr)));
}
Beispiel #5
0
static void execute_common(
    struct w_query_ctx* ctx,
    w_perf_t* sample,
    w_query_res* res,
    w_query_generator generator) {
  if (ctx->query->dedup_results) {
    ctx->dedup.reserve(64);
  }

  res->is_fresh_instance =
      !ctx->since.is_timestamp && ctx->since.clock.is_fresh_instance;

  if (!(res->is_fresh_instance && ctx->query->empty_on_fresh_instance)) {
    if (!generator) {
      generator = default_generators;
    }

    generator(ctx->query, ctx->root, ctx);
  }

  if (!ctx->resultsToRender.empty()) {
    collectAll(ctx->resultsToRender.begin(), ctx->resultsToRender.end())
        .then([&](Result<std::vector<Result<json_ref>>>&& results) {
          auto& vec = results.value();
          for (auto& item : vec) {
            json_array_append_new(ctx->resultsArray, std::move(item.value()));
          }
        })
        .get();
  }

  if (sample && sample->finish()) {
    sample->add_root_meta(ctx->root);
    sample->add_meta(
        "query_execute",
        json_object(
            {{"fresh_instance", json_boolean(res->is_fresh_instance)},
             {"num_deduped", json_integer(ctx->num_deduped)},
             {"num_results", json_integer(json_array_size(ctx->resultsArray))},
             {"num_walked", json_integer(ctx->getNumWalked())},
             {"query", ctx->query->query_spec}}));
    sample->log();
  }

  res->resultsArray = ctx->resultsArray;
  res->dedupedFileNames = std::move(ctx->dedup);
}
Beispiel #6
0
// Given a target of the form "absolute_path/filename", return
// realpath(absolute_path) + filename, where realpath(absolute_path) resolves
// all the symlinks in absolute_path.
static w_string get_normalized_target(const w_string& target) {
  int err;

  w_assert(
      w_string_path_is_absolute(target),
      "get_normalized_target: path %s is not absolute\n",
      target.c_str());

  auto dir_name = target.dirName();
  auto dir_name_real = realPath(dir_name.c_str());
  err = errno;

  if (dir_name_real) {
    auto file_name = target.baseName();
    return w_string::pathCat({dir_name_real, file_name});
  }

  errno = err;
  return nullptr;
}
Beispiel #7
0
// For watch-project, take a root path string and resolve the
// containing project directory, then update the args to reflect
// that path.
// relpath will hold the path to the project dir, relative to the
// watched dir.  If it is NULL it means that the project dir is
// equivalent to the watched dir.
static w_string
resolve_projpath(const json_ref& args, char** errmsg, w_string& relpath) {
  const char* path;
  bool enforcing;
  if (json_array_size(args) < 2) {
    ignore_result(asprintf(errmsg, "wrong number of arguments"));
    return nullptr;
  }

  path = json_string_value(json_array_get(args, 1));
  if (!path) {
    ignore_result(asprintf(errmsg, "second argument must be a string"));
    return nullptr;
  }

  auto resolved = realPath(path);

  auto root_files = cfg_compute_root_files(&enforcing);
  if (!root_files) {
    ignore_result(asprintf(errmsg,
          "resolve_projpath: error computing root_files configuration value, "
          "consult your log file at %s for more details", log_name));
    return nullptr;
  }

  // See if we're requesting something in a pre-existing watch

  w_string_piece prefix;
  w_string_piece relpiece;
  if (findEnclosingRoot(resolved, prefix, relpiece)) {
    relpath = relpiece.asWString();
    resolved = prefix.asWString();
    json_array_set_new(args, 1, w_string_to_json(resolved));
    return resolved;
  }
  auto resolvedpiece = resolved.piece();
  if (find_project_root(root_files, resolvedpiece, relpiece)) {
    relpath = relpiece.asWString();
    resolved = resolvedpiece.asWString();
    json_array_set_new(args, 1, w_string_to_json(resolved));
    return resolved;
  }

  if (!enforcing) {
    // We'll use the path they originally requested
    return resolved;
  }

  // Convert root files to comma delimited string for error message
  auto root_files_list = cfg_pretty_print_root_files(root_files);

  ignore_result(asprintf(
      errmsg,
      "resolve_projpath:  None of the files listed in global config "
      "root_files are present in path `%s` or any of its "
      "parent directories.  root_files is defined by the "
      "`%s` config file and includes %s.  "
      "One or more of these files must be present in order to allow "
      "a watch. Try pulling and checking out a newer version of the project?",
      path,
      cfg_get_global_config_file_path().c_str(),
      root_files_list.c_str()));

  return nullptr;
}
Beispiel #8
0
static FileDescriptor get_listener_socket(const char *path)
{
  struct sockaddr_un un;
  mode_t perms = cfg_get_perms(
      "sock_access", true /* write bits */, false /* execute bits */);
  FileDescriptor listener_fd;

#ifdef __APPLE__
  listener_fd = w_get_listener_socket_from_launchd();
  if (listener_fd) {
    w_log(W_LOG_ERR, "Using socket from launchd as listening socket\n");
    return listener_fd;
  }
#endif

  if (strlen(path) >= sizeof(un.sun_path) - 1) {
    w_log(W_LOG_ERR, "%s: path is too long\n",
        path);
    return FileDescriptor();
  }

  listener_fd = FileDescriptor(socket(PF_LOCAL, SOCK_STREAM, 0), "socket");

  un.sun_family = PF_LOCAL;
  memcpy(un.sun_path, path, strlen(path) + 1);
  unlink(path);

  if (bind(listener_fd.fd(), (struct sockaddr*)&un, sizeof(un)) != 0) {
    w_log(W_LOG_ERR, "bind(%s): %s\n",
      path, strerror(errno));
    return FileDescriptor();
  }

  // The permissions in the containing directory should be correct, so this
  // should be correct as well. But set the permissions in any case.
  if (chmod(path, perms) == -1) {
    w_log(W_LOG_ERR, "chmod(%s, %#o): %s", path, perms, strerror(errno));
    return FileDescriptor();
  }

  // Double-check that the socket has the right permissions. This can happen
  // when the containing directory was created in a previous run, with a group
  // the user is no longer in.
  struct stat st;
  if (lstat(path, &st) == -1) {
    watchman::log(watchman::ERR, "lstat(", path, "): ", strerror(errno), "\n");
    return FileDescriptor();
  }

  // This is for testing only
  // (test_sock_perms.py:test_user_previously_in_sock_group). Do not document.
  const char *sock_group_name = cfg_get_string("__sock_file_group", nullptr);
  if (!sock_group_name) {
    sock_group_name = cfg_get_string("sock_group", nullptr);
  }

  if (sock_group_name) {
    const struct group *sock_group = w_get_group(sock_group_name);
    if (!sock_group) {
      return FileDescriptor();
    }
    if (st.st_gid != sock_group->gr_gid) {
      watchman::log(
        watchman::ERR,
        "for socket '", path, "', gid ", st.st_gid,
        " doesn't match expected gid ", sock_group->gr_gid, " (group name ",
        sock_group_name, "). Ensure that you are still a member of group ",
        sock_group_name, ".\n");
      return FileDescriptor();
    }
  }

  if (listen(listener_fd.fd(), 200) != 0) {
    w_log(W_LOG_ERR, "listen(%s): %s\n",
        path, strerror(errno));
    return FileDescriptor();
  }

  return listener_fd;
}
Beispiel #9
0
static void named_pipe_accept_loop_internal(const char* path) {
  HANDLE handles[2];
  auto olap = OVERLAPPED();
  HANDLE connected_event = CreateEvent(NULL, FALSE, TRUE, NULL);

  if (!connected_event) {
    w_log(
        W_LOG_ERR,
        "named_pipe_accept_loop_internal: CreateEvent failed: %s\n",
        win32_strerror(GetLastError()));
    return;
  }

  handles[0] = connected_event;
  handles[1] = listener_thread_event;
  olap.hEvent = connected_event;

  w_log(W_LOG_ERR, "waiting for pipe clients on %s\n", path);
  while (!stopping) {
    FileDescriptor client_fd;
    DWORD res;

    client_fd = FileDescriptor(intptr_t(CreateNamedPipe(
        path,
        PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
        PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_REJECT_REMOTE_CLIENTS,
        PIPE_UNLIMITED_INSTANCES,
        WATCHMAN_IO_BUF_SIZE,
        512,
        0,
        nullptr)));

    if (!client_fd) {
      w_log(W_LOG_ERR, "CreateNamedPipe(%s) failed: %s\n",
          path, win32_strerror(GetLastError()));
      continue;
    }

    ResetEvent(connected_event);
    if (!ConnectNamedPipe((HANDLE)client_fd.handle(), &olap)) {
      res = GetLastError();

      if (res == ERROR_PIPE_CONNECTED) {
        make_new_client(w_stm_fdopen(std::move(client_fd)));
        continue;
      }

      if (res != ERROR_IO_PENDING) {
        w_log(W_LOG_ERR, "ConnectNamedPipe: %s\n",
            win32_strerror(GetLastError()));
        continue;
      }

      res = WaitForMultipleObjectsEx(2, handles, false, INFINITE, true);
      if (res == WAIT_OBJECT_0 + 1) {
        // Signalled to stop
        CancelIoEx((HANDLE)client_fd.handle(), &olap);
        continue;
      }

      if (res != WAIT_OBJECT_0) {
        w_log(
            W_LOG_ERR,
            "WaitForMultipleObjectsEx: ConnectNamedPipe: "
            "unexpected status %u\n",
            res);
        CancelIoEx((HANDLE)client_fd.handle(), &olap);
        continue;
      }
    }
    make_new_client(w_stm_fdopen(std::move(client_fd)));
  }
}
Beispiel #10
0
KQueueWatcher::KQueueWatcher(w_root_t* root)
    : Watcher("kqueue", 0),
      maps_(maps(root->config.getInt(CFG_HINT_NUM_DIRS, HINT_NUM_DIRS))) {
  kq_fd = FileDescriptor(kqueue(), "kqueue");
  kq_fd.setCloExec();
}