@@ -7,18 +7,144 @@
#include "khash.h"
static const char * const builtin_fsmonitor__daemon_usage[] = {
+ N_("git fsmonitor--daemon --stop"),
+ N_("git fsmonitor--daemon --is-running"),
+ N_("git fsmonitor--daemon --query <token>"),
+ N_("git fsmonitor--daemon --query-index"),
+ N_("git fsmonitor--daemon --flush"),
NULL
};
#ifdef HAVE_FSMONITOR_DAEMON_BACKEND
+/*
+ * Acting as a CLIENT.
+ *
+ * Send an IPC query to a `git-fsmonitor--daemon` SERVER process and
+ * ask for the changes since the given token. This will implicitly
+ * start a daemon process if necessary. The daemon process will
+ * persist after we exit.
+ *
+ * This feature is primarily used by the test suite.
+ */
+static int do_as_client__query_token(const char *token)
+{
+ struct strbuf answer = STRBUF_INIT;
+ int ret;
+
+ ret = fsmonitor_ipc__send_query(token, &answer);
+ if (ret < 0)
+ die(_("could not query fsmonitor--daemon"));
+
+ write_in_full(1, answer.buf, answer.len);
+ strbuf_release(&answer);
+
+ return 0;
+}
+
+/*
+ * Acting as a CLIENT.
+ *
+ * Read the `.git/index` to get the last token written to the FSMonitor index
+ * extension and use that to make a query.
+ *
+ * This feature is primarily used by the test suite.
+ */
+static int do_as_client__query_from_index(void)
+{
+ struct index_state *istate = the_repository->index;
+
+ setup_git_directory();
+ if (do_read_index(istate, the_repository->index_file, 0) < 0)
+ die("unable to read index file");
+ if (!istate->fsmonitor_last_update)
+ die("index file does not have fsmonitor extension");
+
+ return do_as_client__query_token(istate->fsmonitor_last_update);
+}
+
+/*
+ * Acting as a CLIENT.
+ *
+ * Send a "quit" command to the `git-fsmonitor--daemon` (if running)
+ * and wait for it to shutdown.
+ */
+static int do_as_client__send_stop(void)
+{
+ struct strbuf answer = STRBUF_INIT;
+ int ret;
+
+ ret = fsmonitor_ipc__send_command("quit", &answer);
+
+ /* The quit command does not return any response data. */
+ strbuf_release(&answer);
+
+ if (ret)
+ return ret;
+
+ trace2_region_enter("fsm_client", "polling-for-daemon-exit", NULL);
+ while (fsmonitor_ipc__get_state() == IPC_STATE__LISTENING)
+ sleep_millisec(50);
+ trace2_region_leave("fsm_client", "polling-for-daemon-exit", NULL);
+
+ return 0;
+}
+
+/*
+ * Acting as a CLIENT.
+ *
+ * Send a "flush" command to the `git-fsmonitor--daemon` (if running)
+ * and tell it to flush its cache.
+ *
+ * This feature is primarily used by the test suite to simulate a loss of
+ * sync with the filesystem where we miss kernel events.
+ */
+static int do_as_client__send_flush(void)
+{
+ struct strbuf answer = STRBUF_INIT;
+ int ret;
+
+ ret = fsmonitor_ipc__send_command("flush", &answer);
+ if (ret)
+ return ret;
+
+ write_in_full(1, answer.buf, answer.len);
+ strbuf_release(&answer);
+
+ return 0;
+}
+
+static int is_ipc_daemon_listening(void)
+{
+ return fsmonitor_ipc__get_state() == IPC_STATE__LISTENING;
+}
int cmd_fsmonitor__daemon(int argc, const char **argv, const char *prefix)
{
enum daemon_mode {
UNDEFINED_MODE,
+ STOP,
+ IS_RUNNING,
+ QUERY,
+ QUERY_INDEX,
+ FLUSH,
} mode = UNDEFINED_MODE;
struct option options[] = {
+ OPT_CMDMODE(0, "stop", &mode, N_("stop the running daemon"),
+ STOP),
+
+ OPT_CMDMODE(0, "is-running", &mode,
+ N_("test whether the daemon is running"),
+ IS_RUNNING),
+
+ OPT_CMDMODE(0, "query", &mode,
+ N_("query the daemon (starting if necessary)"),
+ QUERY),
+ OPT_CMDMODE(0, "query-index", &mode,
+ N_("query the daemon (starting if necessary) using token from index"),
+ QUERY_INDEX),
+ OPT_CMDMODE(0, "flush", &mode, N_("flush cached filesystem events"),
+ FLUSH),
OPT_END()
};
@@ -31,6 +157,24 @@ int cmd_fsmonitor__daemon(int argc, const char **argv, const char *prefix)
builtin_fsmonitor__daemon_usage, 0);
switch (mode) {
+ case STOP:
+ return !!do_as_client__send_stop();
+
+ case IS_RUNNING:
+ return !is_ipc_daemon_listening();
+
+ case QUERY:
+ if (argc != 1)
+ usage_with_options(builtin_fsmonitor__daemon_usage,
+ options);
+ return !!do_as_client__query_token(argv[0]);
+
+ case QUERY_INDEX:
+ return !!do_as_client__query_from_index();
+
+ case FLUSH:
+ return !!do_as_client__send_flush();
+
case UNDEFINED_MODE:
default:
die(_("Unhandled command mode %d"), mode);