Exemplo n.º 1
0
static json_t *build_subscription_results(
    struct watchman_client_subscription *sub,
    w_root_t *root)
{
  w_query_res res;
  json_t *response;
  json_t *file_list;
  char clockbuf[128];

  w_log(W_LOG_DBG, "running subscription rules! since %" PRIu32 "\n",
      sub->since.ticks);

  // Subscriptions never need to sync explicitly; we are only dispatched
  // at settle points which are by definition sync'd to the present time
  sub->query->sync_timeout = 0;
  if (!w_query_execute(sub->query, root, &res, subscription_generator, sub)) {
    w_log(W_LOG_ERR, "error running subscription query: %s", res.errmsg);
    w_query_result_free(&res);
    return NULL;
  }

  w_log(W_LOG_DBG, "subscription generated %" PRIu32 " results\n",
      res.num_results);

  file_list = w_query_results_to_json(&sub->field_list,
      res.num_results, res.results);
  w_query_result_free(&res);

  if (res.num_results == 0) {
    return NULL;
  }

  response = make_response();

  if (clock_id_string(sub->since.ticks, clockbuf, sizeof(clockbuf))) {
    set_prop(response, "since", json_string_nocheck(clockbuf));
  }
  if (clock_id_string(res.ticks, clockbuf, sizeof(clockbuf))) {
    set_prop(response, "clock", json_string_nocheck(clockbuf));
  }
  sub->since.is_timestamp = false;
  sub->since.ticks = res.ticks;

  set_prop(response, "files", file_list);
  set_prop(response, "root", json_string(root->root_path->buf));
  set_prop(response, "subscription", json_string(sub->name->buf));

  return response;
}
Exemplo n.º 2
0
/* find /root [patterns] */
static void cmd_find(struct watchman_client *client, json_t *args)
{
    w_root_t *root;
    w_query *query;
    char *errmsg = NULL;
    struct w_query_field_list field_list;
    w_query_res res;
    json_t *response;
    json_t *file_list;
    char clockbuf[128];

    /* resolve the root */
    if (json_array_size(args) < 2) {
        send_error_response(client, "not enough arguments for 'find'");
        return;
    }

    root = resolve_root_or_err(client, args, 1, false);
    if (!root) {
        return;
    }

    query = w_query_parse_legacy(root, args, &errmsg, 2, NULL, NULL, NULL);
    if (errmsg) {
        send_error_response(client, "%s", errmsg);
        free(errmsg);
        w_root_delref(root);
        return;
    }

    w_query_legacy_field_list(&field_list);

    if (client->client_mode) {
        query->sync_timeout = 0;
    }

    if (!w_query_execute(query, root, &res, NULL, NULL)) {
        send_error_response(client, "query failed: %s", res.errmsg);
        w_query_result_free(&res);
        w_root_delref(root);
        w_query_delref(query);
        return;
    }

    w_query_delref(query);

    file_list = w_query_results_to_json(&field_list,
                                        res.num_results, res.results);
    w_query_result_free(&res);

    response = make_response();
    if (clock_id_string(res.root_number, res.ticks, clockbuf, sizeof(clockbuf))) {
        set_prop(response, "clock", json_string_nocheck(clockbuf));
    }
    set_prop(response, "files", file_list);

    send_and_dispose_response(client, response);
    w_root_delref(root);
}
Exemplo n.º 3
0
// Renders the current clock id string to the supplied buffer.
// Must be called with the root locked.
static bool current_clock_id_string(w_root_t *root,
    char *buf, size_t bufsize)
{
  return clock_id_string(root->number, root->ticks, buf, bufsize);
}
Exemplo n.º 4
0
json_t *w_match_results_to_json(
    uint32_t num_matches,
    struct watchman_rule_match *matches)
{
  json_t *file_list = json_array_of_size(num_matches);
  uint32_t i;

  if (num_matches) {
    // Build a template for the serializer
    json_t *templ = json_array_of_size(15);
    static const char *field_list[] = {
      "name", "exists", "size", "mode", "uid", "gid",
      "mtime", "ctime", "ino", "dev", "nlink", "new",
      "oclock", "cclock",
    };

    for (i = 0; i < sizeof(field_list)/sizeof(field_list[0]); i++) {
      json_array_append_new(templ, json_string_nocheck(field_list[i]));
    }

    json_array_set_template_new(file_list, templ);
  }

