#include <syslog.h>
#include "pkt-line.h"
#include "cache.h"
+#include "exec_cmd.h"
static int log_syslog;
static int verbose;
+static int reuseaddr;
static const char daemon_usage[] =
"git-daemon [--verbose] [--syslog] [--inetd | --port=n] [--export-all]\n"
-" [--timeout=n] [--init-timeout=n] [--strict-paths] [directory...]";
+" [--timeout=n] [--init-timeout=n] [--strict-paths]\n"
+" [--base-path=path] [--user-path | --user-path=path]\n"
+" [--reuseaddr] [directory...]";
/* List of acceptable pathname prefixes */
static char **ok_paths = NULL;
/* If this is set, git-daemon-export-ok is not required */
static int export_all_trees = 0;
+/* Take all paths relative to this one if non-NULL */
+static char *base_path = NULL;
+
+/* If defined, ~user notation is allowed and the string is inserted
+ * after ~user/. E.g. a request to git://host/~alice/frotz would
+ * go to /home/alice/pub_git/frotz with --user-path=pub_git.
+ */
+static char *user_path = NULL;
+
/* Timeout, and initial timeout */
static unsigned int timeout = 0;
static unsigned int init_timeout = 0;
static char *path_ok(char *dir)
{
+ static char rpath[PATH_MAX];
char *path;
if (avoid_alias(dir)) {
return NULL;
}
+ if (*dir == '~') {
+ if (!user_path) {
+ logerror("'%s': User-path not allowed", dir);
+ return NULL;
+ }
+ if (*user_path) {
+ /* Got either "~alice" or "~alice/foo";
+ * rewrite them to "~alice/%s" or
+ * "~alice/%s/foo".
+ */
+ int namlen, restlen = strlen(dir);
+ char *slash = strchr(dir, '/');
+ if (!slash)
+ slash = dir + restlen;
+ namlen = slash - dir;
+ restlen -= namlen;
+ loginfo("userpath <%s>, request <%s>, namlen %d, restlen %d, slash <%s>", user_path, dir, namlen, restlen, slash);
+ snprintf(rpath, PATH_MAX, "%.*s/%s%.*s",
+ namlen, dir, user_path, restlen, slash);
+ dir = rpath;
+ }
+ }
+ else if (base_path) {
+ if (*dir != '/') {
+ /* Allow only absolute */
+ logerror("'%s': Non-absolute path denied (base-path active)", dir);
+ return NULL;
+ }
+ else {
+ snprintf(rpath, PATH_MAX, "%s%s", base_path, dir);
+ dir = rpath;
+ }
+ }
+
path = enter_repo(dir, strict_paths);
if (!path) {
snprintf(timeout_buf, sizeof timeout_buf, "--timeout=%u", timeout);
/* git-upload-pack only ever reads stuff, so this is safe */
- execlp("git-upload-pack", "git-upload-pack", "--strict", timeout_buf, ".", NULL);
+ execl_git_cmd("upload-pack", "--strict", timeout_buf, ".", NULL);
return -1;
}
-static int execute(void)
+static int execute(struct sockaddr *addr)
{
static char line[1000];
- int len;
+ int pktlen, len;
+
+ if (addr) {
+ char addrbuf[256] = "";
+ int port = -1;
+
+ if (addr->sa_family == AF_INET) {
+ struct sockaddr_in *sin_addr = (void *) addr;
+ inet_ntop(addr->sa_family, &sin_addr->sin_addr, addrbuf, sizeof(addrbuf));
+ port = sin_addr->sin_port;
+#ifndef NO_IPV6
+ } else if (addr && addr->sa_family == AF_INET6) {
+ struct sockaddr_in6 *sin6_addr = (void *) addr;
+
+ char *buf = addrbuf;
+ *buf++ = '['; *buf = '\0'; /* stpcpy() is cool */
+ inet_ntop(AF_INET6, &sin6_addr->sin6_addr, buf, sizeof(addrbuf) - 1);
+ strcat(buf, "]");
+
+ port = sin6_addr->sin6_port;
+#endif
+ }
+ loginfo("Connection from %s:%d", addrbuf, port);
+ }
alarm(init_timeout ? init_timeout : timeout);
- len = packet_read_line(0, line, sizeof(line));
+ pktlen = packet_read_line(0, line, sizeof(line));
alarm(0);
+ len = strlen(line);
+ if (pktlen != len)
+ loginfo("Extended attributes (%d bytes) exist <%.*s>",
+ (int) pktlen - len,
+ (int) pktlen - len, line + len + 1);
if (len && line[len-1] == '\n')
line[--len] = 0;
static void handle(int incoming, struct sockaddr *addr, int addrlen)
{
pid_t pid = fork();
- char addrbuf[256] = "";
- int port = -1;
if (pid) {
unsigned idx;
dup2(incoming, 1);
close(incoming);
- if (addr->sa_family == AF_INET) {
- struct sockaddr_in *sin_addr = (void *) addr;
- inet_ntop(AF_INET, &sin_addr->sin_addr, addrbuf, sizeof(addrbuf));
- port = sin_addr->sin_port;
-
-#ifndef NO_IPV6
- } else if (addr->sa_family == AF_INET6) {
- struct sockaddr_in6 *sin6_addr = (void *) addr;
-
- char *buf = addrbuf;
- *buf++ = '['; *buf = '\0'; /* stpcpy() is cool */
- inet_ntop(AF_INET6, &sin6_addr->sin6_addr, buf, sizeof(addrbuf) - 1);
- strcat(buf, "]");
-
- port = sin6_addr->sin6_port;
-#endif
- }
- loginfo("Connection from %s:%d", addrbuf, port);
-
- exit(execute());
+ exit(execute(addr));
}
static void child_handler(int signo)
}
}
+static int set_reuse_addr(int sockfd)
+{
+ int on = 1;
+
+ if (!reuseaddr)
+ return 0;
+ return setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
+ &on, sizeof(on));
+}
+
#ifndef NO_IPV6
static int socksetup(int port, int **socklist_p)
}
#endif
+ if (set_reuse_addr(sockfd)) {
+ close(sockfd);
+ continue;
+ }
+
if (bind(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) {
close(sockfd);
continue; /* not fatal */
sin.sin_addr.s_addr = htonl(INADDR_ANY);
sin.sin_port = htons(port);
+ if (set_reuse_addr(sockfd)) {
+ close(sockfd);
+ return 0;
+ }
+
if ( bind(sockfd, (struct sockaddr *)&sin, sizeof sin) < 0 ) {
close(sockfd);
return 0;
int inetd_mode = 0;
int i;
+ /* Without this we cannot rely on waitpid() to tell
+ * what happened to our children.
+ */
+ signal(SIGCHLD, SIG_DFL);
+
for (i = 1; i < argc; i++) {
char *arg = argv[i];
strict_paths = 1;
continue;
}
+ if (!strncmp(arg, "--base-path=", 12)) {
+ base_path = arg+12;
+ continue;
+ }
+ if (!strcmp(arg, "--reuseaddr")) {
+ reuseaddr = 1;
+ continue;
+ }
+ if (!strcmp(arg, "--user-path")) {
+ user_path = "";
+ continue;
+ }
+ if (!strncmp(arg, "--user-path=", 12)) {
+ user_path = arg + 12;
+ continue;
+ }
if (!strcmp(arg, "--")) {
ok_paths = &argv[i+1];
break;
}
if (inetd_mode) {
- fclose(stderr); //FIXME: workaround
- return execute();
+ struct sockaddr_storage ss;
+ struct sockaddr *peer = (struct sockaddr *)&ss;
+ socklen_t slen = sizeof(ss);
+
+ freopen("/dev/null", "w", stderr);
+
+ if (getpeername(0, peer, &slen))
+ peer = NULL;
+
+ return execute(peer);
}
return serve(port);