fsmonitor-ipc: create client routines for git-fsmonitor--daemon

Create fsmonitor_ipc__*() client routines to spawn the built-in file system monitor daemon and send it an IPC request using the `Simple IPC` API. Stub in empty fsmonitor_ipc__*() functions for unsupported platforms. Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jeff Hostetler committed Mar 25, 2022 at 18:02 UTC d2bd862e7a4a791d88740cc81aad14cdcd90dd2c
3 files changed +220
Makefile
+1
@@ -907,6 +907,7 @@ LIB_OBJS += fetch-pack.o
907 LIB_OBJS += fmt-merge-msg.o
908 LIB_OBJS += fsck.o
909 LIB_OBJS += fsmonitor.o
910 +LIB_OBJS += fsmonitor-ipc.o
911 LIB_OBJS += gettext.o
912 LIB_OBJS += gpg-interface.o
913 LIB_OBJS += graph.o
fsmonitor-ipc.c new
+171
@@ -0,0 +1,171 @@
1 +#include "cache.h"
2 +#include "fsmonitor.h"
3 +#include "simple-ipc.h"
4 +#include "fsmonitor-ipc.h"
5 +#include "run-command.h"
6 +#include "strbuf.h"
7 +#include "trace2.h"
8 +
9 +#ifndef HAVE_FSMONITOR_DAEMON_BACKEND
10 +
11 +/*
12 + * A trivial implementation of the fsmonitor_ipc__ API for unsupported
13 + * platforms.
14 + */
15 +
16 +int fsmonitor_ipc__is_supported(void)
17 +{
18 + return 0;
19 +}
20 +
21 +const char *fsmonitor_ipc__get_path(void)
22 +{
23 + return NULL;
24 +}
25 +
26 +enum ipc_active_state fsmonitor_ipc__get_state(void)
27 +{
28 + return IPC_STATE__OTHER_ERROR;
29 +}
30 +
31 +int fsmonitor_ipc__send_query(const char *since_token,
32 + struct strbuf *answer)
33 +{
34 + return -1;
35 +}
36 +
37 +int fsmonitor_ipc__send_command(const char *command,
38 + struct strbuf *answer)
39 +{
40 + return -1;
41 +}
42 +
43 +#else
44 +
45 +int fsmonitor_ipc__is_supported(void)
46 +{
47 + return 1;
48 +}
49 +
50 +GIT_PATH_FUNC(fsmonitor_ipc__get_path, "fsmonitor--daemon.ipc")
51 +
52 +enum ipc_active_state fsmonitor_ipc__get_state(void)
53 +{
54 + return ipc_get_active_state(fsmonitor_ipc__get_path());
55 +}
56 +
57 +static int spawn_daemon(void)
58 +{
59 + const char *args[] = { "fsmonitor--daemon", "start", NULL };
60 +
61 + return run_command_v_opt_tr2(args, RUN_COMMAND_NO_STDIN | RUN_GIT_CMD,
62 + "fsmonitor");
63 +}
64 +
65 +int fsmonitor_ipc__send_query(const char *since_token,
66 + struct strbuf *answer)
67 +{
68 + int ret = -1;
69 + int tried_to_spawn = 0;
70 + enum ipc_active_state state = IPC_STATE__OTHER_ERROR;
71 + struct ipc_client_connection *connection = NULL;
72 + struct ipc_client_connect_options options
73 + = IPC_CLIENT_CONNECT_OPTIONS_INIT;
74 + const char *tok = since_token ? since_token : "";
75 + size_t tok_len = since_token ? strlen(since_token) : 0;
76 +
77 + options.wait_if_busy = 1;
78 + options.wait_if_not_found = 0;
79 +
80 + trace2_region_enter("fsm_client", "query", NULL);
81 + trace2_data_string("fsm_client", NULL, "query/command", tok);
82 +
83 +try_again:
84 + state = ipc_client_try_connect(fsmonitor_ipc__get_path(), &options,
85 + &connection);
86 +
87 + switch (state) {
88 + case IPC_STATE__LISTENING:
89 + ret = ipc_client_send_command_to_connection(
90 + connection, tok, tok_len, answer);
91 + ipc_client_close_connection(connection);
92 +
93 + trace2_data_intmax("fsm_client", NULL,
94 + "query/response-length", answer->len);
95 + goto done;
96 +
97 + case IPC_STATE__NOT_LISTENING:
98 + case IPC_STATE__PATH_NOT_FOUND:
99 + if (tried_to_spawn)
100 + goto done;
101 +
102 + tried_to_spawn++;
103 + if (spawn_daemon())
104 + goto done;
105 +
106 + /*
107 + * Try again, but this time give the daemon a chance to
108 + * actually create the pipe/socket.
109 + *
110 + * Granted, the daemon just started so it can't possibly have
111 + * any FS cached yet, so we'll always get a trivial answer.
112 + * BUT the answer should include a new token that can serve
113 + * as the basis for subsequent requests.
114 + */
115 + options.wait_if_not_found = 1;
116 + goto try_again;
117 +
118 + case IPC_STATE__INVALID_PATH:
119 + ret = error(_("fsmonitor_ipc__send_query: invalid path '%s'"),
120 + fsmonitor_ipc__get_path());
121 + goto done;
122 +
123 + case IPC_STATE__OTHER_ERROR:
124 + default:
125 + ret = error(_("fsmonitor_ipc__send_query: unspecified error on '%s'"),
126 + fsmonitor_ipc__get_path());
127 + goto done;
128 + }
129 +
130 +done:
131 + trace2_region_leave("fsm_client", "query", NULL);
132 +
133 + return ret;
134 +}
135 +
136 +int fsmonitor_ipc__send_command(const char *command,
137 + struct strbuf *answer)
138 +{
139 + struct ipc_client_connection *connection = NULL;
140 + struct ipc_client_connect_options options
141 + = IPC_CLIENT_CONNECT_OPTIONS_INIT;
142 + int ret;
143 + enum ipc_active_state state;
144 + const char *c = command ? command : "";
145 + size_t c_len = command ? strlen(command) : 0;
146 +
147 + strbuf_reset(answer);
148 +
149 + options.wait_if_busy = 1;
150 + options.wait_if_not_found = 0;
151 +
152 + state = ipc_client_try_connect(fsmonitor_ipc__get_path(), &options,
153 + &connection);
154 + if (state != IPC_STATE__LISTENING) {
155 + die(_("fsmonitor--daemon is not running"));
156 + return -1;
157 + }
158 +
159 + ret = ipc_client_send_command_to_connection(connection, c, c_len,
160 + answer);
161 + ipc_client_close_connection(connection);
162 +
163 + if (ret == -1) {
164 + die(_("could not send '%s' command to fsmonitor--daemon"), c);
165 + return -1;
166 + }
167 +
168 + return 0;
169 +}
170 +
171 +#endif
fsmonitor-ipc.h new
+48
@@ -0,0 +1,48 @@
1 +#ifndef FSMONITOR_IPC_H
2 +#define FSMONITOR_IPC_H
3 +
4 +#include "simple-ipc.h"
5 +
6 +/*
7 + * Returns true if built-in file system monitor daemon is defined
8 + * for this platform.
9 + */
10 +int fsmonitor_ipc__is_supported(void);
11 +
12 +/*
13 + * Returns the pathname to the IPC named pipe or Unix domain socket
14 + * where a `git-fsmonitor--daemon` process will listen. This is a
15 + * per-worktree value.
16 + *
17 + * Returns NULL if the daemon is not supported on this platform.
18 + */
19 +const char *fsmonitor_ipc__get_path(void);
20 +
21 +/*
22 + * Try to determine whether there is a `git-fsmonitor--daemon` process
23 + * listening on the IPC pipe/socket.
24 + */
25 +enum ipc_active_state fsmonitor_ipc__get_state(void);
26 +
27 +/*
28 + * Connect to a `git-fsmonitor--daemon` process via simple-ipc
29 + * and ask for the set of changed files since the given token.
30 + *
31 + * Spawn a daemon process in the background if necessary.
32 + *
33 + * Returns -1 on error; 0 on success.
34 + */
35 +int fsmonitor_ipc__send_query(const char *since_token,
36 + struct strbuf *answer);
37 +
38 +/*
39 + * Connect to a `git-fsmonitor--daemon` process via simple-ipc and
40 + * send a command verb. If no daemon is available, we DO NOT try to
41 + * start one.
42 + *
43 + * Returns -1 on error; 0 on success.
44 + */
45 +int fsmonitor_ipc__send_command(const char *command,
46 + struct strbuf *answer);
47 +
48 +#endif /* FSMONITOR_IPC_H */