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