/* 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); }
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; }
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; }
/* 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); }
static int prepare_stdin( struct watchman_trigger_command *cmd, w_query_res *res) { uint32_t n_files; char stdin_file_name[WATCHMAN_NAME_MAX]; int stdin_fd = -1; if (cmd->stdin_style == input_dev_null) { return open("/dev/null", O_RDONLY|O_CLOEXEC); } n_files = res->num_results; if (cmd->max_files_stdin > 0) { n_files = MIN(cmd->max_files_stdin, n_files); } /* prepare the input stream for the child process */ snprintf(stdin_file_name, sizeof(stdin_file_name), "%s/wmanXXXXXX", watchman_tmp_dir); stdin_fd = w_mkstemp(stdin_file_name); if (stdin_fd == -1) { w_log(W_LOG_ERR, "unable to create a temporary file: %s\n", strerror(errno)); return -1; } /* unlink the file, we don't need it in the filesystem; * we'll pass the fd on to the child as stdin */ unlink(stdin_file_name); switch (cmd->stdin_style) { case input_json: { w_jbuffer_t buffer; json_t *file_list; if (!w_json_buffer_init(&buffer)) { w_log(W_LOG_ERR, "failed to init json buffer\n"); close(stdin_fd); return -1; } file_list = w_query_results_to_json(&cmd->field_list, n_files, res->results); w_json_buffer_write(&buffer, stdin_fd, file_list, 0); w_json_buffer_free(&buffer); json_decref(file_list); break; } case input_name_list: { struct iovec iov[2]; uint32_t i; iov[1].iov_base = "\n"; iov[1].iov_len = 1; for (i = 0; i < n_files; i++) { iov[0].iov_base = (void*)res->results[i].relname->buf; iov[0].iov_len = res->results[i].relname->len; if (writev(stdin_fd, iov, 2) != (ssize_t)iov[0].iov_len + 1) { w_log(W_LOG_ERR, "write failure while producing trigger stdin: %s\n", strerror(errno)); close(stdin_fd); return -1; } } break; } case input_dev_null: // already handled above break; } lseek(stdin_fd, 0, SEEK_SET); return stdin_fd; }
static w_stm_t prepare_stdin( struct watchman_trigger_command *cmd, w_query_res *res) { uint32_t n_files; char stdin_file_name[WATCHMAN_NAME_MAX]; w_stm_t stdin_file = NULL; if (cmd->stdin_style == input_dev_null) { return w_stm_open("/dev/null", O_RDONLY|O_CLOEXEC); } n_files = res->num_results; if (cmd->max_files_stdin > 0) { n_files = MIN(cmd->max_files_stdin, n_files); } /* prepare the input stream for the child process */ snprintf(stdin_file_name, sizeof(stdin_file_name), "%s%cwmanXXXXXX", watchman_tmp_dir, WATCHMAN_DIR_SEP); stdin_file = w_mkstemp(stdin_file_name); if (!stdin_file) { w_log(W_LOG_ERR, "unable to create a temporary file: %s %s\n", stdin_file_name, strerror(errno)); return NULL; } /* unlink the file, we don't need it in the filesystem; * we'll pass the fd on to the child as stdin */ unlink(stdin_file_name); // FIXME: windows path translation switch (cmd->stdin_style) { case input_json: { w_jbuffer_t buffer; json_t *file_list; if (!w_json_buffer_init(&buffer)) { w_log(W_LOG_ERR, "failed to init json buffer\n"); w_stm_close(stdin_file); return NULL; } file_list = w_query_results_to_json(&cmd->field_list, n_files, res->results); w_log(W_LOG_ERR, "input_json: sending json object to stm\n"); if (!w_json_buffer_write(&buffer, stdin_file, file_list, 0)) { w_log(W_LOG_ERR, "input_json: failed to write json data to stream: %s\n", strerror(errno)); w_stm_close(stdin_file); return NULL; } w_json_buffer_free(&buffer); json_decref(file_list); break; } case input_name_list: { uint32_t i; for (i = 0; i < n_files; i++) { if (w_stm_write(stdin_file, res->results[i].relname->buf, res->results[i].relname->len) != (int)res->results[i].relname->len || w_stm_write(stdin_file, "\n", 1) != 1) { w_log(W_LOG_ERR, "write failure while producing trigger stdin: %s\n", strerror(errno)); w_stm_close(stdin_file); return NULL; } } break; } case input_dev_null: // already handled above break; } w_stm_rewind(stdin_file); return stdin_file; }
/* 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); }
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; }