Merge branch 'ab/config-based-hooks-base' into seen
[git] / fsmonitor-ipc.c
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 #ifdef HAVE_FSMONITOR_DAEMON_BACKEND
10
11 int fsmonitor_ipc__is_supported(void)
12 {
13         return 1;
14 }
15
16 GIT_PATH_FUNC(fsmonitor_ipc__get_path, "fsmonitor--daemon.ipc")
17
18 enum ipc_active_state fsmonitor_ipc__get_state(void)
19 {
20         return ipc_get_active_state(fsmonitor_ipc__get_path());
21 }
22
23 static int spawn_daemon(void)
24 {
25         const char *args[] = { "fsmonitor--daemon", "start", NULL };
26
27         return run_command_v_opt_tr2(args, RUN_COMMAND_NO_STDIN | RUN_GIT_CMD,
28                                     "fsmonitor");
29 }
30
31 int fsmonitor_ipc__send_query(const char *since_token,
32                               struct strbuf *answer)
33 {
34         int ret = -1;
35         int tried_to_spawn = 0;
36         enum ipc_active_state state = IPC_STATE__OTHER_ERROR;
37         struct ipc_client_connection *connection = NULL;
38         struct ipc_client_connect_options options
39                 = IPC_CLIENT_CONNECT_OPTIONS_INIT;
40
41         options.wait_if_busy = 1;
42         options.wait_if_not_found = 0;
43
44         trace2_region_enter("fsm_client", "query", NULL);
45
46         trace2_data_string("fsm_client", NULL, "query/command",
47                            since_token);
48
49 try_again:
50         state = ipc_client_try_connect(fsmonitor_ipc__get_path(), &options,
51                                        &connection);
52
53         switch (state) {
54         case IPC_STATE__LISTENING:
55                 ret = ipc_client_send_command_to_connection(
56                         connection, since_token, strlen(since_token), answer);
57                 ipc_client_close_connection(connection);
58
59                 trace2_data_intmax("fsm_client", NULL,
60                                    "query/response-length", answer->len);
61
62                 if (fsmonitor_is_trivial_response(answer))
63                         trace2_data_intmax("fsm_client", NULL,
64                                            "query/trivial-response", 1);
65
66                 goto done;
67
68         case IPC_STATE__NOT_LISTENING:
69                 ret = error(_("fsmonitor_ipc__send_query: daemon not available"));
70                 goto done;
71
72         case IPC_STATE__PATH_NOT_FOUND:
73                 if (tried_to_spawn)
74                         goto done;
75
76                 tried_to_spawn++;
77                 if (spawn_daemon())
78                         goto done;
79
80                 /*
81                  * Try again, but this time give the daemon a chance to
82                  * actually create the pipe/socket.
83                  *
84                  * Granted, the daemon just started so it can't possibly have
85                  * any FS cached yet, so we'll always get a trivial answer.
86                  * BUT the answer should include a new token that can serve
87                  * as the basis for subsequent requests.
88                  */
89                 options.wait_if_not_found = 1;
90                 goto try_again;
91
92         case IPC_STATE__INVALID_PATH:
93                 ret = error(_("fsmonitor_ipc__send_query: invalid path '%s'"),
94                             fsmonitor_ipc__get_path());
95                 goto done;
96
97         case IPC_STATE__OTHER_ERROR:
98         default:
99                 ret = error(_("fsmonitor_ipc__send_query: unspecified error on '%s'"),
100                             fsmonitor_ipc__get_path());
101                 goto done;
102         }
103
104 done:
105         trace2_region_leave("fsm_client", "query", NULL);
106
107         return ret;
108 }
109
110 int fsmonitor_ipc__send_command(const char *command,
111                                 struct strbuf *answer)
112 {
113         struct ipc_client_connection *connection = NULL;
114         struct ipc_client_connect_options options
115                 = IPC_CLIENT_CONNECT_OPTIONS_INIT;
116         int ret;
117         enum ipc_active_state state;
118
119         strbuf_reset(answer);
120
121         options.wait_if_busy = 1;
122         options.wait_if_not_found = 0;
123
124         state = ipc_client_try_connect(fsmonitor_ipc__get_path(), &options,
125                                        &connection);
126         if (state != IPC_STATE__LISTENING) {
127                 die("fsmonitor--daemon is not running");
128                 return -1;
129         }
130
131         ret = ipc_client_send_command_to_connection(connection,
132                                                     command, strlen(command),
133                                                     answer);
134         ipc_client_close_connection(connection);
135
136         if (ret == -1) {
137                 die("could not send '%s' command to fsmonitor--daemon",
138                     command);
139                 return -1;
140         }
141
142         return 0;
143 }
144
145 #else
146
147 /*
148  * A trivial implementation of the fsmonitor_ipc__ API for unsupported
149  * platforms.
150  */
151
152 int fsmonitor_ipc__is_supported(void)
153 {
154         return 0;
155 }
156
157 const char *fsmonitor_ipc__get_path(void)
158 {
159         return NULL;
160 }
161
162 enum ipc_active_state fsmonitor_ipc__get_state(void)
163 {
164         return IPC_STATE__OTHER_ERROR;
165 }
166
167 int fsmonitor_ipc__send_query(const char *since_token,
168                               struct strbuf *answer)
169 {
170         return -1;
171 }
172
173 int fsmonitor_ipc__send_command(const char *command,
174                                 struct strbuf *answer)
175 {
176         return -1;
177 }
178
179 #endif