| #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) |
| { |
| 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, |
| struct strbuf *answer) |
| { |
| return -1; |
| } |
| |
| int fsmonitor_ipc__send_command(const char *command, |
| struct strbuf *answer) |
| { |
| 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 |