void send_error_response(struct watchman_client *client, const char *fmt, ...) { char buf[WATCHMAN_NAME_MAX]; va_list ap; json_t *resp = make_response(); json_t *errstr; va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); errstr = typed_string_to_json(buf, W_STRING_MIXED); set_prop(resp, "error", errstr); json_incref(errstr); w_perf_add_meta(&client->perf_sample, "error", errstr); if (client->current_command) { char *command = NULL; command = json_dumps(client->current_command, 0); w_log(W_LOG_ERR, "send_error_response: %s failed: %s\n", command, buf); free(command); } else { w_log(W_LOG_ERR, "send_error_response: %s\n", buf); } send_and_dispose_response(client, resp); }
bool dispatch_command(struct watchman_client *client, json_t *args, int mode) { struct watchman_command_handler_def *def; char *errmsg = NULL; bool result = false; char sample_name[128]; // Stash a reference to the current command to make it easier to log // the command context in some of the error paths client->current_command = args; json_incref(client->current_command); def = lookup(args, &errmsg, mode); if (!def) { send_error_response(client, "%s", errmsg); goto done; } if (poisoned_reason && (def->flags & CMD_POISON_IMMUNE) == 0) { send_error_response(client, "%s", poisoned_reason); goto done; } if (!client->client_is_owner && (def->flags & CMD_ALLOW_ANY_USER) == 0) { send_error_response(client, "you must be the process owner to execute '%s'", def->name); return false; } w_log(W_LOG_DBG, "dispatch_command: %s\n", def->name); snprintf(sample_name, sizeof(sample_name), "dispatch_command:%s", def->name); w_perf_start(&client->perf_sample, sample_name); w_perf_set_wall_time_thresh( &client->perf_sample, cfg_get_double(NULL, "slow_command_log_threshold_seconds", 1.0)); result = true; def->func(client, args); if (w_perf_finish(&client->perf_sample)) { json_incref(args); w_perf_add_meta(&client->perf_sample, "args", args); w_perf_log(&client->perf_sample); } else { w_log(W_LOG_DBG, "dispatch_command: %s (completed)\n", def->name); } done: free(errmsg); json_decref(client->current_command); client->current_command = NULL; w_perf_destroy(&client->perf_sample); return result; }
void w_perf_add_root_meta(w_perf_t *perf, w_root_t *root) { // Note: if the root lock isn't held, we may read inaccurate numbers for // some of these properties. We're ok with that, and don't want to force // the root lock to be re-acquired just for this. // The funky comments at the end of the line force clang-format to keep the // elements on lines of their own w_perf_add_meta(perf, "root", json_pack("{s:o, s:i, s:i, s:i, s:b, s:u}", // "path", w_string_to_json(root->root_path), // "recrawl_count", root->recrawl_count, // "number", root->number, // "ticks", root->ticks, // "case_sensitive", root->case_sensitive, // "watcher", root->watcher_ops->name // )); }
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; w_perf_t sample; int64_t num_walked = 0; memset(&ctx, 0, sizeof(ctx)); ctx.query = query; ctx.root = root; memset(res, 0, sizeof(*res)); w_perf_start(&sample, "query_execute"); 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 if (!w_root_lock_with_timeout(root, "w_query_execute", query->lock_timeout)) { ignore_result(asprintf(&res->errmsg, "couldn't acquire root lock within " "lock_timeout of %dms. root is " "currently busy (%s)\n", query->lock_timeout, root->lock_reason)); return false; } 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, &num_walked); } if (w_perf_finish(&sample)) { w_perf_add_root_meta(&sample, root); w_perf_add_meta(&sample, "query_execute", json_pack("{s:b, s:i, s:i, s:O}", // "fresh_instance", res->is_fresh_instance, // "num_results", ctx.num_results, // "num_walked", num_walked, // "query", ctx.query->query_spec // )); w_perf_log(&sample); } w_root_unlock(root); w_perf_destroy(&sample); if (ctx.wholename) { w_string_delref(ctx.wholename); } if (ctx.last_parent_path) { w_string_delref(ctx.last_parent_path); } res->results = ctx.results; res->num_results = ctx.num_results; return true; }