blob: 4efb1417344e998d7e278d1ded8df89e044b5dbd [file] [log] [blame]
Jeff Hostetler16d9d612022-03-25 18:02:49 +00001#include "builtin.h"
Elijah Newren0b027f62023-03-21 06:25:58 +00002#include "abspath.h"
Elijah Newren36bf1952023-02-24 00:09:24 +00003#include "alloc.h"
Jeff Hostetler16d9d612022-03-25 18:02:49 +00004#include "config.h"
Elijah Newrenf394e092023-03-21 06:25:54 +00005#include "gettext.h"
Jeff Hostetler16d9d612022-03-25 18:02:49 +00006#include "parse-options.h"
7#include "fsmonitor.h"
8#include "fsmonitor-ipc.h"
Eric DeCosta12fd27d2022-10-04 17:32:29 +00009#include "fsmonitor-path-utils.h"
Jeff Hostetlerd0605552022-05-26 21:47:10 +000010#include "compat/fsmonitor/fsm-health.h"
Jeff Hostetler9dcba0b2022-03-25 18:02:53 +000011#include "compat/fsmonitor/fsm-listen.h"
12#include "fsmonitor--daemon.h"
Jeff Hostetler16d9d612022-03-25 18:02:49 +000013#include "simple-ipc.h"
14#include "khash.h"
Jeff Hostetler518a5222022-03-25 18:03:01 +000015#include "pkt-line.h"
Jeff Hostetler16d9d612022-03-25 18:02:49 +000016
17static const char * const builtin_fsmonitor__daemon_usage[] = {
Jeff Hostetlerc284e272022-03-25 18:02:54 +000018 N_("git fsmonitor--daemon start [<options>]"),
Jeff Hostetler9dcba0b2022-03-25 18:02:53 +000019 N_("git fsmonitor--daemon run [<options>]"),
Alex Henrie02cb8b92022-09-19 23:07:09 -060020 "git fsmonitor--daemon stop",
21 "git fsmonitor--daemon status",
Jeff Hostetler16d9d612022-03-25 18:02:49 +000022 NULL
23};
24
25#ifdef HAVE_FSMONITOR_DAEMON_BACKEND
Jeff Hostetlerabc9dbc2022-03-25 18:02:50 +000026/*
Jeff Hostetler9dcba0b2022-03-25 18:02:53 +000027 * Global state loaded from config.
28 */
29#define FSMONITOR__IPC_THREADS "fsmonitor.ipcthreads"
30static int fsmonitor__ipc_threads = 8;
31
Jeff Hostetlerc284e272022-03-25 18:02:54 +000032#define FSMONITOR__START_TIMEOUT "fsmonitor.starttimeout"
33static int fsmonitor__start_timeout_sec = 60;
34
Jeff Hostetler9dcba0b2022-03-25 18:02:53 +000035#define FSMONITOR__ANNOUNCE_STARTUP "fsmonitor.announcestartup"
36static int fsmonitor__announce_startup = 0;
37
38static int fsmonitor_config(const char *var, const char *value, void *cb)
39{
40 if (!strcmp(var, FSMONITOR__IPC_THREADS)) {
41 int i = git_config_int(var, value);
42 if (i < 1)
43 return error(_("value of '%s' out of range: %d"),
44 FSMONITOR__IPC_THREADS, i);
45 fsmonitor__ipc_threads = i;
46 return 0;
47 }
48
Jeff Hostetlerc284e272022-03-25 18:02:54 +000049 if (!strcmp(var, FSMONITOR__START_TIMEOUT)) {
50 int i = git_config_int(var, value);
51 if (i < 0)
52 return error(_("value of '%s' out of range: %d"),
53 FSMONITOR__START_TIMEOUT, i);
54 fsmonitor__start_timeout_sec = i;
55 return 0;
56 }
57
Jeff Hostetler9dcba0b2022-03-25 18:02:53 +000058 if (!strcmp(var, FSMONITOR__ANNOUNCE_STARTUP)) {
59 int is_bool;
60 int i = git_config_bool_or_int(var, value, &is_bool);
61 if (i < 0)
62 return error(_("value of '%s' not bool or int: %d"),
63 var, i);
64 fsmonitor__announce_startup = i;
65 return 0;
66 }
67
68 return git_default_config(var, value, cb);
69}
70
71/*
Jeff Hostetlerabc9dbc2022-03-25 18:02:50 +000072 * Acting as a CLIENT.
73 *
74 * Send a "quit" command to the `git-fsmonitor--daemon` (if running)
75 * and wait for it to shutdown.
76 */
77static int do_as_client__send_stop(void)
78{
79 struct strbuf answer = STRBUF_INIT;
80 int ret;
81
82 ret = fsmonitor_ipc__send_command("quit", &answer);
83
84 /* The quit command does not return any response data. */
85 strbuf_release(&answer);
86
87 if (ret)
88 return ret;
89
90 trace2_region_enter("fsm_client", "polling-for-daemon-exit", NULL);
91 while (fsmonitor_ipc__get_state() == IPC_STATE__LISTENING)
92 sleep_millisec(50);
93 trace2_region_leave("fsm_client", "polling-for-daemon-exit", NULL);
94
95 return 0;
96}
97
98static int do_as_client__status(void)
99{
100 enum ipc_active_state state = fsmonitor_ipc__get_state();
101
102 switch (state) {
103 case IPC_STATE__LISTENING:
104 printf(_("fsmonitor-daemon is watching '%s'\n"),
105 the_repository->worktree);
106 return 0;
107
108 default:
109 printf(_("fsmonitor-daemon is not watching '%s'\n"),
110 the_repository->worktree);
111 return 1;
112 }
113}
Jeff Hostetler16d9d612022-03-25 18:02:49 +0000114
Jeff Hostetlerb05880d2022-03-25 18:03:11 +0000115enum fsmonitor_cookie_item_result {
116 FCIR_ERROR = -1, /* could not create cookie file ? */
117 FCIR_INIT,
118 FCIR_SEEN,
119 FCIR_ABORT,
120};
121
122struct fsmonitor_cookie_item {
123 struct hashmap_entry entry;
124 char *name;
125 enum fsmonitor_cookie_item_result result;
126};
127
128static int cookies_cmp(const void *data, const struct hashmap_entry *he1,
129 const struct hashmap_entry *he2, const void *keydata)
130{
131 const struct fsmonitor_cookie_item *a =
132 container_of(he1, const struct fsmonitor_cookie_item, entry);
133 const struct fsmonitor_cookie_item *b =
134 container_of(he2, const struct fsmonitor_cookie_item, entry);
135
136 return strcmp(a->name, keydata ? keydata : b->name);
137}
138
139static enum fsmonitor_cookie_item_result with_lock__wait_for_cookie(
140 struct fsmonitor_daemon_state *state)
141{
142 /* assert current thread holding state->main_lock */
143
144 int fd;
145 struct fsmonitor_cookie_item *cookie;
146 struct strbuf cookie_pathname = STRBUF_INIT;
147 struct strbuf cookie_filename = STRBUF_INIT;
148 enum fsmonitor_cookie_item_result result;
149 int my_cookie_seq;
150
151 CALLOC_ARRAY(cookie, 1);
152
153 my_cookie_seq = state->cookie_seq++;
154
155 strbuf_addf(&cookie_filename, "%i-%i", getpid(), my_cookie_seq);
156
157 strbuf_addbuf(&cookie_pathname, &state->path_cookie_prefix);
158 strbuf_addbuf(&cookie_pathname, &cookie_filename);
159
160 cookie->name = strbuf_detach(&cookie_filename, NULL);
161 cookie->result = FCIR_INIT;
162 hashmap_entry_init(&cookie->entry, strhash(cookie->name));
163
164 hashmap_add(&state->cookies, &cookie->entry);
165
166 trace_printf_key(&trace_fsmonitor, "cookie-wait: '%s' '%s'",
167 cookie->name, cookie_pathname.buf);
168
169 /*
170 * Create the cookie file on disk and then wait for a notification
171 * that the listener thread has seen it.
172 */
173 fd = open(cookie_pathname.buf, O_WRONLY | O_CREAT | O_EXCL, 0600);
174 if (fd < 0) {
175 error_errno(_("could not create fsmonitor cookie '%s'"),
176 cookie->name);
177
178 cookie->result = FCIR_ERROR;
179 goto done;
180 }
181
182 /*
183 * Technically, close() and unlink() can fail, but we don't
184 * care here. We only created the file to trigger a watch
185 * event from the FS to know that when we're up to date.
186 */
187 close(fd);
188 unlink(cookie_pathname.buf);
189
190 /*
191 * Technically, this is an infinite wait (well, unless another
192 * thread sends us an abort). I'd like to change this to
193 * use `pthread_cond_timedwait()` and return an error/timeout
194 * and let the caller do the trivial response thing, but we
195 * don't have that routine in our thread-utils.
196 *
197 * After extensive beta testing I'm not really worried about
198 * this. Also note that the above open() and unlink() calls
199 * will cause at least two FS events on that path, so the odds
200 * of getting stuck are pretty slim.
201 */
202 while (cookie->result == FCIR_INIT)
203 pthread_cond_wait(&state->cookies_cond,
204 &state->main_lock);
205
206done:
207 hashmap_remove(&state->cookies, &cookie->entry, NULL);
208
209 result = cookie->result;
210
211 free(cookie->name);
212 free(cookie);
213 strbuf_release(&cookie_pathname);
214
215 return result;
216}
217
218/*
219 * Mark these cookies as _SEEN and wake up the corresponding client threads.
220 */
221static void with_lock__mark_cookies_seen(struct fsmonitor_daemon_state *state,
222 const struct string_list *cookie_names)
223{
224 /* assert current thread holding state->main_lock */
225
226 int k;
227 int nr_seen = 0;
228
229 for (k = 0; k < cookie_names->nr; k++) {
230 struct fsmonitor_cookie_item key;
231 struct fsmonitor_cookie_item *cookie;
232
233 key.name = cookie_names->items[k].string;
234 hashmap_entry_init(&key.entry, strhash(key.name));
235
236 cookie = hashmap_get_entry(&state->cookies, &key, entry, NULL);
237 if (cookie) {
238 trace_printf_key(&trace_fsmonitor, "cookie-seen: '%s'",
239 cookie->name);
240 cookie->result = FCIR_SEEN;
241 nr_seen++;
242 }
243 }
244
245 if (nr_seen)
246 pthread_cond_broadcast(&state->cookies_cond);
247}
248
249/*
250 * Set _ABORT on all pending cookies and wake up all client threads.
251 */
252static void with_lock__abort_all_cookies(struct fsmonitor_daemon_state *state)
253{
254 /* assert current thread holding state->main_lock */
255
256 struct hashmap_iter iter;
257 struct fsmonitor_cookie_item *cookie;
258 int nr_aborted = 0;
259
260 hashmap_for_each_entry(&state->cookies, &iter, cookie, entry) {
261 trace_printf_key(&trace_fsmonitor, "cookie-abort: '%s'",
262 cookie->name);
263 cookie->result = FCIR_ABORT;
264 nr_aborted++;
265 }
266
267 if (nr_aborted)
268 pthread_cond_broadcast(&state->cookies_cond);
269}
270
Jeff Hostetleraeef7672022-03-25 18:02:56 +0000271/*
272 * Requests to and from a FSMonitor Protocol V2 provider use an opaque
273 * "token" as a virtual timestamp. Clients can request a summary of all
274 * created/deleted/modified files relative to a token. In the response,
275 * clients receive a new token for the next (relative) request.
276 *
277 *
278 * Token Format
279 * ============
280 *
281 * The contents of the token are private and provider-specific.
282 *
283 * For the built-in fsmonitor--daemon, we define a token as follows:
284 *
285 * "builtin" ":" <token_id> ":" <sequence_nr>
286 *
287 * The "builtin" prefix is used as a namespace to avoid conflicts
288 * with other providers (such as Watchman).
289 *
290 * The <token_id> is an arbitrary OPAQUE string, such as a GUID,
291 * UUID, or {timestamp,pid}. It is used to group all filesystem
292 * events that happened while the daemon was monitoring (and in-sync
293 * with the filesystem).
294 *
295 * Unlike FSMonitor Protocol V1, it is not defined as a timestamp
296 * and does not define less-than/greater-than relationships.
297 * (There are too many race conditions to rely on file system
298 * event timestamps.)
299 *
300 * The <sequence_nr> is a simple integer incremented whenever the
301 * daemon needs to make its state public. For example, if 1000 file
302 * system events come in, but no clients have requested the data,
303 * the daemon can continue to accumulate file changes in the same
304 * bin and does not need to advance the sequence number. However,
305 * as soon as a client does arrive, the daemon needs to start a new
306 * bin and increment the sequence number.
307 *
308 * The sequence number serves as the boundary between 2 sets
309 * of bins -- the older ones that the client has already seen
310 * and the newer ones that it hasn't.
311 *
312 * When a new <token_id> is created, the <sequence_nr> is reset to
313 * zero.
314 *
315 *
316 * About Token Ids
317 * ===============
318 *
319 * A new token_id is created:
320 *
321 * [1] each time the daemon is started.
322 *
323 * [2] any time that the daemon must re-sync with the filesystem
324 * (such as when the kernel drops or we miss events on a very
325 * active volume).
326 *
327 * [3] in response to a client "flush" command (for dropped event
328 * testing).
329 *
330 * When a new token_id is created, the daemon is free to discard all
331 * cached filesystem events associated with any previous token_ids.
332 * Events associated with a non-current token_id will never be sent
333 * to a client. A token_id change implicitly means that the daemon
334 * has gap in its event history.
335 *
336 * Therefore, clients that present a token with a stale (non-current)
337 * token_id will always be given a trivial response.
338 */
339struct fsmonitor_token_data {
340 struct strbuf token_id;
341 struct fsmonitor_batch *batch_head;
342 struct fsmonitor_batch *batch_tail;
343 uint64_t client_ref_count;
344};
345
Jeff Hostetlerbec486b2022-03-25 18:02:57 +0000346struct fsmonitor_batch {
347 struct fsmonitor_batch *next;
348 uint64_t batch_seq_nr;
349 const char **interned_paths;
350 size_t nr, alloc;
351 time_t pinned_time;
352};
353
Jeff Hostetleraeef7672022-03-25 18:02:56 +0000354static struct fsmonitor_token_data *fsmonitor_new_token_data(void)
355{
356 static int test_env_value = -1;
357 static uint64_t flush_count = 0;
358 struct fsmonitor_token_data *token;
Jeff Hostetlerbec486b2022-03-25 18:02:57 +0000359 struct fsmonitor_batch *batch;
Jeff Hostetleraeef7672022-03-25 18:02:56 +0000360
361 CALLOC_ARRAY(token, 1);
Jeff Hostetlerbec486b2022-03-25 18:02:57 +0000362 batch = fsmonitor_batch__new();
Jeff Hostetleraeef7672022-03-25 18:02:56 +0000363
364 strbuf_init(&token->token_id, 0);
Jeff Hostetlerbec486b2022-03-25 18:02:57 +0000365 token->batch_head = batch;
366 token->batch_tail = batch;
Jeff Hostetleraeef7672022-03-25 18:02:56 +0000367 token->client_ref_count = 0;
368
369 if (test_env_value < 0)
370 test_env_value = git_env_bool("GIT_TEST_FSMONITOR_TOKEN", 0);
371
372 if (!test_env_value) {
373 struct timeval tv;
374 struct tm tm;
375 time_t secs;
376
377 gettimeofday(&tv, NULL);
378 secs = tv.tv_sec;
379 gmtime_r(&secs, &tm);
380
381 strbuf_addf(&token->token_id,
382 "%"PRIu64".%d.%4d%02d%02dT%02d%02d%02d.%06ldZ",
383 flush_count++,
384 getpid(),
385 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
386 tm.tm_hour, tm.tm_min, tm.tm_sec,
387 (long)tv.tv_usec);
388 } else {
389 strbuf_addf(&token->token_id, "test_%08x", test_env_value++);
390 }
391
Jeff Hostetlerbec486b2022-03-25 18:02:57 +0000392 /*
393 * We created a new <token_id> and are starting a new series
394 * of tokens with a zero <seq_nr>.
395 *
396 * Since clients cannot guess our new (non test) <token_id>
397 * they will always receive a trivial response (because of the
398 * mismatch on the <token_id>). The trivial response will
399 * tell them our new <token_id> so that subsequent requests
400 * will be relative to our new series. (And when sending that
401 * response, we pin the current head of the batch list.)
402 *
403 * Even if the client correctly guesses the <token_id>, their
404 * request of "builtin:<token_id>:0" asks for all changes MORE
405 * RECENT than batch/bin 0.
406 *
407 * This implies that it is a waste to accumulate paths in the
408 * initial batch/bin (because they will never be transmitted).
409 *
410 * So the daemon could be running for days and watching the
411 * file system, but doesn't need to actually accumulate any
412 * paths UNTIL we need to set a reference point for a later
413 * relative request.
414 *
415 * However, it is very useful for testing to always have a
416 * reference point set. Pin batch 0 to force early file system
417 * events to accumulate.
418 */
419 if (test_env_value)
420 batch->pinned_time = time(NULL);
421
Jeff Hostetleraeef7672022-03-25 18:02:56 +0000422 return token;
423}
424
Jeff Hostetlerbec486b2022-03-25 18:02:57 +0000425struct fsmonitor_batch *fsmonitor_batch__new(void)
426{
427 struct fsmonitor_batch *batch;
428
429 CALLOC_ARRAY(batch, 1);
430
431 return batch;
432}
433
434void fsmonitor_batch__free_list(struct fsmonitor_batch *batch)
435{
436 while (batch) {
437 struct fsmonitor_batch *next = batch->next;
438
439 /*
440 * The actual strings within the array of this batch
441 * are interned, so we don't own them. We only own
442 * the array.
443 */
444 free(batch->interned_paths);
445 free(batch);
446
447 batch = next;
448 }
449}
450
451void fsmonitor_batch__add_path(struct fsmonitor_batch *batch,
452 const char *path)
453{
454 const char *interned_path = strintern(path);
455
456 trace_printf_key(&trace_fsmonitor, "event: %s", interned_path);
457
458 ALLOC_GROW(batch->interned_paths, batch->nr + 1, batch->alloc);
459 batch->interned_paths[batch->nr++] = interned_path;
460}
461
462static void fsmonitor_batch__combine(struct fsmonitor_batch *batch_dest,
463 const struct fsmonitor_batch *batch_src)
464{
465 size_t k;
466
467 ALLOC_GROW(batch_dest->interned_paths,
468 batch_dest->nr + batch_src->nr + 1,
469 batch_dest->alloc);
470
471 for (k = 0; k < batch_src->nr; k++)
472 batch_dest->interned_paths[batch_dest->nr++] =
473 batch_src->interned_paths[k];
474}
475
Jeff Hostetler50c725d2022-03-25 18:03:10 +0000476/*
477 * To keep the batch list from growing unbounded in response to filesystem
478 * activity, we try to truncate old batches from the end of the list as
479 * they become irrelevant.
480 *
481 * We assume that the .git/index will be updated with the most recent token
482 * any time the index is updated. And future commands will only ask for
483 * recent changes *since* that new token. So as tokens advance into the
484 * future, older batch items will never be requested/needed. So we can
485 * truncate them without loss of functionality.
486 *
487 * However, multiple commands may be talking to the daemon concurrently
488 * or perform a slow command, so a little "token skew" is possible.
489 * Therefore, we want this to be a little bit lazy and have a generous
490 * delay.
491 *
492 * The current reader thread walked backwards in time from `token->batch_head`
493 * back to `batch_marker` somewhere in the middle of the batch list.
494 *
495 * Let's walk backwards in time from that marker an arbitrary delay
496 * and truncate the list there. Note that these timestamps are completely
497 * artificial (based on when we pinned the batch item) and not on any
498 * filesystem activity.
499 *
500 * Return the obsolete portion of the list after we have removed it from
501 * the official list so that the caller can free it after leaving the lock.
502 */
503#define MY_TIME_DELAY_SECONDS (5 * 60) /* seconds */
504
505static struct fsmonitor_batch *with_lock__truncate_old_batches(
506 struct fsmonitor_daemon_state *state,
507 const struct fsmonitor_batch *batch_marker)
508{
509 /* assert current thread holding state->main_lock */
510
511 const struct fsmonitor_batch *batch;
512 struct fsmonitor_batch *remainder;
513
514 if (!batch_marker)
515 return NULL;
516
517 trace_printf_key(&trace_fsmonitor, "Truncate: mark (%"PRIu64",%"PRIu64")",
518 batch_marker->batch_seq_nr,
519 (uint64_t)batch_marker->pinned_time);
520
521 for (batch = batch_marker; batch; batch = batch->next) {
522 time_t t;
523
524 if (!batch->pinned_time) /* an overflow batch */
525 continue;
526
527 t = batch->pinned_time + MY_TIME_DELAY_SECONDS;
528 if (t > batch_marker->pinned_time) /* too close to marker */
529 continue;
530
531 goto truncate_past_here;
532 }
533
534 return NULL;
535
536truncate_past_here:
537 state->current_token_data->batch_tail = (struct fsmonitor_batch *)batch;
538
539 remainder = ((struct fsmonitor_batch *)batch)->next;
540 ((struct fsmonitor_batch *)batch)->next = NULL;
541
542 return remainder;
543}
544
Jeff Hostetlerbec486b2022-03-25 18:02:57 +0000545static void fsmonitor_free_token_data(struct fsmonitor_token_data *token)
546{
547 if (!token)
548 return;
549
550 assert(token->client_ref_count == 0);
551
552 strbuf_release(&token->token_id);
553
554 fsmonitor_batch__free_list(token->batch_head);
555
556 free(token);
557}
558
559/*
560 * Flush all of our cached data about the filesystem. Call this if we
561 * lose sync with the filesystem and miss some notification events.
562 *
563 * [1] If we are missing events, then we no longer have a complete
564 * history of the directory (relative to our current start token).
565 * We should create a new token and start fresh (as if we just
566 * booted up).
567 *
Jeff Hostetlerb05880d2022-03-25 18:03:11 +0000568 * [2] Some of those lost events may have been for cookie files. We
569 * should assume the worst and abort them rather letting them starve.
570 *
Jeff Hostetlerbec486b2022-03-25 18:02:57 +0000571 * If there are no concurrent threads reading the current token data
572 * series, we can free it now. Otherwise, let the last reader free
573 * it.
574 *
575 * Either way, the old token data series is no longer associated with
576 * our state data.
577 */
578static void with_lock__do_force_resync(struct fsmonitor_daemon_state *state)
579{
580 /* assert current thread holding state->main_lock */
581
582 struct fsmonitor_token_data *free_me = NULL;
583 struct fsmonitor_token_data *new_one = NULL;
584
585 new_one = fsmonitor_new_token_data();
586
587 if (state->current_token_data->client_ref_count == 0)
588 free_me = state->current_token_data;
589 state->current_token_data = new_one;
590
591 fsmonitor_free_token_data(free_me);
Jeff Hostetlerb05880d2022-03-25 18:03:11 +0000592
593 with_lock__abort_all_cookies(state);
Jeff Hostetlerbec486b2022-03-25 18:02:57 +0000594}
595
596void fsmonitor_force_resync(struct fsmonitor_daemon_state *state)
597{
598 pthread_mutex_lock(&state->main_lock);
599 with_lock__do_force_resync(state);
600 pthread_mutex_unlock(&state->main_lock);
601}
602
Jeff Hostetler518a5222022-03-25 18:03:01 +0000603/*
604 * Format an opaque token string to send to the client.
605 */
606static void with_lock__format_response_token(
607 struct strbuf *response_token,
608 const struct strbuf *response_token_id,
609 const struct fsmonitor_batch *batch)
610{
611 /* assert current thread holding state->main_lock */
612
613 strbuf_reset(response_token);
614 strbuf_addf(response_token, "builtin:%s:%"PRIu64,
615 response_token_id->buf, batch->batch_seq_nr);
616}
617
618/*
619 * Parse an opaque token from the client.
620 * Returns -1 on error.
621 */
622static int fsmonitor_parse_client_token(const char *buf_token,
623 struct strbuf *requested_token_id,
624 uint64_t *seq_nr)
625{
626 const char *p;
627 char *p_end;
628
629 strbuf_reset(requested_token_id);
630 *seq_nr = 0;
631
632 if (!skip_prefix(buf_token, "builtin:", &p))
633 return -1;
634
635 while (*p && *p != ':')
636 strbuf_addch(requested_token_id, *p++);
637 if (!*p++)
638 return -1;
639
640 *seq_nr = (uint64_t)strtoumax(p, &p_end, 10);
641 if (*p_end)
642 return -1;
643
644 return 0;
645}
646
647KHASH_INIT(str, const char *, int, 0, kh_str_hash_func, kh_str_hash_equal)
648
649static int do_handle_client(struct fsmonitor_daemon_state *state,
650 const char *command,
651 ipc_server_reply_cb *reply,
652 struct ipc_server_reply_data *reply_data)
653{
654 struct fsmonitor_token_data *token_data = NULL;
655 struct strbuf response_token = STRBUF_INIT;
656 struct strbuf requested_token_id = STRBUF_INIT;
657 struct strbuf payload = STRBUF_INIT;
658 uint64_t requested_oldest_seq_nr = 0;
659 uint64_t total_response_len = 0;
660 const char *p;
661 const struct fsmonitor_batch *batch_head;
662 const struct fsmonitor_batch *batch;
Jeff Hostetler50c725d2022-03-25 18:03:10 +0000663 struct fsmonitor_batch *remainder = NULL;
Jeff Hostetler518a5222022-03-25 18:03:01 +0000664 intmax_t count = 0, duplicates = 0;
665 kh_str_t *shown;
666 int hash_ret;
667 int do_trivial = 0;
668 int do_flush = 0;
Jeff Hostetlerb05880d2022-03-25 18:03:11 +0000669 int do_cookie = 0;
670 enum fsmonitor_cookie_item_result cookie_result;
Jeff Hostetler518a5222022-03-25 18:03:01 +0000671
672 /*
673 * We expect `command` to be of the form:
674 *
675 * <command> := quit NUL
676 * | flush NUL
677 * | <V1-time-since-epoch-ns> NUL
678 * | <V2-opaque-fsmonitor-token> NUL
679 */
680
681 if (!strcmp(command, "quit")) {
682 /*
683 * A client has requested over the socket/pipe that the
684 * daemon shutdown.
685 *
686 * Tell the IPC thread pool to shutdown (which completes
687 * the await in the main thread (which can stop the
688 * fsmonitor listener thread)).
689 *
690 * There is no reply to the client.
691 */
692 return SIMPLE_IPC_QUIT;
693
694 } else if (!strcmp(command, "flush")) {
695 /*
696 * Flush all of our cached data and generate a new token
697 * just like if we lost sync with the filesystem.
698 *
699 * Then send a trivial response using the new token.
700 */
701 do_flush = 1;
702 do_trivial = 1;
703
704 } else if (!skip_prefix(command, "builtin:", &p)) {
705 /* assume V1 timestamp or garbage */
706
707 char *p_end;
708
709 strtoumax(command, &p_end, 10);
710 trace_printf_key(&trace_fsmonitor,
711 ((*p_end) ?
712 "fsmonitor: invalid command line '%s'" :
713 "fsmonitor: unsupported V1 protocol '%s'"),
714 command);
715 do_trivial = 1;
Jeff Hostetler6692d452022-12-01 23:32:14 +0000716 do_cookie = 1;
Jeff Hostetler518a5222022-03-25 18:03:01 +0000717
718 } else {
719 /* We have "builtin:*" */
720 if (fsmonitor_parse_client_token(command, &requested_token_id,
721 &requested_oldest_seq_nr)) {
722 trace_printf_key(&trace_fsmonitor,
723 "fsmonitor: invalid V2 protocol token '%s'",
724 command);
725 do_trivial = 1;
Jeff Hostetler6692d452022-12-01 23:32:14 +0000726 do_cookie = 1;
Jeff Hostetler518a5222022-03-25 18:03:01 +0000727
728 } else {
729 /*
730 * We have a V2 valid token:
731 * "builtin:<token_id>:<seq_nr>"
732 */
Jeff Hostetlerb05880d2022-03-25 18:03:11 +0000733 do_cookie = 1;
Jeff Hostetler518a5222022-03-25 18:03:01 +0000734 }
735 }
736
737 pthread_mutex_lock(&state->main_lock);
738
739 if (!state->current_token_data)
740 BUG("fsmonitor state does not have a current token");
741
Jeff Hostetlerb05880d2022-03-25 18:03:11 +0000742 /*
743 * Write a cookie file inside the directory being watched in
744 * an effort to flush out existing filesystem events that we
745 * actually care about. Suspend this client thread until we
746 * see the filesystem events for this cookie file.
747 *
748 * Creating the cookie lets us guarantee that our FS listener
749 * thread has drained the kernel queue and we are caught up
750 * with the kernel.
751 *
752 * If we cannot create the cookie (or otherwise guarantee that
753 * we are caught up), we send a trivial response. We have to
754 * assume that there might be some very, very recent activity
755 * on the FS still in flight.
756 */
757 if (do_cookie) {
758 cookie_result = with_lock__wait_for_cookie(state);
759 if (cookie_result != FCIR_SEEN) {
760 error(_("fsmonitor: cookie_result '%d' != SEEN"),
761 cookie_result);
762 do_trivial = 1;
763 }
764 }
765
Jeff Hostetler518a5222022-03-25 18:03:01 +0000766 if (do_flush)
767 with_lock__do_force_resync(state);
768
769 /*
770 * We mark the current head of the batch list as "pinned" so
771 * that the listener thread will treat this item as read-only
772 * (and prevent any more paths from being added to it) from
773 * now on.
774 */
775 token_data = state->current_token_data;
776 batch_head = token_data->batch_head;
777 ((struct fsmonitor_batch *)batch_head)->pinned_time = time(NULL);
778
779 /*
780 * FSMonitor Protocol V2 requires that we send a response header
781 * with a "new current token" and then all of the paths that changed
782 * since the "requested token". We send the seq_nr of the just-pinned
783 * head batch so that future requests from a client will be relative
784 * to it.
785 */
786 with_lock__format_response_token(&response_token,
787 &token_data->token_id, batch_head);
788
789 reply(reply_data, response_token.buf, response_token.len + 1);
790 total_response_len += response_token.len + 1;
791
792 trace2_data_string("fsmonitor", the_repository, "response/token",
793 response_token.buf);
794 trace_printf_key(&trace_fsmonitor, "response token: %s",
795 response_token.buf);
796
797 if (!do_trivial) {
798 if (strcmp(requested_token_id.buf, token_data->token_id.buf)) {
799 /*
800 * The client last spoke to a different daemon
801 * instance -OR- the daemon had to resync with
802 * the filesystem (and lost events), so reject.
803 */
804 trace2_data_string("fsmonitor", the_repository,
805 "response/token", "different");
806 do_trivial = 1;
807
808 } else if (requested_oldest_seq_nr <
809 token_data->batch_tail->batch_seq_nr) {
810 /*
811 * The client wants older events than we have for
812 * this token_id. This means that the end of our
813 * batch list was truncated and we cannot give the
814 * client a complete snapshot relative to their
815 * request.
816 */
817 trace_printf_key(&trace_fsmonitor,
818 "client requested truncated data");
819 do_trivial = 1;
820 }
821 }
822
823 if (do_trivial) {
824 pthread_mutex_unlock(&state->main_lock);
825
826 reply(reply_data, "/", 2);
827
828 trace2_data_intmax("fsmonitor", the_repository,
829 "response/trivial", 1);
830
831 goto cleanup;
832 }
833
834 /*
835 * We're going to hold onto a pointer to the current
836 * token-data while we walk the list of batches of files.
837 * During this time, we will NOT be under the lock.
838 * So we ref-count it.
839 *
840 * This allows the listener thread to continue prepending
841 * new batches of items to the token-data (which we'll ignore).
842 *
843 * AND it allows the listener thread to do a token-reset
844 * (and install a new `current_token_data`).
845 */
846 token_data->client_ref_count++;
847
848 pthread_mutex_unlock(&state->main_lock);
849
850 /*
851 * The client request is relative to the token that they sent,
852 * so walk the batch list backwards from the current head back
853 * to the batch (sequence number) they named.
854 *
855 * We use khash to de-dup the list of pathnames.
856 *
857 * NEEDSWORK: each batch contains a list of interned strings,
858 * so we only need to do pointer comparisons here to build the
859 * hash table. Currently, we're still comparing the string
860 * values.
861 */
862 shown = kh_init_str();
863 for (batch = batch_head;
864 batch && batch->batch_seq_nr > requested_oldest_seq_nr;
865 batch = batch->next) {
866 size_t k;
867
868 for (k = 0; k < batch->nr; k++) {
869 const char *s = batch->interned_paths[k];
870 size_t s_len;
871
872 if (kh_get_str(shown, s) != kh_end(shown))
873 duplicates++;
874 else {
875 kh_put_str(shown, s, &hash_ret);
876
877 trace_printf_key(&trace_fsmonitor,
878 "send[%"PRIuMAX"]: %s",
879 count, s);
880
881 /* Each path gets written with a trailing NUL */
882 s_len = strlen(s) + 1;
883
884 if (payload.len + s_len >=
885 LARGE_PACKET_DATA_MAX) {
886 reply(reply_data, payload.buf,
887 payload.len);
888 total_response_len += payload.len;
889 strbuf_reset(&payload);
890 }
891
892 strbuf_add(&payload, s, s_len);
893 count++;
894 }
895 }
896 }
897
898 if (payload.len) {
899 reply(reply_data, payload.buf, payload.len);
900 total_response_len += payload.len;
901 }
902
903 kh_release_str(shown);
904
905 pthread_mutex_lock(&state->main_lock);
906
907 if (token_data->client_ref_count > 0)
908 token_data->client_ref_count--;
909
910 if (token_data->client_ref_count == 0) {
911 if (token_data != state->current_token_data) {
912 /*
913 * The listener thread did a token-reset while we were
914 * walking the batch list. Therefore, this token is
915 * stale and can be discarded completely. If we are
916 * the last reader thread using this token, we own
917 * that work.
918 */
919 fsmonitor_free_token_data(token_data);
Jeff Hostetler50c725d2022-03-25 18:03:10 +0000920 } else if (batch) {
921 /*
922 * We are holding the lock and are the only
923 * reader of the ref-counted portion of the
924 * list, so we get the honor of seeing if the
925 * list can be truncated to save memory.
926 *
927 * The main loop did not walk to the end of the
928 * list, so this batch is the first item in the
929 * batch-list that is older than the requested
930 * end-point sequence number. See if the tail
931 * end of the list is obsolete.
932 */
933 remainder = with_lock__truncate_old_batches(state,
934 batch);
Jeff Hostetler518a5222022-03-25 18:03:01 +0000935 }
936 }
937
938 pthread_mutex_unlock(&state->main_lock);
939
Jeff Hostetler50c725d2022-03-25 18:03:10 +0000940 if (remainder)
941 fsmonitor_batch__free_list(remainder);
942
Jeff Hostetler518a5222022-03-25 18:03:01 +0000943 trace2_data_intmax("fsmonitor", the_repository, "response/length", total_response_len);
944 trace2_data_intmax("fsmonitor", the_repository, "response/count/files", count);
945 trace2_data_intmax("fsmonitor", the_repository, "response/count/duplicates", duplicates);
946
947cleanup:
948 strbuf_release(&response_token);
949 strbuf_release(&requested_token_id);
950 strbuf_release(&payload);
951
952 return 0;
953}
954
Jeff Hostetler9dcba0b2022-03-25 18:02:53 +0000955static ipc_server_application_cb handle_client;
956
957static int handle_client(void *data,
958 const char *command, size_t command_len,
959 ipc_server_reply_cb *reply,
960 struct ipc_server_reply_data *reply_data)
961{
Jeff Hostetler518a5222022-03-25 18:03:01 +0000962 struct fsmonitor_daemon_state *state = data;
Jeff Hostetler9dcba0b2022-03-25 18:02:53 +0000963 int result;
964
965 /*
966 * The Simple IPC API now supports {char*, len} arguments, but
967 * FSMonitor always uses proper null-terminated strings, so
968 * we can ignore the command_len argument. (Trust, but verify.)
969 */
970 if (command_len != strlen(command))
971 BUG("FSMonitor assumes text messages");
972
Jeff Hostetler518a5222022-03-25 18:03:01 +0000973 trace_printf_key(&trace_fsmonitor, "requested token: %s", command);
974
Jeff Hostetler9dcba0b2022-03-25 18:02:53 +0000975 trace2_region_enter("fsmonitor", "handle_client", the_repository);
976 trace2_data_string("fsmonitor", the_repository, "request", command);
977
Jeff Hostetler518a5222022-03-25 18:03:01 +0000978 result = do_handle_client(state, command, reply, reply_data);
Jeff Hostetler9dcba0b2022-03-25 18:02:53 +0000979
980 trace2_region_leave("fsmonitor", "handle_client", the_repository);
981
982 return result;
983}
984
Jeff Hostetlerb05880d2022-03-25 18:03:11 +0000985#define FSMONITOR_DIR "fsmonitor--daemon"
986#define FSMONITOR_COOKIE_DIR "cookies"
987#define FSMONITOR_COOKIE_PREFIX (FSMONITOR_DIR "/" FSMONITOR_COOKIE_DIR "/")
Jeff Hostetler0ae7a1d2022-03-25 18:02:55 +0000988
989enum fsmonitor_path_type fsmonitor_classify_path_workdir_relative(
990 const char *rel)
991{
992 if (fspathncmp(rel, ".git", 4))
993 return IS_WORKDIR_PATH;
994 rel += 4;
995
996 if (!*rel)
997 return IS_DOT_GIT;
998 if (*rel != '/')
999 return IS_WORKDIR_PATH; /* e.g. .gitignore */
1000 rel++;
1001
1002 if (!fspathncmp(rel, FSMONITOR_COOKIE_PREFIX,
1003 strlen(FSMONITOR_COOKIE_PREFIX)))
1004 return IS_INSIDE_DOT_GIT_WITH_COOKIE_PREFIX;
1005
1006 return IS_INSIDE_DOT_GIT;
1007}
1008
1009enum fsmonitor_path_type fsmonitor_classify_path_gitdir_relative(
1010 const char *rel)
1011{
1012 if (!fspathncmp(rel, FSMONITOR_COOKIE_PREFIX,
1013 strlen(FSMONITOR_COOKIE_PREFIX)))
1014 return IS_INSIDE_GITDIR_WITH_COOKIE_PREFIX;
1015
1016 return IS_INSIDE_GITDIR;
1017}
1018
1019static enum fsmonitor_path_type try_classify_workdir_abs_path(
1020 struct fsmonitor_daemon_state *state,
1021 const char *path)
1022{
1023 const char *rel;
1024
1025 if (fspathncmp(path, state->path_worktree_watch.buf,
1026 state->path_worktree_watch.len))
1027 return IS_OUTSIDE_CONE;
1028
1029 rel = path + state->path_worktree_watch.len;
1030
1031 if (!*rel)
1032 return IS_WORKDIR_PATH; /* it is the root dir exactly */
1033 if (*rel != '/')
1034 return IS_OUTSIDE_CONE;
1035 rel++;
1036
1037 return fsmonitor_classify_path_workdir_relative(rel);
1038}
1039
1040enum fsmonitor_path_type fsmonitor_classify_path_absolute(
1041 struct fsmonitor_daemon_state *state,
1042 const char *path)
1043{
1044 const char *rel;
1045 enum fsmonitor_path_type t;
1046
1047 t = try_classify_workdir_abs_path(state, path);
1048 if (state->nr_paths_watching == 1)
1049 return t;
1050 if (t != IS_OUTSIDE_CONE)
1051 return t;
1052
1053 if (fspathncmp(path, state->path_gitdir_watch.buf,
1054 state->path_gitdir_watch.len))
1055 return IS_OUTSIDE_CONE;
1056
1057 rel = path + state->path_gitdir_watch.len;
1058
1059 if (!*rel)
1060 return IS_GITDIR; /* it is the <gitdir> exactly */
1061 if (*rel != '/')
1062 return IS_OUTSIDE_CONE;
1063 rel++;
1064
1065 return fsmonitor_classify_path_gitdir_relative(rel);
1066}
1067
Jeff Hostetlerbec486b2022-03-25 18:02:57 +00001068/*
1069 * We try to combine small batches at the front of the batch-list to avoid
1070 * having a long list. This hopefully makes it a little easier when we want
1071 * to truncate and maintain the list. However, we don't want the paths array
1072 * to just keep growing and growing with realloc, so we insert an arbitrary
1073 * limit.
1074 */
1075#define MY_COMBINE_LIMIT (1024)
1076
1077void fsmonitor_publish(struct fsmonitor_daemon_state *state,
1078 struct fsmonitor_batch *batch,
1079 const struct string_list *cookie_names)
1080{
1081 if (!batch && !cookie_names->nr)
1082 return;
1083
1084 pthread_mutex_lock(&state->main_lock);
1085
1086 if (batch) {
1087 struct fsmonitor_batch *head;
1088
1089 head = state->current_token_data->batch_head;
1090 if (!head) {
1091 BUG("token does not have batch");
1092 } else if (head->pinned_time) {
1093 /*
1094 * We cannot alter the current batch list
1095 * because:
1096 *
1097 * [a] it is being transmitted to at least one
1098 * client and the handle_client() thread has a
1099 * ref-count, but not a lock on the batch list
1100 * starting with this item.
1101 *
1102 * [b] it has been transmitted in the past to
1103 * at least one client such that future
1104 * requests are relative to this head batch.
1105 *
1106 * So, we can only prepend a new batch onto
1107 * the front of the list.
1108 */
1109 batch->batch_seq_nr = head->batch_seq_nr + 1;
1110 batch->next = head;
1111 state->current_token_data->batch_head = batch;
1112 } else if (!head->batch_seq_nr) {
1113 /*
1114 * Batch 0 is unpinned. See the note in
1115 * `fsmonitor_new_token_data()` about why we
1116 * don't need to accumulate these paths.
1117 */
1118 fsmonitor_batch__free_list(batch);
1119 } else if (head->nr + batch->nr > MY_COMBINE_LIMIT) {
1120 /*
1121 * The head batch in the list has never been
1122 * transmitted to a client, but folding the
1123 * contents of the new batch onto it would
1124 * exceed our arbitrary limit, so just prepend
1125 * the new batch onto the list.
1126 */
1127 batch->batch_seq_nr = head->batch_seq_nr + 1;
1128 batch->next = head;
1129 state->current_token_data->batch_head = batch;
1130 } else {
1131 /*
1132 * We are free to add the paths in the given
1133 * batch onto the end of the current head batch.
1134 */
1135 fsmonitor_batch__combine(head, batch);
1136 fsmonitor_batch__free_list(batch);
1137 }
1138 }
1139
Jeff Hostetlerb05880d2022-03-25 18:03:11 +00001140 if (cookie_names->nr)
1141 with_lock__mark_cookies_seen(state, cookie_names);
1142
Jeff Hostetlerbec486b2022-03-25 18:02:57 +00001143 pthread_mutex_unlock(&state->main_lock);
1144}
1145
Jeff Hostetlerd0605552022-05-26 21:47:10 +00001146static void *fsm_health__thread_proc(void *_state)
1147{
1148 struct fsmonitor_daemon_state *state = _state;
1149
1150 trace2_thread_start("fsm-health");
1151
1152 fsm_health__loop(state);
1153
1154 trace2_thread_exit();
1155 return NULL;
1156}
1157
Jeff Hostetler9dcba0b2022-03-25 18:02:53 +00001158static void *fsm_listen__thread_proc(void *_state)
1159{
1160 struct fsmonitor_daemon_state *state = _state;
1161
1162 trace2_thread_start("fsm-listen");
1163
1164 trace_printf_key(&trace_fsmonitor, "Watching: worktree '%s'",
1165 state->path_worktree_watch.buf);
1166 if (state->nr_paths_watching > 1)
1167 trace_printf_key(&trace_fsmonitor, "Watching: gitdir '%s'",
1168 state->path_gitdir_watch.buf);
1169
1170 fsm_listen__loop(state);
1171
Jeff Hostetlerbec486b2022-03-25 18:02:57 +00001172 pthread_mutex_lock(&state->main_lock);
1173 if (state->current_token_data &&
1174 state->current_token_data->client_ref_count == 0)
1175 fsmonitor_free_token_data(state->current_token_data);
1176 state->current_token_data = NULL;
1177 pthread_mutex_unlock(&state->main_lock);
1178
Jeff Hostetler9dcba0b2022-03-25 18:02:53 +00001179 trace2_thread_exit();
1180 return NULL;
1181}
1182
1183static int fsmonitor_run_daemon_1(struct fsmonitor_daemon_state *state)
1184{
1185 struct ipc_server_opts ipc_opts = {
1186 .nr_threads = fsmonitor__ipc_threads,
1187
1188 /*
1189 * We know that there are no other active threads yet,
1190 * so we can let the IPC layer temporarily chdir() if
1191 * it needs to when creating the server side of the
1192 * Unix domain socket.
1193 */
1194 .uds_disallow_chdir = 0
1195 };
Jeff Hostetlerd0605552022-05-26 21:47:10 +00001196 int health_started = 0;
Jeff Hostetler802aa312022-05-26 21:47:08 +00001197 int listener_started = 0;
1198 int err = 0;
Jeff Hostetler9dcba0b2022-03-25 18:02:53 +00001199
1200 /*
1201 * Start the IPC thread pool before the we've started the file
1202 * system event listener thread so that we have the IPC handle
1203 * before we need it.
1204 */
1205 if (ipc_server_run_async(&state->ipc_server_data,
Jeff Hostetler39664e92022-05-26 21:47:07 +00001206 state->path_ipc.buf, &ipc_opts,
Jeff Hostetler9dcba0b2022-03-25 18:02:53 +00001207 handle_client, state))
1208 return error_errno(
1209 _("could not start IPC thread pool on '%s'"),
Jeff Hostetler39664e92022-05-26 21:47:07 +00001210 state->path_ipc.buf);
Jeff Hostetler9dcba0b2022-03-25 18:02:53 +00001211
1212 /*
1213 * Start the fsmonitor listener thread to collect filesystem
1214 * events.
1215 */
1216 if (pthread_create(&state->listener_thread, NULL,
Seija786e6762022-12-02 17:02:58 +00001217 fsm_listen__thread_proc, state)) {
Jeff Hostetler9dcba0b2022-03-25 18:02:53 +00001218 ipc_server_stop_async(state->ipc_server_data);
Jeff Hostetler802aa312022-05-26 21:47:08 +00001219 err = error(_("could not start fsmonitor listener thread"));
1220 goto cleanup;
Jeff Hostetler9dcba0b2022-03-25 18:02:53 +00001221 }
Jeff Hostetler802aa312022-05-26 21:47:08 +00001222 listener_started = 1;
Jeff Hostetler9dcba0b2022-03-25 18:02:53 +00001223
1224 /*
Jeff Hostetlerd0605552022-05-26 21:47:10 +00001225 * Start the health thread to watch over our process.
1226 */
1227 if (pthread_create(&state->health_thread, NULL,
Seija786e6762022-12-02 17:02:58 +00001228 fsm_health__thread_proc, state)) {
Jeff Hostetlerd0605552022-05-26 21:47:10 +00001229 ipc_server_stop_async(state->ipc_server_data);
1230 err = error(_("could not start fsmonitor health thread"));
1231 goto cleanup;
1232 }
1233 health_started = 1;
1234
1235 /*
Jeff Hostetler9dcba0b2022-03-25 18:02:53 +00001236 * The daemon is now fully functional in background threads.
Jeff Hostetler802aa312022-05-26 21:47:08 +00001237 * Our primary thread should now just wait while the threads
1238 * do all the work.
1239 */
1240cleanup:
1241 /*
Jeff Hostetler9dcba0b2022-03-25 18:02:53 +00001242 * Wait for the IPC thread pool to shutdown (whether by client
Jeff Hostetler802aa312022-05-26 21:47:08 +00001243 * request, from filesystem activity, or an error).
Jeff Hostetler9dcba0b2022-03-25 18:02:53 +00001244 */
1245 ipc_server_await(state->ipc_server_data);
1246
1247 /*
1248 * The fsmonitor listener thread may have received a shutdown
1249 * event from the IPC thread pool, but it doesn't hurt to tell
1250 * it again. And wait for it to shutdown.
1251 */
Jeff Hostetler802aa312022-05-26 21:47:08 +00001252 if (listener_started) {
1253 fsm_listen__stop_async(state);
1254 pthread_join(state->listener_thread, NULL);
1255 }
Jeff Hostetler9dcba0b2022-03-25 18:02:53 +00001256
Jeff Hostetlerd0605552022-05-26 21:47:10 +00001257 if (health_started) {
1258 fsm_health__stop_async(state);
1259 pthread_join(state->health_thread, NULL);
1260 }
1261
Jeff Hostetler802aa312022-05-26 21:47:08 +00001262 if (err)
1263 return err;
Jeff Hostetler207534e2022-05-26 21:47:09 +00001264 if (state->listen_error_code)
1265 return state->listen_error_code;
Jeff Hostetlerd0605552022-05-26 21:47:10 +00001266 if (state->health_error_code)
1267 return state->health_error_code;
Jeff Hostetler802aa312022-05-26 21:47:08 +00001268 return 0;
Jeff Hostetler9dcba0b2022-03-25 18:02:53 +00001269}
1270
1271static int fsmonitor_run_daemon(void)
1272{
1273 struct fsmonitor_daemon_state state;
Jeff Hostetler39664e92022-05-26 21:47:07 +00001274 const char *home;
Jeff Hostetler9dcba0b2022-03-25 18:02:53 +00001275 int err;
1276
1277 memset(&state, 0, sizeof(state));
1278
Jeff Hostetlerb05880d2022-03-25 18:03:11 +00001279 hashmap_init(&state.cookies, cookies_cmp, NULL, 0);
Jeff Hostetler9dcba0b2022-03-25 18:02:53 +00001280 pthread_mutex_init(&state.main_lock, NULL);
Jeff Hostetlerb05880d2022-03-25 18:03:11 +00001281 pthread_cond_init(&state.cookies_cond, NULL);
Jeff Hostetler207534e2022-05-26 21:47:09 +00001282 state.listen_error_code = 0;
Jeff Hostetlerd0605552022-05-26 21:47:10 +00001283 state.health_error_code = 0;
Jeff Hostetleraeef7672022-03-25 18:02:56 +00001284 state.current_token_data = fsmonitor_new_token_data();
Jeff Hostetler9dcba0b2022-03-25 18:02:53 +00001285
1286 /* Prepare to (recursively) watch the <worktree-root> directory. */
1287 strbuf_init(&state.path_worktree_watch, 0);
1288 strbuf_addstr(&state.path_worktree_watch, absolute_path(get_git_work_tree()));
1289 state.nr_paths_watching = 1;
1290
Eric DeCosta12fd27d2022-10-04 17:32:29 +00001291 strbuf_init(&state.alias.alias, 0);
1292 strbuf_init(&state.alias.points_to, 0);
1293 if ((err = fsmonitor__get_alias(state.path_worktree_watch.buf, &state.alias)))
1294 goto done;
1295
Jeff Hostetler9dcba0b2022-03-25 18:02:53 +00001296 /*
1297 * We create and delete cookie files somewhere inside the .git
1298 * directory to help us keep sync with the file system. If
1299 * ".git" is not a directory, then <gitdir> is not inside the
1300 * cone of <worktree-root>, so set up a second watch to watch
1301 * the <gitdir> so that we get events for the cookie files.
1302 */
1303 strbuf_init(&state.path_gitdir_watch, 0);
1304 strbuf_addbuf(&state.path_gitdir_watch, &state.path_worktree_watch);
1305 strbuf_addstr(&state.path_gitdir_watch, "/.git");
1306 if (!is_directory(state.path_gitdir_watch.buf)) {
1307 strbuf_reset(&state.path_gitdir_watch);
1308 strbuf_addstr(&state.path_gitdir_watch, absolute_path(get_git_dir()));
1309 state.nr_paths_watching = 2;
1310 }
1311
1312 /*
Jeff Hostetlerb05880d2022-03-25 18:03:11 +00001313 * We will write filesystem syncing cookie files into
1314 * <gitdir>/<fsmonitor-dir>/<cookie-dir>/<pid>-<seq>.
1315 *
1316 * The extra layers of subdirectories here keep us from
1317 * changing the mtime on ".git/" or ".git/foo/" when we create
1318 * or delete cookie files.
1319 *
1320 * There have been problems with some IDEs that do a
1321 * non-recursive watch of the ".git/" directory and run a
1322 * series of commands any time something happens.
1323 *
1324 * For example, if we place our cookie files directly in
1325 * ".git/" or ".git/foo/" then a `git status` (or similar
1326 * command) from the IDE will cause a cookie file to be
1327 * created in one of those dirs. This causes the mtime of
1328 * those dirs to change. This triggers the IDE's watch
1329 * notification. This triggers the IDE to run those commands
1330 * again. And the process repeats and the machine never goes
1331 * idle.
1332 *
1333 * Adding the extra layers of subdirectories prevents the
1334 * mtime of ".git/" and ".git/foo" from changing when a
1335 * cookie file is created.
1336 */
1337 strbuf_init(&state.path_cookie_prefix, 0);
1338 strbuf_addbuf(&state.path_cookie_prefix, &state.path_gitdir_watch);
1339
1340 strbuf_addch(&state.path_cookie_prefix, '/');
1341 strbuf_addstr(&state.path_cookie_prefix, FSMONITOR_DIR);
1342 mkdir(state.path_cookie_prefix.buf, 0777);
1343
1344 strbuf_addch(&state.path_cookie_prefix, '/');
1345 strbuf_addstr(&state.path_cookie_prefix, FSMONITOR_COOKIE_DIR);
1346 mkdir(state.path_cookie_prefix.buf, 0777);
1347
1348 strbuf_addch(&state.path_cookie_prefix, '/');
1349
1350 /*
Jeff Hostetler39664e92022-05-26 21:47:07 +00001351 * We create a named-pipe or unix domain socket inside of the
1352 * ".git" directory. (Well, on Windows, we base our named
1353 * pipe in the NPFS on the absolute path of the git
1354 * directory.)
1355 */
1356 strbuf_init(&state.path_ipc, 0);
Eric DeCosta6beb2682022-10-04 17:32:27 +00001357 strbuf_addstr(&state.path_ipc,
1358 absolute_path(fsmonitor_ipc__get_path(the_repository)));
Jeff Hostetler39664e92022-05-26 21:47:07 +00001359
1360 /*
Jeff Hostetler9dcba0b2022-03-25 18:02:53 +00001361 * Confirm that we can create platform-specific resources for the
1362 * filesystem listener before we bother starting all the threads.
1363 */
1364 if (fsm_listen__ctor(&state)) {
1365 err = error(_("could not initialize listener thread"));
1366 goto done;
1367 }
1368
Jeff Hostetlerd0605552022-05-26 21:47:10 +00001369 if (fsm_health__ctor(&state)) {
1370 err = error(_("could not initialize health thread"));
1371 goto done;
1372 }
1373
Jeff Hostetler39664e92022-05-26 21:47:07 +00001374 /*
1375 * CD out of the worktree root directory.
1376 *
1377 * The common Git startup mechanism causes our CWD to be the
1378 * root of the worktree. On Windows, this causes our process
1379 * to hold a locked handle on the CWD. This prevents the
1380 * worktree from being moved or deleted while the daemon is
1381 * running.
1382 *
1383 * We assume that our FS and IPC listener threads have either
1384 * opened all of the handles that they need or will do
1385 * everything using absolute paths.
1386 */
1387 home = getenv("HOME");
1388 if (home && *home && chdir(home))
1389 die_errno(_("could not cd home '%s'"), home);
1390
Jeff Hostetler9dcba0b2022-03-25 18:02:53 +00001391 err = fsmonitor_run_daemon_1(&state);
1392
1393done:
Jeff Hostetlerb05880d2022-03-25 18:03:11 +00001394 pthread_cond_destroy(&state.cookies_cond);
Jeff Hostetler9dcba0b2022-03-25 18:02:53 +00001395 pthread_mutex_destroy(&state.main_lock);
1396 fsm_listen__dtor(&state);
Jeff Hostetlerd0605552022-05-26 21:47:10 +00001397 fsm_health__dtor(&state);
Jeff Hostetler9dcba0b2022-03-25 18:02:53 +00001398
1399 ipc_server_free(state.ipc_server_data);
1400
1401 strbuf_release(&state.path_worktree_watch);
1402 strbuf_release(&state.path_gitdir_watch);
Jeff Hostetlerb05880d2022-03-25 18:03:11 +00001403 strbuf_release(&state.path_cookie_prefix);
Jeff Hostetler39664e92022-05-26 21:47:07 +00001404 strbuf_release(&state.path_ipc);
Eric DeCosta12fd27d2022-10-04 17:32:29 +00001405 strbuf_release(&state.alias.alias);
1406 strbuf_release(&state.alias.points_to);
Jeff Hostetler9dcba0b2022-03-25 18:02:53 +00001407
1408 return err;
1409}
1410
Jeff Hostetlerc284e272022-03-25 18:02:54 +00001411static int try_to_run_foreground_daemon(int detach_console)
Jeff Hostetler9dcba0b2022-03-25 18:02:53 +00001412{
1413 /*
1414 * Technically, we don't need to probe for an existing daemon
1415 * process, since we could just call `fsmonitor_run_daemon()`
1416 * and let it fail if the pipe/socket is busy.
1417 *
1418 * However, this method gives us a nicer error message for a
1419 * common error case.
1420 */
1421 if (fsmonitor_ipc__get_state() == IPC_STATE__LISTENING)
1422 die(_("fsmonitor--daemon is already running '%s'"),
1423 the_repository->worktree);
1424
1425 if (fsmonitor__announce_startup) {
1426 fprintf(stderr, _("running fsmonitor-daemon in '%s'\n"),
1427 the_repository->worktree);
1428 fflush(stderr);
1429 }
1430
Jeff Hostetlerc284e272022-03-25 18:02:54 +00001431#ifdef GIT_WINDOWS_NATIVE
1432 if (detach_console)
1433 FreeConsole();
1434#endif
1435
Jeff Hostetler9dcba0b2022-03-25 18:02:53 +00001436 return !!fsmonitor_run_daemon();
1437}
1438
Jeff Hostetlerc284e272022-03-25 18:02:54 +00001439static start_bg_wait_cb bg_wait_cb;
1440
1441static int bg_wait_cb(const struct child_process *cp, void *cb_data)
1442{
1443 enum ipc_active_state s = fsmonitor_ipc__get_state();
1444
1445 switch (s) {
1446 case IPC_STATE__LISTENING:
1447 /* child is "ready" */
1448 return 0;
1449
1450 case IPC_STATE__NOT_LISTENING:
1451 case IPC_STATE__PATH_NOT_FOUND:
1452 /* give child more time */
1453 return 1;
1454
1455 default:
1456 case IPC_STATE__INVALID_PATH:
1457 case IPC_STATE__OTHER_ERROR:
1458 /* all the time in world won't help */
1459 return -1;
1460 }
1461}
1462
1463static int try_to_start_background_daemon(void)
1464{
1465 struct child_process cp = CHILD_PROCESS_INIT;
1466 enum start_bg_result sbgr;
1467
1468 /*
1469 * Before we try to create a background daemon process, see
1470 * if a daemon process is already listening. This makes it
1471 * easier for us to report an already-listening error to the
1472 * console, since our spawn/daemon can only report the success
1473 * of creating the background process (and not whether it
1474 * immediately exited).
1475 */
1476 if (fsmonitor_ipc__get_state() == IPC_STATE__LISTENING)
1477 die(_("fsmonitor--daemon is already running '%s'"),
1478 the_repository->worktree);
1479
1480 if (fsmonitor__announce_startup) {
1481 fprintf(stderr, _("starting fsmonitor-daemon in '%s'\n"),
1482 the_repository->worktree);
1483 fflush(stderr);
1484 }
1485
1486 cp.git_cmd = 1;
1487
1488 strvec_push(&cp.args, "fsmonitor--daemon");
1489 strvec_push(&cp.args, "run");
1490 strvec_push(&cp.args, "--detach");
1491 strvec_pushf(&cp.args, "--ipc-threads=%d", fsmonitor__ipc_threads);
1492
1493 cp.no_stdin = 1;
1494 cp.no_stdout = 1;
1495 cp.no_stderr = 1;
1496
1497 sbgr = start_bg_command(&cp, bg_wait_cb, NULL,
1498 fsmonitor__start_timeout_sec);
1499
1500 switch (sbgr) {
1501 case SBGR_READY:
1502 return 0;
1503
1504 default:
1505 case SBGR_ERROR:
1506 case SBGR_CB_ERROR:
1507 return error(_("daemon failed to start"));
1508
1509 case SBGR_TIMEOUT:
1510 return error(_("daemon not online yet"));
1511
1512 case SBGR_DIED:
1513 return error(_("daemon terminated"));
1514 }
1515}
1516
Jeff Hostetler16d9d612022-03-25 18:02:49 +00001517int cmd_fsmonitor__daemon(int argc, const char **argv, const char *prefix)
1518{
1519 const char *subcmd;
Jeff Hostetler62a62a22022-05-26 21:46:58 +00001520 enum fsmonitor_reason reason;
Jeff Hostetlerc284e272022-03-25 18:02:54 +00001521 int detach_console = 0;
Jeff Hostetler16d9d612022-03-25 18:02:49 +00001522
1523 struct option options[] = {
Jeff Hostetlerc284e272022-03-25 18:02:54 +00001524 OPT_BOOL(0, "detach", &detach_console, N_("detach from console")),
Jeff Hostetler9dcba0b2022-03-25 18:02:53 +00001525 OPT_INTEGER(0, "ipc-threads",
1526 &fsmonitor__ipc_threads,
1527 N_("use <n> ipc worker threads")),
Jeff Hostetlerc284e272022-03-25 18:02:54 +00001528 OPT_INTEGER(0, "start-timeout",
1529 &fsmonitor__start_timeout_sec,
1530 N_("max seconds to wait for background daemon startup")),
1531
Jeff Hostetler16d9d612022-03-25 18:02:49 +00001532 OPT_END()
1533 };
1534
Jeff Hostetler9dcba0b2022-03-25 18:02:53 +00001535 git_config(fsmonitor_config, NULL);
Jeff Hostetler16d9d612022-03-25 18:02:49 +00001536
1537 argc = parse_options(argc, argv, prefix, options,
1538 builtin_fsmonitor__daemon_usage, 0);
1539 if (argc != 1)
1540 usage_with_options(builtin_fsmonitor__daemon_usage, options);
1541 subcmd = argv[0];
1542
Jeff Hostetler9dcba0b2022-03-25 18:02:53 +00001543 if (fsmonitor__ipc_threads < 1)
1544 die(_("invalid 'ipc-threads' value (%d)"),
1545 fsmonitor__ipc_threads);
1546
Jeff Hostetler62a62a22022-05-26 21:46:58 +00001547 prepare_repo_settings(the_repository);
1548 /*
1549 * If the repo is fsmonitor-compatible, explicitly set IPC-mode
1550 * (without bothering to load the `core.fsmonitor` config settings).
1551 *
1552 * If the repo is not compatible, the repo-settings will be set to
1553 * incompatible rather than IPC, so we can use one of the __get
1554 * routines to detect the discrepancy.
1555 */
1556 fsm_settings__set_ipc(the_repository);
1557
1558 reason = fsm_settings__get_reason(the_repository);
1559 if (reason > FSMONITOR_REASON_OK)
1560 die("%s",
1561 fsm_settings__get_incompatible_msg(the_repository,
1562 reason));
1563
Jeff Hostetlerc284e272022-03-25 18:02:54 +00001564 if (!strcmp(subcmd, "start"))
1565 return !!try_to_start_background_daemon();
1566
Jeff Hostetler9dcba0b2022-03-25 18:02:53 +00001567 if (!strcmp(subcmd, "run"))
Jeff Hostetlerc284e272022-03-25 18:02:54 +00001568 return !!try_to_run_foreground_daemon(detach_console);
Jeff Hostetler9dcba0b2022-03-25 18:02:53 +00001569
Jeff Hostetlerabc9dbc2022-03-25 18:02:50 +00001570 if (!strcmp(subcmd, "stop"))
1571 return !!do_as_client__send_stop();
1572
1573 if (!strcmp(subcmd, "status"))
1574 return !!do_as_client__status();
1575
Jeff Hostetler16d9d612022-03-25 18:02:49 +00001576 die(_("Unhandled subcommand '%s'"), subcmd);
1577}
1578
1579#else
1580int cmd_fsmonitor__daemon(int argc, const char **argv, const char *prefix)
1581{
1582 struct option options[] = {
1583 OPT_END()
1584 };
1585
1586 if (argc == 2 && !strcmp(argv[1], "-h"))
1587 usage_with_options(builtin_fsmonitor__daemon_usage, options);
1588
1589 die(_("fsmonitor--daemon not supported on this platform"));
1590}
1591#endif