Merge branch 'master' of git://github.com/alshopov/git-po
[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 #define FLAG_SPAWN 0x1
9 #define FLAG_RELAY 0x2
10
11 static int send_request(const char *socket, const struct strbuf *out)
12 {
13         int got_data = 0;
14         int fd = unix_stream_connect(socket);
15
16         if (fd < 0)
17                 return -1;
18
19         if (write_in_full(fd, out->buf, out->len) < 0)
20                 die_errno("unable to write to cache daemon");
21         shutdown(fd, SHUT_WR);
22
23         while (1) {
24                 char in[1024];
25                 int r;
26
27                 r = read_in_full(fd, in, sizeof(in));
28                 if (r == 0)
29                         break;
30                 if (r < 0)
31                         die_errno("read error from cache daemon");
32                 write_or_die(1, in, r);
33                 got_data = 1;
34         }
35         close(fd);
36         return got_data;
37 }
38
39 static void spawn_daemon(const char *socket)
40 {
41         struct child_process daemon = CHILD_PROCESS_INIT;
42         const char *argv[] = { NULL, NULL, NULL };
43         char buf[128];
44         int r;
45
46         argv[0] = "git-credential-cache--daemon";
47         argv[1] = socket;
48         daemon.argv = argv;
49         daemon.no_stdin = 1;
50         daemon.out = -1;
51
52         if (start_command(&daemon))
53                 die_errno("unable to start cache daemon");
54         r = read_in_full(daemon.out, buf, sizeof(buf));
55         if (r < 0)
56                 die_errno("unable to read result code from cache daemon");
57         if (r != 3 || memcmp(buf, "ok\n", 3))
58                 die("cache daemon did not start: %.*s", r, buf);
59         close(daemon.out);
60 }
61
62 static void do_cache(const char *socket, const char *action, int timeout,
63                      int flags)
64 {
65         struct strbuf buf = STRBUF_INIT;
66
67         strbuf_addf(&buf, "action=%s\n", action);
68         strbuf_addf(&buf, "timeout=%d\n", timeout);
69         if (flags & FLAG_RELAY) {
70                 if (strbuf_read(&buf, 0, 0) < 0)
71                         die_errno("unable to relay credential");
72         }
73
74         if (send_request(socket, &buf) < 0) {
75                 if (errno != ENOENT && errno != ECONNREFUSED)
76                         die_errno("unable to connect to cache daemon");
77                 if (flags & FLAG_SPAWN) {
78                         spawn_daemon(socket);
79                         if (send_request(socket, &buf) < 0)
80                                 die_errno("unable to connect to cache daemon");
81                 }
82         }
83         strbuf_release(&buf);
84 }
85
86 static char *get_socket_path(void)
87 {
88         struct stat sb;
89         char *old_dir, *socket;
90         old_dir = expand_user_path("~/.git-credential-cache", 0);
91         if (old_dir && !stat(old_dir, &sb) && S_ISDIR(sb.st_mode))
92                 socket = xstrfmt("%s/socket", old_dir);
93         else
94                 socket = xdg_cache_home("credential/socket");
95         free(old_dir);
96         return socket;
97 }
98
99 int cmd_main(int argc, const char **argv)
100 {
101         char *socket_path = NULL;
102         int timeout = 900;
103         const char *op;
104         const char * const usage[] = {
105                 "git credential-cache [<options>] <action>",
106                 NULL
107         };
108         struct option options[] = {
109                 OPT_INTEGER(0, "timeout", &timeout,
110                             "number of seconds to cache credentials"),
111                 OPT_STRING(0, "socket", &socket_path, "path",
112                            "path of cache-daemon socket"),
113                 OPT_END()
114         };
115
116         argc = parse_options(argc, argv, NULL, options, usage, 0);
117         if (!argc)
118                 usage_with_options(usage, options);
119         op = argv[0];
120
121         if (!socket_path)
122                 socket_path = get_socket_path();
123         if (!socket_path)
124                 die("unable to find a suitable socket path; use --socket");
125
126         if (!strcmp(op, "exit"))
127                 do_cache(socket_path, op, timeout, 0);
128         else if (!strcmp(op, "get") || !strcmp(op, "erase"))
129                 do_cache(socket_path, op, timeout, FLAG_RELAY);
130         else if (!strcmp(op, "store"))
131                 do_cache(socket_path, op, timeout, FLAG_RELAY|FLAG_SPAWN);
132         else
133                 ; /* ignore unknown operation */
134
135         return 0;
136 }