  for (i = 0; i < num_matches; i++) {
    struct watchman_file *file = matches[i].file;
    w_string_t *relname = matches[i].relname;
    char buf[128];

    json_t *record = json_object_of_size(15);

    set_prop(record, "name", json_string_nocheck(relname->buf));
    set_prop(record, "exists", json_boolean(file->exists));
    // Only report stat data if we think this file exists.  If it doesn't,
    // we probably have stale data cached in file->st which is useless to
    // report on.
    if (file->exists) {
      // Note: our JSON library supports 64-bit integers, but this may
      // pose a compatibility issue for others.  We'll see if anyone
      // runs into an issue and deal with it then...
      set_prop(record, "size", json_integer(file->st.st_size));
      set_prop(record, "mode", json_integer(file->st.st_mode));
      set_prop(record, "uid", json_integer(file->st.st_uid));
      set_prop(record, "gid", json_integer(file->st.st_gid));
      set_prop(record, "mtime", json_integer(file->st.st_mtime));
      set_prop(record, "ctime", json_integer(file->st.st_ctime));
      set_prop(record, "ino", json_integer(file->st.st_ino));
      set_prop(record, "dev", json_integer(file->st.st_dev));
      set_prop(record, "nlink", json_integer(file->st.st_nlink));
      set_prop(record, "new", json_boolean(matches[i].is_new));

      if (clock_id_string(matches[i].root_number, file->ctime.ticks, buf,
                          sizeof(buf))) {
        set_prop(record, "cclock", json_string_nocheck(buf));
      }
    }
    if (clock_id_string(matches[i].root_number, file->otime.ticks, buf,
                        sizeof(buf))) {
      set_prop(record, "oclock", json_string_nocheck(buf));
    }

    json_array_append_new(file_list, record);
  }

  return file_list;
}
Exemplo n.º 5
0
static json_t *build_subscription_results(
    struct watchman_client_subscription *sub,
    w_root_t *root)
{
  w_query_res res;
  json_t *response;
  json_t *file_list;
  char clockbuf[128];
  struct w_clockspec *since_spec = sub->query->since_spec;

  if (since_spec && since_spec->tag == w_cs_clock) {
    w_log(W_LOG_DBG, "running subscription rules! since %" PRIu32 "\n",
        since_spec->clock.ticks);
  } else {
    w_log(W_LOG_DBG, "running subscription rules!\n");
  }

  // Subscriptions never need to sync explicitly; we are only dispatched
  // at settle points which are by definition sync'd to the present time
  sub->query->sync_timeout = 0;
  if (!w_query_execute(sub->query, root, &res, subscription_generator, sub)) {
    w_log(W_LOG_ERR, "error running subscription query: %s", res.errmsg);
    w_query_result_free(&res);
    return NULL;
  }

  w_log(W_LOG_DBG, "subscription generated %" PRIu32 " results\n",
      res.num_results);

  if (res.num_results == 0) {
    w_query_result_free(&res);
    return NULL;
  }

  file_list = w_query_results_to_json(&sub->field_list,
      res.num_results, res.results);
  w_query_result_free(&res);

  response = make_response();

  // It is way too much of a hassle to try to recreate the clock value if it's
  // not a relative clock spec, and it's only going to happen on the first run
  // anyway, so just skip doing that entirely.
  if (since_spec && since_spec->tag == w_cs_clock &&
      clock_id_string(since_spec->clock.root_number, since_spec->clock.ticks,
                      clockbuf, sizeof(clockbuf))) {
    set_prop(response, "since", json_string_nocheck(clockbuf));
  }
  if (clock_id_string(res.root_number, res.ticks, clockbuf, sizeof(clockbuf))) {
    set_prop(response, "clock", json_string_nocheck(clockbuf));
  }
  // create a new spec that will be used the next time
  if (since_spec) {
    w_clockspec_free(since_spec);
    since_spec = NULL;
  }
  sub->query->since_spec = w_clockspec_new_clock(res.root_number, res.ticks);

  set_prop(response, "is_fresh_instance", json_boolean(res.is_fresh_instance));
  set_prop(response, "files", file_list);
  set_prop(response, "root", json_string(root->root_path->buf));
  set_prop(response, "subscription", json_string(sub->name->buf));

  return response;
}
Exemplo n.º 6
0
/* query /root {query} */
static void cmd_query(struct watchman_client *client, json_t *args)
{
  w_root_t *root;
  w_query *query;
  json_t *query_spec;
  char *errmsg = NULL;
  w_query_res res;
  json_t *response;
  json_t *file_list, *jfield_list;
  char clockbuf[128];
  struct w_query_field_list field_list;

  if (json_array_size(args) != 3) {
    send_error_response(client, "wrong number of arguments for 'query'");
    return;
  }

  root = resolve_root_or_err(client, args, 1, false);
  if (!root) {
    return;
  }

  query_spec = json_array_get(args, 2);

  jfield_list = json_object_get(query_spec, "fields");
  if (!parse_field_list(jfield_list, &field_list, &errmsg)) {
    send_error_response(client, "invalid field list: %s", errmsg);
    free(errmsg);
    w_root_delref(root);
    return;
  }

  query = w_query_parse(root, query_spec, &errmsg);
  if (!query) {
    send_error_response(client, "failed to parse query: %s", errmsg);
    free(errmsg);
    w_root_delref(root);
    return;
  }

  if (client->client_mode) {
    query->sync_timeout = 0;
  }

  if (!w_query_execute(query, root, &res, NULL, NULL)) {
    send_error_response(client, "query failed: %s", res.errmsg);
    w_query_result_free(&res);
    w_root_delref(root);
    w_query_delref(query);
    return;
  }

  w_query_delref(query);

  file_list = w_query_results_to_json(&field_list,
                res.num_results, res.results);
  w_query_result_free(&res);

  response = make_response();
  if (clock_id_string(res.root_number, res.ticks, clockbuf, sizeof(clockbuf))) {
    set_prop(response, "clock", json_string_nocheck(clockbuf));
  }
  set_prop(response, "is_fresh_instance",
           json_pack("b", res.is_fresh_instance));
  set_prop(response, "files", file_list);

  send_and_dispose_response(client, response);
  w_root_delref(root);
}
Exemplo n.º 7
0
static void spawn_command(w_root_t *root,
  struct watchman_trigger_command *cmd,
  w_query_res *res,
  struct w_clockspec *since_spec)
{
  char **envp = NULL;
  uint32_t i = 0;
  int ret;
  int stdin_fd = -1;
  json_t *args;
  char **argv = NULL;
  uint32_t env_size;
  posix_spawn_file_actions_t actions;
  posix_spawnattr_t attr;
  sigset_t mask;
  long arg_max;
  uint32_t argspace_remaining;
  bool file_overflow = false;
  int result_log_level;
  char clockbuf[128];
  const char *cwd = NULL;

