Merge branch 'ab/config-based-hooks-base' into seen
[git] / serve.c
1 #include "cache.h"
2 #include "repository.h"
3 #include "config.h"
4 #include "pkt-line.h"
5 #include "version.h"
6 #include "strvec.h"
7 #include "ls-refs.h"
8 #include "protocol-caps.h"
9 #include "serve.h"
10 #include "upload-pack.h"
11
12 static int advertise_sid;
13
14 static int always_advertise(struct repository *r,
15                             struct strbuf *value)
16 {
17         return 1;
18 }
19
20 static int agent_advertise(struct repository *r,
21                            struct strbuf *value)
22 {
23         if (value)
24                 strbuf_addstr(value, git_user_agent_sanitized());
25         return 1;
26 }
27
28 static int object_format_advertise(struct repository *r,
29                                    struct strbuf *value)
30 {
31         if (value)
32                 strbuf_addstr(value, r->hash_algo->name);
33         return 1;
34 }
35
36 static int session_id_advertise(struct repository *r, struct strbuf *value)
37 {
38         if (!advertise_sid)
39                 return 0;
40         if (value)
41                 strbuf_addstr(value, trace2_session_id());
42         return 1;
43 }
44
45 struct protocol_capability {
46         /*
47          * The name of the capability.  The server uses this name when
48          * advertising this capability, and the client uses this name to
49          * specify this capability.
50          */
51         const char *name;
52
53         /*
54          * Function queried to see if a capability should be advertised.
55          * Optionally a value can be specified by adding it to 'value'.
56          * If a value is added to 'value', the server will advertise this
57          * capability as "<name>=<value>" instead of "<name>".
58          */
59         int (*advertise)(struct repository *r, struct strbuf *value);
60
61         /*
62          * Function called when a client requests the capability as a command.
63          * The function will be provided the capabilities requested via 'keys'
64          * as well as a struct packet_reader 'request' which the command should
65          * use to read the command specific part of the request.  Every command
66          * MUST read until a flush packet is seen before sending a response.
67          *
68          * This field should be NULL for capabilities which are not commands.
69          */
70         int (*command)(struct repository *r,
71                        struct strvec *keys,
72                        struct packet_reader *request);
73 };
74
75 static struct protocol_capability capabilities[] = {
76         {
77                 .name = "agent",
78                 .advertise = agent_advertise,
79         },
80         {
81                 .name = "ls-refs",
82                 .advertise = ls_refs_advertise,
83                 .command = ls_refs,
84         },
85         {
86                 .name = "fetch",
87                 .advertise = upload_pack_advertise,
88                 .command = upload_pack_v2,
89         },
90         {
91                 .name = "server-option",
92                 .advertise = always_advertise,
93         },
94         {
95                 .name = "object-format",
96                 .advertise = object_format_advertise,
97         },
98         {
99                 .name = "session-id",
100                 .advertise = session_id_advertise,
101         },
102         {
103                 .name = "object-info",
104                 .advertise = always_advertise,
105                 .command = cap_object_info,
106         },
107 };
108
109 static void advertise_capabilities(void)
110 {
111         struct strbuf capability = STRBUF_INIT;
112         struct strbuf value = STRBUF_INIT;
113         int i;
114
115         for (i = 0; i < ARRAY_SIZE(capabilities); i++) {
116                 struct protocol_capability *c = &capabilities[i];
117
118                 if (c->advertise(the_repository, &value)) {
119                         strbuf_addstr(&capability, c->name);
120
121                         if (value.len) {
122                                 strbuf_addch(&capability, '=');
123                                 strbuf_addbuf(&capability, &value);
124                         }
125
126                         strbuf_addch(&capability, '\n');
127                         packet_write(1, capability.buf, capability.len);
128                 }
129
130                 strbuf_reset(&capability);
131                 strbuf_reset(&value);
132         }
133
134         packet_flush(1);
135         strbuf_release(&capability);
136         strbuf_release(&value);
137 }
138
139 static struct protocol_capability *get_capability(const char *key)
140 {
141         int i;
142
143         if (!key)
144                 return NULL;
145
146         for (i = 0; i < ARRAY_SIZE(capabilities); i++) {
147                 struct protocol_capability *c = &capabilities[i];
148                 const char *out;
149                 if (skip_prefix(key, c->name, &out) && (!*out || *out == '='))
150                         return c;
151         }
152
153         return NULL;
154 }
155
156 static int is_valid_capability(const char *key)
157 {
158         const struct protocol_capability *c = get_capability(key);
159
160         return c && c->advertise(the_repository, NULL);
161 }
162
163 static int is_command(const char *key, struct protocol_capability **command)
164 {
165         const char *out;
166
167         if (skip_prefix(key, "command=", &out)) {
168                 struct protocol_capability *cmd = get_capability(out);
169
170                 if (*command)
171                         die("command '%s' requested after already requesting command '%s'",
172                             out, (*command)->name);
173                 if (!cmd || !cmd->advertise(the_repository, NULL) || !cmd->command)
174                         die("invalid command '%s'", out);
175
176                 *command = cmd;
177                 return 1;
178         }
179
180         return 0;
181 }
182
183 static int has_capability(const struct strvec *keys, const char *capability,
184                           const char **value)
185 {
186         int i;
187         for (i = 0; i < keys->nr; i++) {
188                 const char *out;
189                 if (skip_prefix(keys->v[i], capability, &out) &&
190                     (!*out || *out == '=')) {
191                         if (value) {
192                                 if (*out == '=')
193                                         out++;
194                                 *value = out;
195                         }
196                         return 1;
197                 }
198         }
199
200         return 0;
201 }
202
203 static void check_algorithm(struct repository *r, struct strvec *keys)
204 {
205         int client = GIT_HASH_SHA1, server = hash_algo_by_ptr(r->hash_algo);
206         const char *algo_name;
207
208         if (has_capability(keys, "object-format", &algo_name)) {
209                 client = hash_algo_by_name(algo_name);
210                 if (client == GIT_HASH_UNKNOWN)
211                         die("unknown object format '%s'", algo_name);
212         }
213
214         if (client != server)
215                 die("mismatched object format: server %s; client %s\n",
216                     r->hash_algo->name, hash_algos[client].name);
217 }
218
219 enum request_state {
220         PROCESS_REQUEST_KEYS,
221         PROCESS_REQUEST_DONE,
222 };
223
224 static int process_request(void)
225 {
226         enum request_state state = PROCESS_REQUEST_KEYS;
227         struct packet_reader reader;
228         struct strvec keys = STRVEC_INIT;
229         struct protocol_capability *command = NULL;
230         const char *client_sid;
231
232         packet_reader_init(&reader, 0, NULL, 0,
233                            PACKET_READ_CHOMP_NEWLINE |
234                            PACKET_READ_GENTLE_ON_EOF |
235                            PACKET_READ_DIE_ON_ERR_PACKET);
236
237         /*
238          * Check to see if the client closed their end before sending another
239          * request.  If so we can terminate the connection.
240          */
241         if (packet_reader_peek(&reader) == PACKET_READ_EOF)
242                 return 1;
243         reader.options &= ~PACKET_READ_GENTLE_ON_EOF;
244
245         while (state != PROCESS_REQUEST_DONE) {
246                 switch (packet_reader_peek(&reader)) {
247                 case PACKET_READ_EOF:
248                         BUG("Should have already died when seeing EOF");
249                 case PACKET_READ_NORMAL:
250                         /* collect request; a sequence of keys and values */
251                         if (is_command(reader.line, &command) ||
252                             is_valid_capability(reader.line))
253                                 strvec_push(&keys, reader.line);
254                         else
255                                 die("unknown capability '%s'", reader.line);
256
257                         /* Consume the peeked line */
258                         packet_reader_read(&reader);
259                         break;
260                 case PACKET_READ_FLUSH:
261                         /*
262                          * If no command and no keys were given then the client
263                          * wanted to terminate the connection.
264                          */
265                         if (!keys.nr)
266                                 return 1;
267
268                         /*
269                          * The flush packet isn't consume here like it is in
270                          * the other parts of this switch statement.  This is
271                          * so that the command can read the flush packet and
272                          * see the end of the request in the same way it would
273                          * if command specific arguments were provided after a
274                          * delim packet.
275                          */
276                         state = PROCESS_REQUEST_DONE;
277                         break;
278                 case PACKET_READ_DELIM:
279                         /* Consume the peeked line */
280                         packet_reader_read(&reader);
281
282                         state = PROCESS_REQUEST_DONE;
283                         break;
284                 case PACKET_READ_RESPONSE_END:
285                         BUG("unexpected stateless separator packet");
286                 }
287         }
288
289         if (!command)
290                 die("no command requested");
291
292         check_algorithm(the_repository, &keys);
293
294         if (has_capability(&keys, "session-id", &client_sid))
295                 trace2_data_string("transfer", NULL, "client-sid", client_sid);
296
297         command->command(the_repository, &keys, &reader);
298
299         strvec_clear(&keys);
300         return 0;
301 }
302
303 /* Main serve loop for protocol version 2 */
304 void serve(struct serve_options *options)
305 {
306         git_config_get_bool("transfer.advertisesid", &advertise_sid);
307
308         if (options->advertise_capabilities || !options->stateless_rpc) {
309                 /* serve by default supports v2 */
310                 packet_write_fmt(1, "version 2\n");
311
312                 advertise_capabilities();
313                 /*
314                  * If only the list of capabilities was requested exit
315                  * immediately after advertising capabilities
316                  */
317                 if (options->advertise_capabilities)
318                         return;
319         }
320
321         /*
322          * If stateless-rpc was requested then exit after
323          * a single request/response exchange
324          */
325         if (options->stateless_rpc) {
326                 process_request();
327         } else {
328                 for (;;)
329                         if (process_request())
330                                 break;
331         }
332 }