aboutsummaryrefslogtreecommitdiff
path: root/ssh-push.c
blob: 090d6f9f8fbde2d736ac5bf563415b0fa402b5aa (plain)
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
#include "cache.h"
#include "rsh.h"
#include "refs.h"

#include <string.h>

static unsigned char local_version = 1;
static unsigned char remote_version = 0;

static int serve_object(int fd_in, int fd_out) {
	ssize_t size;
	int posn = 0;
	unsigned char sha1[20];
	unsigned long objsize;
	void *buf;
	signed char remote;
	do {
		size = read(fd_in, sha1 + posn, 20 - posn);
		if (size < 0) {
			perror("git-ssh-push: read ");
			return -1;
		}
		if (!size)
			return -1;
		posn += size;
	} while (posn < 20);
	
	/* fprintf(stderr, "Serving %s\n", sha1_to_hex(sha1)); */
	remote = 0;
	
	buf = map_sha1_file(sha1, &objsize);
	
	if (!buf) {
		fprintf(stderr, "git-ssh-push: could not find %s\n", 
			sha1_to_hex(sha1));
		remote = -1;
	}
	
	write(fd_out, &remote, 1);
	
	if (remote < 0)
		return 0;
	
	posn = 0;
	do {
		size = write(fd_out, buf + posn, objsize - posn);
		if (size <= 0) {
			if (!size) {
				fprintf(stderr, "git-ssh-push: write closed");
			} else {
				perror("git-ssh-push: write ");
			}
			return -1;
		}
		posn += size;
	} while (posn < objsize);
	return 0;
}

static int serve_version(int fd_in, int fd_out)
{
	if (read(fd_in, &remote_version, 1) < 1)
		return -1;
	write(fd_out, &local_version, 1);
	return 0;
}

static int serve_ref(int fd_in, int fd_out)
{
	char ref[PATH_MAX];
	unsigned char sha1[20];
	int posn = 0;
	signed char remote = 0;
	do {
		if (read(fd_in, ref + posn, 1) < 1)
			return -1;
		posn++;
	} while (ref[posn - 1]);
	if (get_ref_sha1(ref, sha1))
		remote = -1;
	write(fd_out, &remote, 1);
	if (remote)
		return 0;
	write(fd_out, sha1, 20);
        return 0;
}


static void service(int fd_in, int fd_out) {
	char type;
	int retval;
	do {
		retval = read(fd_in, &type, 1);
		if (retval < 1) {
			if (retval < 0)
				perror("git-ssh-push: read ");
			return;
		}
		if (type == 'v' && serve_version(fd_in, fd_out))
			return;
		if (type == 'o' && serve_object(fd_in, fd_out))
			return;
		if (type == 'r' && serve_ref(fd_in, fd_out))
			return;
	} while (1);
}

static const char *ssh_push_usage =
	"git-ssh-push [-c] [-t] [-a] [-w ref] commit-id url";

int main(int argc, char **argv)
{
	int arg = 1;
        char *commit_id;
        char *url;
	int fd_in, fd_out;
	const char *prog = getenv("GIT_SSH_PULL") ? : "git-ssh-pull";
	unsigned char sha1[20];
	char hex[41];

	while (arg < argc && argv[arg][0] == '-') {
		if (argv[arg][1] == 'w')
			arg++;
                arg++;
        }
	if (argc < arg + 2)
		usage(ssh_push_usage);
	commit_id = argv[arg];
	url = argv[arg + 1];
	if (get_sha1(commit_id, sha1))
		usage(ssh_push_usage);
	memcpy(hex, sha1_to_hex(sha1), sizeof(hex));
	argv[arg] = hex;

	if (setup_connection(&fd_in, &fd_out, prog, url, arg, argv + 1))
		return 1;

	service(fd_in, fd_out);
	return 0;
}