  arg_max = sysconf(_SC_ARG_MAX);

  if (arg_max <= 0) {
    argspace_remaining = UINT_MAX;
  } else {
    argspace_remaining = (uint32_t)arg_max;
  }

  // Allow some misc working overhead
  argspace_remaining -= 32;

  stdin_fd = prepare_stdin(cmd, res);

  // Assumption: that only one thread will be executing on a given
  // cmd instance so that mutation of cmd->envht is safe.
  // This is guaranteed in the current architecture.

  if (cmd->max_files_stdin > 0 && res->num_results > cmd->max_files_stdin) {
    file_overflow = true;
  }

  // It is way too much of a hassle to try to recreate the clock value if it's
  // not a relative clock spec, and it's only going to happen on the first run
  // anyway, so just skip doing that entirely.
  if (since_spec && since_spec->tag == w_cs_clock &&
      clock_id_string(since_spec->clock.root_number, since_spec->clock.ticks,
                      clockbuf, sizeof(clockbuf))) {
    w_envp_set_cstring(cmd->envht, "WATCHMAN_SINCE", clockbuf);
  } else {
    w_envp_unset(cmd->envht, "WATCHMAN_SINCE");
  }

  if (clock_id_string(res->root_number, res->ticks,
        clockbuf, sizeof(clockbuf))) {
    w_envp_set_cstring(cmd->envht, "WATCHMAN_CLOCK", clockbuf);
  } else {
    w_envp_unset(cmd->envht, "WATCHMAN_CLOCK");
  }

  if (cmd->query->relative_root) {
    w_envp_set(cmd->envht, "WATCHMAN_RELATIVE_ROOT", cmd->query->relative_root);
  } else {
    w_envp_unset(cmd->envht, "WATCHMAN_RELATIVE_ROOT");
  }

  // Compute args
  args = json_deep_copy(cmd->command);

