示例#1
0
文件: debug.c 项目: niclasr/watchman
static void cmd_debug_recrawl(struct watchman_client *client, json_t *args)
{
    w_root_t *root;
    json_t *resp;

    /* resolve the root */
    if (json_array_size(args) != 2) {
        send_error_response(client,
                            "wrong number of arguments for 'debug-recrawl'");
        return;
    }

    root = resolve_root_or_err(client, args, 1, false);

    if (!root) {
        return;
    }

    resp = make_response();

    w_root_lock(root);
    w_root_schedule_recrawl(root, "debug-recrawl");
    w_root_unlock(root);

    set_prop(resp, "recrawl", json_true());
    send_and_dispose_response(client, resp);
    w_root_delref(root);
}
示例#2
0
文件: watch.c 项目: Kingside/watchman
/* watch /root */
static void cmd_watch(struct watchman_client *client, json_t *args)
{
  w_root_t *root;
  json_t *resp;

  /* resolve the root */
  if (json_array_size(args) != 2) {
    send_error_response(client, "wrong number of arguments to 'watch'");
    return;
  }

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

  resp = make_response();

  w_root_lock(root);
  if (root->failure_reason) {
    set_prop(resp, "error", json_string_nocheck(root->failure_reason->buf));
  } else if (root->cancelled) {
    set_prop(resp, "error", json_string_nocheck("root was cancelled"));
  } else {
    set_prop(resp, "watch", json_string_nocheck(root->root_path->buf));
  }
  send_and_dispose_response(client, resp);
  w_root_unlock(root);
  w_root_delref(root);
}
示例#3
0
文件: debug.c 项目: niclasr/watchman
/* debug-ageout */
static void cmd_debug_ageout(struct watchman_client *client, json_t *args)
{
    w_root_t *root;
    json_t *resp;
    int min_age;

    /* resolve the root */
    if (json_array_size(args) != 3) {
        send_error_response(client,
                            "wrong number of arguments for 'debug-ageout'");
        return;
    }

    root = resolve_root_or_err(client, args, 1, false);

    if (!root) {
        return;
    }

    min_age = json_integer_value(json_array_get(args, 2));

    resp = make_response();

    w_root_lock(root);
    w_root_perform_age_out(root, min_age);
    w_root_unlock(root);

    set_prop(resp, "ageout", json_true());
    send_and_dispose_response(client, resp);
    w_root_delref(root);
}
示例#4
0
文件: debug.c 项目: niclasr/watchman
static void cmd_debug_show_cursors(struct watchman_client *client, json_t *args)
{
    w_root_t *root;
    json_t *resp, *cursors;
    w_ht_iter_t i;

    /* resolve the root */
    if (json_array_size(args) != 2) {
        send_error_response(client,
                            "wrong number of arguments for 'debug-show-cursors'");
        return;
    }

    root = resolve_root_or_err(client, args, 1, false);

    if (!root) {
        return;
    }

    resp = make_response();

    w_root_lock(root);
    cursors = json_object_of_size(w_ht_size(root->cursors));
    if (w_ht_first(root->cursors, &i)) do {
            w_string_t *name = w_ht_val_ptr(i.key);
            set_prop(cursors, name->buf, json_integer(i.value));
        } while (w_ht_next(root->cursors, &i));
    w_root_unlock(root);

    set_prop(resp, "cursors", cursors);
    send_and_dispose_response(client, resp);
    w_root_delref(root);
}
示例#5
0
/* trigger /root triggername [watch patterns] -- cmd to run
 * Sets up a trigger so that we can execute a command when a change
 * is detected */
