git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
blob 90e3defe83618fc8f4f81aa1dde2afa4bf160bc3 5805 bytes (raw)
name: serve.c 	 # note: path name is non-authoritative(*)

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
 
#include "cache.h"
#include "repository.h"
#include "config.h"
#include "pkt-line.h"
#include "version.h"
#include "argv-array.h"
#include "serve.h"

static int always_advertise(struct repository *r,
			    struct strbuf *value)
{
	return 1;
}

static int agent_advertise(struct repository *r,
			   struct strbuf *value)
{
	if (value)
		strbuf_addstr(value, git_user_agent_sanitized());
	return 1;
}

struct protocol_capability {
	/*
	 * The name of the capability.  The server uses this name when
	 * advertising this capability, and the client uses this name to
	 * specify this capability.
	 */
	const char *name;

	/*
	 * Function queried to see if a capability should be advertised.
	 * Optionally a value can be specified by adding it to 'value'.
	 * If a value is added to 'value', the server will advertise this
	 * capability as "<name>=<value>" instead of "<name>".
	 */
	int (*advertise)(struct repository *r, struct strbuf *value);

	/*
	 * Function called when a client requests the capability as a command.
	 * The command request will be provided to the function via 'keys', the
	 * capabilities requested, and 'args', the command specific parameters.
	 *
	 * This field should be NULL for capabilities which are not commands.
	 */
	int (*command)(struct repository *r,
		       struct argv_array *keys,
		       struct argv_array *args);
};

static struct protocol_capability capabilities[] = {
	{ "agent", agent_advertise, NULL },
	{ "stateless-rpc", always_advertise, NULL },
};

static void advertise_capabilities(void)
{
	struct strbuf capability = STRBUF_INIT;
	struct strbuf value = STRBUF_INIT;
	int i;

	for (i = 0; i < ARRAY_SIZE(capabilities); i++) {
		struct protocol_capability *c = &capabilities[i];

		if (c->advertise(the_repository, &value)) {
			strbuf_addstr(&capability, c->name);

			if (value.len) {
				strbuf_addch(&capability, '=');
				strbuf_addbuf(&capability, &value);
			}

			strbuf_addch(&capability, '\n');
			packet_write(1, capability.buf, capability.len);
		}

		strbuf_reset(&capability);
		strbuf_reset(&value);
	}

	packet_flush(1);
	strbuf_release(&capability);
	strbuf_release(&value);
}

static struct protocol_capability *get_capability(const char *key)
{
	int i;

	if (!key)
		return NULL;

	for (i = 0; i < ARRAY_SIZE(capabilities); i++) {
		struct protocol_capability *c = &capabilities[i];
		const char *out;
		if (skip_prefix(key, c->name, &out) && (!*out || *out == '='))
			return c;
	}

	return NULL;
}

static int is_valid_capability(const char *key)
{
	const struct protocol_capability *c = get_capability(key);

	return c && c->advertise(the_repository, NULL);
}

static int is_command(const char *key, struct protocol_capability **command)
{
	const char *out;

	if (skip_prefix(key, "command=", &out)) {
		struct protocol_capability *cmd = get_capability(out);

		if (!cmd || !cmd->advertise(the_repository, NULL) || !cmd->command)
			die("invalid command '%s'", out);
		if (*command)
			die("command already requested");

		*command = cmd;
		return 1;
	}

	return 0;
}

int has_capability(const struct argv_array *keys, const char *capability,
		   const char **value)
{
	int i;
	for (i = 0; i < keys->argc; i++) {
		const char *out;
		if (skip_prefix(keys->argv[i], capability, &out) &&
		    (!*out || *out == '=')) {
			if (value) {
				if (*out == '=')
					out++;
				*value = out;
			}
			return 1;
		}
	}

	return 0;
}

enum request_state {
	PROCESS_REQUEST_KEYS = 0,
	PROCESS_REQUEST_ARGS,
	PROCESS_REQUEST_DONE,
};

static int process_request(void)
{
	enum request_state state = PROCESS_REQUEST_KEYS;
	char *buffer = packet_buffer;
	unsigned buffer_size = sizeof(packet_buffer);
	int pktlen;
	struct argv_array keys = ARGV_ARRAY_INIT;
	struct argv_array args = ARGV_ARRAY_INIT;
	struct protocol_capability *command = NULL;

	while (state != PROCESS_REQUEST_DONE) {
		switch (packet_read_with_status(0, NULL, NULL, buffer,
						buffer_size, &pktlen,
						PACKET_READ_CHOMP_NEWLINE)) {
		case PACKET_READ_EOF:
			BUG("Should have already died when seeing EOF");
		case PACKET_READ_NORMAL:
			break;
		case PACKET_READ_FLUSH:
			state = PROCESS_REQUEST_DONE;
			continue;
		case PACKET_READ_DELIM:
			if (state != PROCESS_REQUEST_KEYS)
				die("protocol error");
			state = PROCESS_REQUEST_ARGS;
			/*
			 * maybe include a check to make sure that a
			 * command/capabilities were given.
			 */
			continue;
		}

		switch (state) {
		case PROCESS_REQUEST_KEYS:
			/* collect request; a sequence of keys and values */
			if (is_command(buffer, &command) ||
			    is_valid_capability(buffer))
				argv_array_push(&keys, buffer);
			else
				die("unknown capability '%s'", buffer);
			break;
		case PROCESS_REQUEST_ARGS:
			/* collect arguments for the requested command */
			argv_array_push(&args, buffer);
			break;
		case PROCESS_REQUEST_DONE:
			continue;
		}
	}

	/*
	 * If no command and no keys were given then the client wanted to
	 * terminate the connection.
	 */
	if (!keys.argc && !args.argc)
		return 1;

	if (!command)
		die("no command requested");

	command->command(the_repository, &keys, &args);

	argv_array_clear(&keys);
	argv_array_clear(&args);
	return 0;
}

/* Main serve loop for protocol version 2 */
void serve(struct serve_options *options)
{
	if (options->advertise_capabilities || !options->stateless_rpc) {
		/* serve by default supports v2 */
		packet_write_fmt(1, "version 2\n");

		advertise_capabilities();
		/*
		 * If only the list of capabilities was requested exit
		 * immediately after advertising capabilities
		 */
		if (options->advertise_capabilities)
			return;
	}

	/*
	 * If stateless-rpc was requested then exit after
	 * a single request/response exchange
	 */
	if (options->stateless_rpc) {
		process_request();
	} else {
		for (;;)
			if (process_request())
				break;
	}
}

debug log:

solving 90e3defe8 ...
found 90e3defe8 in https://public-inbox.org/git/20180125235838.138135-13-bmwill@google.com/

applying [1/1] https://public-inbox.org/git/20180125235838.138135-13-bmwill@google.com/
diff --git a/serve.c b/serve.c
new file mode 100644
index 000000000..90e3defe8

Checking patch serve.c...
Applied patch serve.c cleanly.

index at:
100644 90e3defe83618fc8f4f81aa1dde2afa4bf160bc3	serve.c

(*) Git path names are given by the tree(s) the blob belongs to.
    Blobs themselves have no identifier aside from the hash of its contents.^

Code repositories for project(s) associated with this public inbox

	https://80x24.org/mirrors/git.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).