  if (cmd->append_files) {
    // Measure how much space the base args take up
    for (i = 0; i < json_array_size(args); i++) {
      const char *ele = json_string_value(json_array_get(args, i));

      argspace_remaining -= strlen(ele) + 1 + sizeof(char*);
    }

    // Dry run with env to compute space
    envp = w_envp_make_from_ht(cmd->envht, &env_size);
    free(envp);
    envp = NULL;
    argspace_remaining -= env_size;

    for (i = 0; i < res->num_results; i++) {
      // also: NUL terminator and entry in argv
      uint32_t size = res->results[i].relname->len + 1 + sizeof(char*);

      if (argspace_remaining < size) {
        file_overflow = true;
        break;
      }
      argspace_remaining -= size;

      json_array_append_new(
        args,
        json_string_nocheck(res->results[i].relname->buf)
      );
    }
  }

  argv = w_argv_copy_from_json(args, 0);
  json_decref(args);
  args = NULL;

  w_envp_set_bool(cmd->envht, "WATCHMAN_FILES_OVERFLOW", file_overflow);

  envp = w_envp_make_from_ht(cmd->envht, &env_size);

  posix_spawnattr_init(&attr);
  sigemptyset(&mask);
  posix_spawnattr_setsigmask(&attr, &mask);
  posix_spawnattr_setflags(&attr,
      POSIX_SPAWN_SETSIGMASK|
#ifdef POSIX_SPAWN_CLOEXEC_DEFAULT
      // Darwin: close everything except what we put in file actions
      POSIX_SPAWN_CLOEXEC_DEFAULT|
#endif
      POSIX_SPAWN_SETPGROUP);

  posix_spawn_file_actions_init(&actions);

  posix_spawn_file_actions_adddup2(&actions, stdin_fd, STDIN_FILENO);
  if (cmd->stdout_name) {
    posix_spawn_file_actions_addopen(&actions, STDOUT_FILENO,
        cmd->stdout_name, cmd->stdout_flags, 0666);
  } else {
    posix_spawn_file_actions_adddup2(&actions, STDOUT_FILENO, STDOUT_FILENO);
  }

  if (cmd->stderr_name) {
    posix_spawn_file_actions_addopen(&actions, STDERR_FILENO,
        cmd->stderr_name, cmd->stderr_flags, 0666);
  } else {
    posix_spawn_file_actions_adddup2(&actions, STDERR_FILENO, STDERR_FILENO);
  }

  pthread_mutex_lock(&spawn_lock);
  if (cmd->query->relative_root) {
    ignore_result(chdir(cmd->query->relative_root->buf));
  } else {
    ignore_result(chdir(root->root_path->buf));
  }

  json_unpack(cmd->definition, "{s:s}", "chdir", &cwd);
  if (cwd) {
    ignore_result(chdir(cwd));
  }

  ret = posix_spawnp(&cmd->current_proc, argv[0], &actions, &attr, argv, envp);
  if (ret == 0) {
    w_root_addref(root);
    insert_running_pid(cmd->current_proc, root);
  } else {
    // On Darwin (at least), posix_spawn can fail but will still populate the
    // pid.  Since we use the pid to gate future spawns, we need to ensure
    // that we clear out the pid on failure, otherwise the trigger would be
    // effectively disabled for the rest of the watch lifetime
    cmd->current_proc = 0;
  }
  ignore_result(chdir("/"));
  pthread_mutex_unlock(&spawn_lock);

  // If failed, we want to make sure we log enough info to figure out why
  result_log_level = res == 0 ? W_LOG_DBG : W_LOG_ERR;

  w_log(result_log_level, "posix_spawnp:\n");
  for (i = 0; argv[i]; i++) {
    w_log(result_log_level, "argv[%d] %s\n", i, argv[i]);
  }
  for (i = 0; envp[i]; i++) {
    w_log(result_log_level, "envp[%d] %s\n", i, envp[i]);
  }

  w_log(result_log_level, "trigger %.*s:%s pid=%d ret=%d %s\n",
      (int)root->root_path->len,
      root->root_path->buf,
      cmd->triggername->buf, (int)cmd->current_proc, ret, strerror(ret));

  free(argv);
  free(envp);

  posix_spawnattr_destroy(&attr);
  posix_spawn_file_actions_destroy(&actions);

