3 #include "parse-options.h"
5 #include "fsmonitor-ipc.h"
6 #include "compat/fsmonitor/fsm-listen.h"
7 #include "fsmonitor--daemon.h"
8 #include "simple-ipc.h"
11 static const char * const builtin_fsmonitor__daemon_usage
[] = {
12 N_("git fsmonitor--daemon run [<options>]"),
13 N_("git fsmonitor--daemon stop"),
14 N_("git fsmonitor--daemon status"),
18 #ifdef HAVE_FSMONITOR_DAEMON_BACKEND
20 * Global state loaded from config.
22 #define FSMONITOR__IPC_THREADS "fsmonitor.ipcthreads"
23 static int fsmonitor__ipc_threads
= 8;
25 #define FSMONITOR__ANNOUNCE_STARTUP "fsmonitor.announcestartup"
26 static int fsmonitor__announce_startup
= 0;
28 static int fsmonitor_config(const char *var
, const char *value
, void *cb
)
30 if (!strcmp(var
, FSMONITOR__IPC_THREADS
)) {
31 int i
= git_config_int(var
, value
);
33 return error(_("value of '%s' out of range: %d"),
34 FSMONITOR__IPC_THREADS
, i
);
35 fsmonitor__ipc_threads
= i
;
39 if (!strcmp(var
, FSMONITOR__ANNOUNCE_STARTUP
)) {
41 int i
= git_config_bool_or_int(var
, value
, &is_bool
);
43 return error(_("value of '%s' not bool or int: %d"),
45 fsmonitor__announce_startup
= i
;
49 return git_default_config(var
, value
, cb
);
55 * Send a "quit" command to the `git-fsmonitor--daemon` (if running)
56 * and wait for it to shutdown.
58 static int do_as_client__send_stop(void)
60 struct strbuf answer
= STRBUF_INIT
;
63 ret
= fsmonitor_ipc__send_command("quit", &answer
);
65 /* The quit command does not return any response data. */
66 strbuf_release(&answer
);
71 trace2_region_enter("fsm_client", "polling-for-daemon-exit", NULL
);
72 while (fsmonitor_ipc__get_state() == IPC_STATE__LISTENING
)
74 trace2_region_leave("fsm_client", "polling-for-daemon-exit", NULL
);
79 static int do_as_client__status(void)
81 enum ipc_active_state state
= fsmonitor_ipc__get_state();
84 case IPC_STATE__LISTENING
:
85 printf(_("fsmonitor-daemon is watching '%s'\n"),
86 the_repository
->worktree
);
90 printf(_("fsmonitor-daemon is not watching '%s'\n"),
91 the_repository
->worktree
);
96 static ipc_server_application_cb handle_client
;
98 static int handle_client(void *data
,
99 const char *command
, size_t command_len
,
100 ipc_server_reply_cb
*reply
,
101 struct ipc_server_reply_data
*reply_data
)
103 /* struct fsmonitor_daemon_state *state = data; */
107 * The Simple IPC API now supports {char*, len} arguments, but
108 * FSMonitor always uses proper null-terminated strings, so
109 * we can ignore the command_len argument. (Trust, but verify.)
111 if (command_len
!= strlen(command
))
112 BUG("FSMonitor assumes text messages");
114 trace2_region_enter("fsmonitor", "handle_client", the_repository
);
115 trace2_data_string("fsmonitor", the_repository
, "request", command
);
117 result
= 0; /* TODO Do something here. */
119 trace2_region_leave("fsmonitor", "handle_client", the_repository
);
124 static void *fsm_listen__thread_proc(void *_state
)
126 struct fsmonitor_daemon_state
*state
= _state
;
128 trace2_thread_start("fsm-listen");
130 trace_printf_key(&trace_fsmonitor
, "Watching: worktree '%s'",
131 state
->path_worktree_watch
.buf
);
132 if (state
->nr_paths_watching
> 1)
133 trace_printf_key(&trace_fsmonitor
, "Watching: gitdir '%s'",
134 state
->path_gitdir_watch
.buf
);
136 fsm_listen__loop(state
);
138 trace2_thread_exit();
142 static int fsmonitor_run_daemon_1(struct fsmonitor_daemon_state
*state
)
144 struct ipc_server_opts ipc_opts
= {
145 .nr_threads
= fsmonitor__ipc_threads
,
148 * We know that there are no other active threads yet,
149 * so we can let the IPC layer temporarily chdir() if
150 * it needs to when creating the server side of the
151 * Unix domain socket.
153 .uds_disallow_chdir
= 0
157 * Start the IPC thread pool before the we've started the file
158 * system event listener thread so that we have the IPC handle
161 if (ipc_server_run_async(&state
->ipc_server_data
,
162 fsmonitor_ipc__get_path(), &ipc_opts
,
163 handle_client
, state
))
165 _("could not start IPC thread pool on '%s'"),
166 fsmonitor_ipc__get_path());
169 * Start the fsmonitor listener thread to collect filesystem
172 if (pthread_create(&state
->listener_thread
, NULL
,
173 fsm_listen__thread_proc
, state
) < 0) {
174 ipc_server_stop_async(state
->ipc_server_data
);
175 ipc_server_await(state
->ipc_server_data
);
177 return error(_("could not start fsmonitor listener thread"));
181 * The daemon is now fully functional in background threads.
182 * Wait for the IPC thread pool to shutdown (whether by client
183 * request or from filesystem activity).
185 ipc_server_await(state
->ipc_server_data
);
188 * The fsmonitor listener thread may have received a shutdown
189 * event from the IPC thread pool, but it doesn't hurt to tell
190 * it again. And wait for it to shutdown.
192 fsm_listen__stop_async(state
);
193 pthread_join(state
->listener_thread
, NULL
);
195 return state
->error_code
;
198 static int fsmonitor_run_daemon(void)
200 struct fsmonitor_daemon_state state
;
203 memset(&state
, 0, sizeof(state
));
205 pthread_mutex_init(&state
.main_lock
, NULL
);
206 state
.error_code
= 0;
207 state
.current_token_data
= NULL
;
209 /* Prepare to (recursively) watch the <worktree-root> directory. */
210 strbuf_init(&state
.path_worktree_watch
, 0);
211 strbuf_addstr(&state
.path_worktree_watch
, absolute_path(get_git_work_tree()));
212 state
.nr_paths_watching
= 1;
215 * We create and delete cookie files somewhere inside the .git
216 * directory to help us keep sync with the file system. If
217 * ".git" is not a directory, then <gitdir> is not inside the
218 * cone of <worktree-root>, so set up a second watch to watch
219 * the <gitdir> so that we get events for the cookie files.
221 strbuf_init(&state
.path_gitdir_watch
, 0);
222 strbuf_addbuf(&state
.path_gitdir_watch
, &state
.path_worktree_watch
);
223 strbuf_addstr(&state
.path_gitdir_watch
, "/.git");
224 if (!is_directory(state
.path_gitdir_watch
.buf
)) {
225 strbuf_reset(&state
.path_gitdir_watch
);
226 strbuf_addstr(&state
.path_gitdir_watch
, absolute_path(get_git_dir()));
227 state
.nr_paths_watching
= 2;
231 * Confirm that we can create platform-specific resources for the
232 * filesystem listener before we bother starting all the threads.
234 if (fsm_listen__ctor(&state
)) {
235 err
= error(_("could not initialize listener thread"));
239 err
= fsmonitor_run_daemon_1(&state
);
242 pthread_mutex_destroy(&state
.main_lock
);
243 fsm_listen__dtor(&state
);
245 ipc_server_free(state
.ipc_server_data
);
247 strbuf_release(&state
.path_worktree_watch
);
248 strbuf_release(&state
.path_gitdir_watch
);
253 static int try_to_run_foreground_daemon(void)
256 * Technically, we don't need to probe for an existing daemon
257 * process, since we could just call `fsmonitor_run_daemon()`
258 * and let it fail if the pipe/socket is busy.
260 * However, this method gives us a nicer error message for a
263 if (fsmonitor_ipc__get_state() == IPC_STATE__LISTENING
)
264 die(_("fsmonitor--daemon is already running '%s'"),
265 the_repository
->worktree
);
267 if (fsmonitor__announce_startup
) {
268 fprintf(stderr
, _("running fsmonitor-daemon in '%s'\n"),
269 the_repository
->worktree
);
273 return !!fsmonitor_run_daemon();
276 int cmd_fsmonitor__daemon(int argc
, const char **argv
, const char *prefix
)
280 struct option options
[] = {
281 OPT_INTEGER(0, "ipc-threads",
282 &fsmonitor__ipc_threads
,
283 N_("use <n> ipc worker threads")),
287 git_config(fsmonitor_config
, NULL
);
289 argc
= parse_options(argc
, argv
, prefix
, options
,
290 builtin_fsmonitor__daemon_usage
, 0);
292 usage_with_options(builtin_fsmonitor__daemon_usage
, options
);
295 if (fsmonitor__ipc_threads
< 1)
296 die(_("invalid 'ipc-threads' value (%d)"),
297 fsmonitor__ipc_threads
);
299 if (!strcmp(subcmd
, "run"))
300 return !!try_to_run_foreground_daemon();
302 if (!strcmp(subcmd
, "stop"))
303 return !!do_as_client__send_stop();
305 if (!strcmp(subcmd
, "status"))
306 return !!do_as_client__status();
308 die(_("Unhandled subcommand '%s'"), subcmd
);
312 int cmd_fsmonitor__daemon(int argc
, const char **argv
, const char *prefix
)
314 struct option options
[] = {
318 if (argc
== 2 && !strcmp(argv
[1], "-h"))
319 usage_with_options(builtin_fsmonitor__daemon_usage
, options
);
321 die(_("fsmonitor--daemon not supported on this platform"));