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 intagent_advertise(struct repository *r, 10struct strbuf *value) 11{ 12if(value) 13strbuf_addstr(value,git_user_agent_sanitized()); 14return1; 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 */ 23const 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 */ 31int(*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 */ 42int(*command)(struct repository *r, 43struct argv_array *keys, 44struct packet_reader *request); 45}; 46 47static struct protocol_capability capabilities[] = { 48{"agent", agent_advertise, NULL }, 49}; 50 51static voidadvertise_capabilities(void) 52{ 53struct strbuf capability = STRBUF_INIT; 54struct strbuf value = STRBUF_INIT; 55int i; 56 57for(i =0; i <ARRAY_SIZE(capabilities); i++) { 58struct protocol_capability *c = &capabilities[i]; 59 60if(c->advertise(the_repository, &value)) { 61strbuf_addstr(&capability, c->name); 62 63if(value.len) { 64strbuf_addch(&capability,'='); 65strbuf_addbuf(&capability, &value); 66} 67 68strbuf_addch(&capability,'\n'); 69packet_write(1, capability.buf, capability.len); 70} 71 72strbuf_reset(&capability); 73strbuf_reset(&value); 74} 75 76packet_flush(1); 77strbuf_release(&capability); 78strbuf_release(&value); 79} 80 81static struct protocol_capability *get_capability(const char*key) 82{ 83int i; 84 85if(!key) 86return NULL; 87 88for(i =0; i <ARRAY_SIZE(capabilities); i++) { 89struct protocol_capability *c = &capabilities[i]; 90const char*out; 91if(skip_prefix(key, c->name, &out) && (!*out || *out =='=')) 92return c; 93} 94 95return NULL; 96} 97 98static intis_valid_capability(const char*key) 99{ 100const struct protocol_capability *c =get_capability(key); 101 102return c && c->advertise(the_repository, NULL); 103} 104 105static intis_command(const char*key,struct protocol_capability **command) 106{ 107const char*out; 108 109if(skip_prefix(key,"command=", &out)) { 110struct protocol_capability *cmd =get_capability(out); 111 112if(*command) 113die("command '%s' requested after already requesting command '%s'", 114 out, (*command)->name); 115if(!cmd || !cmd->advertise(the_repository, NULL) || !cmd->command) 116die("invalid command '%s'", out); 117 118*command = cmd; 119return1; 120} 121 122return0; 123} 124 125inthas_capability(const struct argv_array *keys,const char*capability, 126const char**value) 127{ 128int i; 129for(i =0; i < keys->argc; i++) { 130const char*out; 131if(skip_prefix(keys->argv[i], capability, &out) && 132(!*out || *out =='=')) { 133if(value) { 134if(*out =='=') 135 out++; 136*value = out; 137} 138return1; 139} 140} 141 142return0; 143} 144 145enum request_state { 146 PROCESS_REQUEST_KEYS, 147 PROCESS_REQUEST_DONE, 148}; 149 150static intprocess_request(void) 151{ 152enum request_state state = PROCESS_REQUEST_KEYS; 153struct packet_reader reader; 154struct argv_array keys = ARGV_ARRAY_INIT; 155struct protocol_capability *command = NULL; 156 157packet_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 */ 165if(packet_reader_peek(&reader) == PACKET_READ_EOF) 166return1; 167 reader.options = PACKET_READ_CHOMP_NEWLINE; 168 169while(state != PROCESS_REQUEST_DONE) { 170switch(packet_reader_peek(&reader)) { 171case PACKET_READ_EOF: 172BUG("Should have already died when seeing EOF"); 173case PACKET_READ_NORMAL: 174/* collect request; a sequence of keys and values */ 175if(is_command(reader.line, &command) || 176is_valid_capability(reader.line)) 177argv_array_push(&keys, reader.line); 178else 179die("unknown capability '%s'", reader.line); 180 181/* Consume the peeked line */ 182packet_reader_read(&reader); 183break; 184case PACKET_READ_FLUSH: 185/* 186 * If no command and no keys were given then the client 187 * wanted to terminate the connection. 188 */ 189if(!keys.argc) 190return1; 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; 201break; 202case PACKET_READ_DELIM: 203/* Consume the peeked line */ 204packet_reader_read(&reader); 205 206 state = PROCESS_REQUEST_DONE; 207break; 208} 209} 210 211if(!command) 212die("no command requested"); 213 214 command->command(the_repository, &keys, &reader); 215 216argv_array_clear(&keys); 217return0; 218} 219 220/* Main serve loop for protocol version 2 */ 221voidserve(struct serve_options *options) 222{ 223if(options->advertise_capabilities || !options->stateless_rpc) { 224/* serve by default supports v2 */ 225packet_write_fmt(1,"version 2\n"); 226 227advertise_capabilities(); 228/* 229 * If only the list of capabilities was requested exit 230 * immediately after advertising capabilities 231 */ 232if(options->advertise_capabilities) 233return; 234} 235 236/* 237 * If stateless-rpc was requested then exit after 238 * a single request/response exchange 239 */ 240if(options->stateless_rpc) { 241process_request(); 242}else{ 243for(;;) 244if(process_request()) 245break; 246} 247}