  if (stdin_fd != -1) {
    close(stdin_fd);
  }
}
Exemplo n.º 8
0
/* since /root <timestamp> [patterns] */
static void cmd_since(struct watchman_client *client, json_t *args)
{
  const char *clockspec;
  w_root_t *root;
  w_query *query;
  char *errmsg = NULL;
  struct w_query_field_list field_list;
  w_query_res res;
  json_t *response, *clock_ele;
  json_t *file_list;
  char clockbuf[128];

  /* resolve the root */
  if (json_array_size(args) < 3) {
    send_error_response(client, "not enough arguments for 'since'");
    return;
  }

  root = resolve_root_or_err(client, args, 1, false);
  if (!root) {
    return;
  }

  clock_ele = json_array_get(args, 2);
  clockspec = json_string_value(clock_ele);
  if (!clockspec) {
    send_error_response(client,
        "expected argument 2 to be a valid clockspec");
    w_root_delref(root);
    return;
  }

  query = w_query_parse_legacy(args, &errmsg, 3, NULL, clockspec, NULL);
  if (errmsg) {
    send_error_response(client, "%s", errmsg);
    free(errmsg);
    w_root_delref(root);
    return;
  }

  w_query_legacy_field_list(&field_list);

  if (!w_query_execute(query, root, &res, NULL, NULL)) {
    send_error_response(client, "query failed: %s", res.errmsg);
    w_query_result_free(&res);
    w_root_delref(root);
    w_query_delref(query);
    return;
  }

  w_query_delref(query);

  file_list = w_query_results_to_json(&field_list,
                res.num_results, res.results);
  w_query_result_free(&res);

  response = make_response();
  if (clock_id_string(res.root_number, res.ticks, clockbuf, sizeof(clockbuf))) {
    set_prop(response, "clock", json_string_nocheck(clockbuf));
  }
  set_prop(response, "is_fresh_instance",
           json_pack("b", res.is_fresh_instance));
  set_prop(response, "files", file_list);

  send_and_dispose_response(client, response);
  w_root_delref(root);
}
Exemplo n.º 9
0
static json_t *build_subscription_results(
    struct watchman_client_subscription *sub,
    w_root_t *root)
{
  w_query_res res;
  json_t *response;
  json_t *file_list;
  char clockbuf[128];
  struct w_clockspec *since_spec = sub->query->since_spec;

  if (since_spec && since_spec->tag == w_cs_clock) {
    w_log(W_LOG_DBG, "running subscription %s rules since %" PRIu32 "\n",
        sub->name->buf, since_spec->clock.ticks);
  } else {
    w_log(W_LOG_DBG, "running subscription %s rules (no since)\n",
        sub->name->buf);
  }

  // Subscriptions never need to sync explicitly; we are only dispatched
  // at settle points which are by definition sync'd to the present time
  sub->query->sync_timeout = 0;
  // We're called by the io thread, so there's little chance that the root
  // could be legitimately blocked by something else.  That means that we
  // can use a short lock_timeout
  sub->query->lock_timeout =
      (uint32_t)cfg_get_int(root, "subscription_lock_timeout_ms", 100);
  if (!w_query_execute(sub->query, root, &res, subscription_generator, sub)) {
    w_log(W_LOG_ERR, "error running subscription %s query: %s",
        sub->name->buf, res.errmsg);
    w_query_result_free(&res);
    return NULL;
  }

  w_log(W_LOG_DBG, "subscription %s generated %" PRIu32 " results\n",
      sub->name->buf, res.num_results);

  if (res.num_results == 0) {
    update_subscription_ticks(sub, &res);
    w_query_result_free(&res);
    return NULL;
  }

  file_list = w_query_results_to_json(&sub->field_list,
      res.num_results, res.results);
  w_query_result_free(&res);

  response = make_response();

  // It is way too much of a hassle to try to recreate the clock value if it's
  // not a relative clock spec, and it's only going to happen on the first run
  // anyway, so just skip doing that entirely.
  if (since_spec && since_spec->tag == w_cs_clock &&
      clock_id_string(since_spec->clock.root_number, since_spec->clock.ticks,
                      clockbuf, sizeof(clockbuf))) {
    set_unicode_prop(response, "since", clockbuf);
  }
  if (clock_id_string(res.root_number, res.ticks, clockbuf, sizeof(clockbuf))) {
    set_unicode_prop(response, "clock", clockbuf);
  }
  update_subscription_ticks(sub, &res);

  set_prop(response, "is_fresh_instance", json_boolean(res.is_fresh_instance));
  set_prop(response, "files", file_list);
  set_prop(response, "root", w_string_to_json(root->root_path));
  set_prop(response, "subscription", w_string_to_json(sub->name));
  set_prop(response, "unilateral", json_true());

  return response;
}