| #include "git-compat-util.h" | |
| #include "fsmonitor-ll.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.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 |