mirror of
https://github.com/git/git.git
synced 2026-03-07 15:57:43 +01:00
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>
180 lines
4.1 KiB
C
180 lines
4.1 KiB
C
#define USE_THE_REPOSITORY_VARIABLE
|
|
|
|
#include "git-compat-util.h"
|
|
#include "gettext.h"
|
|
#include "simple-ipc.h"
|
|
#include "fsmonitor-ipc.h"
|
|
#include "repository.h"
|
|
#include "run-command.h"
|
|
#include "strbuf.h"
|
|
#include "trace2.h"
|
|
|
|
#ifndef HAVE_FSMONITOR_DAEMON_BACKEND
|
|
|
|
/*
|
|
* A trivial implementation of the fsmonitor_ipc__ API for unsupported
|
|
* platforms.
|
|
*/
|
|
|
|
int fsmonitor_ipc__is_supported(void)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
const char *fsmonitor_ipc__get_path(struct repository *r UNUSED)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
enum ipc_active_state fsmonitor_ipc__get_state(void)
|
|
{
|
|
return IPC_STATE__OTHER_ERROR;
|
|
}
|
|
|
|
int fsmonitor_ipc__send_query(const char *since_token UNUSED,
|
|
struct strbuf *answer UNUSED)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
int fsmonitor_ipc__send_command(const char *command UNUSED,
|
|
struct strbuf *answer UNUSED)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
#else
|
|
|
|
int fsmonitor_ipc__is_supported(void)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
enum ipc_active_state fsmonitor_ipc__get_state(void)
|
|
{
|
|
return ipc_get_active_state(fsmonitor_ipc__get_path(the_repository));
|
|
}
|
|
|
|
static int spawn_daemon(void)
|
|
{
|
|
struct child_process cmd = CHILD_PROCESS_INIT;
|
|
|
|
cmd.git_cmd = 1;
|
|
cmd.no_stdin = 1;
|
|
cmd.no_stdout = 1;
|
|
cmd.no_stderr = 1;
|
|
cmd.close_fd_above_stderr = 1;
|
|
cmd.trace2_child_class = "fsmonitor";
|
|
strvec_pushl(&cmd.args, "fsmonitor--daemon", "start", NULL);
|
|
|
|
return run_command(&cmd);
|
|
}
|
|
|
|
int fsmonitor_ipc__send_query(const char *since_token,
|
|
struct strbuf *answer)
|
|
{
|
|
int ret = -1;
|
|
int tried_to_spawn = 0;
|
|
enum ipc_active_state state = IPC_STATE__OTHER_ERROR;
|
|
struct ipc_client_connection *connection = NULL;
|
|
struct ipc_client_connect_options options
|
|
= IPC_CLIENT_CONNECT_OPTIONS_INIT;
|
|
const char *tok = since_token ? since_token : "";
|
|
size_t tok_len = since_token ? strlen(since_token) : 0;
|
|
|
|
options.wait_if_busy = 1;
|
|
options.wait_if_not_found = 0;
|
|
|
|
trace2_region_enter("fsm_client", "query", NULL);
|
|
trace2_data_string("fsm_client", NULL, "query/command", tok);
|
|
|
|
try_again:
|
|
state = ipc_client_try_connect(fsmonitor_ipc__get_path(the_repository),
|
|
&options, &connection);
|
|
|
|
switch (state) {
|
|
case IPC_STATE__LISTENING:
|
|
ret = ipc_client_send_command_to_connection(
|
|
connection, tok, tok_len, answer);
|
|
ipc_client_close_connection(connection);
|
|
|
|
trace2_data_intmax("fsm_client", NULL,
|
|
"query/response-length", answer->len);
|
|
goto done;
|
|
|
|
case IPC_STATE__NOT_LISTENING:
|
|
case IPC_STATE__PATH_NOT_FOUND:
|
|
if (tried_to_spawn)
|
|
goto done;
|
|
|
|
tried_to_spawn++;
|
|
if (spawn_daemon())
|
|
goto done;
|
|
|
|
/*
|
|
* Try again, but this time give the daemon a chance to
|
|
* actually create the pipe/socket.
|
|
*
|
|
* Granted, the daemon just started so it can't possibly have
|
|
* any FS cached yet, so we'll always get a trivial answer.
|
|
* BUT the answer should include a new token that can serve
|
|
* as the basis for subsequent requests.
|
|
*/
|
|
options.wait_if_not_found = 1;
|
|
goto try_again;
|
|
|
|
case IPC_STATE__INVALID_PATH:
|
|
ret = error(_("fsmonitor_ipc__send_query: invalid path '%s'"),
|
|
fsmonitor_ipc__get_path(the_repository));
|
|
goto done;
|
|
|
|
case IPC_STATE__OTHER_ERROR:
|
|
default:
|
|
ret = error(_("fsmonitor_ipc__send_query: unspecified error on '%s'"),
|
|
fsmonitor_ipc__get_path(the_repository));
|
|
goto done;
|
|
}
|
|
|
|
done:
|
|
trace2_region_leave("fsm_client", "query", NULL);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int fsmonitor_ipc__send_command(const char *command,
|
|
struct strbuf *answer)
|
|
{
|
|
struct ipc_client_connection *connection = NULL;
|
|
struct ipc_client_connect_options options
|
|
= IPC_CLIENT_CONNECT_OPTIONS_INIT;
|
|
int ret;
|
|
enum ipc_active_state state;
|
|
const char *c = command ? command : "";
|
|
size_t c_len = command ? strlen(command) : 0;
|
|
|
|
strbuf_reset(answer);
|
|
|
|
options.wait_if_busy = 1;
|
|
options.wait_if_not_found = 0;
|
|
|
|
state = ipc_client_try_connect(fsmonitor_ipc__get_path(the_repository),
|
|
&options, &connection);
|
|
if (state != IPC_STATE__LISTENING) {
|
|
die(_("fsmonitor--daemon is not running"));
|
|
return -1;
|
|
}
|
|
|
|
ret = ipc_client_send_command_to_connection(connection, c, c_len,
|
|
answer);
|
|
ipc_client_close_connection(connection);
|
|
|
|
if (ret == -1) {
|
|
die(_("could not send '%s' command to fsmonitor--daemon"), c);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif
|