void cmd_trigger(struct watchman_client *client, json_t *args)
{
  w_root_t *root;
  struct watchman_trigger_command *cmd;
  json_t *resp;
  json_t *trig;
  char *errmsg = NULL;

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

  if (json_array_size(args) < 3) {
    send_error_response(client, "not enough arguments");
    goto done;
  }

  trig = json_array_get(args, 2);
  if (json_is_string(trig)) {
    trig = build_legacy_trigger(client, args);
    if (!trig) {
      goto done;
    }
  } else {
    // Add a ref so that we don't need to conditionally decref later
    // for the legacy case later
    json_incref(trig);
  }

  cmd = w_build_trigger_from_def(root, trig, &errmsg);
  json_decref(trig);

  if (!cmd) {
    send_error_response(client, "%s", errmsg);
    goto done;
  }

  w_root_lock(root);
  w_ht_replace(root->commands, w_ht_ptr_val(cmd->triggername),
      w_ht_ptr_val(cmd));
  w_root_unlock(root);

  w_state_save();

  resp = make_response();
  set_prop(resp, "triggerid", json_string_nocheck(cmd->triggername->buf));
  send_and_dispose_response(client, resp);

done:
  if (errmsg) {
    free(errmsg);
  }
  w_root_delref(root);
}
示例#6
0
void w_mark_dead(pid_t pid)
{
  w_root_t *root = NULL;
  w_ht_iter_t iter;

  pthread_mutex_lock(&spawn_lock);
  root = lookup_running_pid(pid);
  if (!root) {
    pthread_mutex_unlock(&spawn_lock);
    return;
  }
  delete_running_pid(pid);
  pthread_mutex_unlock(&spawn_lock);

  w_log(W_LOG_DBG, "mark_dead: %.*s child pid %d\n",
      root->root_path->len, root->root_path->buf, (int)pid);

  /* now walk the cmds and try to find our match */
  w_root_lock(root);

  /* walk the list of triggers, and run their rules */
  if (w_ht_first(root->commands, &iter)) do {
    struct watchman_trigger_command *cmd;

    cmd = w_ht_val_ptr(iter.value);
    if (cmd->current_proc != pid) {
      w_log(W_LOG_DBG, "mark_dead: is [%.*s] %d == %d\n",
          cmd->triggername->len, cmd->triggername->buf,
          (int)cmd->current_proc, (int)pid);
      continue;
    }

    /* first mark the process as dead */
    cmd->current_proc = 0;
    if (root->cancelled) {
      w_log(W_LOG_DBG, "mark_dead: root was cancelled\n");
      break;
    }

    w_assess_trigger(root, cmd);
    break;
  } while (w_ht_next(root->commands, &iter));

  w_root_unlock(root);
  w_root_delref(root);
}
示例#7
0
/* trigger-del /root triggername
 * Delete a trigger from a root
 */
static void cmd_trigger_delete(struct watchman_client *client, json_t *args)
{
    w_root_t *root;
    json_t *resp;
    json_t *jname;
    w_string_t *tname;
    bool res;

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

    if (json_array_size(args) != 3) {
        send_error_response(client, "wrong number of arguments");
        w_root_delref(root);
        return;
    }
    jname = json_array_get(args, 2);
    if (!json_is_string(jname)) {
        send_error_response(client, "expected 2nd parameter to be trigger name");
        w_root_delref(root);
        return;
    }
    tname = json_to_w_string_incref(jname);

    w_root_lock(root, "trigger-del");
    res = w_ht_del(root->commands, w_ht_ptr_val(tname));
    w_root_unlock(root);

    if (res) {
        w_state_save();
    }

    w_string_delref(tname);

    resp = make_response();
    set_prop(resp, "deleted", json_boolean(res));
    json_incref(jname);
    set_prop(resp, "trigger", jname);
    send_and_dispose_response(client, resp);
    w_root_delref(root);
}
示例#8
0
/* trigger-list /root
 * Displays a list of registered triggers for a given root
 */
void cmd_trigger_list(struct watchman_client *client, json_t *args)
{
  w_root_t *root;
  json_t *resp;
  json_t *arr;

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

  resp = make_response();
  w_root_lock(root);
  arr = w_root_trigger_list_to_json(root);
  w_root_unlock(root);

  set_prop(resp, "triggers", arr);
  send_and_dispose_response(client, resp);
  w_root_delref(root);
}
示例#9
0
文件: trigger.c 项目: baeeq/watchman
/* trigger-del /root triggername
 * Delete a trigger from a root
 */
