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