1#include "cache.h" 2#include "repository.h" 3#include "config.h" 4#include "pkt-line.h" 5#include "version.h" 6#include "argv-array.h" 7#include "ls-refs.h" 8#include "serve.h" 9#include "upload-pack.h" 10 11static int always_advertise(struct repository *r, 12 struct strbuf *value) 13{ 14 return 1; 15} 16 17static int agent_advertise(struct repository *r, 18 struct strbuf *value) 19{ 20 if (value) 21 strbuf_addstr(value, git_user_agent_sanitized()); 22 return 1; 23} 24 25struct protocol_capability { 26 /* 27 * The name of the capability. The server uses this name when 28 * advertising this capability, and the client uses this name to 29 * specify this capability. 30 */ 31 const char *name; 32 33 /* 34 * Function queried to see if a capability should be advertised. 35 * Optionally a value can be specified by adding it to 'value'. 36 * If a value is added to 'value', the server will advertise this 37 * capability as "<name>=<value>" instead of "<name>". 38 */ 39 int (*advertise)(struct repository *r, struct strbuf *value); 40 41 /* 42 * Function called when a client requests the capability as a command. 43 * The function will be provided the capabilities requested via 'keys' 44 * as well as a struct packet_reader 'request' which the command should 45 * use to read the command specific part of the request. Every command 46 * MUST read until a flush packet is seen before sending a response. 47 * 48 * This field should be NULL for capabilities which are not commands. 49 */ 50 int (*command)(struct repository *r, 51 struct argv_array *keys, 52 struct packet_reader *request); 53}; 54 55static struct protocol_capability capabilities[] = { 56 { "agent", agent_advertise, NULL }, 57 { "ls-refs", always_advertise, ls_refs }, 58 { "fetch", always_advertise, upload_pack_v2 }, 59}; 60 61static void advertise_capabilities(void) 62{ 63 struct strbuf capability = STRBUF_INIT; 64 struct strbuf value = STRBUF_INIT; 65 int i; 66 67 for (i = 0; i < ARRAY_SIZE(capabilities); i++) { 68 struct protocol_capability *c = &capabilities[i]; 69 70 if (c->advertise(the_repository, &value)) { 71 strbuf_addstr(&capability, c->name); 72 73 if (value.len) { 74 strbuf_addch(&capability, '='); 75 strbuf_addbuf(&capability, &value); 76 } 77 78 strbuf_addch(&capability, '\n'); 79 packet_write(1, capability.buf, capability.len); 80 } 81 82 strbuf_reset(&capability); 83 strbuf_reset(&value); 84 } 85 86 packet_flush(1); 87 strbuf_release(&capability); 88 strbuf_release(&value); 89} 90 91static struct protocol_capability *get_capability(const char *key) 92{ 93 int i; 94 95 if (!key) 96 return NULL; 97 98 for (i = 0; i < ARRAY_SIZE(capabilities); i++) { 99 struct protocol_capability *c = &capabilities[i]; 100 const char *out; 101 if (skip_prefix(key, c->name, &out) && (!*out || *out == '=')) 102 return c; 103 } 104 105 return NULL; 106} 107 108static int is_valid_capability(const char *key) 109{ 110 const struct protocol_capability *c = get_capability(key); 111 112 return c && c->advertise(the_repository, NULL); 113} 114 115static int is_command(const char *key, struct protocol_capability **command) 116{ 117 const char *out; 118 119 if (skip_prefix(key, "command=", &out)) { 120 struct protocol_capability *cmd = get_capability(out); 121 122 if (*command) 123 die("command '%s' requested after already requesting command '%s'", 124 out, (*command)->name); 125 if (!cmd || !cmd->advertise(the_repository, NULL) || !cmd->command) 126 die("invalid command '%s'", out); 127 128 *command = cmd; 129 return 1; 130 } 131 132 return 0; 133} 134 135int has_capability(const struct argv_array *keys, const char *capability, 136 const char **value) 137{ 138 int i; 139 for (i = 0; i < keys->argc; i++) { 140 const char *out; 141 if (skip_prefix(keys->argv[i], capability, &out) && 142 (!*out || *out == '=')) { 143 if (value) { 144 if (*out == '=') 145 out++; 146 *value = out; 147 } 148 return 1; 149 } 150 } 151 152 return 0; 153} 154 155enum request_state { 156 PROCESS_REQUEST_KEYS, 157 PROCESS_REQUEST_DONE, 158}; 159 160static int process_request(void) 161{ 162 enum request_state state = PROCESS_REQUEST_KEYS; 163 struct packet_reader reader; 164 struct argv_array keys = ARGV_ARRAY_INIT; 165 struct protocol_capability *command = NULL; 166 167 packet_reader_init(&reader, 0, NULL, 0, 168 PACKET_READ_CHOMP_NEWLINE | 169 PACKET_READ_GENTLE_ON_EOF); 170 171 /* 172 * Check to see if the client closed their end before sending another 173 * request. If so we can terminate the connection. 174 */ 175 if (packet_reader_peek(&reader) == PACKET_READ_EOF) 176 return 1; 177 reader.options = PACKET_READ_CHOMP_NEWLINE; 178 179 while (state != PROCESS_REQUEST_DONE) { 180 switch (packet_reader_peek(&reader)) { 181 case PACKET_READ_EOF: 182 BUG("Should have already died when seeing EOF"); 183 case PACKET_READ_NORMAL: 184 /* collect request; a sequence of keys and values */ 185 if (is_command(reader.line, &command) || 186 is_valid_capability(reader.line)) 187 argv_array_push(&keys, reader.line); 188 else 189 die("unknown capability '%s'", reader.line); 190 191 /* Consume the peeked line */ 192 packet_reader_read(&reader); 193 break; 194 case PACKET_READ_FLUSH: 195 /* 196 * If no command and no keys were given then the client 197 * wanted to terminate the connection. 198 */ 199 if (!keys.argc) 200 return 1; 201 202 /* 203 * The flush packet isn't consume here like it is in 204 * the other parts of this switch statement. This is 205 * so that the command can read the flush packet and 206 * see the end of the request in the same way it would 207 * if command specific arguments were provided after a 208 * delim packet. 209 */ 210 state = PROCESS_REQUEST_DONE; 211 break; 212 case PACKET_READ_DELIM: 213 /* Consume the peeked line */ 214 packet_reader_read(&reader); 215 216 state = PROCESS_REQUEST_DONE; 217 break; 218 } 219 } 220 221 if (!command) 222 die("no command requested"); 223 224 command->command(the_repository, &keys, &reader); 225 226 argv_array_clear(&keys); 227 return 0; 228} 229 230/* Main serve loop for protocol version 2 */ 231void serve(struct serve_options *options) 232{ 233 if (options->advertise_capabilities || !options->stateless_rpc) { 234 /* serve by default supports v2 */ 235 packet_write_fmt(1, "version 2\n"); 236 237 advertise_capabilities(); 238 /* 239 * If only the list of capabilities was requested exit 240 * immediately after advertising capabilities 241 */ 242 if (options->advertise_capabilities) 243 return; 244 } 245 246 /* 247 * If stateless-rpc was requested then exit after 248 * a single request/response exchange 249 */ 250 if (options->stateless_rpc) { 251 process_request(); 252 } else { 253 for (;;) 254 if (process_request()) 255 break; 256 } 257}