void cmd_trigger_delete(struct watchman_client *client, json_t *args)
{
  w_root_t *root;
  json_t *resp;
  const char *name;
  w_string_t *tname;
  bool res;

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

  if (json_array_size(args) != 3) {
    send_error_response(client, "wrong number of arguments");
    w_root_delref(root);
    return;
  }
  name = json_string_value(json_array_get(args, 2));
  if (!name) {
    send_error_response(client, "expected 2nd parameter to be trigger name");
    w_root_delref(root);
    return;
  }
  tname = w_string_new(name);

  w_root_lock(root);
  res = w_ht_del(root->commands, (w_ht_val_t)tname);
  w_root_unlock(root);

  w_state_save();

  w_string_delref(tname);

  resp = make_response();
  set_prop(resp, "deleted", json_boolean(res));
  set_prop(resp, "trigger", json_string_nocheck(name));
  send_and_dispose_response(client, resp);
  w_root_delref(root);
}
示例#10
0
static void cmd_get_config(struct watchman_client *client, json_t *args)
{
  w_root_t *root;
  json_t *resp;
  json_t *config;

  if (json_array_size(args) != 2) {
    send_error_response(client, "wrong number of arguments for 'get-config'");
    return;
  }

  root = resolve_root_or_err(client, args, 1, false);

  if (!root) {
    return;
  }

  resp = make_response();

  w_root_lock(root);
  {
    config = root->config_file;
    if (config) {
      // set_prop will claim this ref
      json_incref(config);
    }
  }
  w_root_unlock(root);

  if (!config) {
    // set_prop will own this
    config = json_object();
  }

  json_incref(root->config_file);
  set_prop(resp, "config", config);
  send_and_dispose_response(client, resp);
  w_root_delref(root);
}
示例#11
0
文件: watch.c 项目: Kingside/watchman
/* clock /root
 * Returns the current clock value for a watched root
 */
