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; }
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; }