FIXME: hotpatch for compatibility with latest hg
[git] / credential-cache.c
1 #include "cache.h"
2 #include "credential.h"
3 #include "string-list.h"
4 #include "parse-options.h"
5 #include "unix-socket.h"
6 #include "run-command.h"
7
8 static int send_request(const char *socket, const struct strbuf *out)
9 {
10         int got_data = 0;
11         int fd = unix_stream_connect(socket);
12
13         if (fd < 0)
14                 return -1;
15
16         if (write_in_full(fd, out->buf, out->len) < 0)
17                 die_errno("unable to write to cache daemon");
18         shutdown(fd, SHUT_WR);
19
20         while (1) {
21                 char in[1024];
22                 int r;
23
24                 r = read_in_full(fd, in, sizeof(in));
25                 if (r == 0)
26                         break;
27                 if (r < 0)
28                         die_errno("read error from cache daemon");
29                 write_or_die(1, in, r);
30                 got_data = 1;
31         }
32         return got_data;
33 }
34
35 static void out_str(struct strbuf *out, const char *key, const char *value)
36 {
37         if (!value)
38                 return;
39         strbuf_addf(out, "%s=%s", key, value);
40         strbuf_addch(out, '\0');
41 }
42
43 static void out_int(struct strbuf *out, const char *key, int value)
44 {
45         strbuf_addf(out, "%s=%d", key, value);
46         strbuf_addch(out, '\0');
47 }
48
49 static int do_cache(const char *socket, const char *action,
50                     const struct credential *c, int timeout)
51 {
52         struct strbuf buf = STRBUF_INIT;
53         int ret;
54
55         out_str(&buf, "action", action);
56         if (c) {
57                 out_str(&buf, "unique", c->unique);
58                 out_str(&buf, "username", c->username);
59                 out_str(&buf, "password", c->password);
60         }
61         if (timeout > 0)
62                 out_int(&buf, "timeout", timeout);
63
64         ret = send_request(socket, &buf);
65
66         strbuf_release(&buf);
67         return ret;
68 }
69
70 static void spawn_daemon(const char *socket)
71 {
72         struct child_process daemon;
73         const char *argv[] = { NULL, NULL, NULL };
74         char buf[128];
75         int r;
76
77         memset(&daemon, 0, sizeof(daemon));
78         argv[0] = "git-credential-cache--daemon";
79         argv[1] = socket;
80         daemon.argv = argv;
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         close(daemon.out);
92 }
93
94 int main(int argc, const char **argv)
95 {
96         struct credential c = { NULL };
97         char *socket_path = NULL;
98         int timeout = 900;
99         struct string_list chain = STRING_LIST_INIT_NODUP;
100         int exit_mode = 0;
101         int reject_mode = 0;
102         const char * const usage[] = {
103                 "git credential-cache [options]",
104                 NULL
105         };
106         struct option options[] = {
107                 OPT_BOOLEAN(0, "exit", &exit_mode,
108                             "tell a running daemon to exit"),
109                 OPT_BOOLEAN(0, "reject", &reject_mode,
110                             "reject a cached credential"),
111                 OPT_INTEGER(0, "timeout", &timeout,
112                             "number of seconds to cache credentials"),
113                 OPT_STRING(0, "socket", &socket_path, "path",
114                            "path of cache-daemon socket"),
115                 OPT_STRING_LIST(0, "chain", &chain, "helper",
116                                 "use <helper> to get non-cached credentials"),
117                 OPT_STRING(0, "username", &c.username, "name",
118                            "an existing username"),
119                 OPT_STRING(0, "description", &c.description, "desc",
120                            "human-readable description of the credential"),
121                 OPT_STRING(0, "unique", &c.unique, "token",
122                            "a unique context for the credential"),
123                 OPT_END()
124         };
125
126         argc = parse_options(argc, argv, NULL, options, usage, 0);
127         if (argc)
128                 usage_with_options(usage, options);
129         /* credential_reject wants to free() these */
130         if (c.username)
131                 c.username = xstrdup(c.username);
132         if (c.password)
133                 c.password = xstrdup(c.password);
134
135         if (!socket_path)
136                 socket_path = expand_user_path("~/.git-credential-cache/socket");
137         if (!socket_path)
138                 die("unable to find a suitable socket path; use --socket");
139
140         if (exit_mode) {
141                 do_cache(socket_path, "exit", NULL, -1);
142                 return 0;
143         }
144
145         if (reject_mode) {
146                 do_cache(socket_path, "erase", &c, -1);
147                 credential_reject(&c, &chain);
148                 return 0;
149         }
150
151         if (do_cache(socket_path, "get", &c, -1) > 0)
152                 return 0;
153
154         credential_fill(&c, &chain);
155         printf("username=%s\n", c.username);
156         printf("password=%s\n", c.password);
157
158         if (do_cache(socket_path, "store", &c, timeout) < 0) {
159                 spawn_daemon(socket_path);
160                 do_cache(socket_path, "store", &c, timeout);
161         }
162         return 0;
163 }