Git fork
at reftables-rust 207 lines 4.8 kB view raw
1#include "builtin.h" 2#include "credential.h" 3#include "gettext.h" 4#include "parse-options.h" 5#include "path.h" 6#include "strbuf.h" 7#include "write-or-die.h" 8 9#ifndef NO_UNIX_SOCKETS 10 11#include "unix-socket.h" 12#include "run-command.h" 13 14#define FLAG_SPAWN 0x1 15#define FLAG_RELAY 0x2 16 17#ifdef GIT_WINDOWS_NATIVE 18 19static int connection_closed(int error) 20{ 21 return (error == EINVAL); 22} 23 24static int connection_fatally_broken(int error) 25{ 26 return (error != ENOENT) && (error != ENETDOWN); 27} 28 29#else 30 31static int connection_closed(int error) 32{ 33 return error == ECONNRESET || error == ECONNABORTED; 34} 35 36static int connection_fatally_broken(int error) 37{ 38 return (error != ENOENT) && (error != ECONNREFUSED); 39} 40 41#endif 42 43static int send_request(const char *socket, const struct strbuf *out) 44{ 45 int got_data = 0; 46 int fd = unix_stream_connect(socket, 0); 47 48 if (fd < 0) 49 return -1; 50 51 if (write_in_full(fd, out->buf, out->len) < 0) 52 die_errno("unable to write to cache daemon"); 53 shutdown(fd, SHUT_WR); 54 55 while (1) { 56 char in[1024]; 57 int r; 58 59 r = read_in_full(fd, in, sizeof(in)); 60 if (r == 0 || (r < 0 && connection_closed(errno))) 61 break; 62 if (r < 0) 63 die_errno("read error from cache daemon"); 64 write_or_die(1, in, r); 65 got_data = 1; 66 } 67 close(fd); 68 return got_data; 69} 70 71static void spawn_daemon(const char *socket) 72{ 73 struct child_process daemon = CHILD_PROCESS_INIT; 74 char buf[128]; 75 int r; 76 77 strvec_pushl(&daemon.args, 78 "credential-cache--daemon", socket, 79 NULL); 80 daemon.git_cmd = 1; 81 daemon.no_stdin = 1; 82 daemon.out = -1; 83 84 if (start_command(&daemon)) 85 die_errno("unable to start cache daemon"); 86 r = read_in_full(daemon.out, buf, sizeof(buf)); 87 if (r < 0) 88 die_errno("unable to read result code from cache daemon"); 89 if (r != 3 || memcmp(buf, "ok\n", 3)) 90 die("cache daemon did not start: %.*s", r, buf); 91 92 child_process_clear(&daemon); 93 close(daemon.out); 94} 95 96static void do_cache(const char *socket, const char *action, int timeout, 97 int flags) 98{ 99 struct strbuf buf = STRBUF_INIT; 100 101 strbuf_addf(&buf, "action=%s\n", action); 102 strbuf_addf(&buf, "timeout=%d\n", timeout); 103 if (flags & FLAG_RELAY) { 104 if (strbuf_read(&buf, 0, 0) < 0) 105 die_errno("unable to relay credential"); 106 } 107 108 if (send_request(socket, &buf) < 0) { 109 if (connection_fatally_broken(errno)) 110 die_errno("unable to connect to cache daemon"); 111 if (flags & FLAG_SPAWN) { 112 spawn_daemon(socket); 113 if (send_request(socket, &buf) < 0) 114 die_errno("unable to connect to cache daemon"); 115 } 116 } 117 strbuf_release(&buf); 118} 119 120static char *get_socket_path(void) 121{ 122 struct stat sb; 123 char *old_dir, *socket; 124 old_dir = interpolate_path("~/.git-credential-cache", 0); 125 if (old_dir && !stat(old_dir, &sb) && S_ISDIR(sb.st_mode)) 126 socket = xstrfmt("%s/socket", old_dir); 127 else 128 socket = xdg_cache_home("credential/socket"); 129 free(old_dir); 130 return socket; 131} 132 133static void announce_capabilities(void) 134{ 135 struct credential c = CREDENTIAL_INIT; 136 c.capa_authtype.request_initial = 1; 137 credential_announce_capabilities(&c, stdout); 138} 139 140int cmd_credential_cache(int argc, 141 const char **argv, 142 const char *prefix, 143 struct repository *repo UNUSED) 144{ 145 const char *socket_path_arg = NULL; 146 char *socket_path; 147 int timeout = 900; 148 const char *op; 149 const char * const usage[] = { 150 "git credential-cache [<options>] <action>", 151 NULL 152 }; 153 struct option options[] = { 154 OPT_INTEGER(0, "timeout", &timeout, 155 "number of seconds to cache credentials"), 156 OPT_STRING(0, "socket", &socket_path_arg, "path", 157 "path of cache-daemon socket"), 158 OPT_END() 159 }; 160 161 argc = parse_options(argc, argv, prefix, options, usage, 0); 162 if (!argc) 163 usage_with_options(usage, options); 164 op = argv[0]; 165 166 if (!have_unix_sockets()) 167 die(_("credential-cache unavailable; no unix socket support")); 168 169 socket_path = xstrdup_or_null(socket_path_arg); 170 if (!socket_path) 171 socket_path = get_socket_path(); 172 if (!socket_path) 173 die("unable to find a suitable socket path; use --socket"); 174 175 if (!strcmp(op, "exit")) 176 do_cache(socket_path, op, timeout, 0); 177 else if (!strcmp(op, "get") || !strcmp(op, "erase")) 178 do_cache(socket_path, op, timeout, FLAG_RELAY); 179 else if (!strcmp(op, "store")) 180 do_cache(socket_path, op, timeout, FLAG_RELAY|FLAG_SPAWN); 181 else if (!strcmp(op, "capability")) 182 announce_capabilities(); 183 else 184 ; /* ignore unknown operation */ 185 186 free(socket_path); 187 return 0; 188} 189 190#else 191 192int cmd_credential_cache(int argc, const char **argv, const char *prefix, 193 struct repository *repo UNUSED) 194{ 195 const char * const usage[] = { 196 "git credential-cache [options] <action>", 197 "", 198 "credential-cache is disabled in this build of Git", 199 NULL 200 }; 201 struct option options[] = { OPT_END() }; 202 203 argc = parse_options(argc, argv, prefix, options, usage, 0); 204 die(_("credential-cache unavailable; no unix socket support")); 205} 206 207#endif /* NO_UNIX_SOCKETS */