fsmonitor: close inherited file descriptors and detach in daemon

When the fsmonitor daemon is spawned as a background process, it may inherit file descriptors from its parent that it does not need. In particular, when the test harness or a CI system captures output through pipes, the daemon can inherit duplicated pipe endpoints. If the daemon holds these open, the parent process never sees EOF and may appear to hang. Set close_fd_above_stderr on the child process at both daemon startup paths: the explicit "fsmonitor--daemon start" command and the implicit spawn triggered by fsmonitor-ipc when a client finds no running daemon. Also suppress stdout and stderr on the implicit spawn path to prevent the background daemon from writing to the client's terminal. Additionally, call setsid() when the daemon starts with --detach to create a new session and process group. This prevents the daemon from being part of the spawning shell's process group, which could cause the shell's "wait" to block until the daemon exits. Signed-off-by: Paul Tarjan <github@paulisageek.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Paul Tarjan committed Apr 15, 2026 at 13:27 UTC 9266aaff0aba923eb6ef08a24d413ed7052818d7
2 files changed +17 -2
builtin/fsmonitor--daemon.c
+14 -2
@@ -1439,7 +1439,7 @@ done:
1439 return err;
1440 }
1441
1442 -static int try_to_run_foreground_daemon(int detach_console MAYBE_UNUSED)
1442 +static int try_to_run_foreground_daemon(int detach_console)
1443 {
1444 /*
1445 * Technically, we don't need to probe for an existing daemon
@@ -1459,10 +1459,21 @@ static int try_to_run_foreground_daemon(int detach_console MAYBE_UNUSED)
1459 fflush(stderr);
1460 }
1461
1462 + if (detach_console) {
1463 #ifdef GIT_WINDOWS_NATIVE
1463 - if (detach_console)
1464 FreeConsole();
1465 +#else
1466 + /*
1467 + * Create a new session so that the daemon is detached
1468 + * from the parent's process group. This prevents
1469 + * shells with job control (e.g. bash with "set -m")
1470 + * from waiting on the daemon when they wait for a
1471 + * foreground command that implicitly spawned it.
1472 + */
1473 + if (setsid() == -1)
1474 + warning_errno(_("setsid failed"));
1475 #endif
1476 + }
1477
1478 return !!fsmonitor_run_daemon();
1479 }
@@ -1525,6 +1536,7 @@ static int try_to_start_background_daemon(void)
1536 cp.no_stdin = 1;
1537 cp.no_stdout = 1;
1538 cp.no_stderr = 1;
1539 + cp.close_fd_above_stderr = 1;
1540
1541 sbgr = start_bg_command(&cp, bg_wait_cb, NULL,
1542 fsmonitor__start_timeout_sec);
fsmonitor-ipc.c
+3
@@ -61,6 +61,9 @@ static int spawn_daemon(void)
61
62 cmd.git_cmd = 1;
63 cmd.no_stdin = 1;
64 + cmd.no_stdout = 1;
65 + cmd.no_stderr = 1;
66 + cmd.close_fd_above_stderr = 1;
67 cmd.trace2_child_class = "fsmonitor";
68 strvec_pushl(&cmd.args, "fsmonitor--daemon", "start", NULL);
69