static void cmd_clock(struct watchman_client *client, json_t *args)
{
  w_root_t *root;
  json_t *resp;

  /* resolve the root */
  if (json_array_size(args) != 2) {
    send_error_response(client, "wrong number of arguments to 'clock'");
    return;
  }

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

  resp = make_response();
  w_root_lock(root);
  annotate_with_clock(root, resp);
  w_root_unlock(root);

  send_and_dispose_response(client, resp);
  w_root_delref(root);
}
示例#12
0
static void *readchanges_thread(void *arg) {
  w_root_t *root = arg;
  struct winwatch_root_state *state = root->watch;
  DWORD size = WATCHMAN_BATCH_LIMIT * (sizeof(FILE_NOTIFY_INFORMATION) + 512);
  char *buf;
  DWORD err, filter;
  OVERLAPPED olap;
  BOOL initiate_read = true;
  HANDLE handles[2] = { state->olap, state->ping };
  DWORD bytes;

  w_set_thread_name("readchange %.*s",
      root->root_path->len, root->root_path->buf);

  // Block until winmatch_root_st is waiting for our initialization
  pthread_mutex_lock(&state->mtx);

  filter = FILE_NOTIFY_CHANGE_FILE_NAME|FILE_NOTIFY_CHANGE_DIR_NAME|
    FILE_NOTIFY_CHANGE_ATTRIBUTES|FILE_NOTIFY_CHANGE_SIZE|
    FILE_NOTIFY_CHANGE_LAST_WRITE;

  memset(&olap, 0, sizeof(olap));
  olap.hEvent = state->olap;

  buf = malloc(size);
  if (!buf) {
    w_log(W_LOG_ERR, "failed to allocate %u bytes for dirchanges buf\n", size);
    goto out;
  }

  if (!ReadDirectoryChangesW(state->dir_handle, buf, size,
        TRUE, filter, NULL, &olap, NULL)) {
    err = GetLastError();
    w_log(W_LOG_ERR,
        "ReadDirectoryChangesW: failed, cancel watch. %s\n",
        win32_strerror(err));
    w_root_lock(root);
    w_root_cancel(root);
    w_root_unlock(root);
    goto out;
  }
  // Signal that we are done with init.  We MUST do this AFTER our first
  // successful ReadDirectoryChangesW, otherwise there is a race condition
  // where we'll miss observing the cookie for a query that comes in
  // after we've crawled but before the watch is established.
  w_log(W_LOG_DBG, "ReadDirectoryChangesW signalling as init done");
  pthread_cond_signal(&state->cond);
  pthread_mutex_unlock(&state->mtx);
  initiate_read = false;

  // The state->mutex must not be held when we enter the loop
  while (!root->cancelled) {
    if (initiate_read) {
      if (!ReadDirectoryChangesW(state->dir_handle,
            buf, size, TRUE, filter, NULL, &olap, NULL)) {
        err = GetLastError();
        w_log(W_LOG_ERR,
            "ReadDirectoryChangesW: failed, cancel watch. %s\n",
            win32_strerror(err));
        w_root_lock(root);
        w_root_cancel(root);
        w_root_unlock(root);
        break;
      } else {
        initiate_read = false;
      }
    }

    w_log(W_LOG_DBG, "waiting for change notifications");
    DWORD status = WaitForMultipleObjects(2, handles, FALSE, INFINITE);

    if (status == WAIT_OBJECT_0) {
      bytes = 0;
      if (!GetOverlappedResult(state->dir_handle, &olap,
            &bytes, FALSE)) {
        err = GetLastError();
        w_log(W_LOG_ERR, "overlapped ReadDirectoryChangesW(%s): 0x%x %s\n",
            root->root_path->buf,
            err, win32_strerror(err));

        if (err == ERROR_INVALID_PARAMETER && size > NETWORK_BUF_SIZE) {
          // May be a network buffer related size issue; the docs say that
          // we can hit this when watching a UNC path. Let's downsize and
          // retry the read just one time
          w_log(W_LOG_ERR, "retrying watch for possible network location %s "
              "with smaller buffer\n", root->root_path->buf);
          size = NETWORK_BUF_SIZE;
          initiate_read = true;
          continue;
        }

        if (err == ERROR_NOTIFY_ENUM_DIR) {
          w_root_schedule_recrawl(root, "ERROR_NOTIFY_ENUM_DIR");
        } else {
          w_log(W_LOG_ERR, "Cancelling watch for %s\n",
              root->root_path->buf);
          w_root_lock(root);
          w_root_cancel(root);
          w_root_unlock(root);
          break;
        }
      } else {
        PFILE_NOTIFY_INFORMATION not = (PFILE_NOTIFY_INFORMATION)buf;
        struct winwatch_changed_item *head = NULL, *tail = NULL;
        while (true) {
          struct winwatch_changed_item *item;
          DWORD n_chars;
          w_string_t *name, *full;

          // FileNameLength is in BYTES, but FileName is WCHAR
          n_chars = not->FileNameLength / sizeof(not->FileName[0]);
          name = w_string_new_wchar(not->FileName, n_chars);

          full = w_string_path_cat(root->root_path, name);
          w_string_delref(name);

          if (w_is_ignored(root, full->buf, full->len)) {
            w_string_delref(full);
          } else {
            item = calloc(1, sizeof(*item));
            item->name = full;

            if (tail) {
              tail->next = item;
            } else {
              head = item;
            }
            tail = item;
          }

          // Advance to next item
          if (not->NextEntryOffset == 0) {
            break;
          }
          not = (PFILE_NOTIFY_INFORMATION)(not->NextEntryOffset + (char*)not);
        }

        if (tail) {
          pthread_mutex_lock(&state->mtx);
          if (state->tail) {
            state->tail->next = head;
          } else {
            state->head = head;
          }
          state->tail = tail;
          pthread_mutex_unlock(&state->mtx);
          pthread_cond_signal(&state->cond);
        }
        ResetEvent(state->olap);
        initiate_read = true;
      }
    } else if (status == WAIT_OBJECT_0 + 1) {
      w_log(W_LOG_ERR, "signalled\n");
      break;
    } else {
      w_log(W_LOG_ERR, "impossible wait status=%d\n",
          status);
      break;
    }
  }

  pthread_mutex_lock(&state->mtx);
out:
  // Signal to winwatch_root_start that we're done initializing in
  // the failure path.  We'll also do this after we've completed
  // the run loop in the success path; it's a spurious wakeup but
  // harmless and saves us from adding and setting a control flag
  // in each of the failure `goto` statements. winwatch_root_dtor
  // will `pthread_join` us before `state` is freed.
  pthread_cond_signal(&state->cond);
  pthread_mutex_unlock(&state->mtx);

  if (buf) {
    free(buf);
  }
  w_log(W_LOG_DBG, "done\n");
  w_root_delref(root);
  return NULL;
}
示例#13
0
/* trigger /root triggername [watch patterns] -- cmd to run
 * Sets up a trigger so that we can execute a command when a change
 * is detected */
