File: iki-git-shell.c

package info (click to toggle)
ikiwiki-hosting 0.20140613
  • links: PTS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 788 kB
  • ctags: 283
  • sloc: perl: 5,124; sh: 182; ansic: 168; makefile: 50
file content (119 lines) | stat: -rw-r--r-- 2,822 bytes parent folder | download | duplicates (6)
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
/*
 * Wrapper for git-shell that only allows it to access ~/source.git.
 *
 * Also allows running a very few other special commands:
 *
 *   logview   tails site logs
 *   logdump   dumps site logs
 *
 * In C for speed.
 *
 * Use in .ssh/authorized_keys:
 * command="iki-git-shell",no-agent-forwarding,no-port-forwarding,no-X11-forwarding,no-pty,no-user-rc <key>
 *
 */

#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <pwd.h>

/* A "parked" flag file in the repo disables remote access.
 * The file content explains why. */
void checkparked (const char *repo) {
	char *flagfile;
	FILE *f;
	if (asprintf(&flagfile, "%s/parked", repo) == -1) {
		perror("asprintf");
		exit(1);
	}
	if ((f=fopen(flagfile, "r")) != NULL) {
		int bufsize=1024;
		char *buf=malloc(bufsize);
		while (fread(buf, bufsize, 1, f) > 0) {
			fwrite(buf, bufsize, 1, stderr);
		}
		exit(1);
	}
}

void logs (const char *param) {
	execlp("ikisite", "ikisite", "logs", param, NULL);
	perror("ikisite");
	exit(1);
}

int main (int argc, char **argv) {
	char *repo, *command, *subcommand, *s;
	struct passwd *passwd;

	passwd=getpwuid(geteuid());
	if (! passwd || ! passwd->pw_dir || ! strlen(passwd->pw_dir)) {
		fprintf(stderr, "error: failed to determine home directory\n");
		exit(1);
	}
	if (asprintf(&repo, "%s/source.git", passwd->pw_dir) == -1) {
		perror("asprintf");
		exit(1);
	}

	checkparked(repo);

	/*
	 * The original command passed to ssh will be
	 * "git subcommand path" or "git-subcommand path".
	 * Pass the subcommand on to git-shell, along with the path to the
	 * repository.
	 */

	command=getenv("SSH_ORIGINAL_COMMAND");
	if (! command) {
		fprintf(stderr, "error: SSH_ORIGINAL_COMMAND not set\n");
		exit(1);
	}

	s=strtok(command, " -"); /* handle "git-" and "git " */
	if (! s) {
		fprintf(stderr, "error: missing command\n");
		exit(1);
	}
	if (strcmp(s, "logview") == 0) {
		logs("--tail");
		exit(0);
	}
	if (strcmp(s, "logdump") == 0) {
		logs("--dump");
		exit(0);
	}
	if (strcmp(s, "git") != 0) {
		fprintf(stderr, "error: unknown command \"%s\"\n", s);
		exit(1);
	}

	s=strtok(NULL, " ");
	if (! s || ! strlen(s)) {
		fprintf(stderr, "error: missing git subcommand\n");
	}
	if (asprintf(&subcommand, "git-%s", s) == -1) {
		perror("asprintf");
		exit(1);
	}

	/* git-shell currently needs some wacky quoting around the
	 * subcommand and repo that its man page does not show */
	if (asprintf(&subcommand, "%s '%s'", subcommand, repo) == -1) {
		perror("asprintf");
		exit(1);
	}

	/* this variable is used to communicate to iki-git-hook-update
	 * that the commit is coming in from an external source */
	setenv("UNTRUSTED_COMMIT", "1", 1);

	execlp("git-shell", "git-shell", "-c", subcommand, NULL);
	perror("git-shell");
	exit(1);
}