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