static void cmd_trigger(struct watchman_client *client, json_t *args)
{
  w_root_t *root;
  struct watchman_trigger_command *cmd, *old;
  json_t *resp;
  json_t *trig;
  char *errmsg = NULL;
  bool need_save = true;

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

  if (json_array_size(args) < 3) {
    send_error_response(client, "not enough arguments");
    goto done;
  }

  trig = json_array_get(args, 2);
  if (json_is_string(trig)) {
    trig = build_legacy_trigger(root, client, args);
    if (!trig) {
      goto done;
    }
  } else {
    // Add a ref so that we don't need to conditionally decref later
    // for the legacy case later
    json_incref(trig);
  }

  cmd = w_build_trigger_from_def(root, trig, &errmsg);
  json_decref(trig);

  if (!cmd) {
    send_error_response(client, "%s", errmsg);
    goto done;
  }

  resp = make_response();
  set_prop(resp, "triggerid", json_string_nocheck(cmd->triggername->buf));

  w_root_lock(root);

  old = w_ht_val_ptr(w_ht_get(root->commands,
          w_ht_ptr_val(cmd->triggername)));
  if (old && json_equal(cmd->definition, old->definition)) {
    // Same definition: we don't and shouldn't touch things, so that we
    // preserve the associated trigger clock and don't cause the trigger
    // to re-run immediately
    set_prop(resp, "disposition", json_string_nocheck("already_defined"));
    w_trigger_command_free(cmd);
    cmd = NULL;
    need_save = false;
  } else {
    set_prop(resp, "disposition", json_string_nocheck(
          old ? "replaced" : "created"));
    w_ht_replace(root->commands, w_ht_ptr_val(cmd->triggername),
        w_ht_ptr_val(cmd));
    // Force the trigger to be eligible to run now
    root->ticks++;
    root->pending_trigger_tick = root->ticks;
  }
  w_root_unlock(root);

  if (need_save) {
    w_state_save();
  }

  send_and_dispose_response(client, resp);

done:
  if (errmsg) {
    free(errmsg);
  }
  w_root_delref(root);
}
示例#14
0
文件: listener.c 项目: baeeq/watchman
// may attempt to lock the root!
bool w_parse_clockspec(w_root_t *root,
    json_t *value,
    struct w_clockspec_query *since,
    bool allow_cursor)
{
  const char *str;
  int pid;

  if (json_is_integer(value)) {
    since->is_timestamp = true;
    since->tv.tv_usec = 0;
    since->tv.tv_sec = json_integer_value(value);
    return true;
  }

  str = json_string_value(value);
  if (!str) {
    return false;
  }

  if (allow_cursor && root && str[0] == 'n' && str[1] == ':') {
    w_string_t *name = w_string_new(str);

    since->is_timestamp = false;
    w_root_lock(root);

    // If we've never seen it before, ticks will be set to 0
    // which is exactly what we want here.
    since->ticks = (uint32_t)w_ht_get(root->cursors, (w_ht_val_t)name);

    // Bump the tick value and record it against the cursor.
    // We need to bump the tick value so that repeated queries
    // when nothing has changed in the filesystem won't continue
    // to return the same set of files; we only want the first
    // of these to return the files and the rest to return nothing
    // until something subsequently changes
    w_ht_replace(root->cursors, (w_ht_val_t)name, ++root->ticks);

    w_log(W_LOG_DBG, "resolved cursor %s -> %" PRIu32 "\n",
        str, since->ticks);

    w_string_delref(name);

    w_root_unlock(root);
    return true;
  }

  if (sscanf(str, "c:%d:%" PRIu32, &pid, &since->ticks) == 2) {
    since->is_timestamp = false;
    if (pid == getpid()) {
      if (root && since->ticks == root->ticks) {
        /* Force ticks to increment.  This avoids returning and querying the
         * same tick value over and over when no files have changed in the
         * meantime */
        w_root_lock(root);
        root->ticks++;
        w_root_unlock(root);
      }
      return true;
    }
    // If the pid doesn't match, they asked a different
    // incarnation of the server, so we treat them as having
    // never spoken to us before
    since->ticks = 0;
    return true;
  }

  return false;
}
示例#15
0
bool w_query_execute(
    w_query *query,
    w_root_t *root,
    w_query_res *res,
    w_query_generator generator,
    void *gendata)
{
  struct w_query_ctx ctx;

  memset(&ctx, 0, sizeof(ctx));
  ctx.query = query;
  ctx.root = root;

  memset(res, 0, sizeof(*res));

  if (query->sync_timeout && !w_root_sync_to_now(root, query->sync_timeout)) {
    ignore_result(asprintf(&res->errmsg, "synchronization failed: %s\n",
        strerror(errno)));
    return false;
  }

  /* The first stage of execution is generation.
   * We generate a series of file inputs to pass to
   * the query executor.
   *
   * We evaluate each of the generators one after the
   * other.  If multiple generators are used, it is
   * possible and expected that the same file name
   * will be evaluated multiple times if those generators
   * both emit the same file.
   */

  // Lock the root and begin generation
  w_root_lock(root);
  res->root_number = root->number;
  res->ticks = root->ticks;

  // Evaluate the cursor for this root
  w_clockspec_eval(root, query->since_spec, &ctx.since);

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

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

    generator(query, root, &ctx, gendata);
  }

  w_root_unlock(root);

  if (ctx.wholename) {
    w_string_delref(ctx.wholename);
  }
  res->results = ctx.results;
  res->num_results = ctx.num_results;

  return true;
}
示例#16
0
文件: trigger.c 项目: baeeq/watchman
/* trigger /root triggername [watch patterns] -- cmd to run
 * Sets up a trigger so that we can execute a command when a change
 * is detected */
