2005-10-26 07:29:09 +08:00
|
|
|
#include "cache.h"
|
2006-12-20 06:34:12 +08:00
|
|
|
#include "pkt-line.h"
|
2006-01-11 10:12:17 +08:00
|
|
|
#include "exec_cmd.h"
|
2006-09-20 09:31:51 +08:00
|
|
|
#include "interpolate.h"
|
2005-09-22 17:25:28 +08:00
|
|
|
|
2006-12-20 06:34:12 +08:00
|
|
|
#include <syslog.h>
|
|
|
|
|
2006-09-28 18:00:35 +08:00
|
|
|
#ifndef HOST_NAME_MAX
|
|
|
|
#define HOST_NAME_MAX 256
|
|
|
|
#endif
|
|
|
|
|
2007-10-19 01:17:39 +08:00
|
|
|
#ifndef NI_MAXSERV
|
|
|
|
#define NI_MAXSERV 32
|
|
|
|
#endif
|
|
|
|
|
2005-09-24 22:13:01 +08:00
|
|
|
static int log_syslog;
|
2005-09-22 17:25:28 +08:00
|
|
|
static int verbose;
|
2006-02-04 04:27:04 +08:00
|
|
|
static int reuseaddr;
|
2005-09-22 17:25:28 +08:00
|
|
|
|
2005-10-20 05:27:01 +08:00
|
|
|
static const char daemon_usage[] =
|
2006-09-26 22:47:43 +08:00
|
|
|
"git-daemon [--verbose] [--syslog] [--export-all]\n"
|
2005-12-23 09:27:40 +08:00
|
|
|
" [--timeout=n] [--init-timeout=n] [--strict-paths]\n"
|
2007-07-28 05:00:29 +08:00
|
|
|
" [--base-path=path] [--base-path-relaxed]\n"
|
|
|
|
" [--user-path | --user-path=path]\n"
|
2006-09-20 09:31:51 +08:00
|
|
|
" [--interpolated-path=path]\n"
|
2006-08-23 01:37:41 +08:00
|
|
|
" [--reuseaddr] [--detach] [--pid-file=file]\n"
|
2006-09-07 16:40:04 +08:00
|
|
|
" [--[enable|disable|allow-override|forbid-override]=service]\n"
|
2006-09-26 22:47:43 +08:00
|
|
|
" [--inetd | [--listen=host_or_ipaddr] [--port=n]\n"
|
|
|
|
" [--user=user [--group=group]]\n"
|
|
|
|
" [directory...]";
|
2005-09-27 10:10:55 +08:00
|
|
|
|
|
|
|
/* List of acceptable pathname prefixes */
|
2006-08-16 01:23:48 +08:00
|
|
|
static char **ok_paths;
|
|
|
|
static int strict_paths;
|
2005-09-27 10:10:55 +08:00
|
|
|
|
|
|
|
/* If this is set, git-daemon-export-ok is not required */
|
2006-08-16 01:23:48 +08:00
|
|
|
static int export_all_trees;
|
2005-09-22 17:25:28 +08:00
|
|
|
|
2005-12-23 09:27:40 +08:00
|
|
|
/* Take all paths relative to this one if non-NULL */
|
2006-08-16 01:23:48 +08:00
|
|
|
static char *base_path;
|
2006-09-20 09:31:51 +08:00
|
|
|
static char *interpolated_path;
|
2007-07-28 05:00:29 +08:00
|
|
|
static int base_path_relaxed;
|
2006-09-20 09:31:51 +08:00
|
|
|
|
|
|
|
/* Flag indicating client sent extra args. */
|
|
|
|
static int saw_extended_args;
|
2005-12-23 09:27:40 +08:00
|
|
|
|
2006-02-05 14:27:29 +08:00
|
|
|
/* 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.
|
|
|
|
*/
|
2006-08-16 01:23:48 +08:00
|
|
|
static const char *user_path;
|
2006-02-05 14:27:29 +08:00
|
|
|
|
2005-10-20 05:27:01 +08:00
|
|
|
/* Timeout, and initial timeout */
|
2006-08-16 01:23:48 +08:00
|
|
|
static unsigned int timeout;
|
|
|
|
static unsigned int init_timeout;
|
2005-09-22 17:25:28 +08:00
|
|
|
|
2006-09-20 09:31:51 +08:00
|
|
|
/*
|
|
|
|
* Static table for now. Ugh.
|
|
|
|
* Feel free to make dynamic as needed.
|
|
|
|
*/
|
|
|
|
#define INTERP_SLOT_HOST (0)
|
2006-09-26 22:47:43 +08:00
|
|
|
#define INTERP_SLOT_CANON_HOST (1)
|
|
|
|
#define INTERP_SLOT_IP (2)
|
|
|
|
#define INTERP_SLOT_PORT (3)
|
|
|
|
#define INTERP_SLOT_DIR (4)
|
|
|
|
#define INTERP_SLOT_PERCENT (5)
|
2006-09-20 09:31:51 +08:00
|
|
|
|
|
|
|
static struct interp interp_table[] = {
|
|
|
|
{ "%H", 0},
|
2006-09-26 22:47:43 +08:00
|
|
|
{ "%CH", 0},
|
|
|
|
{ "%IP", 0},
|
|
|
|
{ "%P", 0},
|
2006-09-20 09:31:51 +08:00
|
|
|
{ "%D", 0},
|
2006-09-28 00:16:10 +08:00
|
|
|
{ "%%", 0},
|
2006-09-20 09:31:51 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2005-09-24 22:13:01 +08:00
|
|
|
static void logreport(int priority, const char *err, va_list params)
|
2005-09-22 17:25:28 +08:00
|
|
|
{
|
|
|
|
/* We should do a single write so that it is atomic and output
|
|
|
|
* of several processes do not get intermingled. */
|
|
|
|
char buf[1024];
|
|
|
|
int buflen;
|
|
|
|
int maxlen, msglen;
|
|
|
|
|
|
|
|
/* sizeof(buf) should be big enough for "[pid] \n" */
|
2005-09-24 14:26:55 +08:00
|
|
|
buflen = snprintf(buf, sizeof(buf), "[%ld] ", (long) getpid());
|
2005-09-22 17:25:28 +08:00
|
|
|
|
|
|
|
maxlen = sizeof(buf) - buflen - 1; /* -1 for our own LF */
|
|
|
|
msglen = vsnprintf(buf + buflen, maxlen, err, params);
|
|
|
|
|
2005-09-24 22:13:01 +08:00
|
|
|
if (log_syslog) {
|
|
|
|
syslog(priority, "%s", buf);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2005-09-22 17:25:28 +08:00
|
|
|
/* maxlen counted our own LF but also counts space given to
|
|
|
|
* vsnprintf for the terminating NUL. We want to make sure that
|
|
|
|
* we have space for our own LF and NUL after the "meat" of the
|
|
|
|
* message, so truncate it at maxlen - 1.
|
|
|
|
*/
|
|
|
|
if (msglen > maxlen - 1)
|
|
|
|
msglen = maxlen - 1;
|
|
|
|
else if (msglen < 0)
|
|
|
|
msglen = 0; /* Protect against weird return values. */
|
|
|
|
buflen += msglen;
|
|
|
|
|
|
|
|
buf[buflen++] = '\n';
|
|
|
|
buf[buflen] = '\0';
|
|
|
|
|
2007-01-08 23:58:23 +08:00
|
|
|
write_in_full(2, buf, buflen);
|
2005-09-22 17:25:28 +08:00
|
|
|
}
|
|
|
|
|
2005-09-30 04:53:14 +08:00
|
|
|
static void logerror(const char *err, ...)
|
2005-09-22 17:25:28 +08:00
|
|
|
{
|
|
|
|
va_list params;
|
|
|
|
va_start(params, err);
|
2005-09-24 22:13:01 +08:00
|
|
|
logreport(LOG_ERR, err, params);
|
2005-09-22 17:25:28 +08:00
|
|
|
va_end(params);
|
|
|
|
}
|
|
|
|
|
2005-09-30 04:53:14 +08:00
|
|
|
static void loginfo(const char *err, ...)
|
2005-09-22 17:25:28 +08:00
|
|
|
{
|
|
|
|
va_list params;
|
|
|
|
if (!verbose)
|
|
|
|
return;
|
|
|
|
va_start(params, err);
|
2005-09-24 22:13:01 +08:00
|
|
|
logreport(LOG_INFO, err, params);
|
2005-09-22 17:25:28 +08:00
|
|
|
va_end(params);
|
|
|
|
}
|
2005-07-14 10:45:26 +08:00
|
|
|
|
2006-07-13 18:02:29 +08:00
|
|
|
static void NORETURN daemon_die(const char *err, va_list params)
|
|
|
|
{
|
|
|
|
logreport(LOG_ERR, err, params);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
[PATCH] daemon.c and path.enter_repo(): revamp path validation.
The whitelist of git-daemon is checked against return value from
enter_repo(), and enter_repo() used to return the value obtained
from getcwd() to avoid directory aliasing issues as discussed
earier (mid October 2005).
Unfortunately, it did not go well as we hoped.
For example, /pub on a kernel.org public machine is a symlink to
its real mountpoint, and it is understandable that the
administrator does not want to adjust the whitelist every time
/pub needs to point at a different partition for storage
allcation or whatever reasons. Being able to keep using
/pub/scm as the whitelist is a desirable property.
So this version of enter_repo() reports what it used to chdir()
and validate, but does not use getcwd() to canonicalize the
directory name. When it sees a user relative path ~user/path,
it internally resolves it to try chdir() there, but it still
reports ~user/path (possibly after appending .git if allowed to
do so, in which case it would report ~user/path.git).
What this means is that if a whitelist wants to allow a user
relative path, it needs to say "~" (for all users) or list user
home directories like "~alice" "~bob". And no, you cannot say
/home if the advertised way to access user home directories are
~alice,~bob, etc. The whole point of this is to avoid
unnecessary aliasing issues.
Anyway, because of this, daemon needs to do a bit more work to
guard itself. Namely, it needs to make sure that the accessor
does not try to exploit its leading path match rule by inserting
/../ in the middle or hanging /.. at the end. I resurrected the
belts and suspender paranoia code HPA did for this purpose.
This check cannot be done in the enter_repo() unconditionally,
because there are valid callers of enter_repo() that want to
honor /../; authorized users coming over ssh to run send-pack
and fetch-pack should be allowed to do so.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2005-12-03 17:45:57 +08:00
|
|
|
static int avoid_alias(char *p)
|
|
|
|
{
|
|
|
|
int sl, ndot;
|
|
|
|
|
2007-06-07 15:04:01 +08:00
|
|
|
/*
|
[PATCH] daemon.c and path.enter_repo(): revamp path validation.
The whitelist of git-daemon is checked against return value from
enter_repo(), and enter_repo() used to return the value obtained
from getcwd() to avoid directory aliasing issues as discussed
earier (mid October 2005).
Unfortunately, it did not go well as we hoped.
For example, /pub on a kernel.org public machine is a symlink to
its real mountpoint, and it is understandable that the
administrator does not want to adjust the whitelist every time
/pub needs to point at a different partition for storage
allcation or whatever reasons. Being able to keep using
/pub/scm as the whitelist is a desirable property.
So this version of enter_repo() reports what it used to chdir()
and validate, but does not use getcwd() to canonicalize the
directory name. When it sees a user relative path ~user/path,
it internally resolves it to try chdir() there, but it still
reports ~user/path (possibly after appending .git if allowed to
do so, in which case it would report ~user/path.git).
What this means is that if a whitelist wants to allow a user
relative path, it needs to say "~" (for all users) or list user
home directories like "~alice" "~bob". And no, you cannot say
/home if the advertised way to access user home directories are
~alice,~bob, etc. The whole point of this is to avoid
unnecessary aliasing issues.
Anyway, because of this, daemon needs to do a bit more work to
guard itself. Namely, it needs to make sure that the accessor
does not try to exploit its leading path match rule by inserting
/../ in the middle or hanging /.. at the end. I resurrected the
belts and suspender paranoia code HPA did for this purpose.
This check cannot be done in the enter_repo() unconditionally,
because there are valid callers of enter_repo() that want to
honor /../; authorized users coming over ssh to run send-pack
and fetch-pack should be allowed to do so.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2005-12-03 17:45:57 +08:00
|
|
|
* This resurrects the belts and suspenders paranoia check by HPA
|
|
|
|
* done in <435560F7.4080006@zytor.com> thread, now enter_repo()
|
|
|
|
* does not do getcwd() based path canonicalizations.
|
|
|
|
*
|
|
|
|
* sl becomes true immediately after seeing '/' and continues to
|
|
|
|
* be true as long as dots continue after that without intervening
|
|
|
|
* non-dot character.
|
|
|
|
*/
|
|
|
|
if (!p || (*p != '/' && *p != '~'))
|
|
|
|
return -1;
|
|
|
|
sl = 1; ndot = 0;
|
|
|
|
p++;
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
char ch = *p++;
|
|
|
|
if (sl) {
|
|
|
|
if (ch == '.')
|
|
|
|
ndot++;
|
|
|
|
else if (ch == '/') {
|
|
|
|
if (ndot < 3)
|
|
|
|
/* reject //, /./ and /../ */
|
|
|
|
return -1;
|
|
|
|
ndot = 0;
|
|
|
|
}
|
|
|
|
else if (ch == 0) {
|
|
|
|
if (0 < ndot && ndot < 3)
|
|
|
|
/* reject /.$ and /..$ */
|
|
|
|
return -1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
sl = ndot = 0;
|
|
|
|
}
|
|
|
|
else if (ch == 0)
|
|
|
|
return 0;
|
|
|
|
else if (ch == '/') {
|
|
|
|
sl = 1;
|
|
|
|
ndot = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-09-20 09:31:51 +08:00
|
|
|
static char *path_ok(struct interp *itable)
|
2005-09-27 10:10:55 +08:00
|
|
|
{
|
2006-02-05 14:27:29 +08:00
|
|
|
static char rpath[PATH_MAX];
|
2006-09-20 09:31:51 +08:00
|
|
|
static char interp_path[PATH_MAX];
|
2007-07-28 05:00:29 +08:00
|
|
|
int retried_path = 0;
|
[PATCH] daemon.c and path.enter_repo(): revamp path validation.
The whitelist of git-daemon is checked against return value from
enter_repo(), and enter_repo() used to return the value obtained
from getcwd() to avoid directory aliasing issues as discussed
earier (mid October 2005).
Unfortunately, it did not go well as we hoped.
For example, /pub on a kernel.org public machine is a symlink to
its real mountpoint, and it is understandable that the
administrator does not want to adjust the whitelist every time
/pub needs to point at a different partition for storage
allcation or whatever reasons. Being able to keep using
/pub/scm as the whitelist is a desirable property.
So this version of enter_repo() reports what it used to chdir()
and validate, but does not use getcwd() to canonicalize the
directory name. When it sees a user relative path ~user/path,
it internally resolves it to try chdir() there, but it still
reports ~user/path (possibly after appending .git if allowed to
do so, in which case it would report ~user/path.git).
What this means is that if a whitelist wants to allow a user
relative path, it needs to say "~" (for all users) or list user
home directories like "~alice" "~bob". And no, you cannot say
/home if the advertised way to access user home directories are
~alice,~bob, etc. The whole point of this is to avoid
unnecessary aliasing issues.
Anyway, because of this, daemon needs to do a bit more work to
guard itself. Namely, it needs to make sure that the accessor
does not try to exploit its leading path match rule by inserting
/../ in the middle or hanging /.. at the end. I resurrected the
belts and suspender paranoia code HPA did for this purpose.
This check cannot be done in the enter_repo() unconditionally,
because there are valid callers of enter_repo() that want to
honor /../; authorized users coming over ssh to run send-pack
and fetch-pack should be allowed to do so.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2005-12-03 17:45:57 +08:00
|
|
|
char *path;
|
2006-09-20 09:31:51 +08:00
|
|
|
char *dir;
|
|
|
|
|
|
|
|
dir = itable[INTERP_SLOT_DIR].value;
|
[PATCH] daemon.c and path.enter_repo(): revamp path validation.
The whitelist of git-daemon is checked against return value from
enter_repo(), and enter_repo() used to return the value obtained
from getcwd() to avoid directory aliasing issues as discussed
earier (mid October 2005).
Unfortunately, it did not go well as we hoped.
For example, /pub on a kernel.org public machine is a symlink to
its real mountpoint, and it is understandable that the
administrator does not want to adjust the whitelist every time
/pub needs to point at a different partition for storage
allcation or whatever reasons. Being able to keep using
/pub/scm as the whitelist is a desirable property.
So this version of enter_repo() reports what it used to chdir()
and validate, but does not use getcwd() to canonicalize the
directory name. When it sees a user relative path ~user/path,
it internally resolves it to try chdir() there, but it still
reports ~user/path (possibly after appending .git if allowed to
do so, in which case it would report ~user/path.git).
What this means is that if a whitelist wants to allow a user
relative path, it needs to say "~" (for all users) or list user
home directories like "~alice" "~bob". And no, you cannot say
/home if the advertised way to access user home directories are
~alice,~bob, etc. The whole point of this is to avoid
unnecessary aliasing issues.
Anyway, because of this, daemon needs to do a bit more work to
guard itself. Namely, it needs to make sure that the accessor
does not try to exploit its leading path match rule by inserting
/../ in the middle or hanging /.. at the end. I resurrected the
belts and suspender paranoia code HPA did for this purpose.
This check cannot be done in the enter_repo() unconditionally,
because there are valid callers of enter_repo() that want to
honor /../; authorized users coming over ssh to run send-pack
and fetch-pack should be allowed to do so.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2005-12-03 17:45:57 +08:00
|
|
|
|
|
|
|
if (avoid_alias(dir)) {
|
|
|
|
logerror("'%s': aliased", dir);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2006-02-05 14:27:29 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2006-09-20 09:31:51 +08:00
|
|
|
else if (interpolated_path && saw_extended_args) {
|
|
|
|
if (*dir != '/') {
|
|
|
|
/* Allow only absolute */
|
|
|
|
logerror("'%s': Non-absolute path denied (interpolated-path active)", dir);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
interpolate(interp_path, PATH_MAX, interpolated_path,
|
|
|
|
interp_table, ARRAY_SIZE(interp_table));
|
|
|
|
loginfo("Interpolated dir '%s'", interp_path);
|
|
|
|
|
|
|
|
dir = interp_path;
|
|
|
|
}
|
2006-02-05 14:27:29 +08:00
|
|
|
else if (base_path) {
|
|
|
|
if (*dir != '/') {
|
|
|
|
/* Allow only absolute */
|
2006-02-04 04:27:02 +08:00
|
|
|
logerror("'%s': Non-absolute path denied (base-path active)", dir);
|
2005-12-23 09:27:40 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
2006-09-20 09:31:51 +08:00
|
|
|
snprintf(rpath, PATH_MAX, "%s%s", base_path, dir);
|
|
|
|
dir = rpath;
|
2005-12-23 09:27:40 +08:00
|
|
|
}
|
|
|
|
|
2007-07-28 05:00:29 +08:00
|
|
|
do {
|
|
|
|
path = enter_repo(dir, strict_paths);
|
|
|
|
if (path)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* if we fail and base_path_relaxed is enabled, try without
|
|
|
|
* prefixing the base path
|
|
|
|
*/
|
|
|
|
if (base_path && base_path_relaxed && !retried_path) {
|
|
|
|
dir = itable[INTERP_SLOT_DIR].value;
|
|
|
|
retried_path = 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
} while (1);
|
2005-10-19 09:26:52 +08:00
|
|
|
|
2005-11-18 03:37:14 +08:00
|
|
|
if (!path) {
|
|
|
|
logerror("'%s': unable to chdir or not a git archive", dir);
|
|
|
|
return NULL;
|
2005-09-27 10:10:55 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if ( ok_paths && *ok_paths ) {
|
2005-11-21 17:21:18 +08:00
|
|
|
char **pp;
|
2005-11-18 03:37:14 +08:00
|
|
|
int pathlen = strlen(path);
|
2005-09-27 10:10:55 +08:00
|
|
|
|
2005-11-21 17:21:18 +08:00
|
|
|
/* The validation is done on the paths after enter_repo
|
2007-06-07 15:04:01 +08:00
|
|
|
* appends optional {.git,.git/.git} and friends, but
|
[PATCH] daemon.c and path.enter_repo(): revamp path validation.
The whitelist of git-daemon is checked against return value from
enter_repo(), and enter_repo() used to return the value obtained
from getcwd() to avoid directory aliasing issues as discussed
earier (mid October 2005).
Unfortunately, it did not go well as we hoped.
For example, /pub on a kernel.org public machine is a symlink to
its real mountpoint, and it is understandable that the
administrator does not want to adjust the whitelist every time
/pub needs to point at a different partition for storage
allcation or whatever reasons. Being able to keep using
/pub/scm as the whitelist is a desirable property.
So this version of enter_repo() reports what it used to chdir()
and validate, but does not use getcwd() to canonicalize the
directory name. When it sees a user relative path ~user/path,
it internally resolves it to try chdir() there, but it still
reports ~user/path (possibly after appending .git if allowed to
do so, in which case it would report ~user/path.git).
What this means is that if a whitelist wants to allow a user
relative path, it needs to say "~" (for all users) or list user
home directories like "~alice" "~bob". And no, you cannot say
/home if the advertised way to access user home directories are
~alice,~bob, etc. The whole point of this is to avoid
unnecessary aliasing issues.
Anyway, because of this, daemon needs to do a bit more work to
guard itself. Namely, it needs to make sure that the accessor
does not try to exploit its leading path match rule by inserting
/../ in the middle or hanging /.. at the end. I resurrected the
belts and suspender paranoia code HPA did for this purpose.
This check cannot be done in the enter_repo() unconditionally,
because there are valid callers of enter_repo() that want to
honor /../; authorized users coming over ssh to run send-pack
and fetch-pack should be allowed to do so.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2005-12-03 17:45:57 +08:00
|
|
|
* it does not use getcwd(). So if your /pub is
|
|
|
|
* a symlink to /mnt/pub, you can whitelist /pub and
|
|
|
|
* do not have to say /mnt/pub.
|
|
|
|
* Do not say /pub/.
|
2005-11-21 17:21:18 +08:00
|
|
|
*/
|
2005-09-27 10:10:55 +08:00
|
|
|
for ( pp = ok_paths ; *pp ; pp++ ) {
|
|
|
|
int len = strlen(*pp);
|
2005-11-21 17:21:18 +08:00
|
|
|
if (len <= pathlen &&
|
|
|
|
!memcmp(*pp, path, len) &&
|
|
|
|
(path[len] == '\0' ||
|
|
|
|
(!strict_paths && path[len] == '/')))
|
|
|
|
return path;
|
2005-09-27 10:10:55 +08:00
|
|
|
}
|
2005-11-18 03:37:14 +08:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* be backwards compatible */
|
|
|
|
if (!strict_paths)
|
|
|
|
return path;
|
2005-09-27 10:10:55 +08:00
|
|
|
}
|
|
|
|
|
2005-11-18 03:37:14 +08:00
|
|
|
logerror("'%s': not in whitelist", path);
|
|
|
|
return NULL; /* Fallthrough. Deny by default */
|
2005-09-27 10:10:55 +08:00
|
|
|
}
|
2005-07-14 10:45:26 +08:00
|
|
|
|
2006-08-21 10:03:13 +08:00
|
|
|
typedef int (*daemon_service_fn)(void);
|
|
|
|
struct daemon_service {
|
|
|
|
const char *name;
|
|
|
|
const char *config_name;
|
|
|
|
daemon_service_fn fn;
|
|
|
|
int enabled;
|
|
|
|
int overridable;
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct daemon_service *service_looking_at;
|
|
|
|
static int service_enabled;
|
|
|
|
|
|
|
|
static int git_daemon_config(const char *var, const char *value)
|
|
|
|
{
|
Mechanical conversion to use prefixcmp()
This mechanically converts strncmp() to use prefixcmp(), but only when
the parameters match specific patterns, so that they can be verified
easily. Leftover from this will be fixed in a separate step, including
idiotic conversions like
if (!strncmp("foo", arg, 3))
=>
if (!(-prefixcmp(arg, "foo")))
This was done by using this script in px.perl
#!/usr/bin/perl -i.bak -p
if (/strncmp\(([^,]+), "([^\\"]*)", (\d+)\)/ && (length($2) == $3)) {
s|strncmp\(([^,]+), "([^\\"]*)", (\d+)\)|prefixcmp($1, "$2")|;
}
if (/strncmp\("([^\\"]*)", ([^,]+), (\d+)\)/ && (length($1) == $3)) {
s|strncmp\("([^\\"]*)", ([^,]+), (\d+)\)|(-prefixcmp($2, "$1"))|;
}
and running:
$ git grep -l strncmp -- '*.c' | xargs perl px.perl
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-02-20 17:53:29 +08:00
|
|
|
if (!prefixcmp(var, "daemon.") &&
|
2006-08-21 10:03:13 +08:00
|
|
|
!strcmp(var + 7, service_looking_at->config_name)) {
|
|
|
|
service_enabled = git_config_bool(var, value);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* we are not interested in parsing any other configuration here */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-09-20 09:31:51 +08:00
|
|
|
static int run_service(struct interp *itable, struct daemon_service *service)
|
2005-07-14 10:45:26 +08:00
|
|
|
{
|
2005-11-18 03:37:14 +08:00
|
|
|
const char *path;
|
2006-08-21 10:03:13 +08:00
|
|
|
int enabled = service->enabled;
|
|
|
|
|
2006-09-20 09:31:51 +08:00
|
|
|
loginfo("Request %s for '%s'",
|
|
|
|
service->name,
|
|
|
|
itable[INTERP_SLOT_DIR].value);
|
2005-11-18 03:37:14 +08:00
|
|
|
|
2006-08-21 10:03:13 +08:00
|
|
|
if (!enabled && !service->overridable) {
|
|
|
|
logerror("'%s': service not enabled.", service->name);
|
|
|
|
errno = EACCES;
|
|
|
|
return -1;
|
|
|
|
}
|
2005-09-27 10:10:55 +08:00
|
|
|
|
2006-09-20 09:31:51 +08:00
|
|
|
if (!(path = path_ok(itable)))
|
2005-07-14 10:45:26 +08:00
|
|
|
return -1;
|
2005-09-27 23:49:40 +08:00
|
|
|
|
2005-07-14 10:45:26 +08:00
|
|
|
/*
|
|
|
|
* Security on the cheap.
|
|
|
|
*
|
2005-10-21 14:19:36 +08:00
|
|
|
* We want a readable HEAD, usable "objects" directory, and
|
2005-07-14 10:45:26 +08:00
|
|
|
* a "git-daemon-export-ok" flag that says that the other side
|
|
|
|
* is ok with us doing this.
|
2005-11-18 03:37:14 +08:00
|
|
|
*
|
|
|
|
* path_ok() uses enter_repo() and does whitelist checking.
|
|
|
|
* We only need to make sure the repository is exported.
|
2005-07-14 10:45:26 +08:00
|
|
|
*/
|
2005-11-18 03:37:14 +08:00
|
|
|
|
2005-10-19 09:26:52 +08:00
|
|
|
if (!export_all_trees && access("git-daemon-export-ok", F_OK)) {
|
2005-11-18 03:37:14 +08:00
|
|
|
logerror("'%s': repository not exported.", path);
|
2005-10-19 09:26:52 +08:00
|
|
|
errno = EACCES;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2006-08-21 10:03:13 +08:00
|
|
|
if (service->overridable) {
|
|
|
|
service_looking_at = service;
|
|
|
|
service_enabled = -1;
|
|
|
|
git_config(git_daemon_config);
|
|
|
|
if (0 <= service_enabled)
|
|
|
|
enabled = service_enabled;
|
|
|
|
}
|
|
|
|
if (!enabled) {
|
|
|
|
logerror("'%s': service not enabled for '%s'",
|
|
|
|
service->name, path);
|
|
|
|
errno = EACCES;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2005-07-16 13:53:31 +08:00
|
|
|
/*
|
|
|
|
* We'll ignore SIGTERM from now on, we have a
|
|
|
|
* good client.
|
|
|
|
*/
|
|
|
|
signal(SIGTERM, SIG_IGN);
|
|
|
|
|
2006-08-21 10:03:13 +08:00
|
|
|
return service->fn();
|
|
|
|
}
|
|
|
|
|
|
|
|
static int upload_pack(void)
|
|
|
|
{
|
|
|
|
/* Timeout as string */
|
|
|
|
char timeout_buf[64];
|
|
|
|
|
2005-10-20 05:27:01 +08:00
|
|
|
snprintf(timeout_buf, sizeof timeout_buf, "--timeout=%u", timeout);
|
|
|
|
|
2005-07-14 10:45:26 +08:00
|
|
|
/* git-upload-pack only ever reads stuff, so this is safe */
|
2006-01-11 10:12:17 +08:00
|
|
|
execl_git_cmd("upload-pack", "--strict", timeout_buf, ".", NULL);
|
2005-07-14 10:45:26 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2006-09-07 21:12:05 +08:00
|
|
|
static int upload_archive(void)
|
|
|
|
{
|
|
|
|
execl_git_cmd("upload-archive", ".", NULL);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2007-01-22 03:04:13 +08:00
|
|
|
static int receive_pack(void)
|
|
|
|
{
|
|
|
|
execl_git_cmd("receive-pack", ".", NULL);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2006-08-21 10:03:13 +08:00
|
|
|
static struct daemon_service daemon_service[] = {
|
2006-09-07 21:12:05 +08:00
|
|
|
{ "upload-archive", "uploadarch", upload_archive, 0, 1 },
|
2006-08-21 10:03:13 +08:00
|
|
|
{ "upload-pack", "uploadpack", upload_pack, 1, 1 },
|
2007-01-22 03:04:13 +08:00
|
|
|
{ "receive-pack", "receivepack", receive_pack, 0, 1 },
|
2006-08-21 10:03:13 +08:00
|
|
|
};
|
|
|
|
|
2007-11-09 07:35:32 +08:00
|
|
|
static void enable_service(const char *name, int ena)
|
|
|
|
{
|
2006-08-21 10:03:13 +08:00
|
|
|
int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(daemon_service); i++) {
|
|
|
|
if (!strcmp(daemon_service[i].name, name)) {
|
|
|
|
daemon_service[i].enabled = ena;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
die("No such service %s", name);
|
|
|
|
}
|
|
|
|
|
2007-11-09 07:35:32 +08:00
|
|
|
static void make_service_overridable(const char *name, int ena)
|
|
|
|
{
|
2006-08-21 10:03:13 +08:00
|
|
|
int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(daemon_service); i++) {
|
|
|
|
if (!strcmp(daemon_service[i].name, name)) {
|
|
|
|
daemon_service[i].overridable = ena;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
die("No such service %s", name);
|
|
|
|
}
|
|
|
|
|
2006-09-28 00:16:10 +08:00
|
|
|
/*
|
|
|
|
* Separate the "extra args" information as supplied by the client connection.
|
2007-02-04 12:49:16 +08:00
|
|
|
* Any resulting data is squirreled away in the given interpolation table.
|
2006-09-28 00:16:10 +08:00
|
|
|
*/
|
|
|
|
static void parse_extra_args(struct interp *table, char *extra_args, int buflen)
|
2006-09-20 09:31:51 +08:00
|
|
|
{
|
|
|
|
char *val;
|
|
|
|
int vallen;
|
|
|
|
char *end = extra_args + buflen;
|
|
|
|
|
|
|
|
while (extra_args < end && *extra_args) {
|
|
|
|
saw_extended_args = 1;
|
|
|
|
if (strncasecmp("host=", extra_args, 5) == 0) {
|
|
|
|
val = extra_args + 5;
|
|
|
|
vallen = strlen(val) + 1;
|
|
|
|
if (*val) {
|
2006-09-28 00:16:10 +08:00
|
|
|
/* Split <host>:<port> at colon. */
|
|
|
|
char *host = val;
|
|
|
|
char *port = strrchr(host, ':');
|
2006-09-26 22:47:43 +08:00
|
|
|
if (port) {
|
|
|
|
*port = 0;
|
|
|
|
port++;
|
2006-09-28 00:16:10 +08:00
|
|
|
interp_set_entry(table, INTERP_SLOT_PORT, port);
|
2006-09-26 22:47:43 +08:00
|
|
|
}
|
2006-09-28 00:16:10 +08:00
|
|
|
interp_set_entry(table, INTERP_SLOT_HOST, host);
|
2006-09-20 09:31:51 +08:00
|
|
|
}
|
2006-09-28 00:16:10 +08:00
|
|
|
|
2006-09-20 09:31:51 +08:00
|
|
|
/* On to the next one */
|
|
|
|
extra_args = val + vallen;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-06-08 04:45:00 +08:00
|
|
|
static void fill_in_extra_table_entries(struct interp *itable)
|
2006-09-26 22:47:43 +08:00
|
|
|
{
|
|
|
|
char *hp;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Replace literal host with lowercase-ized hostname.
|
|
|
|
*/
|
|
|
|
hp = interp_table[INTERP_SLOT_HOST].value;
|
2006-10-24 09:26:05 +08:00
|
|
|
if (!hp)
|
|
|
|
return;
|
2006-09-26 22:47:43 +08:00
|
|
|
for ( ; *hp; hp++)
|
|
|
|
*hp = tolower(*hp);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Locate canonical hostname and its IP address.
|
|
|
|
*/
|
|
|
|
#ifndef NO_IPV6
|
|
|
|
{
|
|
|
|
struct addrinfo hints;
|
|
|
|
struct addrinfo *ai, *ai0;
|
|
|
|
int gai;
|
|
|
|
static char addrbuf[HOST_NAME_MAX + 1];
|
|
|
|
|
|
|
|
memset(&hints, 0, sizeof(hints));
|
|
|
|
hints.ai_flags = AI_CANONNAME;
|
|
|
|
|
|
|
|
gai = getaddrinfo(interp_table[INTERP_SLOT_HOST].value, 0, &hints, &ai0);
|
|
|
|
if (!gai) {
|
|
|
|
for (ai = ai0; ai; ai = ai->ai_next) {
|
|
|
|
struct sockaddr_in *sin_addr = (void *)ai->ai_addr;
|
|
|
|
|
|
|
|
inet_ntop(AF_INET, &sin_addr->sin_addr,
|
|
|
|
addrbuf, sizeof(addrbuf));
|
2006-09-28 00:16:10 +08:00
|
|
|
interp_set_entry(interp_table,
|
|
|
|
INTERP_SLOT_CANON_HOST, ai->ai_canonname);
|
|
|
|
interp_set_entry(interp_table,
|
|
|
|
INTERP_SLOT_IP, addrbuf);
|
2006-09-26 22:47:43 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
freeaddrinfo(ai0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
{
|
|
|
|
struct hostent *hent;
|
|
|
|
struct sockaddr_in sa;
|
|
|
|
char **ap;
|
|
|
|
static char addrbuf[HOST_NAME_MAX + 1];
|
|
|
|
|
|
|
|
hent = gethostbyname(interp_table[INTERP_SLOT_HOST].value);
|
|
|
|
|
|
|
|
ap = hent->h_addr_list;
|
|
|
|
memset(&sa, 0, sizeof sa);
|
|
|
|
sa.sin_family = hent->h_addrtype;
|
|
|
|
sa.sin_port = htons(0);
|
|
|
|
memcpy(&sa.sin_addr, *ap, hent->h_length);
|
|
|
|
|
|
|
|
inet_ntop(hent->h_addrtype, &sa.sin_addr,
|
|
|
|
addrbuf, sizeof(addrbuf));
|
2006-09-28 00:16:10 +08:00
|
|
|
|
|
|
|
interp_set_entry(interp_table, INTERP_SLOT_CANON_HOST, hent->h_name);
|
|
|
|
interp_set_entry(interp_table, INTERP_SLOT_IP, addrbuf);
|
2006-09-26 22:47:43 +08:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-06-20 22:38:13 +08:00
|
|
|
static int execute(struct sockaddr *addr)
|
2005-07-14 10:45:26 +08:00
|
|
|
{
|
2005-07-16 00:27:05 +08:00
|
|
|
static char line[1000];
|
2006-08-21 10:03:13 +08:00
|
|
|
int pktlen, len, i;
|
2005-07-16 00:27:05 +08:00
|
|
|
|
2006-06-20 22:38:13 +08:00
|
|
|
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));
|
2007-11-05 17:16:22 +08:00
|
|
|
port = ntohs(sin_addr->sin_port);
|
2006-06-20 22:38:13 +08:00
|
|
|
#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, "]");
|
|
|
|
|
2007-11-05 17:16:22 +08:00
|
|
|
port = ntohs(sin6_addr->sin6_port);
|
2006-06-20 22:38:13 +08:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
loginfo("Connection from %s:%d", addrbuf, port);
|
|
|
|
}
|
|
|
|
|
2005-10-20 05:27:01 +08:00
|
|
|
alarm(init_timeout ? init_timeout : timeout);
|
2006-06-07 11:58:41 +08:00
|
|
|
pktlen = packet_read_line(0, line, sizeof(line));
|
2005-10-20 05:27:01 +08:00
|
|
|
alarm(0);
|
2005-07-16 00:27:05 +08:00
|
|
|
|
2006-06-07 11:58:41 +08:00
|
|
|
len = strlen(line);
|
|
|
|
if (pktlen != len)
|
|
|
|
loginfo("Extended attributes (%d bytes) exist <%.*s>",
|
|
|
|
(int) pktlen - len,
|
|
|
|
(int) pktlen - len, line + len + 1);
|
2006-10-24 09:26:05 +08:00
|
|
|
if (len && line[len-1] == '\n') {
|
2005-07-16 00:27:05 +08:00
|
|
|
line[--len] = 0;
|
2006-10-24 09:26:05 +08:00
|
|
|
pktlen--;
|
|
|
|
}
|
2005-07-16 00:27:05 +08:00
|
|
|
|
2006-09-28 00:16:10 +08:00
|
|
|
/*
|
|
|
|
* Initialize the path interpolation table for this connection.
|
|
|
|
*/
|
|
|
|
interp_clear_table(interp_table, ARRAY_SIZE(interp_table));
|
|
|
|
interp_set_entry(interp_table, INTERP_SLOT_PERCENT, "%");
|
|
|
|
|
2006-09-26 22:47:43 +08:00
|
|
|
if (len != pktlen) {
|
2006-09-28 00:16:10 +08:00
|
|
|
parse_extra_args(interp_table, line + len + 1, pktlen - len - 1);
|
2006-09-26 22:47:43 +08:00
|
|
|
fill_in_extra_table_entries(interp_table);
|
|
|
|
}
|
2006-09-20 09:31:51 +08:00
|
|
|
|
2006-08-21 10:03:13 +08:00
|
|
|
for (i = 0; i < ARRAY_SIZE(daemon_service); i++) {
|
|
|
|
struct daemon_service *s = &(daemon_service[i]);
|
|
|
|
int namelen = strlen(s->name);
|
2007-02-20 17:54:00 +08:00
|
|
|
if (!prefixcmp(line, "git-") &&
|
2006-08-21 10:03:13 +08:00
|
|
|
!strncmp(s->name, line + 4, namelen) &&
|
2006-09-20 09:31:51 +08:00
|
|
|
line[namelen + 4] == ' ') {
|
2006-09-28 00:16:10 +08:00
|
|
|
/*
|
|
|
|
* Note: The directory here is probably context sensitive,
|
|
|
|
* and might depend on the actual service being performed.
|
|
|
|
*/
|
|
|
|
interp_set_entry(interp_table,
|
|
|
|
INTERP_SLOT_DIR, line + namelen + 5);
|
2006-09-20 09:31:51 +08:00
|
|
|
return run_service(interp_table, s);
|
|
|
|
}
|
2006-08-21 10:03:13 +08:00
|
|
|
}
|
2005-07-14 10:45:26 +08:00
|
|
|
|
2005-09-22 17:25:28 +08:00
|
|
|
logerror("Protocol error: '%s'", line);
|
2005-07-14 10:45:26 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2005-07-16 12:51:57 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We count spawned/reaped separately, just to avoid any
|
|
|
|
* races when updating them from signals. The SIGCHLD handler
|
|
|
|
* will only update children_reaped, and the fork logic will
|
|
|
|
* only update children_spawned.
|
|
|
|
*
|
|
|
|
* MAX_CHILDREN should be a power-of-two to make the modulus
|
|
|
|
* operation cheap. It should also be at least twice
|
|
|
|
* the maximum number of connections we will ever allow.
|
|
|
|
*/
|
|
|
|
#define MAX_CHILDREN 128
|
|
|
|
|
|
|
|
static int max_connections = 25;
|
|
|
|
|
|
|
|
/* These are updated by the signal handler */
|
2006-08-16 01:23:48 +08:00
|
|
|
static volatile unsigned int children_reaped;
|
2005-08-02 03:11:53 +08:00
|
|
|
static pid_t dead_child[MAX_CHILDREN];
|
2005-07-16 12:51:57 +08:00
|
|
|
|
|
|
|
/* These are updated by the main loop */
|
2006-08-16 01:23:48 +08:00
|
|
|
static unsigned int children_spawned;
|
|
|
|
static unsigned int children_deleted;
|
2005-07-16 12:51:57 +08:00
|
|
|
|
2005-08-02 03:11:53 +08:00
|
|
|
static struct child {
|
2005-07-16 12:51:57 +08:00
|
|
|
pid_t pid;
|
2005-09-12 04:58:41 +08:00
|
|
|
int addrlen;
|
2005-07-23 16:24:59 +08:00
|
|
|
struct sockaddr_storage address;
|
2005-07-16 12:51:57 +08:00
|
|
|
} live_child[MAX_CHILDREN];
|
|
|
|
|
2005-09-12 04:58:41 +08:00
|
|
|
static void add_child(int idx, pid_t pid, struct sockaddr *addr, int addrlen)
|
2005-07-16 12:51:57 +08:00
|
|
|
{
|
|
|
|
live_child[idx].pid = pid;
|
|
|
|
live_child[idx].addrlen = addrlen;
|
2005-07-23 16:24:59 +08:00
|
|
|
memcpy(&live_child[idx].address, addr, addrlen);
|
2005-07-16 12:51:57 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Walk from "deleted" to "spawned", and remove child "pid".
|
|
|
|
*
|
|
|
|
* We move everything up by one, since the new "deleted" will
|
|
|
|
* be one higher.
|
|
|
|
*/
|
|
|
|
static void remove_child(pid_t pid, unsigned deleted, unsigned spawned)
|
|
|
|
{
|
|
|
|
struct child n;
|
|
|
|
|
|
|
|
deleted %= MAX_CHILDREN;
|
|
|
|
spawned %= MAX_CHILDREN;
|
|
|
|
if (live_child[deleted].pid == pid) {
|
|
|
|
live_child[deleted].pid = -1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
n = live_child[deleted];
|
|
|
|
for (;;) {
|
|
|
|
struct child m;
|
|
|
|
deleted = (deleted + 1) % MAX_CHILDREN;
|
|
|
|
if (deleted == spawned)
|
|
|
|
die("could not find dead child %d\n", pid);
|
|
|
|
m = live_child[deleted];
|
|
|
|
live_child[deleted] = n;
|
|
|
|
if (m.pid == pid)
|
|
|
|
return;
|
|
|
|
n = m;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This gets called if the number of connections grows
|
|
|
|
* past "max_connections".
|
|
|
|
*
|
|
|
|
* We _should_ start off by searching for connections
|
|
|
|
* from the same IP, and if there is some address wth
|
|
|
|
* multiple connections, we should kill that first.
|
|
|
|
*
|
|
|
|
* As it is, we just "randomly" kill 25% of the connections,
|
|
|
|
* and our pseudo-random generator sucks too. I have no
|
|
|
|
* shame.
|
|
|
|
*
|
|
|
|
* Really, this is just a place-holder for a _real_ algorithm.
|
|
|
|
*/
|
2005-07-16 13:53:31 +08:00
|
|
|
static void kill_some_children(int signo, unsigned start, unsigned stop)
|
2005-07-16 12:51:57 +08:00
|
|
|
{
|
|
|
|
start %= MAX_CHILDREN;
|
|
|
|
stop %= MAX_CHILDREN;
|
|
|
|
while (start != stop) {
|
|
|
|
if (!(start & 3))
|
2005-07-16 13:53:31 +08:00
|
|
|
kill(live_child[start].pid, signo);
|
2005-07-16 12:51:57 +08:00
|
|
|
start = (start + 1) % MAX_CHILDREN;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-07-16 13:53:31 +08:00
|
|
|
static void check_max_connections(void)
|
2005-07-14 10:45:26 +08:00
|
|
|
{
|
2005-07-16 13:53:31 +08:00
|
|
|
for (;;) {
|
2005-07-16 11:42:28 +08:00
|
|
|
int active;
|
2005-07-16 12:51:57 +08:00
|
|
|
unsigned spawned, reaped, deleted;
|
2005-07-16 11:42:28 +08:00
|
|
|
|
2005-07-16 12:51:57 +08:00
|
|
|
spawned = children_spawned;
|
|
|
|
reaped = children_reaped;
|
|
|
|
deleted = children_deleted;
|
|
|
|
|
|
|
|
while (deleted < reaped) {
|
|
|
|
pid_t pid = dead_child[deleted % MAX_CHILDREN];
|
|
|
|
remove_child(pid, deleted, spawned);
|
|
|
|
deleted++;
|
|
|
|
}
|
|
|
|
children_deleted = deleted;
|
|
|
|
|
|
|
|
active = spawned - deleted;
|
2005-07-16 13:53:31 +08:00
|
|
|
if (active <= max_connections)
|
|
|
|
break;
|
2005-07-16 12:51:57 +08:00
|
|
|
|
2005-07-16 13:53:31 +08:00
|
|
|
/* Kill some unstarted connections with SIGTERM */
|
|
|
|
kill_some_children(SIGTERM, deleted, spawned);
|
|
|
|
if (active <= max_connections << 1)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* If the SIGTERM thing isn't helping use SIGKILL */
|
|
|
|
kill_some_children(SIGKILL, deleted, spawned);
|
|
|
|
sleep(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-09-12 04:58:41 +08:00
|
|
|
static void handle(int incoming, struct sockaddr *addr, int addrlen)
|
2005-07-16 13:53:31 +08:00
|
|
|
{
|
|
|
|
pid_t pid = fork();
|
|
|
|
|
|
|
|
if (pid) {
|
|
|
|
unsigned idx;
|
|
|
|
|
|
|
|
close(incoming);
|
|
|
|
if (pid < 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
idx = children_spawned % MAX_CHILDREN;
|
|
|
|
children_spawned++;
|
|
|
|
add_child(idx, pid, addr, addrlen);
|
2005-07-16 12:51:57 +08:00
|
|
|
|
2005-07-16 13:53:31 +08:00
|
|
|
check_max_connections();
|
2005-07-14 10:45:26 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
dup2(incoming, 0);
|
|
|
|
dup2(incoming, 1);
|
|
|
|
close(incoming);
|
2005-09-22 17:25:28 +08:00
|
|
|
|
2006-06-20 22:38:13 +08:00
|
|
|
exit(execute(addr));
|
2005-07-14 10:45:26 +08:00
|
|
|
}
|
|
|
|
|
2005-07-16 11:42:28 +08:00
|
|
|
static void child_handler(int signo)
|
|
|
|
{
|
|
|
|
for (;;) {
|
2005-09-24 22:13:01 +08:00
|
|
|
int status;
|
|
|
|
pid_t pid = waitpid(-1, &status, WNOHANG);
|
2005-07-16 12:51:57 +08:00
|
|
|
|
|
|
|
if (pid > 0) {
|
|
|
|
unsigned reaped = children_reaped;
|
|
|
|
dead_child[reaped % MAX_CHILDREN] = pid;
|
|
|
|
children_reaped = reaped + 1;
|
2005-09-22 17:25:28 +08:00
|
|
|
/* XXX: Custom logging, since we don't wanna getpid() */
|
2005-09-24 22:13:01 +08:00
|
|
|
if (verbose) {
|
2006-06-28 17:04:39 +08:00
|
|
|
const char *dead = "";
|
2005-09-24 22:13:01 +08:00
|
|
|
if (!WIFEXITED(status) || WEXITSTATUS(status) > 0)
|
|
|
|
dead = " (with error)";
|
|
|
|
if (log_syslog)
|
|
|
|
syslog(LOG_INFO, "[%d] Disconnected%s", pid, dead);
|
|
|
|
else
|
|
|
|
fprintf(stderr, "[%d] Disconnected%s\n", pid, dead);
|
|
|
|
}
|
2005-07-16 11:42:28 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-02-04 04:27:04 +08:00
|
|
|
static int set_reuse_addr(int sockfd)
|
|
|
|
{
|
|
|
|
int on = 1;
|
|
|
|
|
|
|
|
if (!reuseaddr)
|
|
|
|
return 0;
|
|
|
|
return setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
|
|
|
|
&on, sizeof(on));
|
|
|
|
}
|
|
|
|
|
2005-09-29 08:26:44 +08:00
|
|
|
#ifndef NO_IPV6
|
|
|
|
|
2006-09-26 22:47:43 +08:00
|
|
|
static int socksetup(char *listen_addr, int listen_port, int **socklist_p)
|
2005-07-14 10:45:26 +08:00
|
|
|
{
|
2005-07-23 16:24:59 +08:00
|
|
|
int socknum = 0, *socklist = NULL;
|
|
|
|
int maxfd = -1;
|
|
|
|
char pbuf[NI_MAXSERV];
|
2005-09-29 08:26:44 +08:00
|
|
|
struct addrinfo hints, *ai0, *ai;
|
|
|
|
int gai;
|
2007-02-15 01:10:26 +08:00
|
|
|
long flags;
|
2005-07-23 16:24:59 +08:00
|
|
|
|
2006-09-26 22:47:43 +08:00
|
|
|
sprintf(pbuf, "%d", listen_port);
|
2005-07-23 16:24:59 +08:00
|
|
|
memset(&hints, 0, sizeof(hints));
|
|
|
|
hints.ai_family = AF_UNSPEC;
|
|
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
|
|
hints.ai_protocol = IPPROTO_TCP;
|
|
|
|
hints.ai_flags = AI_PASSIVE;
|
|
|
|
|
2006-09-26 22:47:43 +08:00
|
|
|
gai = getaddrinfo(listen_addr, pbuf, &hints, &ai0);
|
2005-07-23 16:24:59 +08:00
|
|
|
if (gai)
|
|
|
|
die("getaddrinfo() failed: %s\n", gai_strerror(gai));
|
|
|
|
|
|
|
|
for (ai = ai0; ai; ai = ai->ai_next) {
|
|
|
|
int sockfd;
|
|
|
|
|
|
|
|
sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
|
|
|
|
if (sockfd < 0)
|
|
|
|
continue;
|
|
|
|
if (sockfd >= FD_SETSIZE) {
|
|
|
|
error("too large socket descriptor.");
|
|
|
|
close(sockfd);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef IPV6_V6ONLY
|
|
|
|
if (ai->ai_family == AF_INET6) {
|
|
|
|
int on = 1;
|
|
|
|
setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY,
|
|
|
|
&on, sizeof(on));
|
|
|
|
/* Note: error is not fatal */
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2006-02-04 04:27:04 +08:00
|
|
|
if (set_reuse_addr(sockfd)) {
|
|
|
|
close(sockfd);
|
2006-04-18 21:11:06 +08:00
|
|
|
continue;
|
2006-02-04 04:27:04 +08:00
|
|
|
}
|
|
|
|
|
2005-07-23 16:24:59 +08:00
|
|
|
if (bind(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) {
|
|
|
|
close(sockfd);
|
|
|
|
continue; /* not fatal */
|
|
|
|
}
|
|
|
|
if (listen(sockfd, 5) < 0) {
|
|
|
|
close(sockfd);
|
|
|
|
continue; /* not fatal */
|
|
|
|
}
|
|
|
|
|
2007-02-15 01:10:26 +08:00
|
|
|
flags = fcntl(sockfd, F_GETFD, 0);
|
|
|
|
if (flags >= 0)
|
|
|
|
fcntl(sockfd, F_SETFD, flags | FD_CLOEXEC);
|
|
|
|
|
2006-08-26 22:16:18 +08:00
|
|
|
socklist = xrealloc(socklist, sizeof(int) * (socknum + 1));
|
2005-07-23 16:24:59 +08:00
|
|
|
socklist[socknum++] = sockfd;
|
|
|
|
|
|
|
|
if (maxfd < sockfd)
|
|
|
|
maxfd = sockfd;
|
|
|
|
}
|
|
|
|
|
|
|
|
freeaddrinfo(ai0);
|
|
|
|
|
2005-09-29 08:26:44 +08:00
|
|
|
*socklist_p = socklist;
|
|
|
|
return socknum;
|
|
|
|
}
|
|
|
|
|
|
|
|
#else /* NO_IPV6 */
|
|
|
|
|
2006-09-29 02:48:14 +08:00
|
|
|
static int socksetup(char *listen_addr, int listen_port, int **socklist_p)
|
2005-09-29 08:26:44 +08:00
|
|
|
{
|
|
|
|
struct sockaddr_in sin;
|
|
|
|
int sockfd;
|
2007-02-15 01:10:26 +08:00
|
|
|
long flags;
|
2005-09-29 08:26:44 +08:00
|
|
|
|
2006-09-26 22:47:43 +08:00
|
|
|
memset(&sin, 0, sizeof sin);
|
|
|
|
sin.sin_family = AF_INET;
|
|
|
|
sin.sin_port = htons(listen_port);
|
|
|
|
|
|
|
|
if (listen_addr) {
|
|
|
|
/* Well, host better be an IP address here. */
|
|
|
|
if (inet_pton(AF_INET, listen_addr, &sin.sin_addr.s_addr) <= 0)
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
sin.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
|
|
}
|
|
|
|
|
2005-09-29 08:26:44 +08:00
|
|
|
sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
|
|
|
if (sockfd < 0)
|
|
|
|
return 0;
|
|
|
|
|
2006-02-04 04:27:04 +08:00
|
|
|
if (set_reuse_addr(sockfd)) {
|
|
|
|
close(sockfd);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-09-29 08:26:44 +08:00
|
|
|
if ( bind(sockfd, (struct sockaddr *)&sin, sizeof sin) < 0 ) {
|
|
|
|
close(sockfd);
|
|
|
|
return 0;
|
|
|
|
}
|
2005-07-14 10:45:26 +08:00
|
|
|
|
2005-11-22 01:07:23 +08:00
|
|
|
if (listen(sockfd, 5) < 0) {
|
|
|
|
close(sockfd);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-02-15 01:10:26 +08:00
|
|
|
flags = fcntl(sockfd, F_GETFD, 0);
|
|
|
|
if (flags >= 0)
|
|
|
|
fcntl(sockfd, F_SETFD, flags | FD_CLOEXEC);
|
|
|
|
|
2005-10-01 01:47:50 +08:00
|
|
|
*socklist_p = xmalloc(sizeof(int));
|
2005-09-29 08:26:44 +08:00
|
|
|
**socklist_p = sockfd;
|
2005-11-22 01:07:23 +08:00
|
|
|
return 1;
|
2005-09-29 08:26:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static int service_loop(int socknum, int *socklist)
|
|
|
|
{
|
|
|
|
struct pollfd *pfd;
|
|
|
|
int i;
|
|
|
|
|
2005-10-01 01:47:50 +08:00
|
|
|
pfd = xcalloc(socknum, sizeof(struct pollfd));
|
2005-09-29 08:26:44 +08:00
|
|
|
|
|
|
|
for (i = 0; i < socknum; i++) {
|
|
|
|
pfd[i].fd = socklist[i];
|
|
|
|
pfd[i].events = POLLIN;
|
|
|
|
}
|
2005-10-01 02:01:57 +08:00
|
|
|
|
|
|
|
signal(SIGCHLD, child_handler);
|
2005-07-14 10:45:26 +08:00
|
|
|
|
|
|
|
for (;;) {
|
2005-07-23 16:24:59 +08:00
|
|
|
int i;
|
2005-09-29 08:26:44 +08:00
|
|
|
|
2005-10-20 15:52:32 +08:00
|
|
|
if (poll(pfd, socknum, -1) < 0) {
|
2005-07-27 04:26:52 +08:00
|
|
|
if (errno != EINTR) {
|
2005-09-29 08:26:44 +08:00
|
|
|
error("poll failed, resuming: %s",
|
2005-07-27 04:26:52 +08:00
|
|
|
strerror(errno));
|
|
|
|
sleep(1);
|
|
|
|
}
|
2005-07-23 16:24:59 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < socknum; i++) {
|
2005-09-29 08:26:44 +08:00
|
|
|
if (pfd[i].revents & POLLIN) {
|
2005-07-23 16:24:59 +08:00
|
|
|
struct sockaddr_storage ss;
|
2005-10-01 01:48:21 +08:00
|
|
|
unsigned int sslen = sizeof(ss);
|
2005-09-29 08:26:44 +08:00
|
|
|
int incoming = accept(pfd[i].fd, (struct sockaddr *)&ss, &sslen);
|
2005-07-23 16:24:59 +08:00
|
|
|
if (incoming < 0) {
|
|
|
|
switch (errno) {
|
|
|
|
case EAGAIN:
|
|
|
|
case EINTR:
|
|
|
|
case ECONNABORTED:
|
|
|
|
continue;
|
|
|
|
default:
|
|
|
|
die("accept returned %s", strerror(errno));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
handle(incoming, (struct sockaddr *)&ss, sslen);
|
2005-07-14 10:45:26 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-07-14 00:32:11 +08:00
|
|
|
/* if any standard file descriptor is missing open it to /dev/null */
|
|
|
|
static void sanitize_stdfds(void)
|
|
|
|
{
|
|
|
|
int fd = open("/dev/null", O_RDWR, 0);
|
|
|
|
while (fd != -1 && fd < 2)
|
|
|
|
fd = dup(fd);
|
|
|
|
if (fd == -1)
|
|
|
|
die("open /dev/null or dup failed: %s", strerror(errno));
|
|
|
|
if (fd > 2)
|
|
|
|
close(fd);
|
|
|
|
}
|
|
|
|
|
2006-07-14 00:47:13 +08:00
|
|
|
static void daemonize(void)
|
|
|
|
{
|
|
|
|
switch (fork()) {
|
|
|
|
case 0:
|
|
|
|
break;
|
|
|
|
case -1:
|
|
|
|
die("fork failed: %s", strerror(errno));
|
|
|
|
default:
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
if (setsid() == -1)
|
|
|
|
die("setsid failed: %s", strerror(errno));
|
|
|
|
close(0);
|
|
|
|
close(1);
|
|
|
|
close(2);
|
|
|
|
sanitize_stdfds();
|
|
|
|
}
|
|
|
|
|
2006-07-13 18:18:08 +08:00
|
|
|
static void store_pid(const char *path)
|
|
|
|
{
|
|
|
|
FILE *f = fopen(path, "w");
|
|
|
|
if (!f)
|
|
|
|
die("cannot open pid file %s: %s", path, strerror(errno));
|
2007-05-21 15:58:01 +08:00
|
|
|
if (fprintf(f, "%d\n", getpid()) < 0 || fclose(f) != 0)
|
|
|
|
die("failed to write pid file %s: %s", path, strerror(errno));
|
2006-07-13 18:18:08 +08:00
|
|
|
}
|
|
|
|
|
2006-09-26 22:47:43 +08:00
|
|
|
static int serve(char *listen_addr, int listen_port, struct passwd *pass, gid_t gid)
|
2005-09-29 08:26:44 +08:00
|
|
|
{
|
|
|
|
int socknum, *socklist;
|
2005-10-21 14:21:50 +08:00
|
|
|
|
2006-09-26 22:47:43 +08:00
|
|
|
socknum = socksetup(listen_addr, listen_port, &socklist);
|
2005-09-29 08:26:44 +08:00
|
|
|
if (socknum == 0)
|
2006-09-26 22:47:43 +08:00
|
|
|
die("unable to allocate any listen sockets on host %s port %u",
|
|
|
|
listen_addr, listen_port);
|
2005-10-21 14:21:50 +08:00
|
|
|
|
2006-08-23 01:37:41 +08:00
|
|
|
if (pass && gid &&
|
|
|
|
(initgroups(pass->pw_name, gid) || setgid (gid) ||
|
|
|
|
setuid(pass->pw_uid)))
|
|
|
|
die("cannot drop privileges");
|
|
|
|
|
2005-09-29 08:26:44 +08:00
|
|
|
return service_loop(socknum, socklist);
|
2005-10-21 14:21:50 +08:00
|
|
|
}
|
2005-09-29 08:26:44 +08:00
|
|
|
|
2005-07-14 10:45:26 +08:00
|
|
|
int main(int argc, char **argv)
|
|
|
|
{
|
2006-09-26 22:47:43 +08:00
|
|
|
int listen_port = 0;
|
|
|
|
char *listen_addr = NULL;
|
2005-07-16 00:32:16 +08:00
|
|
|
int inetd_mode = 0;
|
2006-08-23 01:37:41 +08:00
|
|
|
const char *pid_file = NULL, *user_name = NULL, *group_name = NULL;
|
2006-07-14 00:47:13 +08:00
|
|
|
int detach = 0;
|
2006-08-23 01:37:41 +08:00
|
|
|
struct passwd *pass = NULL;
|
|
|
|
struct group *group;
|
|
|
|
gid_t gid = 0;
|
2005-07-14 10:45:26 +08:00
|
|
|
int i;
|
|
|
|
|
2006-06-20 09:25:21 +08:00
|
|
|
/* Without this we cannot rely on waitpid() to tell
|
|
|
|
* what happened to our children.
|
|
|
|
*/
|
|
|
|
signal(SIGCHLD, SIG_DFL);
|
|
|
|
|
2005-07-14 10:45:26 +08:00
|
|
|
for (i = 1; i < argc; i++) {
|
|
|
|
char *arg = argv[i];
|
|
|
|
|
Mechanical conversion to use prefixcmp()
This mechanically converts strncmp() to use prefixcmp(), but only when
the parameters match specific patterns, so that they can be verified
easily. Leftover from this will be fixed in a separate step, including
idiotic conversions like
if (!strncmp("foo", arg, 3))
=>
if (!(-prefixcmp(arg, "foo")))
This was done by using this script in px.perl
#!/usr/bin/perl -i.bak -p
if (/strncmp\(([^,]+), "([^\\"]*)", (\d+)\)/ && (length($2) == $3)) {
s|strncmp\(([^,]+), "([^\\"]*)", (\d+)\)|prefixcmp($1, "$2")|;
}
if (/strncmp\("([^\\"]*)", ([^,]+), (\d+)\)/ && (length($1) == $3)) {
s|strncmp\("([^\\"]*)", ([^,]+), (\d+)\)|(-prefixcmp($2, "$1"))|;
}
and running:
$ git grep -l strncmp -- '*.c' | xargs perl px.perl
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-02-20 17:53:29 +08:00
|
|
|
if (!prefixcmp(arg, "--listen=")) {
|
2006-09-26 22:47:43 +08:00
|
|
|
char *p = arg + 9;
|
|
|
|
char *ph = listen_addr = xmalloc(strlen(arg + 9) + 1);
|
|
|
|
while (*p)
|
|
|
|
*ph++ = tolower(*p++);
|
|
|
|
*ph = 0;
|
|
|
|
continue;
|
|
|
|
}
|
Mechanical conversion to use prefixcmp()
This mechanically converts strncmp() to use prefixcmp(), but only when
the parameters match specific patterns, so that they can be verified
easily. Leftover from this will be fixed in a separate step, including
idiotic conversions like
if (!strncmp("foo", arg, 3))
=>
if (!(-prefixcmp(arg, "foo")))
This was done by using this script in px.perl
#!/usr/bin/perl -i.bak -p
if (/strncmp\(([^,]+), "([^\\"]*)", (\d+)\)/ && (length($2) == $3)) {
s|strncmp\(([^,]+), "([^\\"]*)", (\d+)\)|prefixcmp($1, "$2")|;
}
if (/strncmp\("([^\\"]*)", ([^,]+), (\d+)\)/ && (length($1) == $3)) {
s|strncmp\("([^\\"]*)", ([^,]+), (\d+)\)|(-prefixcmp($2, "$1"))|;
}
and running:
$ git grep -l strncmp -- '*.c' | xargs perl px.perl
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-02-20 17:53:29 +08:00
|
|
|
if (!prefixcmp(arg, "--port=")) {
|
2005-07-14 10:45:26 +08:00
|
|
|
char *end;
|
|
|
|
unsigned long n;
|
|
|
|
n = strtoul(arg+7, &end, 0);
|
|
|
|
if (arg[7] && !*end) {
|
2006-09-26 22:47:43 +08:00
|
|
|
listen_port = n;
|
2005-07-14 10:45:26 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
2005-07-16 00:32:16 +08:00
|
|
|
if (!strcmp(arg, "--inetd")) {
|
|
|
|
inetd_mode = 1;
|
2005-11-17 07:38:29 +08:00
|
|
|
log_syslog = 1;
|
2005-07-16 00:32:16 +08:00
|
|
|
continue;
|
|
|
|
}
|
2005-09-22 17:25:28 +08:00
|
|
|
if (!strcmp(arg, "--verbose")) {
|
|
|
|
verbose = 1;
|
|
|
|
continue;
|
|
|
|
}
|
2005-09-24 22:13:01 +08:00
|
|
|
if (!strcmp(arg, "--syslog")) {
|
|
|
|
log_syslog = 1;
|
|
|
|
continue;
|
|
|
|
}
|
2005-09-27 10:10:55 +08:00
|
|
|
if (!strcmp(arg, "--export-all")) {
|
|
|
|
export_all_trees = 1;
|
|
|
|
continue;
|
|
|
|
}
|
Mechanical conversion to use prefixcmp()
This mechanically converts strncmp() to use prefixcmp(), but only when
the parameters match specific patterns, so that they can be verified
easily. Leftover from this will be fixed in a separate step, including
idiotic conversions like
if (!strncmp("foo", arg, 3))
=>
if (!(-prefixcmp(arg, "foo")))
This was done by using this script in px.perl
#!/usr/bin/perl -i.bak -p
if (/strncmp\(([^,]+), "([^\\"]*)", (\d+)\)/ && (length($2) == $3)) {
s|strncmp\(([^,]+), "([^\\"]*)", (\d+)\)|prefixcmp($1, "$2")|;
}
if (/strncmp\("([^\\"]*)", ([^,]+), (\d+)\)/ && (length($1) == $3)) {
s|strncmp\("([^\\"]*)", ([^,]+), (\d+)\)|(-prefixcmp($2, "$1"))|;
}
and running:
$ git grep -l strncmp -- '*.c' | xargs perl px.perl
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-02-20 17:53:29 +08:00
|
|
|
if (!prefixcmp(arg, "--timeout=")) {
|
2005-10-20 05:27:01 +08:00
|
|
|
timeout = atoi(arg+10);
|
2005-11-17 07:38:29 +08:00
|
|
|
continue;
|
2005-10-20 05:27:01 +08:00
|
|
|
}
|
Mechanical conversion to use prefixcmp()
This mechanically converts strncmp() to use prefixcmp(), but only when
the parameters match specific patterns, so that they can be verified
easily. Leftover from this will be fixed in a separate step, including
idiotic conversions like
if (!strncmp("foo", arg, 3))
=>
if (!(-prefixcmp(arg, "foo")))
This was done by using this script in px.perl
#!/usr/bin/perl -i.bak -p
if (/strncmp\(([^,]+), "([^\\"]*)", (\d+)\)/ && (length($2) == $3)) {
s|strncmp\(([^,]+), "([^\\"]*)", (\d+)\)|prefixcmp($1, "$2")|;
}
if (/strncmp\("([^\\"]*)", ([^,]+), (\d+)\)/ && (length($1) == $3)) {
s|strncmp\("([^\\"]*)", ([^,]+), (\d+)\)|(-prefixcmp($2, "$1"))|;
}
and running:
$ git grep -l strncmp -- '*.c' | xargs perl px.perl
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-02-20 17:53:29 +08:00
|
|
|
if (!prefixcmp(arg, "--init-timeout=")) {
|
2005-10-20 05:27:01 +08:00
|
|
|
init_timeout = atoi(arg+15);
|
2005-11-17 07:38:29 +08:00
|
|
|
continue;
|
2005-10-20 05:27:01 +08:00
|
|
|
}
|
2005-11-18 03:37:14 +08:00
|
|
|
if (!strcmp(arg, "--strict-paths")) {
|
|
|
|
strict_paths = 1;
|
|
|
|
continue;
|
|
|
|
}
|
Mechanical conversion to use prefixcmp()
This mechanically converts strncmp() to use prefixcmp(), but only when
the parameters match specific patterns, so that they can be verified
easily. Leftover from this will be fixed in a separate step, including
idiotic conversions like
if (!strncmp("foo", arg, 3))
=>
if (!(-prefixcmp(arg, "foo")))
This was done by using this script in px.perl
#!/usr/bin/perl -i.bak -p
if (/strncmp\(([^,]+), "([^\\"]*)", (\d+)\)/ && (length($2) == $3)) {
s|strncmp\(([^,]+), "([^\\"]*)", (\d+)\)|prefixcmp($1, "$2")|;
}
if (/strncmp\("([^\\"]*)", ([^,]+), (\d+)\)/ && (length($1) == $3)) {
s|strncmp\("([^\\"]*)", ([^,]+), (\d+)\)|(-prefixcmp($2, "$1"))|;
}
and running:
$ git grep -l strncmp -- '*.c' | xargs perl px.perl
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-02-20 17:53:29 +08:00
|
|
|
if (!prefixcmp(arg, "--base-path=")) {
|
2005-12-23 09:27:40 +08:00
|
|
|
base_path = arg+12;
|
|
|
|
continue;
|
|
|
|
}
|
2007-07-28 05:00:29 +08:00
|
|
|
if (!strcmp(arg, "--base-path-relaxed")) {
|
|
|
|
base_path_relaxed = 1;
|
|
|
|
continue;
|
|
|
|
}
|
Mechanical conversion to use prefixcmp()
This mechanically converts strncmp() to use prefixcmp(), but only when
the parameters match specific patterns, so that they can be verified
easily. Leftover from this will be fixed in a separate step, including
idiotic conversions like
if (!strncmp("foo", arg, 3))
=>
if (!(-prefixcmp(arg, "foo")))
This was done by using this script in px.perl
#!/usr/bin/perl -i.bak -p
if (/strncmp\(([^,]+), "([^\\"]*)", (\d+)\)/ && (length($2) == $3)) {
s|strncmp\(([^,]+), "([^\\"]*)", (\d+)\)|prefixcmp($1, "$2")|;
}
if (/strncmp\("([^\\"]*)", ([^,]+), (\d+)\)/ && (length($1) == $3)) {
s|strncmp\("([^\\"]*)", ([^,]+), (\d+)\)|(-prefixcmp($2, "$1"))|;
}
and running:
$ git grep -l strncmp -- '*.c' | xargs perl px.perl
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-02-20 17:53:29 +08:00
|
|
|
if (!prefixcmp(arg, "--interpolated-path=")) {
|
2006-09-20 09:31:51 +08:00
|
|
|
interpolated_path = arg+20;
|
|
|
|
continue;
|
|
|
|
}
|
2006-02-04 04:27:04 +08:00
|
|
|
if (!strcmp(arg, "--reuseaddr")) {
|
|
|
|
reuseaddr = 1;
|
|
|
|
continue;
|
|
|
|
}
|
2006-02-05 14:27:29 +08:00
|
|
|
if (!strcmp(arg, "--user-path")) {
|
|
|
|
user_path = "";
|
|
|
|
continue;
|
|
|
|
}
|
Mechanical conversion to use prefixcmp()
This mechanically converts strncmp() to use prefixcmp(), but only when
the parameters match specific patterns, so that they can be verified
easily. Leftover from this will be fixed in a separate step, including
idiotic conversions like
if (!strncmp("foo", arg, 3))
=>
if (!(-prefixcmp(arg, "foo")))
This was done by using this script in px.perl
#!/usr/bin/perl -i.bak -p
if (/strncmp\(([^,]+), "([^\\"]*)", (\d+)\)/ && (length($2) == $3)) {
s|strncmp\(([^,]+), "([^\\"]*)", (\d+)\)|prefixcmp($1, "$2")|;
}
if (/strncmp\("([^\\"]*)", ([^,]+), (\d+)\)/ && (length($1) == $3)) {
s|strncmp\("([^\\"]*)", ([^,]+), (\d+)\)|(-prefixcmp($2, "$1"))|;
}
and running:
$ git grep -l strncmp -- '*.c' | xargs perl px.perl
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-02-20 17:53:29 +08:00
|
|
|
if (!prefixcmp(arg, "--user-path=")) {
|
2006-02-05 14:27:29 +08:00
|
|
|
user_path = arg + 12;
|
|
|
|
continue;
|
|
|
|
}
|
Mechanical conversion to use prefixcmp()
This mechanically converts strncmp() to use prefixcmp(), but only when
the parameters match specific patterns, so that they can be verified
easily. Leftover from this will be fixed in a separate step, including
idiotic conversions like
if (!strncmp("foo", arg, 3))
=>
if (!(-prefixcmp(arg, "foo")))
This was done by using this script in px.perl
#!/usr/bin/perl -i.bak -p
if (/strncmp\(([^,]+), "([^\\"]*)", (\d+)\)/ && (length($2) == $3)) {
s|strncmp\(([^,]+), "([^\\"]*)", (\d+)\)|prefixcmp($1, "$2")|;
}
if (/strncmp\("([^\\"]*)", ([^,]+), (\d+)\)/ && (length($1) == $3)) {
s|strncmp\("([^\\"]*)", ([^,]+), (\d+)\)|(-prefixcmp($2, "$1"))|;
}
and running:
$ git grep -l strncmp -- '*.c' | xargs perl px.perl
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-02-20 17:53:29 +08:00
|
|
|
if (!prefixcmp(arg, "--pid-file=")) {
|
2006-07-13 18:18:08 +08:00
|
|
|
pid_file = arg + 11;
|
|
|
|
continue;
|
|
|
|
}
|
2006-07-14 00:47:13 +08:00
|
|
|
if (!strcmp(arg, "--detach")) {
|
|
|
|
detach = 1;
|
|
|
|
log_syslog = 1;
|
|
|
|
continue;
|
|
|
|
}
|
Mechanical conversion to use prefixcmp()
This mechanically converts strncmp() to use prefixcmp(), but only when
the parameters match specific patterns, so that they can be verified
easily. Leftover from this will be fixed in a separate step, including
idiotic conversions like
if (!strncmp("foo", arg, 3))
=>
if (!(-prefixcmp(arg, "foo")))
This was done by using this script in px.perl
#!/usr/bin/perl -i.bak -p
if (/strncmp\(([^,]+), "([^\\"]*)", (\d+)\)/ && (length($2) == $3)) {
s|strncmp\(([^,]+), "([^\\"]*)", (\d+)\)|prefixcmp($1, "$2")|;
}
if (/strncmp\("([^\\"]*)", ([^,]+), (\d+)\)/ && (length($1) == $3)) {
s|strncmp\("([^\\"]*)", ([^,]+), (\d+)\)|(-prefixcmp($2, "$1"))|;
}
and running:
$ git grep -l strncmp -- '*.c' | xargs perl px.perl
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-02-20 17:53:29 +08:00
|
|
|
if (!prefixcmp(arg, "--user=")) {
|
2006-08-23 01:37:41 +08:00
|
|
|
user_name = arg + 7;
|
|
|
|
continue;
|
|
|
|
}
|
Mechanical conversion to use prefixcmp()
This mechanically converts strncmp() to use prefixcmp(), but only when
the parameters match specific patterns, so that they can be verified
easily. Leftover from this will be fixed in a separate step, including
idiotic conversions like
if (!strncmp("foo", arg, 3))
=>
if (!(-prefixcmp(arg, "foo")))
This was done by using this script in px.perl
#!/usr/bin/perl -i.bak -p
if (/strncmp\(([^,]+), "([^\\"]*)", (\d+)\)/ && (length($2) == $3)) {
s|strncmp\(([^,]+), "([^\\"]*)", (\d+)\)|prefixcmp($1, "$2")|;
}
if (/strncmp\("([^\\"]*)", ([^,]+), (\d+)\)/ && (length($1) == $3)) {
s|strncmp\("([^\\"]*)", ([^,]+), (\d+)\)|(-prefixcmp($2, "$1"))|;
}
and running:
$ git grep -l strncmp -- '*.c' | xargs perl px.perl
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-02-20 17:53:29 +08:00
|
|
|
if (!prefixcmp(arg, "--group=")) {
|
2006-08-23 01:37:41 +08:00
|
|
|
group_name = arg + 8;
|
|
|
|
continue;
|
|
|
|
}
|
Mechanical conversion to use prefixcmp()
This mechanically converts strncmp() to use prefixcmp(), but only when
the parameters match specific patterns, so that they can be verified
easily. Leftover from this will be fixed in a separate step, including
idiotic conversions like
if (!strncmp("foo", arg, 3))
=>
if (!(-prefixcmp(arg, "foo")))
This was done by using this script in px.perl
#!/usr/bin/perl -i.bak -p
if (/strncmp\(([^,]+), "([^\\"]*)", (\d+)\)/ && (length($2) == $3)) {
s|strncmp\(([^,]+), "([^\\"]*)", (\d+)\)|prefixcmp($1, "$2")|;
}
if (/strncmp\("([^\\"]*)", ([^,]+), (\d+)\)/ && (length($1) == $3)) {
s|strncmp\("([^\\"]*)", ([^,]+), (\d+)\)|(-prefixcmp($2, "$1"))|;
}
and running:
$ git grep -l strncmp -- '*.c' | xargs perl px.perl
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-02-20 17:53:29 +08:00
|
|
|
if (!prefixcmp(arg, "--enable=")) {
|
2006-08-21 10:03:13 +08:00
|
|
|
enable_service(arg + 9, 1);
|
|
|
|
continue;
|
|
|
|
}
|
Mechanical conversion to use prefixcmp()
This mechanically converts strncmp() to use prefixcmp(), but only when
the parameters match specific patterns, so that they can be verified
easily. Leftover from this will be fixed in a separate step, including
idiotic conversions like
if (!strncmp("foo", arg, 3))
=>
if (!(-prefixcmp(arg, "foo")))
This was done by using this script in px.perl
#!/usr/bin/perl -i.bak -p
if (/strncmp\(([^,]+), "([^\\"]*)", (\d+)\)/ && (length($2) == $3)) {
s|strncmp\(([^,]+), "([^\\"]*)", (\d+)\)|prefixcmp($1, "$2")|;
}
if (/strncmp\("([^\\"]*)", ([^,]+), (\d+)\)/ && (length($1) == $3)) {
s|strncmp\("([^\\"]*)", ([^,]+), (\d+)\)|(-prefixcmp($2, "$1"))|;
}
and running:
$ git grep -l strncmp -- '*.c' | xargs perl px.perl
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-02-20 17:53:29 +08:00
|
|
|
if (!prefixcmp(arg, "--disable=")) {
|
2006-08-21 10:03:13 +08:00
|
|
|
enable_service(arg + 10, 0);
|
|
|
|
continue;
|
|
|
|
}
|
Mechanical conversion to use prefixcmp()
This mechanically converts strncmp() to use prefixcmp(), but only when
the parameters match specific patterns, so that they can be verified
easily. Leftover from this will be fixed in a separate step, including
idiotic conversions like
if (!strncmp("foo", arg, 3))
=>
if (!(-prefixcmp(arg, "foo")))
This was done by using this script in px.perl
#!/usr/bin/perl -i.bak -p
if (/strncmp\(([^,]+), "([^\\"]*)", (\d+)\)/ && (length($2) == $3)) {
s|strncmp\(([^,]+), "([^\\"]*)", (\d+)\)|prefixcmp($1, "$2")|;
}
if (/strncmp\("([^\\"]*)", ([^,]+), (\d+)\)/ && (length($1) == $3)) {
s|strncmp\("([^\\"]*)", ([^,]+), (\d+)\)|(-prefixcmp($2, "$1"))|;
}
and running:
$ git grep -l strncmp -- '*.c' | xargs perl px.perl
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-02-20 17:53:29 +08:00
|
|
|
if (!prefixcmp(arg, "--allow-override=")) {
|
2006-08-21 10:03:50 +08:00
|
|
|
make_service_overridable(arg + 17, 1);
|
2006-08-21 10:03:13 +08:00
|
|
|
continue;
|
|
|
|
}
|
Mechanical conversion to use prefixcmp()
This mechanically converts strncmp() to use prefixcmp(), but only when
the parameters match specific patterns, so that they can be verified
easily. Leftover from this will be fixed in a separate step, including
idiotic conversions like
if (!strncmp("foo", arg, 3))
=>
if (!(-prefixcmp(arg, "foo")))
This was done by using this script in px.perl
#!/usr/bin/perl -i.bak -p
if (/strncmp\(([^,]+), "([^\\"]*)", (\d+)\)/ && (length($2) == $3)) {
s|strncmp\(([^,]+), "([^\\"]*)", (\d+)\)|prefixcmp($1, "$2")|;
}
if (/strncmp\("([^\\"]*)", ([^,]+), (\d+)\)/ && (length($1) == $3)) {
s|strncmp\("([^\\"]*)", ([^,]+), (\d+)\)|(-prefixcmp($2, "$1"))|;
}
and running:
$ git grep -l strncmp -- '*.c' | xargs perl px.perl
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-02-20 17:53:29 +08:00
|
|
|
if (!prefixcmp(arg, "--forbid-override=")) {
|
2006-08-21 10:03:50 +08:00
|
|
|
make_service_overridable(arg + 18, 0);
|
2006-08-21 10:03:13 +08:00
|
|
|
continue;
|
|
|
|
}
|
2005-09-27 10:10:55 +08:00
|
|
|
if (!strcmp(arg, "--")) {
|
|
|
|
ok_paths = &argv[i+1];
|
|
|
|
break;
|
|
|
|
} else if (arg[0] != '-') {
|
|
|
|
ok_paths = &argv[i];
|
|
|
|
break;
|
|
|
|
}
|
2005-07-16 00:32:16 +08:00
|
|
|
|
2005-07-14 10:45:26 +08:00
|
|
|
usage(daemon_usage);
|
|
|
|
}
|
|
|
|
|
2008-02-25 21:25:20 +08:00
|
|
|
if (log_syslog) {
|
|
|
|
openlog("git-daemon", 0, LOG_DAEMON);
|
|
|
|
set_die_routine(daemon_die);
|
|
|
|
}
|
|
|
|
|
2006-08-23 01:37:41 +08:00
|
|
|
if (inetd_mode && (group_name || user_name))
|
|
|
|
die("--user and --group are incompatible with --inetd");
|
|
|
|
|
2006-09-26 22:47:43 +08:00
|
|
|
if (inetd_mode && (listen_port || listen_addr))
|
|
|
|
die("--listen= and --port= are incompatible with --inetd");
|
|
|
|
else if (listen_port == 0)
|
|
|
|
listen_port = DEFAULT_GIT_PORT;
|
|
|
|
|
2006-08-23 01:37:41 +08:00
|
|
|
if (group_name && !user_name)
|
|
|
|
die("--group supplied without --user");
|
|
|
|
|
|
|
|
if (user_name) {
|
|
|
|
pass = getpwnam(user_name);
|
|
|
|
if (!pass)
|
|
|
|
die("user not found - %s", user_name);
|
|
|
|
|
|
|
|
if (!group_name)
|
|
|
|
gid = pass->pw_gid;
|
|
|
|
else {
|
|
|
|
group = getgrnam(group_name);
|
|
|
|
if (!group)
|
|
|
|
die("group not found - %s", group_name);
|
|
|
|
|
|
|
|
gid = group->gr_gid;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-07-13 18:02:29 +08:00
|
|
|
if (strict_paths && (!ok_paths || !*ok_paths))
|
|
|
|
die("option --strict-paths requires a whitelist");
|
|
|
|
|
2008-02-26 20:00:55 +08:00
|
|
|
if (base_path) {
|
|
|
|
struct stat st;
|
|
|
|
|
|
|
|
if (stat(base_path, &st) || !S_ISDIR(st.st_mode))
|
|
|
|
die("base-path '%s' does not exist or "
|
|
|
|
"is not a directory", base_path);
|
|
|
|
}
|
|
|
|
|
2005-09-08 09:50:01 +08:00
|
|
|
if (inetd_mode) {
|
2006-06-20 22:38:13 +08:00
|
|
|
struct sockaddr_storage ss;
|
|
|
|
struct sockaddr *peer = (struct sockaddr *)&ss;
|
|
|
|
socklen_t slen = sizeof(ss);
|
|
|
|
|
2006-06-22 07:37:48 +08:00
|
|
|
freopen("/dev/null", "w", stderr);
|
2006-06-20 22:38:13 +08:00
|
|
|
|
|
|
|
if (getpeername(0, peer, &slen))
|
|
|
|
peer = NULL;
|
|
|
|
|
|
|
|
return execute(peer);
|
2005-09-08 09:50:01 +08:00
|
|
|
}
|
2005-11-15 00:41:01 +08:00
|
|
|
|
2006-07-14 00:47:13 +08:00
|
|
|
if (detach)
|
|
|
|
daemonize();
|
|
|
|
else
|
|
|
|
sanitize_stdfds();
|
2006-07-14 00:32:11 +08:00
|
|
|
|
2006-07-13 18:18:08 +08:00
|
|
|
if (pid_file)
|
|
|
|
store_pid(pid_file);
|
|
|
|
|
2006-09-26 22:47:43 +08:00
|
|
|
return serve(listen_addr, listen_port, pass, gid);
|
2005-07-14 10:45:26 +08:00
|
|
|
}
|