void cmd_trigger(struct watchman_client *client, json_t *args)
{
  struct watchman_rule *rules;
  w_root_t *root;
  uint32_t next_arg = 0;
  struct watchman_trigger_command *cmd;
  json_t *resp;
  const char *name;
  char buf[128];

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

  if (json_array_size(args) < 2) {
    send_error_response(client, "not enough arguments");
    goto done;
  }
  name = json_string_value(json_array_get(args, 2));
  if (!name) {
    send_error_response(client, "expected 2nd parameter to be trigger name");
    goto done;
  }

  if (!parse_watch_params(3, args, &rules, &next_arg, buf, sizeof(buf))) {
    send_error_response(client, "invalid rule spec: %s", buf);
    goto done;
  }

  if (next_arg >= json_array_size(args)) {
    send_error_response(client, "no command was specified");
    goto done;
  }

  cmd = calloc(1, sizeof(*cmd));
  if (!cmd) {
    send_error_response(client, "no memory!");
    goto done;
  }

  cmd->rules = rules;
  cmd->argc = json_array_size(args) - next_arg;
  cmd->argv = w_argv_copy_from_json(args, next_arg);
  if (!cmd->argv) {
    free(cmd);
    send_error_response(client, "unable to build argv array");
    goto done;
  }

  cmd->triggername = w_string_new(name);
  w_root_lock(root);
  w_ht_replace(root->commands, (w_ht_val_t)cmd->triggername, (w_ht_val_t)cmd);
  w_root_unlock(root);

  w_state_save();

  resp = make_response();
  set_prop(resp, "triggerid", json_string_nocheck(name));
  send_and_dispose_response(client, resp);
done:
  w_root_delref(root);
}