2005-10-26 07:29:09 +08:00
|
|
|
#include "cache.h"
|
2017-06-15 02:07:36 +08:00
|
|
|
#include "config.h"
|
2006-12-20 06:34:12 +08:00
|
|
|
#include "pkt-line.h"
|
2009-06-15 04:38:51 +08:00
|
|
|
#include "run-command.h"
|
|
|
|
#include "strbuf.h"
|
2010-08-30 19:30:51 +08:00
|
|
|
#include "string-list.h"
|
2005-09-22 17:25:28 +08:00
|
|
|
|
2010-10-27 16:39:52 +08:00
|
|
|
#ifdef NO_INITGROUPS
|
|
|
|
#define initgroups(x, y) (0) /* nothing */
|
|
|
|
#endif
|
|
|
|
|
daemon: add --log-destination=(stderr|syslog|none)
This new option can be used to override the implicit --syslog of
--inetd, or to disable all logging. (While --detach also implies
--syslog, --log-destination=stderr with --detach is useless since
--detach disassociates the process from the original stderr.) --syslog
is retained as an alias for --log-destination=syslog.
--log-destination always overrides implicit --syslog regardless of
option order. This is different than the “last one wins” logic that
applies to some implicit options elsewhere in Git, but should hopefully
be less confusing. (I also don’t know if *all* implicit options in Git
follow “last one wins”.)
The combination of --inetd with --log-destination=stderr is useful, for
instance, when running `git daemon` as an instanced systemd service
(with associated socket unit). In this case, log messages sent via
syslog are received by the journal daemon, but run the risk of being
processed at a time when the `git daemon` process has already exited
(especially if the process was very short-lived, e.g. due to client
error), so that the journal daemon can no longer read its cgroup and
attach the message to the correct systemd unit (see systemd/systemd#2913
[1]). Logging to stderr instead can solve this problem, because systemd
can connect stderr directly to the journal daemon, which then already
knows which unit is associated with this stream.
[1]: https://github.com/systemd/systemd/issues/2913
Helped-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Helped-by: Junio C Hamano <gitster@pobox.com>
Helped-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Lucas Werkmeister <mail@lucaswerkmeister.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-02-05 02:30:37 +08:00
|
|
|
static enum log_destination {
|
|
|
|
LOG_DESTINATION_UNSET = -1,
|
|
|
|
LOG_DESTINATION_NONE = 0,
|
|
|
|
LOG_DESTINATION_STDERR = 1,
|
|
|
|
LOG_DESTINATION_SYSLOG = 2,
|
|
|
|
} log_destination = LOG_DESTINATION_UNSET;
|
2005-09-22 17:25:28 +08:00
|
|
|
static int verbose;
|
2006-02-04 04:27:04 +08:00
|
|
|
static int reuseaddr;
|
2011-10-15 05:19:21 +08:00
|
|
|
static int informative_errors;
|
2005-09-22 17:25:28 +08:00
|
|
|
|
2005-10-20 05:27:01 +08:00
|
|
|
static const char daemon_usage[] =
|
2008-07-13 21:36:15 +08:00
|
|
|
"git daemon [--verbose] [--syslog] [--export-all]\n"
|
2010-10-09 01:31:15 +08:00
|
|
|
" [--timeout=<n>] [--init-timeout=<n>] [--max-connections=<n>]\n"
|
|
|
|
" [--strict-paths] [--base-path=<path>] [--base-path-relaxed]\n"
|
|
|
|
" [--user-path | --user-path=<path>]\n"
|
|
|
|
" [--interpolated-path=<path>]\n"
|
2010-11-04 09:35:23 +08:00
|
|
|
" [--reuseaddr] [--pid-file=<file>]\n"
|
2010-10-09 01:31:15 +08:00
|
|
|
" [--(enable|disable|allow-override|forbid-override)=<service>]\n"
|
2012-08-15 02:37:51 +08:00
|
|
|
" [--access-hook=<path>]\n"
|
2010-10-09 01:31:15 +08:00
|
|
|
" [--inetd | [--listen=<host_or_ipaddr>] [--port=<n>]\n"
|
2010-11-04 09:35:23 +08:00
|
|
|
" [--detach] [--user=<user> [--group=<group>]]\n"
|
daemon: add --log-destination=(stderr|syslog|none)
This new option can be used to override the implicit --syslog of
--inetd, or to disable all logging. (While --detach also implies
--syslog, --log-destination=stderr with --detach is useless since
--detach disassociates the process from the original stderr.) --syslog
is retained as an alias for --log-destination=syslog.
--log-destination always overrides implicit --syslog regardless of
option order. This is different than the “last one wins” logic that
applies to some implicit options elsewhere in Git, but should hopefully
be less confusing. (I also don’t know if *all* implicit options in Git
follow “last one wins”.)
The combination of --inetd with --log-destination=stderr is useful, for
instance, when running `git daemon` as an instanced systemd service
(with associated socket unit). In this case, log messages sent via
syslog are received by the journal daemon, but run the risk of being
processed at a time when the `git daemon` process has already exited
(especially if the process was very short-lived, e.g. due to client
error), so that the journal daemon can no longer read its cgroup and
attach the message to the correct systemd unit (see systemd/systemd#2913
[1]). Logging to stderr instead can solve this problem, because systemd
can connect stderr directly to the journal daemon, which then already
knows which unit is associated with this stream.
[1]: https://github.com/systemd/systemd/issues/2913
Helped-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Helped-by: Junio C Hamano <gitster@pobox.com>
Helped-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Lucas Werkmeister <mail@lucaswerkmeister.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-02-05 02:30:37 +08:00
|
|
|
" [--log-destination=(stderr|syslog|none)]\n"
|
2010-10-09 01:31:15 +08:00
|
|
|
" [<directory>...]";
|
2005-09-27 10:10:55 +08:00
|
|
|
|
|
|
|
/* List of acceptable pathname prefixes */
|
add an extra level of indirection to main()
There are certain startup tasks that we expect every git
process to do. In some cases this is just to improve the
quality of the program (e.g., setting up gettext()). In
others it is a requirement for using certain functions in
libgit.a (e.g., system_path() expects that you have called
git_extract_argv0_path()).
Most commands are builtins and are covered by the git.c
version of main(). However, there are still a few external
commands that use their own main(). Each of these has to
remember to include the correct startup sequence, and we are
not always consistent.
Rather than just fix the inconsistencies, let's make this
harder to get wrong by providing a common main() that can
run this standard startup.
We basically have two options to do this:
- the compat/mingw.h file already does something like this by
adding a #define that replaces the definition of main with a
wrapper that calls mingw_startup().
The upside is that the code in each program doesn't need
to be changed at all; it's rewritten on the fly by the
preprocessor.
The downside is that it may make debugging of the startup
sequence a bit more confusing, as the preprocessor is
quietly inserting new code.
- the builtin functions are all of the form cmd_foo(),
and git.c's main() calls them.
This is much more explicit, which may make things more
obvious to somebody reading the code. It's also more
flexible (because of course we have to figure out _which_
cmd_foo() to call).
The downside is that each of the builtins must define
cmd_foo(), instead of just main().
This patch chooses the latter option, preferring the more
explicit approach, even though it is more invasive. We
introduce a new file common-main.c, with the "real" main. It
expects to call cmd_main() from whatever other objects it is
linked against.
We link common-main.o against anything that links against
libgit.a, since we know that such programs will need to do
this setup. Note that common-main.o can't actually go inside
libgit.a, as the linker would not pick up its main()
function automatically (it has no callers).
The rest of the patch is just adjusting all of the various
external programs (mostly in t/helper) to use cmd_main().
I've provided a global declaration for cmd_main(), which
means that all of the programs also need to match its
signature. In particular, many functions need to switch to
"const char **" instead of "char **" for argv. This effect
ripples out to a few other variables and functions, as well.
This makes the patch even more invasive, but the end result
is much better. We should be treating argv strings as const
anyway, and now all programs conform to the same signature
(which also matches the way builtins are defined).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-07-01 13:58:58 +08:00
|
|
|
static const char **ok_paths;
|
2006-08-16 01:23:48 +08:00
|
|
|
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 */
|
2014-06-19 03:41:58 +08:00
|
|
|
static const char *base_path;
|
|
|
|
static const char *interpolated_path;
|
2007-07-28 05:00:29 +08:00
|
|
|
static int base_path_relaxed;
|
2006-09-20 09:31:51 +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
|
|
|
|
2015-03-07 18:50:37 +08:00
|
|
|
struct hostinfo {
|
|
|
|
struct strbuf hostname;
|
|
|
|
struct strbuf canon_hostname;
|
|
|
|
struct strbuf ip_address;
|
|
|
|
struct strbuf tcp_port;
|
|
|
|
unsigned int hostname_lookup_done:1;
|
|
|
|
unsigned int saw_extended_args:1;
|
|
|
|
};
|
2015-02-16 02:31:41 +08:00
|
|
|
|
2015-03-07 18:50:37 +08:00
|
|
|
static void lookup_hostname(struct hostinfo *hi);
|
2015-02-16 02:31:41 +08:00
|
|
|
|
2015-03-07 18:50:37 +08:00
|
|
|
static const char *get_canon_hostname(struct hostinfo *hi)
|
2015-02-16 02:31:41 +08:00
|
|
|
{
|
2015-03-07 18:50:37 +08:00
|
|
|
lookup_hostname(hi);
|
|
|
|
return hi->canon_hostname.buf;
|
2015-02-16 02:31:41 +08:00
|
|
|
}
|
|
|
|
|
2015-03-07 18:50:37 +08:00
|
|
|
static const char *get_ip_address(struct hostinfo *hi)
|
2015-02-16 02:31:41 +08:00
|
|
|
{
|
2015-03-07 18:50:37 +08:00
|
|
|
lookup_hostname(hi);
|
|
|
|
return hi->ip_address.buf;
|
2015-02-16 02:31:41 +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
|
|
|
{
|
daemon: add --log-destination=(stderr|syslog|none)
This new option can be used to override the implicit --syslog of
--inetd, or to disable all logging. (While --detach also implies
--syslog, --log-destination=stderr with --detach is useless since
--detach disassociates the process from the original stderr.) --syslog
is retained as an alias for --log-destination=syslog.
--log-destination always overrides implicit --syslog regardless of
option order. This is different than the “last one wins” logic that
applies to some implicit options elsewhere in Git, but should hopefully
be less confusing. (I also don’t know if *all* implicit options in Git
follow “last one wins”.)
The combination of --inetd with --log-destination=stderr is useful, for
instance, when running `git daemon` as an instanced systemd service
(with associated socket unit). In this case, log messages sent via
syslog are received by the journal daemon, but run the risk of being
processed at a time when the `git daemon` process has already exited
(especially if the process was very short-lived, e.g. due to client
error), so that the journal daemon can no longer read its cgroup and
attach the message to the correct systemd unit (see systemd/systemd#2913
[1]). Logging to stderr instead can solve this problem, because systemd
can connect stderr directly to the journal daemon, which then already
knows which unit is associated with this stream.
[1]: https://github.com/systemd/systemd/issues/2913
Helped-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Helped-by: Junio C Hamano <gitster@pobox.com>
Helped-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Lucas Werkmeister <mail@lucaswerkmeister.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-02-05 02:30:37 +08:00
|
|
|
switch (log_destination) {
|
|
|
|
case LOG_DESTINATION_SYSLOG: {
|
2008-08-15 02:02:20 +08:00
|
|
|
char buf[1024];
|
|
|
|
vsnprintf(buf, sizeof(buf), err, params);
|
2005-09-24 22:13:01 +08:00
|
|
|
syslog(priority, "%s", buf);
|
daemon: add --log-destination=(stderr|syslog|none)
This new option can be used to override the implicit --syslog of
--inetd, or to disable all logging. (While --detach also implies
--syslog, --log-destination=stderr with --detach is useless since
--detach disassociates the process from the original stderr.) --syslog
is retained as an alias for --log-destination=syslog.
--log-destination always overrides implicit --syslog regardless of
option order. This is different than the “last one wins” logic that
applies to some implicit options elsewhere in Git, but should hopefully
be less confusing. (I also don’t know if *all* implicit options in Git
follow “last one wins”.)
The combination of --inetd with --log-destination=stderr is useful, for
instance, when running `git daemon` as an instanced systemd service
(with associated socket unit). In this case, log messages sent via
syslog are received by the journal daemon, but run the risk of being
processed at a time when the `git daemon` process has already exited
(especially if the process was very short-lived, e.g. due to client
error), so that the journal daemon can no longer read its cgroup and
attach the message to the correct systemd unit (see systemd/systemd#2913
[1]). Logging to stderr instead can solve this problem, because systemd
can connect stderr directly to the journal daemon, which then already
knows which unit is associated with this stream.
[1]: https://github.com/systemd/systemd/issues/2913
Helped-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Helped-by: Junio C Hamano <gitster@pobox.com>
Helped-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Lucas Werkmeister <mail@lucaswerkmeister.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-02-05 02:30:37 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case LOG_DESTINATION_STDERR:
|
2008-08-25 04:27:10 +08:00
|
|
|
/*
|
2010-11-04 09:35:17 +08:00
|
|
|
* Since stderr is set to buffered mode, the
|
2008-08-15 02:02:20 +08:00
|
|
|
* logging of different processes will not overlap
|
2010-11-04 09:35:17 +08:00
|
|
|
* unless they overflow the (rather big) buffers.
|
2008-08-15 02:02:20 +08:00
|
|
|
*/
|
2008-08-31 20:09:39 +08:00
|
|
|
fprintf(stderr, "[%"PRIuMAX"] ", (uintmax_t)getpid());
|
2008-08-15 02:02:20 +08:00
|
|
|
vfprintf(stderr, err, params);
|
|
|
|
fputc('\n', stderr);
|
2010-11-04 09:35:17 +08:00
|
|
|
fflush(stderr);
|
daemon: add --log-destination=(stderr|syslog|none)
This new option can be used to override the implicit --syslog of
--inetd, or to disable all logging. (While --detach also implies
--syslog, --log-destination=stderr with --detach is useless since
--detach disassociates the process from the original stderr.) --syslog
is retained as an alias for --log-destination=syslog.
--log-destination always overrides implicit --syslog regardless of
option order. This is different than the “last one wins” logic that
applies to some implicit options elsewhere in Git, but should hopefully
be less confusing. (I also don’t know if *all* implicit options in Git
follow “last one wins”.)
The combination of --inetd with --log-destination=stderr is useful, for
instance, when running `git daemon` as an instanced systemd service
(with associated socket unit). In this case, log messages sent via
syslog are received by the journal daemon, but run the risk of being
processed at a time when the `git daemon` process has already exited
(especially if the process was very short-lived, e.g. due to client
error), so that the journal daemon can no longer read its cgroup and
attach the message to the correct systemd unit (see systemd/systemd#2913
[1]). Logging to stderr instead can solve this problem, because systemd
can connect stderr directly to the journal daemon, which then already
knows which unit is associated with this stream.
[1]: https://github.com/systemd/systemd/issues/2913
Helped-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Helped-by: Junio C Hamano <gitster@pobox.com>
Helped-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Lucas Werkmeister <mail@lucaswerkmeister.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-02-05 02:30:37 +08:00
|
|
|
break;
|
|
|
|
case LOG_DESTINATION_NONE:
|
|
|
|
break;
|
|
|
|
case LOG_DESTINATION_UNSET:
|
|
|
|
BUG("log destination not initialized correctly");
|
2008-08-15 02:02:20 +08:00
|
|
|
}
|
2005-09-22 17:25:28 +08:00
|
|
|
}
|
|
|
|
|
2009-11-15 05:33:13 +08:00
|
|
|
__attribute__((format (printf, 1, 2)))
|
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);
|
|
|
|
}
|
|
|
|
|
2009-11-15 05:33:13 +08:00
|
|
|
__attribute__((format (printf, 1, 2)))
|
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);
|
|
|
|
}
|
|
|
|
|
2015-02-16 02:33:52 +08:00
|
|
|
struct expand_path_context {
|
|
|
|
const char *directory;
|
2015-03-07 18:50:37 +08:00
|
|
|
struct hostinfo *hostinfo;
|
2015-02-16 02:33:52 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static size_t expand_path(struct strbuf *sb, const char *placeholder, void *ctx)
|
|
|
|
{
|
|
|
|
struct expand_path_context *context = ctx;
|
2015-03-07 18:50:37 +08:00
|
|
|
struct hostinfo *hi = context->hostinfo;
|
2015-02-16 02:33:52 +08:00
|
|
|
|
|
|
|
switch (placeholder[0]) {
|
|
|
|
case 'H':
|
2015-03-07 18:50:37 +08:00
|
|
|
strbuf_addbuf(sb, &hi->hostname);
|
2015-02-16 02:33:52 +08:00
|
|
|
return 1;
|
|
|
|
case 'C':
|
|
|
|
if (placeholder[1] == 'H') {
|
2015-03-07 18:50:37 +08:00
|
|
|
strbuf_addstr(sb, get_canon_hostname(hi));
|
2015-02-16 02:33:52 +08:00
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'I':
|
|
|
|
if (placeholder[1] == 'P') {
|
2015-03-07 18:50:37 +08:00
|
|
|
strbuf_addstr(sb, get_ip_address(hi));
|
2015-02-16 02:33:52 +08:00
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'P':
|
2015-03-07 18:50:37 +08:00
|
|
|
strbuf_addbuf(sb, &hi->tcp_port);
|
2015-02-16 02:33:52 +08:00
|
|
|
return 1;
|
|
|
|
case 'D':
|
|
|
|
strbuf_addstr(sb, context->directory);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-03-07 18:50:37 +08:00
|
|
|
static const char *path_ok(const char *directory, struct hostinfo *hi)
|
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];
|
2016-10-22 12:59:38 +08:00
|
|
|
size_t rlen;
|
2011-10-05 04:02:00 +08:00
|
|
|
const char *path;
|
2014-06-19 03:41:58 +08:00
|
|
|
const char *dir;
|
2006-09-20 09:31:51 +08:00
|
|
|
|
2008-11-23 07:15:01 +08:00
|
|
|
dir = directory;
|
[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
|
|
|
|
2009-11-10 03:26:43 +08:00
|
|
|
if (daemon_avoid_alias(dir)) {
|
[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
|
|
|
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);
|
2014-06-19 03:41:58 +08:00
|
|
|
const char *slash = strchr(dir, '/');
|
2006-02-05 14:27:29 +08:00
|
|
|
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);
|
2016-10-22 12:59:38 +08:00
|
|
|
rlen = snprintf(rpath, sizeof(rpath), "%.*s/%s%.*s",
|
|
|
|
namlen, dir, user_path, restlen, slash);
|
|
|
|
if (rlen >= sizeof(rpath)) {
|
|
|
|
logerror("user-path too large: %s", rpath);
|
|
|
|
return NULL;
|
|
|
|
}
|
2006-02-05 14:27:29 +08:00
|
|
|
dir = rpath;
|
|
|
|
}
|
|
|
|
}
|
2015-03-07 18:50:37 +08:00
|
|
|
else if (interpolated_path && hi->saw_extended_args) {
|
2008-11-23 07:15:01 +08:00
|
|
|
struct strbuf expanded_path = STRBUF_INIT;
|
2015-02-16 02:33:52 +08:00
|
|
|
struct expand_path_context context;
|
|
|
|
|
|
|
|
context.directory = directory;
|
2015-03-07 18:50:37 +08:00
|
|
|
context.hostinfo = hi;
|
2015-02-16 02:33:52 +08:00
|
|
|
|
2006-09-20 09:31:51 +08:00
|
|
|
if (*dir != '/') {
|
|
|
|
/* Allow only absolute */
|
|
|
|
logerror("'%s': Non-absolute path denied (interpolated-path active)", dir);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2008-11-23 07:15:01 +08:00
|
|
|
strbuf_expand(&expanded_path, interpolated_path,
|
2015-02-16 02:33:52 +08:00
|
|
|
expand_path, &context);
|
2016-10-22 12:59:38 +08:00
|
|
|
|
|
|
|
rlen = strlcpy(interp_path, expanded_path.buf,
|
|
|
|
sizeof(interp_path));
|
|
|
|
if (rlen >= sizeof(interp_path)) {
|
|
|
|
logerror("interpolated path too large: %s",
|
|
|
|
interp_path);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2008-11-23 07:15:01 +08:00
|
|
|
strbuf_release(&expanded_path);
|
2006-09-20 09:31:51 +08:00
|
|
|
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;
|
|
|
|
}
|
2016-10-22 12:59:38 +08:00
|
|
|
rlen = snprintf(rpath, sizeof(rpath), "%s%s", base_path, dir);
|
|
|
|
if (rlen >= sizeof(rpath)) {
|
|
|
|
logerror("base-path too large: %s", rpath);
|
|
|
|
return NULL;
|
|
|
|
}
|
2006-09-20 09:31:51 +08:00
|
|
|
dir = rpath;
|
2005-12-23 09:27:40 +08:00
|
|
|
}
|
|
|
|
|
2008-12-26 18:01:57 +08:00
|
|
|
path = enter_repo(dir, strict_paths);
|
|
|
|
if (!path && base_path && base_path_relaxed) {
|
2007-07-28 05:00:29 +08:00
|
|
|
/*
|
|
|
|
* if we fail and base_path_relaxed is enabled, try without
|
|
|
|
* prefixing the base path
|
|
|
|
*/
|
2008-12-26 18:01:57 +08:00
|
|
|
dir = directory;
|
|
|
|
path = enter_repo(dir, strict_paths);
|
|
|
|
}
|
2005-10-19 09:26:52 +08:00
|
|
|
|
2005-11-18 03:37:14 +08:00
|
|
|
if (!path) {
|
2009-03-04 16:32:29 +08:00
|
|
|
logerror("'%s' does not appear to be a git repository", dir);
|
2005-11-18 03:37:14 +08:00
|
|
|
return NULL;
|
2005-09-27 10:10:55 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if ( ok_paths && *ok_paths ) {
|
add an extra level of indirection to main()
There are certain startup tasks that we expect every git
process to do. In some cases this is just to improve the
quality of the program (e.g., setting up gettext()). In
others it is a requirement for using certain functions in
libgit.a (e.g., system_path() expects that you have called
git_extract_argv0_path()).
Most commands are builtins and are covered by the git.c
version of main(). However, there are still a few external
commands that use their own main(). Each of these has to
remember to include the correct startup sequence, and we are
not always consistent.
Rather than just fix the inconsistencies, let's make this
harder to get wrong by providing a common main() that can
run this standard startup.
We basically have two options to do this:
- the compat/mingw.h file already does something like this by
adding a #define that replaces the definition of main with a
wrapper that calls mingw_startup().
The upside is that the code in each program doesn't need
to be changed at all; it's rewritten on the fly by the
preprocessor.
The downside is that it may make debugging of the startup
sequence a bit more confusing, as the preprocessor is
quietly inserting new code.
- the builtin functions are all of the form cmd_foo(),
and git.c's main() calls them.
This is much more explicit, which may make things more
obvious to somebody reading the code. It's also more
flexible (because of course we have to figure out _which_
cmd_foo() to call).
The downside is that each of the builtins must define
cmd_foo(), instead of just main().
This patch chooses the latter option, preferring the more
explicit approach, even though it is more invasive. We
introduce a new file common-main.c, with the "real" main. It
expects to call cmd_main() from whatever other objects it is
linked against.
We link common-main.o against anything that links against
libgit.a, since we know that such programs will need to do
this setup. Note that common-main.o can't actually go inside
libgit.a, as the linker would not pick up its main()
function automatically (it has no callers).
The rest of the patch is just adjusting all of the various
external programs (mostly in t/helper) to use cmd_main().
I've provided a global declaration for cmd_main(), which
means that all of the programs also need to match its
signature. In particular, many functions need to switch to
"const char **" instead of "char **" for argv. This effect
ripples out to a few other variables and functions, as well.
This makes the patch even more invasive, but the end result
is much better. We should be treating argv strings as const
anyway, and now all programs conform to the same signature
(which also matches the way builtins are defined).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-07-01 13:58:58 +08:00
|
|
|
const 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
|
|
|
|
2017-10-17 01:55:25 +08:00
|
|
|
typedef int (*daemon_service_fn)(const struct argv_array *env);
|
2006-08-21 10:03:13 +08:00
|
|
|
struct daemon_service {
|
|
|
|
const char *name;
|
|
|
|
const char *config_name;
|
|
|
|
daemon_service_fn fn;
|
|
|
|
int enabled;
|
|
|
|
int overridable;
|
|
|
|
};
|
|
|
|
|
2011-10-15 05:19:21 +08:00
|
|
|
static int daemon_error(const char *dir, const char *msg)
|
|
|
|
{
|
|
|
|
if (!informative_errors)
|
|
|
|
msg = "access denied or repository not exported";
|
2016-10-17 07:20:29 +08:00
|
|
|
packet_write_fmt(1, "ERR %s: %s", msg, dir);
|
2011-10-15 05:19:21 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-06-19 03:41:58 +08:00
|
|
|
static const char *access_hook;
|
2012-08-15 02:37:51 +08:00
|
|
|
|
2015-03-07 18:50:37 +08:00
|
|
|
static int run_access_hook(struct daemon_service *service, const char *dir,
|
|
|
|
const char *path, struct hostinfo *hi)
|
2012-08-15 02:37:51 +08:00
|
|
|
{
|
2014-08-20 03:09:35 +08:00
|
|
|
struct child_process child = CHILD_PROCESS_INIT;
|
2012-08-15 02:37:51 +08:00
|
|
|
struct strbuf buf = STRBUF_INIT;
|
|
|
|
const char *argv[8];
|
|
|
|
const char **arg = argv;
|
|
|
|
char *eol;
|
|
|
|
int seen_errors = 0;
|
|
|
|
|
|
|
|
*arg++ = access_hook;
|
|
|
|
*arg++ = service->name;
|
|
|
|
*arg++ = path;
|
2015-03-07 18:50:37 +08:00
|
|
|
*arg++ = hi->hostname.buf;
|
|
|
|
*arg++ = get_canon_hostname(hi);
|
|
|
|
*arg++ = get_ip_address(hi);
|
|
|
|
*arg++ = hi->tcp_port.buf;
|
2012-08-15 02:37:51 +08:00
|
|
|
*arg = NULL;
|
|
|
|
|
|
|
|
child.use_shell = 1;
|
|
|
|
child.argv = argv;
|
|
|
|
child.no_stdin = 1;
|
|
|
|
child.no_stderr = 1;
|
|
|
|
child.out = -1;
|
|
|
|
if (start_command(&child)) {
|
|
|
|
logerror("daemon access hook '%s' failed to start",
|
|
|
|
access_hook);
|
|
|
|
goto error_return;
|
|
|
|
}
|
|
|
|
if (strbuf_read(&buf, child.out, 0) < 0) {
|
|
|
|
logerror("failed to read from pipe to daemon access hook '%s'",
|
|
|
|
access_hook);
|
|
|
|
strbuf_reset(&buf);
|
|
|
|
seen_errors = 1;
|
|
|
|
}
|
|
|
|
if (close(child.out) < 0) {
|
|
|
|
logerror("failed to close pipe to daemon access hook '%s'",
|
|
|
|
access_hook);
|
|
|
|
seen_errors = 1;
|
|
|
|
}
|
|
|
|
if (finish_command(&child))
|
|
|
|
seen_errors = 1;
|
|
|
|
|
|
|
|
if (!seen_errors) {
|
|
|
|
strbuf_release(&buf);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
error_return:
|
|
|
|
strbuf_ltrim(&buf);
|
|
|
|
if (!buf.len)
|
|
|
|
strbuf_addstr(&buf, "service rejected");
|
|
|
|
eol = strchr(buf.buf, '\n');
|
|
|
|
if (eol)
|
|
|
|
*eol = '\0';
|
|
|
|
errno = EACCES;
|
|
|
|
daemon_error(dir, buf.buf);
|
|
|
|
strbuf_release(&buf);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2015-03-07 18:50:37 +08:00
|
|
|
static int run_service(const char *dir, struct daemon_service *service,
|
2017-10-17 01:55:25 +08:00
|
|
|
struct hostinfo *hi, const struct argv_array *env)
|
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;
|
2014-08-08 00:21:16 +08:00
|
|
|
struct strbuf var = STRBUF_INIT;
|
2006-08-21 10:03:13 +08:00
|
|
|
|
2008-11-23 07:21:52 +08:00
|
|
|
loginfo("Request %s for '%s'", service->name, dir);
|
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;
|
2011-10-15 05:19:21 +08:00
|
|
|
return daemon_error(dir, "service not enabled");
|
2006-08-21 10:03:13 +08:00
|
|
|
}
|
2005-09-27 10:10:55 +08:00
|
|
|
|
2015-03-07 18:50:37 +08:00
|
|
|
if (!(path = path_ok(dir, hi)))
|
2011-10-15 05:19:21 +08:00
|
|
|
return daemon_error(dir, "no such repository");
|
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;
|
2011-10-15 05:19:21 +08:00
|
|
|
return daemon_error(dir, "repository not exported");
|
2005-10-19 09:26:52 +08:00
|
|
|
}
|
|
|
|
|
2006-08-21 10:03:13 +08:00
|
|
|
if (service->overridable) {
|
2014-08-08 00:21:16 +08:00
|
|
|
strbuf_addf(&var, "daemon.%s", service->config_name);
|
|
|
|
git_config_get_bool(var.buf, &enabled);
|
|
|
|
strbuf_release(&var);
|
2006-08-21 10:03:13 +08:00
|
|
|
}
|
|
|
|
if (!enabled) {
|
|
|
|
logerror("'%s': service not enabled for '%s'",
|
|
|
|
service->name, path);
|
|
|
|
errno = EACCES;
|
2011-10-15 05:19:21 +08:00
|
|
|
return daemon_error(dir, "service not enabled");
|
2006-08-21 10:03:13 +08:00
|
|
|
}
|
|
|
|
|
2012-08-15 02:37:51 +08:00
|
|
|
/*
|
|
|
|
* Optionally, a hook can choose to deny access to the
|
|
|
|
* repository depending on the phase of the moon.
|
|
|
|
*/
|
2015-03-07 18:50:37 +08:00
|
|
|
if (access_hook && run_access_hook(service, dir, path, hi))
|
2012-08-15 02:37:51 +08:00
|
|
|
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);
|
|
|
|
|
2017-10-17 01:55:25 +08:00
|
|
|
return service->fn(env);
|
2006-08-21 10:03:13 +08:00
|
|
|
}
|
|
|
|
|
2009-06-15 04:38:51 +08:00
|
|
|
static void copy_to_log(int fd)
|
|
|
|
{
|
|
|
|
struct strbuf line = STRBUF_INIT;
|
|
|
|
FILE *fp;
|
|
|
|
|
|
|
|
fp = fdopen(fd, "r");
|
|
|
|
if (fp == NULL) {
|
|
|
|
logerror("fdopen of error channel failed");
|
|
|
|
close(fd);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-01-14 07:31:17 +08:00
|
|
|
while (strbuf_getline_lf(&line, fp) != EOF) {
|
2009-06-15 04:38:51 +08:00
|
|
|
logerror("%s", line.buf);
|
|
|
|
strbuf_setlen(&line, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
strbuf_release(&line);
|
|
|
|
fclose(fp);
|
|
|
|
}
|
|
|
|
|
2017-03-29 03:48:10 +08:00
|
|
|
static int run_service_command(struct child_process *cld)
|
2009-06-15 04:38:51 +08:00
|
|
|
{
|
2017-03-29 03:48:10 +08:00
|
|
|
argv_array_push(&cld->args, ".");
|
|
|
|
cld->git_cmd = 1;
|
|
|
|
cld->err = -1;
|
|
|
|
if (start_command(cld))
|
2009-06-15 04:38:51 +08:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
close(0);
|
|
|
|
close(1);
|
|
|
|
|
2017-03-29 03:48:10 +08:00
|
|
|
copy_to_log(cld->err);
|
2009-06-15 04:38:51 +08:00
|
|
|
|
2017-03-29 03:48:10 +08:00
|
|
|
return finish_command(cld);
|
2009-06-15 04:38:51 +08:00
|
|
|
}
|
|
|
|
|
2017-10-17 01:55:25 +08:00
|
|
|
static int upload_pack(const struct argv_array *env)
|
2006-08-21 10:03:13 +08:00
|
|
|
{
|
2017-03-29 03:48:10 +08:00
|
|
|
struct child_process cld = CHILD_PROCESS_INIT;
|
|
|
|
argv_array_pushl(&cld.args, "upload-pack", "--strict", NULL);
|
|
|
|
argv_array_pushf(&cld.args, "--timeout=%u", timeout);
|
2017-10-17 01:55:25 +08:00
|
|
|
|
|
|
|
argv_array_pushv(&cld.env_array, env->argv);
|
|
|
|
|
2017-03-29 03:48:10 +08:00
|
|
|
return run_service_command(&cld);
|
2005-07-14 10:45:26 +08:00
|
|
|
}
|
|
|
|
|
2017-10-17 01:55:25 +08:00
|
|
|
static int upload_archive(const struct argv_array *env)
|
2006-09-07 21:12:05 +08:00
|
|
|
{
|
2017-03-29 03:48:10 +08:00
|
|
|
struct child_process cld = CHILD_PROCESS_INIT;
|
|
|
|
argv_array_push(&cld.args, "upload-archive");
|
2017-10-17 01:55:25 +08:00
|
|
|
|
|
|
|
argv_array_pushv(&cld.env_array, env->argv);
|
|
|
|
|
2017-03-29 03:48:10 +08:00
|
|
|
return run_service_command(&cld);
|
2006-09-07 21:12:05 +08:00
|
|
|
}
|
|
|
|
|
2017-10-17 01:55:25 +08:00
|
|
|
static int receive_pack(const struct argv_array *env)
|
2007-01-22 03:04:13 +08:00
|
|
|
{
|
2017-03-29 03:48:10 +08:00
|
|
|
struct child_process cld = CHILD_PROCESS_INIT;
|
|
|
|
argv_array_push(&cld.args, "receive-pack");
|
2017-10-17 01:55:25 +08:00
|
|
|
|
|
|
|
argv_array_pushv(&cld.env_array, env->argv);
|
|
|
|
|
2017-03-29 03:48:10 +08:00
|
|
|
return run_service_command(&cld);
|
2007-01-22 03:04:13 +08:00
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2010-01-27 02:24:41 +08:00
|
|
|
static void parse_host_and_port(char *hostport, char **host,
|
|
|
|
char **port)
|
|
|
|
{
|
|
|
|
if (*hostport == '[') {
|
|
|
|
char *end;
|
|
|
|
|
|
|
|
end = strchr(hostport, ']');
|
|
|
|
if (!end)
|
2010-02-04 13:23:18 +08:00
|
|
|
die("Invalid request ('[' without ']')");
|
2010-01-27 02:24:41 +08:00
|
|
|
*end = '\0';
|
|
|
|
*host = hostport + 1;
|
|
|
|
if (!end[1])
|
|
|
|
*port = NULL;
|
|
|
|
else if (end[1] == ':')
|
|
|
|
*port = end + 2;
|
|
|
|
else
|
|
|
|
die("Garbage after end of host part");
|
|
|
|
} else {
|
|
|
|
*host = hostport;
|
|
|
|
*port = strrchr(hostport, ':');
|
|
|
|
if (*port) {
|
2010-03-20 10:23:58 +08:00
|
|
|
**port = '\0';
|
2010-01-27 02:24:41 +08:00
|
|
|
++*port;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
daemon: sanitize incoming virtual hostname
We use the daemon_avoid_alias function to make sure that the
pathname the user gives us is sane. However, after applying
that check, we might then interpolate the path using a
string given by the server admin, but which may contain more
untrusted data from the client. We should be sure to
sanitize this data, as well.
We cannot use daemon_avoid_alias here, as it is more strict
than we need in requiring a leading '/'. At the same time,
we can be much more strict here. We are interpreting a
hostname, which should not contain slashes or excessive runs
of dots, as those things are not allowed in DNS names.
Note that in addition to cleansing the hostname field, we
must check the "canonical hostname" (%CH) as well as the
port (%P), which we take as a raw string. For the canonical
hostname, this comes from an actual DNS lookup on the
accessed IP, which makes it a much less likely vector for
problems. But it does not hurt to sanitize it in the same
way. Unfortunately we cannot test this case easily, as it
would involve a custom hostname lookup.
We do not need to check %IP, as it comes straight from
inet_ntop, so must have a sane form.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-02-18 03:09:24 +08:00
|
|
|
/*
|
|
|
|
* Sanitize a string from the client so that it's OK to be inserted into a
|
|
|
|
* filesystem path. Specifically, we disallow slashes, runs of "..", and
|
|
|
|
* trailing and leading dots, which means that the client cannot escape
|
|
|
|
* our base path via ".." traversal.
|
|
|
|
*/
|
2015-03-07 18:50:29 +08:00
|
|
|
static void sanitize_client(struct strbuf *out, const char *in)
|
daemon: sanitize incoming virtual hostname
We use the daemon_avoid_alias function to make sure that the
pathname the user gives us is sane. However, after applying
that check, we might then interpolate the path using a
string given by the server admin, but which may contain more
untrusted data from the client. We should be sure to
sanitize this data, as well.
We cannot use daemon_avoid_alias here, as it is more strict
than we need in requiring a leading '/'. At the same time,
we can be much more strict here. We are interpreting a
hostname, which should not contain slashes or excessive runs
of dots, as those things are not allowed in DNS names.
Note that in addition to cleansing the hostname field, we
must check the "canonical hostname" (%CH) as well as the
port (%P), which we take as a raw string. For the canonical
hostname, this comes from an actual DNS lookup on the
accessed IP, which makes it a much less likely vector for
problems. But it does not hurt to sanitize it in the same
way. Unfortunately we cannot test this case easily, as it
would involve a custom hostname lookup.
We do not need to check %IP, as it comes straight from
inet_ntop, so must have a sane form.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-02-18 03:09:24 +08:00
|
|
|
{
|
|
|
|
for (; *in; in++) {
|
|
|
|
if (*in == '/')
|
|
|
|
continue;
|
|
|
|
if (*in == '.' && (!out->len || out->buf[out->len - 1] == '.'))
|
|
|
|
continue;
|
|
|
|
strbuf_addch(out, *in);
|
|
|
|
}
|
|
|
|
|
|
|
|
while (out->len && out->buf[out->len - 1] == '.')
|
|
|
|
strbuf_setlen(out, out->len - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Like sanitize_client, but we also perform any canonicalization
|
|
|
|
* to make life easier on the admin.
|
|
|
|
*/
|
2015-03-07 18:50:29 +08:00
|
|
|
static void canonicalize_client(struct strbuf *out, const char *in)
|
daemon: sanitize incoming virtual hostname
We use the daemon_avoid_alias function to make sure that the
pathname the user gives us is sane. However, after applying
that check, we might then interpolate the path using a
string given by the server admin, but which may contain more
untrusted data from the client. We should be sure to
sanitize this data, as well.
We cannot use daemon_avoid_alias here, as it is more strict
than we need in requiring a leading '/'. At the same time,
we can be much more strict here. We are interpreting a
hostname, which should not contain slashes or excessive runs
of dots, as those things are not allowed in DNS names.
Note that in addition to cleansing the hostname field, we
must check the "canonical hostname" (%CH) as well as the
port (%P), which we take as a raw string. For the canonical
hostname, this comes from an actual DNS lookup on the
accessed IP, which makes it a much less likely vector for
problems. But it does not hurt to sanitize it in the same
way. Unfortunately we cannot test this case easily, as it
would involve a custom hostname lookup.
We do not need to check %IP, as it comes straight from
inet_ntop, so must have a sane form.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-02-18 03:09:24 +08:00
|
|
|
{
|
2015-03-07 18:50:29 +08:00
|
|
|
sanitize_client(out, in);
|
|
|
|
strbuf_tolower(out);
|
daemon: sanitize incoming virtual hostname
We use the daemon_avoid_alias function to make sure that the
pathname the user gives us is sane. However, after applying
that check, we might then interpolate the path using a
string given by the server admin, but which may contain more
untrusted data from the client. We should be sure to
sanitize this data, as well.
We cannot use daemon_avoid_alias here, as it is more strict
than we need in requiring a leading '/'. At the same time,
we can be much more strict here. We are interpreting a
hostname, which should not contain slashes or excessive runs
of dots, as those things are not allowed in DNS names.
Note that in addition to cleansing the hostname field, we
must check the "canonical hostname" (%CH) as well as the
port (%P), which we take as a raw string. For the canonical
hostname, this comes from an actual DNS lookup on the
accessed IP, which makes it a much less likely vector for
problems. But it does not hurt to sanitize it in the same
way. Unfortunately we cannot test this case easily, as it
would involve a custom hostname lookup.
We do not need to check %IP, as it comes straight from
inet_ntop, so must have a sane form.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-02-18 03:09:24 +08:00
|
|
|
}
|
|
|
|
|
2006-09-28 00:16:10 +08:00
|
|
|
/*
|
2009-06-05 09:33:32 +08:00
|
|
|
* Read the host as supplied by the client connection.
|
2017-10-17 01:55:25 +08:00
|
|
|
*
|
|
|
|
* Returns a pointer to the character after the NUL byte terminating the host
|
2019-11-06 01:07:23 +08:00
|
|
|
* argument, or 'extra_args' if there is no host argument.
|
2006-09-28 00:16:10 +08:00
|
|
|
*/
|
2017-10-17 01:55:25 +08:00
|
|
|
static char *parse_host_arg(struct hostinfo *hi, char *extra_args, int buflen)
|
2006-09-20 09:31:51 +08:00
|
|
|
{
|
|
|
|
char *val;
|
|
|
|
int vallen;
|
|
|
|
char *end = extra_args + buflen;
|
|
|
|
|
2009-06-05 09:33:32 +08:00
|
|
|
if (extra_args < end && *extra_args) {
|
2015-03-07 18:50:37 +08:00
|
|
|
hi->saw_extended_args = 1;
|
2006-09-20 09:31:51 +08:00
|
|
|
if (strncasecmp("host=", extra_args, 5) == 0) {
|
|
|
|
val = extra_args + 5;
|
|
|
|
vallen = strlen(val) + 1;
|
2018-01-25 08:56:20 +08:00
|
|
|
loginfo("Extended attribute \"host\": %s", val);
|
2006-09-20 09:31:51 +08:00
|
|
|
if (*val) {
|
2006-09-28 00:16:10 +08:00
|
|
|
/* Split <host>:<port> at colon. */
|
2010-01-27 02:24:41 +08:00
|
|
|
char *host;
|
|
|
|
char *port;
|
|
|
|
parse_host_and_port(val, &host, &port);
|
2015-03-07 18:50:37 +08:00
|
|
|
if (port)
|
|
|
|
sanitize_client(&hi->tcp_port, port);
|
|
|
|
canonicalize_client(&hi->hostname, host);
|
|
|
|
hi->hostname_lookup_done = 0;
|
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;
|
|
|
|
}
|
2009-06-05 09:33:32 +08:00
|
|
|
if (extra_args < end && *extra_args)
|
|
|
|
die("Invalid request");
|
2006-09-20 09:31:51 +08:00
|
|
|
}
|
2017-10-17 01:55:25 +08:00
|
|
|
|
|
|
|
return extra_args;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void parse_extra_args(struct hostinfo *hi, struct argv_array *env,
|
|
|
|
char *extra_args, int buflen)
|
|
|
|
{
|
|
|
|
const char *end = extra_args + buflen;
|
|
|
|
struct strbuf git_protocol = STRBUF_INIT;
|
|
|
|
|
|
|
|
/* First look for the host argument */
|
|
|
|
extra_args = parse_host_arg(hi, extra_args, buflen);
|
|
|
|
|
|
|
|
/* Look for additional arguments places after a second NUL byte */
|
|
|
|
for (; extra_args < end; extra_args += strlen(extra_args) + 1) {
|
|
|
|
const char *arg = extra_args;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Parse the extra arguments, adding most to 'git_protocol'
|
|
|
|
* which will be used to set the 'GIT_PROTOCOL' envvar in the
|
|
|
|
* service that will be run.
|
|
|
|
*
|
|
|
|
* If there ends up being a particular arg in the future that
|
2019-11-06 01:07:23 +08:00
|
|
|
* git-daemon needs to parse specifically (like the 'host' arg)
|
2017-10-17 01:55:25 +08:00
|
|
|
* then it can be parsed here and not added to 'git_protocol'.
|
|
|
|
*/
|
|
|
|
if (*arg) {
|
|
|
|
if (git_protocol.len > 0)
|
|
|
|
strbuf_addch(&git_protocol, ':');
|
|
|
|
strbuf_addstr(&git_protocol, arg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-25 08:56:20 +08:00
|
|
|
if (git_protocol.len > 0) {
|
|
|
|
loginfo("Extended attribute \"protocol\": %s", git_protocol.buf);
|
2017-10-17 01:55:25 +08:00
|
|
|
argv_array_pushf(env, GIT_PROTOCOL_ENVIRONMENT "=%s",
|
|
|
|
git_protocol.buf);
|
2018-01-25 08:56:20 +08:00
|
|
|
}
|
2017-10-17 01:55:25 +08:00
|
|
|
strbuf_release(&git_protocol);
|
2015-02-16 02:31:41 +08:00
|
|
|
}
|
2006-09-26 22:47:43 +08:00
|
|
|
|
2015-02-16 02:31:41 +08:00
|
|
|
/*
|
|
|
|
* Locate canonical hostname and its IP address.
|
|
|
|
*/
|
2015-03-07 18:50:37 +08:00
|
|
|
static void lookup_hostname(struct hostinfo *hi)
|
2015-02-16 02:31:41 +08:00
|
|
|
{
|
2015-03-07 18:50:37 +08:00
|
|
|
if (!hi->hostname_lookup_done && hi->hostname.len) {
|
2006-09-26 22:47:43 +08:00
|
|
|
#ifndef NO_IPV6
|
|
|
|
struct addrinfo hints;
|
2009-04-27 21:59:49 +08:00
|
|
|
struct addrinfo *ai;
|
2006-09-26 22:47:43 +08:00
|
|
|
int gai;
|
|
|
|
static char addrbuf[HOST_NAME_MAX + 1];
|
|
|
|
|
|
|
|
memset(&hints, 0, sizeof(hints));
|
|
|
|
hints.ai_flags = AI_CANONNAME;
|
|
|
|
|
2015-03-07 18:50:37 +08:00
|
|
|
gai = getaddrinfo(hi->hostname.buf, NULL, &hints, &ai);
|
2006-09-26 22:47:43 +08:00
|
|
|
if (!gai) {
|
2009-04-27 21:59:49 +08:00
|
|
|
struct sockaddr_in *sin_addr = (void *)ai->ai_addr;
|
|
|
|
|
|
|
|
inet_ntop(AF_INET, &sin_addr->sin_addr,
|
|
|
|
addrbuf, sizeof(addrbuf));
|
2015-03-07 18:50:37 +08:00
|
|
|
strbuf_addstr(&hi->ip_address, addrbuf);
|
2009-04-27 21:59:49 +08:00
|
|
|
|
2015-03-07 18:50:29 +08:00
|
|
|
if (ai->ai_canonname)
|
2015-03-07 18:50:37 +08:00
|
|
|
sanitize_client(&hi->canon_hostname,
|
2015-03-07 18:50:29 +08:00
|
|
|
ai->ai_canonname);
|
|
|
|
else
|
2015-03-07 18:50:37 +08:00
|
|
|
strbuf_addbuf(&hi->canon_hostname,
|
|
|
|
&hi->ip_address);
|
2009-04-27 21:59:49 +08:00
|
|
|
|
|
|
|
freeaddrinfo(ai);
|
2006-09-26 22:47:43 +08:00
|
|
|
}
|
|
|
|
#else
|
|
|
|
struct hostent *hent;
|
|
|
|
struct sockaddr_in sa;
|
|
|
|
char **ap;
|
|
|
|
static char addrbuf[HOST_NAME_MAX + 1];
|
|
|
|
|
2015-05-06 02:03:24 +08:00
|
|
|
hent = gethostbyname(hi->hostname.buf);
|
2014-10-01 18:16:17 +08:00
|
|
|
if (hent) {
|
|
|
|
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-26 22:47:43 +08:00
|
|
|
|
2015-03-07 18:50:37 +08:00
|
|
|
sanitize_client(&hi->canon_hostname, hent->h_name);
|
|
|
|
strbuf_addstr(&hi->ip_address, addrbuf);
|
2014-10-01 18:16:17 +08:00
|
|
|
}
|
2006-09-26 22:47:43 +08:00
|
|
|
#endif
|
2015-03-07 18:50:37 +08:00
|
|
|
hi->hostname_lookup_done = 1;
|
2008-12-26 18:12:15 +08:00
|
|
|
}
|
2006-09-26 22:47:43 +08:00
|
|
|
}
|
|
|
|
|
2015-03-07 18:50:37 +08:00
|
|
|
static void hostinfo_init(struct hostinfo *hi)
|
|
|
|
{
|
|
|
|
memset(hi, 0, sizeof(*hi));
|
|
|
|
strbuf_init(&hi->hostname, 0);
|
|
|
|
strbuf_init(&hi->canon_hostname, 0);
|
|
|
|
strbuf_init(&hi->ip_address, 0);
|
|
|
|
strbuf_init(&hi->tcp_port, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void hostinfo_clear(struct hostinfo *hi)
|
|
|
|
{
|
|
|
|
strbuf_release(&hi->hostname);
|
|
|
|
strbuf_release(&hi->canon_hostname);
|
|
|
|
strbuf_release(&hi->ip_address);
|
|
|
|
strbuf_release(&hi->tcp_port);
|
|
|
|
}
|
2006-09-26 22:47:43 +08:00
|
|
|
|
2016-05-25 11:15:05 +08:00
|
|
|
static void set_keep_alive(int sockfd)
|
|
|
|
{
|
|
|
|
int ka = 1;
|
|
|
|
|
2016-07-18 12:59:11 +08:00
|
|
|
if (setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &ka, sizeof(ka)) < 0) {
|
|
|
|
if (errno != ENOTSOCK)
|
|
|
|
logerror("unable to set SO_KEEPALIVE on socket: %s",
|
|
|
|
strerror(errno));
|
|
|
|
}
|
2016-05-25 11:15:05 +08:00
|
|
|
}
|
|
|
|
|
2010-11-04 09:35:19 +08:00
|
|
|
static int execute(void)
|
2005-07-14 10:45:26 +08:00
|
|
|
{
|
pkt-line: provide a LARGE_PACKET_MAX static buffer
Most of the callers of packet_read_line just read into a
static 1000-byte buffer (callers which handle arbitrary
binary data already use LARGE_PACKET_MAX). This works fine
in practice, because:
1. The only variable-sized data in these lines is a ref
name, and refs tend to be a lot shorter than 1000
characters.
2. When sending ref lines, git-core always limits itself
to 1000 byte packets.
However, the only limit given in the protocol specification
in Documentation/technical/protocol-common.txt is
LARGE_PACKET_MAX; the 1000 byte limit is mentioned only in
pack-protocol.txt, and then only describing what we write,
not as a specific limit for readers.
This patch lets us bump the 1000-byte limit to
LARGE_PACKET_MAX. Even though git-core will never write a
packet where this makes a difference, there are two good
reasons to do this:
1. Other git implementations may have followed
protocol-common.txt and used a larger maximum size. We
don't bump into it in practice because it would involve
very long ref names.
2. We may want to increase the 1000-byte limit one day.
Since packets are transferred before any capabilities,
it's difficult to do this in a backwards-compatible
way. But if we bump the size of buffer the readers can
handle, eventually older versions of git will be
obsolete enough that we can justify bumping the
writers, as well. We don't have plans to do this
anytime soon, but there is no reason not to start the
clock ticking now.
Just bumping all of the reading bufs to LARGE_PACKET_MAX
would waste memory. Instead, since most readers just read
into a temporary buffer anyway, let's provide a single
static buffer that all callers can use. We can further wrap
this detail away by having the packet_read_line wrapper just
use the buffer transparently and return a pointer to the
static storage. That covers most of the cases, and the
remaining ones already read into their own LARGE_PACKET_MAX
buffers.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-02-21 04:02:57 +08:00
|
|
|
char *line = packet_buffer;
|
2006-08-21 10:03:13 +08:00
|
|
|
int pktlen, len, i;
|
2010-11-04 09:35:19 +08:00
|
|
|
char *addr = getenv("REMOTE_ADDR"), *port = getenv("REMOTE_PORT");
|
2015-03-07 18:50:37 +08:00
|
|
|
struct hostinfo hi;
|
2017-10-17 01:55:25 +08:00
|
|
|
struct argv_array env = ARGV_ARRAY_INIT;
|
2015-03-07 18:50:37 +08:00
|
|
|
|
|
|
|
hostinfo_init(&hi);
|
2005-07-16 00:27:05 +08:00
|
|
|
|
2010-11-04 09:35:19 +08:00
|
|
|
if (addr)
|
|
|
|
loginfo("Connection from %s:%s", addr, port);
|
2006-06-20 22:38:13 +08:00
|
|
|
|
2016-05-25 11:15:05 +08:00
|
|
|
set_keep_alive(0);
|
2005-10-20 05:27:01 +08:00
|
|
|
alarm(init_timeout ? init_timeout : timeout);
|
pkt-line: share buffer/descriptor reading implementation
The packet_read function reads from a descriptor. The
packet_get_line function is similar, but reads from an
in-memory buffer, and uses a completely separate
implementation. This patch teaches the generic packet_read
function to accept either source, and we can do away with
packet_get_line's implementation.
There are two other differences to account for between the
old and new functions. The first is that we used to read
into a strbuf, but now read into a fixed size buffer. The
only two callers are fine with that, and in fact it
simplifies their code, since they can use the same
static-buffer interface as the rest of the packet_read_line
callers (and we provide a similar convenience wrapper for
reading from a buffer rather than a descriptor).
This is technically an externally-visible behavior change in
that we used to accept arbitrary sized packets up to 65532
bytes, and now cap out at LARGE_PACKET_MAX, 65520. In
practice this doesn't matter, as we use it only for parsing
smart-http headers (of which there is exactly one defined,
and it is small and fixed-size). And any extension headers
would be breaking the protocol to go over LARGE_PACKET_MAX
anyway.
The other difference is that packet_get_line would return
on error rather than dying. However, both callers of
packet_get_line are actually improved by dying.
The first caller does its own error checking, but we can
drop that; as a result, we'll actually get more specific
reporting about protocol breakage when packet_read dies
internally. The only downside is that packet_read will not
print the smart-http URL that failed, but that's not a big
deal; anybody not debugging can already see the remote's URL
already, and anybody debugging would want to run with
GIT_CURL_VERBOSE anyway to see way more information.
The second caller, which is just trying to skip past any
extra smart-http headers (of which there are none defined,
but which we allow to keep room for future expansion), did
not error check at all. As a result, it would treat an error
just like a flush packet. The resulting mess would generally
cause an error later in get_remote_heads, but now we get
error reporting much closer to the source of the problem.
Brown-paper-bag-fixes-by: Ramsay Jones <ramsay@ramsay1.demon.co.uk>
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-02-24 06:31:34 +08:00
|
|
|
pktlen = packet_read(0, NULL, NULL, packet_buffer, sizeof(packet_buffer), 0);
|
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);
|
daemon: fix length computation in newline stripping
When git-daemon gets a pktline request, we strip off any
trailing newline, replacing it with a NUL. Clients prior to
5ad312bede (in git v1.4.0) would send:
git-upload-pack repo.git\n
and we need to strip it off to understand their request.
After 5ad312bede, we send the host attribute but no newline,
like:
git-upload-pack repo.git\0host=example.com\0
Both of these are parsed correctly by git-daemon. But if
some client were to combine the two:
git-upload-pack repo.git\n\0host=example.com\0
we don't parse it correctly. The problem is that we use the
"len" variable to record the position of the NUL separator,
but then decrement it when we strip the newline. So we start
with:
git-upload-pack repo.git\n\0host=example.com\0
^-- len
and end up with:
git-upload-pack repo.git\0\0host=example.com\0
^-- len
This is arguably correct, since "len" tells us the length of
the initial string, but we don't actually use it for that.
What we do use it for is finding the offset of the extended
attributes; they used to be at len+1, but are now at len+2.
We can solve that by just leaving "len" where it is. We
don't have to care about the length of the shortened string,
since we just treat it like a C string.
No version of Git ever produced such a string, but it seems
like the daemon code meant to handle this case (and it seems
like a reasonable thing for somebody to do in a 3rd-party
implementation).
Reported-by: Michael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-01-25 08:58:54 +08:00
|
|
|
if (len && line[len-1] == '\n')
|
|
|
|
line[len-1] = 0;
|
2005-07-16 00:27:05 +08:00
|
|
|
|
2017-10-17 01:55:25 +08:00
|
|
|
/* parse additional args hidden behind a NUL byte */
|
2008-11-23 07:19:09 +08:00
|
|
|
if (len != pktlen)
|
2017-10-17 01:55:25 +08:00
|
|
|
parse_extra_args(&hi, &env, line + len + 1, pktlen - len - 1);
|
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]);
|
2014-06-19 03:49:44 +08:00
|
|
|
const char *arg;
|
|
|
|
|
|
|
|
if (skip_prefix(line, "git-", &arg) &&
|
|
|
|
skip_prefix(arg, s->name, &arg) &&
|
|
|
|
*arg++ == ' ') {
|
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.
|
|
|
|
*/
|
2017-10-17 01:55:25 +08:00
|
|
|
int rc = run_service(arg, s, &hi, &env);
|
2015-03-07 18:50:37 +08:00
|
|
|
hostinfo_clear(&hi);
|
2017-10-17 01:55:25 +08:00
|
|
|
argv_array_clear(&env);
|
2015-03-07 18:50:37 +08:00
|
|
|
return rc;
|
2006-09-20 09:31:51 +08:00
|
|
|
}
|
2006-08-21 10:03:13 +08:00
|
|
|
}
|
2005-07-14 10:45:26 +08:00
|
|
|
|
2015-03-07 18:50:37 +08:00
|
|
|
hostinfo_clear(&hi);
|
2017-10-17 01:55:25 +08:00
|
|
|
argv_array_clear(&env);
|
2005-09-22 17:25:28 +08:00
|
|
|
logerror("Protocol error: '%s'", line);
|
2005-07-14 10:45:26 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2010-01-09 22:13:28 +08:00
|
|
|
static int addrcmp(const struct sockaddr_storage *s1,
|
|
|
|
const struct sockaddr_storage *s2)
|
|
|
|
{
|
2010-03-16 06:10:06 +08:00
|
|
|
const struct sockaddr *sa1 = (const struct sockaddr*) s1;
|
|
|
|
const struct sockaddr *sa2 = (const struct sockaddr*) s2;
|
|
|
|
|
|
|
|
if (sa1->sa_family != sa2->sa_family)
|
|
|
|
return sa1->sa_family - sa2->sa_family;
|
|
|
|
if (sa1->sa_family == AF_INET)
|
2010-01-09 22:13:28 +08:00
|
|
|
return memcmp(&((struct sockaddr_in *)s1)->sin_addr,
|
|
|
|
&((struct sockaddr_in *)s2)->sin_addr,
|
|
|
|
sizeof(struct in_addr));
|
|
|
|
#ifndef NO_IPV6
|
2010-03-16 06:10:06 +08:00
|
|
|
if (sa1->sa_family == AF_INET6)
|
2010-01-09 22:13:28 +08:00
|
|
|
return memcmp(&((struct sockaddr_in6 *)s1)->sin6_addr,
|
|
|
|
&((struct sockaddr_in6 *)s2)->sin6_addr,
|
|
|
|
sizeof(struct in6_addr));
|
|
|
|
#endif
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-08-15 02:02:20 +08:00
|
|
|
static int max_connections = 32;
|
2005-07-16 12:51:57 +08:00
|
|
|
|
2008-08-15 02:02:20 +08:00
|
|
|
static unsigned int live_children;
|
2005-07-16 12:51:57 +08:00
|
|
|
|
2005-08-02 03:11:53 +08:00
|
|
|
static struct child {
|
2008-08-15 02:02:20 +08:00
|
|
|
struct child *next;
|
2010-11-04 09:35:16 +08:00
|
|
|
struct child_process cld;
|
2005-07-23 16:24:59 +08:00
|
|
|
struct sockaddr_storage address;
|
2008-08-15 02:02:20 +08:00
|
|
|
} *firstborn;
|
2005-07-16 12:51:57 +08:00
|
|
|
|
2010-11-04 09:35:22 +08:00
|
|
|
static void add_child(struct child_process *cld, struct sockaddr *addr, socklen_t addrlen)
|
2005-07-16 12:51:57 +08:00
|
|
|
{
|
2008-08-25 04:27:10 +08:00
|
|
|
struct child *newborn, **cradle;
|
|
|
|
|
|
|
|
newborn = xcalloc(1, sizeof(*newborn));
|
|
|
|
live_children++;
|
2010-11-04 09:35:16 +08:00
|
|
|
memcpy(&newborn->cld, cld, sizeof(*cld));
|
2008-08-25 04:27:10 +08:00
|
|
|
memcpy(&newborn->address, addr, addrlen);
|
|
|
|
for (cradle = &firstborn; *cradle; cradle = &(*cradle)->next)
|
2010-01-09 22:13:28 +08:00
|
|
|
if (!addrcmp(&(*cradle)->address, &newborn->address))
|
2008-08-25 04:27:10 +08:00
|
|
|
break;
|
|
|
|
newborn->next = *cradle;
|
|
|
|
*cradle = newborn;
|
2005-07-16 12:51:57 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This gets called if the number of connections grows
|
|
|
|
* past "max_connections".
|
|
|
|
*
|
2008-08-15 02:02:20 +08:00
|
|
|
* We kill the newest connection from a duplicate IP.
|
2005-07-16 12:51:57 +08:00
|
|
|
*/
|
2008-08-15 02:02:20 +08:00
|
|
|
static void kill_some_child(void)
|
2005-07-16 12:51:57 +08:00
|
|
|
{
|
2008-08-25 04:27:10 +08:00
|
|
|
const struct child *blanket, *next;
|
2005-07-16 12:51:57 +08:00
|
|
|
|
2008-08-25 04:27:10 +08:00
|
|
|
if (!(blanket = firstborn))
|
|
|
|
return;
|
2008-08-15 02:02:20 +08:00
|
|
|
|
2008-08-25 04:27:10 +08:00
|
|
|
for (; (next = blanket->next); blanket = next)
|
2010-01-09 22:13:28 +08:00
|
|
|
if (!addrcmp(&blanket->address, &next->address)) {
|
2010-11-04 09:35:16 +08:00
|
|
|
kill(blanket->cld.pid, SIGTERM);
|
2008-08-25 04:27:10 +08:00
|
|
|
break;
|
|
|
|
}
|
2008-07-03 23:27:24 +08:00
|
|
|
}
|
|
|
|
|
2008-08-15 02:02:20 +08:00
|
|
|
static void check_dead_children(void)
|
2005-07-14 10:45:26 +08:00
|
|
|
{
|
2008-08-15 02:02:20 +08:00
|
|
|
int status;
|
|
|
|
pid_t pid;
|
2005-07-16 13:53:31 +08:00
|
|
|
|
2010-11-04 09:35:16 +08:00
|
|
|
struct child **cradle, *blanket;
|
|
|
|
for (cradle = &firstborn; (blanket = *cradle);)
|
|
|
|
if ((pid = waitpid(blanket->cld.pid, &status, WNOHANG)) > 1) {
|
|
|
|
const char *dead = "";
|
|
|
|
if (status)
|
|
|
|
dead = " (with error)";
|
|
|
|
loginfo("[%"PRIuMAX"] Disconnected%s", (uintmax_t)pid, dead);
|
|
|
|
|
|
|
|
/* remove the child */
|
|
|
|
*cradle = blanket->next;
|
|
|
|
live_children--;
|
2015-10-24 20:23:20 +08:00
|
|
|
child_process_clear(&blanket->cld);
|
2010-11-04 09:35:16 +08:00
|
|
|
free(blanket);
|
|
|
|
} else
|
|
|
|
cradle = &blanket->next;
|
2005-07-16 13:53:31 +08:00
|
|
|
}
|
|
|
|
|
2016-02-23 06:44:21 +08:00
|
|
|
static struct argv_array cld_argv = ARGV_ARRAY_INIT;
|
2010-11-04 09:35:22 +08:00
|
|
|
static void handle(int incoming, struct sockaddr *addr, socklen_t addrlen)
|
2005-07-16 13:53:31 +08:00
|
|
|
{
|
2014-08-20 03:09:35 +08:00
|
|
|
struct child_process cld = CHILD_PROCESS_INIT;
|
2005-07-16 13:53:31 +08:00
|
|
|
|
2008-08-15 02:02:20 +08:00
|
|
|
if (max_connections && live_children >= max_connections) {
|
|
|
|
kill_some_child();
|
2008-08-25 04:27:10 +08:00
|
|
|
sleep(1); /* give it some time to die */
|
2008-08-15 02:02:20 +08:00
|
|
|
check_dead_children();
|
|
|
|
if (live_children >= max_connections) {
|
|
|
|
close(incoming);
|
|
|
|
logerror("Too many children, dropping connection");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2005-07-16 13:53:31 +08:00
|
|
|
|
2010-11-04 09:35:19 +08:00
|
|
|
if (addr->sa_family == AF_INET) {
|
2015-09-25 05:08:00 +08:00
|
|
|
char buf[128] = "";
|
2010-11-04 09:35:19 +08:00
|
|
|
struct sockaddr_in *sin_addr = (void *) addr;
|
2015-09-25 05:08:00 +08:00
|
|
|
inet_ntop(addr->sa_family, &sin_addr->sin_addr, buf, sizeof(buf));
|
|
|
|
argv_array_pushf(&cld.env_array, "REMOTE_ADDR=%s", buf);
|
|
|
|
argv_array_pushf(&cld.env_array, "REMOTE_PORT=%d",
|
|
|
|
ntohs(sin_addr->sin_port));
|
2010-11-04 09:35:19 +08:00
|
|
|
#ifndef NO_IPV6
|
2013-07-15 05:35:46 +08:00
|
|
|
} else if (addr->sa_family == AF_INET6) {
|
2015-09-25 05:08:00 +08:00
|
|
|
char buf[128] = "";
|
2010-11-04 09:35:19 +08:00
|
|
|
struct sockaddr_in6 *sin6_addr = (void *) addr;
|
2015-09-25 05:08:00 +08:00
|
|
|
inet_ntop(AF_INET6, &sin6_addr->sin6_addr, buf, sizeof(buf));
|
|
|
|
argv_array_pushf(&cld.env_array, "REMOTE_ADDR=[%s]", buf);
|
|
|
|
argv_array_pushf(&cld.env_array, "REMOTE_PORT=%d",
|
|
|
|
ntohs(sin6_addr->sin6_port));
|
2010-11-04 09:35:19 +08:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2016-02-23 06:44:21 +08:00
|
|
|
cld.argv = cld_argv.argv;
|
2010-11-04 09:35:16 +08:00
|
|
|
cld.in = incoming;
|
|
|
|
cld.out = dup(incoming);
|
2005-07-14 10:45:26 +08:00
|
|
|
|
2010-11-04 09:35:16 +08:00
|
|
|
if (start_command(&cld))
|
|
|
|
logerror("unable to fork");
|
|
|
|
else
|
|
|
|
add_child(&cld, addr, addrlen);
|
2005-07-14 10:45:26 +08:00
|
|
|
}
|
|
|
|
|
2005-07-16 11:42:28 +08:00
|
|
|
static void child_handler(int signo)
|
|
|
|
{
|
2008-08-25 04:27:10 +08:00
|
|
|
/*
|
|
|
|
* Otherwise empty handler because systemcalls will get interrupted
|
2008-08-15 02:02:20 +08:00
|
|
|
* upon signal receipt
|
|
|
|
* SysV needs the handler to be rearmed
|
|
|
|
*/
|
2008-08-13 03:36:13 +08:00
|
|
|
signal(SIGCHLD, child_handler);
|
2005-07-16 11:42:28 +08:00
|
|
|
}
|
|
|
|
|
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));
|
|
|
|
}
|
|
|
|
|
2010-08-30 19:30:50 +08:00
|
|
|
struct socketlist {
|
|
|
|
int *list;
|
|
|
|
size_t nr;
|
|
|
|
size_t alloc;
|
|
|
|
};
|
|
|
|
|
2011-10-04 03:13:28 +08:00
|
|
|
static const char *ip2str(int family, struct sockaddr *sin, socklen_t len)
|
|
|
|
{
|
|
|
|
#ifdef NO_IPV6
|
|
|
|
static char ip[INET_ADDRSTRLEN];
|
|
|
|
#else
|
|
|
|
static char ip[INET6_ADDRSTRLEN];
|
|
|
|
#endif
|
|
|
|
|
|
|
|
switch (family) {
|
|
|
|
#ifndef NO_IPV6
|
|
|
|
case AF_INET6:
|
|
|
|
inet_ntop(family, &((struct sockaddr_in6*)sin)->sin6_addr, ip, len);
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
case AF_INET:
|
|
|
|
inet_ntop(family, &((struct sockaddr_in*)sin)->sin_addr, ip, len);
|
|
|
|
break;
|
|
|
|
default:
|
2015-09-25 05:06:08 +08:00
|
|
|
xsnprintf(ip, sizeof(ip), "<unknown>");
|
2011-10-04 03:13:28 +08:00
|
|
|
}
|
|
|
|
return ip;
|
|
|
|
}
|
|
|
|
|
2005-09-29 08:26:44 +08:00
|
|
|
#ifndef NO_IPV6
|
|
|
|
|
2010-08-30 19:30:50 +08:00
|
|
|
static int setup_named_sock(char *listen_addr, int listen_port, struct socketlist *socklist)
|
2005-07-14 10:45:26 +08:00
|
|
|
{
|
2010-08-30 19:30:50 +08:00
|
|
|
int socknum = 0;
|
2005-07-23 16:24:59 +08:00
|
|
|
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
|
|
|
|
2015-09-25 05:06:08 +08:00
|
|
|
xsnprintf(pbuf, sizeof(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);
|
2010-08-30 19:30:50 +08:00
|
|
|
if (gai) {
|
|
|
|
logerror("getaddrinfo() for %s failed: %s", listen_addr, gai_strerror(gai));
|
|
|
|
return 0;
|
|
|
|
}
|
2005-07-23 16:24:59 +08:00
|
|
|
|
|
|
|
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) {
|
2008-08-15 02:02:20 +08:00
|
|
|
logerror("Socket descriptor too large");
|
2005-07-23 16:24:59 +08:00
|
|
|
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)) {
|
2011-10-04 03:13:28 +08:00
|
|
|
logerror("Could not set SO_REUSEADDR: %s", strerror(errno));
|
2006-02-04 04:27:04 +08:00
|
|
|
close(sockfd);
|
2006-04-18 21:11:06 +08:00
|
|
|
continue;
|
2006-02-04 04:27:04 +08:00
|
|
|
}
|
|
|
|
|
2016-05-25 11:15:05 +08:00
|
|
|
set_keep_alive(sockfd);
|
|
|
|
|
2005-07-23 16:24:59 +08:00
|
|
|
if (bind(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) {
|
2011-10-04 03:13:28 +08:00
|
|
|
logerror("Could not bind to %s: %s",
|
|
|
|
ip2str(ai->ai_family, ai->ai_addr, ai->ai_addrlen),
|
|
|
|
strerror(errno));
|
2005-07-23 16:24:59 +08:00
|
|
|
close(sockfd);
|
|
|
|
continue; /* not fatal */
|
|
|
|
}
|
|
|
|
if (listen(sockfd, 5) < 0) {
|
2011-10-04 03:13:28 +08:00
|
|
|
logerror("Could not listen to %s: %s",
|
|
|
|
ip2str(ai->ai_family, ai->ai_addr, ai->ai_addrlen),
|
|
|
|
strerror(errno));
|
2005-07-23 16:24:59 +08:00
|
|
|
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);
|
|
|
|
|
2010-08-30 19:30:50 +08:00
|
|
|
ALLOC_GROW(socklist->list, socklist->nr + 1, socklist->alloc);
|
|
|
|
socklist->list[socklist->nr++] = sockfd;
|
|
|
|
socknum++;
|
2005-07-23 16:24:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
freeaddrinfo(ai0);
|
|
|
|
|
2005-09-29 08:26:44 +08:00
|
|
|
return socknum;
|
|
|
|
}
|
|
|
|
|
|
|
|
#else /* NO_IPV6 */
|
|
|
|
|
2010-08-30 19:30:50 +08:00
|
|
|
static int setup_named_sock(char *listen_addr, int listen_port, struct socketlist *socklist)
|
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)) {
|
2011-10-04 03:13:28 +08:00
|
|
|
logerror("Could not set SO_REUSEADDR: %s", strerror(errno));
|
2006-02-04 04:27:04 +08:00
|
|
|
close(sockfd);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-05-25 11:15:05 +08:00
|
|
|
set_keep_alive(sockfd);
|
|
|
|
|
2005-09-29 08:26:44 +08:00
|
|
|
if ( bind(sockfd, (struct sockaddr *)&sin, sizeof sin) < 0 ) {
|
2014-10-01 18:18:15 +08:00
|
|
|
logerror("Could not bind to %s: %s",
|
2011-10-04 03:13:28 +08:00
|
|
|
ip2str(AF_INET, (struct sockaddr *)&sin, sizeof(sin)),
|
|
|
|
strerror(errno));
|
2005-09-29 08:26:44 +08:00
|
|
|
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) {
|
2011-10-04 03:13:28 +08:00
|
|
|
logerror("Could not listen to %s: %s",
|
|
|
|
ip2str(AF_INET, (struct sockaddr *)&sin, sizeof(sin)),
|
|
|
|
strerror(errno));
|
2005-11-22 01:07:23 +08:00
|
|
|
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);
|
|
|
|
|
2010-08-30 19:30:50 +08:00
|
|
|
ALLOC_GROW(socklist->list, socklist->nr + 1, socklist->alloc);
|
|
|
|
socklist->list[socklist->nr++] = sockfd;
|
2005-11-22 01:07:23 +08:00
|
|
|
return 1;
|
2005-09-29 08:26:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2010-08-30 19:30:51 +08:00
|
|
|
static void socksetup(struct string_list *listen_addr, int listen_port, struct socketlist *socklist)
|
2010-08-30 19:30:50 +08:00
|
|
|
{
|
2010-08-30 19:30:51 +08:00
|
|
|
if (!listen_addr->nr)
|
|
|
|
setup_named_sock(NULL, listen_port, socklist);
|
|
|
|
else {
|
|
|
|
int i, socknum;
|
|
|
|
for (i = 0; i < listen_addr->nr; i++) {
|
|
|
|
socknum = setup_named_sock(listen_addr->items[i].string,
|
|
|
|
listen_port, socklist);
|
|
|
|
|
|
|
|
if (socknum == 0)
|
|
|
|
logerror("unable to allocate any listen sockets for host %s on port %u",
|
|
|
|
listen_addr->items[i].string, listen_port);
|
|
|
|
}
|
|
|
|
}
|
2010-08-30 19:30:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int service_loop(struct socketlist *socklist)
|
2005-09-29 08:26:44 +08:00
|
|
|
{
|
|
|
|
struct pollfd *pfd;
|
|
|
|
int i;
|
|
|
|
|
2010-08-30 19:30:50 +08:00
|
|
|
pfd = xcalloc(socklist->nr, sizeof(struct pollfd));
|
2005-09-29 08:26:44 +08:00
|
|
|
|
2010-08-30 19:30:50 +08:00
|
|
|
for (i = 0; i < socklist->nr; i++) {
|
|
|
|
pfd[i].fd = socklist->list[i];
|
2005-09-29 08:26:44 +08:00
|
|
|
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
|
|
|
|
2008-08-15 02:02:20 +08:00
|
|
|
check_dead_children();
|
|
|
|
|
2010-08-30 19:30:50 +08:00
|
|
|
if (poll(pfd, socklist->nr, -1) < 0) {
|
2005-07-27 04:26:52 +08:00
|
|
|
if (errno != EINTR) {
|
2008-08-15 02:02:20 +08:00
|
|
|
logerror("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;
|
|
|
|
}
|
|
|
|
|
2010-08-30 19:30:50 +08:00
|
|
|
for (i = 0; i < socklist->nr; i++) {
|
2005-09-29 08:26:44 +08:00
|
|
|
if (pfd[i].revents & POLLIN) {
|
2010-11-04 09:35:19 +08:00
|
|
|
union {
|
|
|
|
struct sockaddr sa;
|
|
|
|
struct sockaddr_in sai;
|
|
|
|
#ifndef NO_IPV6
|
|
|
|
struct sockaddr_in6 sai6;
|
|
|
|
#endif
|
|
|
|
} ss;
|
2010-11-04 09:35:22 +08:00
|
|
|
socklen_t sslen = sizeof(ss);
|
2010-11-04 09:35:19 +08:00
|
|
|
int incoming = accept(pfd[i].fd, &ss.sa, &sslen);
|
2005-07-23 16:24:59 +08:00
|
|
|
if (incoming < 0) {
|
|
|
|
switch (errno) {
|
|
|
|
case EAGAIN:
|
|
|
|
case EINTR:
|
|
|
|
case ECONNABORTED:
|
|
|
|
continue;
|
|
|
|
default:
|
2009-06-27 23:58:46 +08:00
|
|
|
die_errno("accept returned");
|
2005-07-23 16:24:59 +08:00
|
|
|
}
|
|
|
|
}
|
2010-11-04 09:35:19 +08:00
|
|
|
handle(incoming, &ss.sa, sslen);
|
2005-07-14 10:45:26 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-11-04 09:35:24 +08:00
|
|
|
#ifdef NO_POSIX_GOODIES
|
|
|
|
|
|
|
|
struct credentials;
|
|
|
|
|
|
|
|
static void drop_privileges(struct credentials *cred)
|
|
|
|
{
|
|
|
|
/* nothing */
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct credentials *prepare_credentials(const char *user_name,
|
|
|
|
const char *group_name)
|
|
|
|
{
|
|
|
|
die("--user not supported on this platform");
|
|
|
|
}
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
struct credentials {
|
|
|
|
struct passwd *pass;
|
|
|
|
gid_t gid;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void drop_privileges(struct credentials *cred)
|
|
|
|
{
|
|
|
|
if (cred && (initgroups(cred->pass->pw_name, cred->gid) ||
|
|
|
|
setgid (cred->gid) || setuid(cred->pass->pw_uid)))
|
|
|
|
die("cannot drop privileges");
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct credentials *prepare_credentials(const char *user_name,
|
|
|
|
const char *group_name)
|
|
|
|
{
|
|
|
|
static struct credentials c;
|
|
|
|
|
|
|
|
c.pass = getpwnam(user_name);
|
|
|
|
if (!c.pass)
|
|
|
|
die("user not found - %s", user_name);
|
|
|
|
|
|
|
|
if (!group_name)
|
|
|
|
c.gid = c.pass->pw_gid;
|
|
|
|
else {
|
|
|
|
struct group *group = getgrnam(group_name);
|
|
|
|
if (!group)
|
|
|
|
die("group not found - %s", group_name);
|
|
|
|
|
|
|
|
c.gid = group->gr_gid;
|
|
|
|
}
|
|
|
|
|
|
|
|
return &c;
|
|
|
|
}
|
|
|
|
#endif
|
2006-07-14 00:47:13 +08:00
|
|
|
|
2010-11-04 09:35:24 +08:00
|
|
|
static int serve(struct string_list *listen_addr, int listen_port,
|
|
|
|
struct credentials *cred)
|
2005-09-29 08:26:44 +08:00
|
|
|
{
|
2010-08-30 19:30:50 +08:00
|
|
|
struct socketlist socklist = { NULL, 0, 0 };
|
2005-10-21 14:21:50 +08:00
|
|
|
|
2010-08-30 19:30:50 +08:00
|
|
|
socksetup(listen_addr, listen_port, &socklist);
|
|
|
|
if (socklist.nr == 0)
|
2010-08-30 19:30:51 +08:00
|
|
|
die("unable to allocate any listen sockets on port %u",
|
|
|
|
listen_port);
|
2005-10-21 14:21:50 +08:00
|
|
|
|
2010-11-04 09:35:24 +08:00
|
|
|
drop_privileges(cred);
|
2006-08-23 01:37:41 +08:00
|
|
|
|
2012-01-07 19:42:46 +08:00
|
|
|
loginfo("Ready to rumble");
|
|
|
|
|
2010-08-30 19:30:50 +08:00
|
|
|
return service_loop(&socklist);
|
2005-10-21 14:21:50 +08:00
|
|
|
}
|
2005-09-29 08:26:44 +08:00
|
|
|
|
add an extra level of indirection to main()
There are certain startup tasks that we expect every git
process to do. In some cases this is just to improve the
quality of the program (e.g., setting up gettext()). In
others it is a requirement for using certain functions in
libgit.a (e.g., system_path() expects that you have called
git_extract_argv0_path()).
Most commands are builtins and are covered by the git.c
version of main(). However, there are still a few external
commands that use their own main(). Each of these has to
remember to include the correct startup sequence, and we are
not always consistent.
Rather than just fix the inconsistencies, let's make this
harder to get wrong by providing a common main() that can
run this standard startup.
We basically have two options to do this:
- the compat/mingw.h file already does something like this by
adding a #define that replaces the definition of main with a
wrapper that calls mingw_startup().
The upside is that the code in each program doesn't need
to be changed at all; it's rewritten on the fly by the
preprocessor.
The downside is that it may make debugging of the startup
sequence a bit more confusing, as the preprocessor is
quietly inserting new code.
- the builtin functions are all of the form cmd_foo(),
and git.c's main() calls them.
This is much more explicit, which may make things more
obvious to somebody reading the code. It's also more
flexible (because of course we have to figure out _which_
cmd_foo() to call).
The downside is that each of the builtins must define
cmd_foo(), instead of just main().
This patch chooses the latter option, preferring the more
explicit approach, even though it is more invasive. We
introduce a new file common-main.c, with the "real" main. It
expects to call cmd_main() from whatever other objects it is
linked against.
We link common-main.o against anything that links against
libgit.a, since we know that such programs will need to do
this setup. Note that common-main.o can't actually go inside
libgit.a, as the linker would not pick up its main()
function automatically (it has no callers).
The rest of the patch is just adjusting all of the various
external programs (mostly in t/helper) to use cmd_main().
I've provided a global declaration for cmd_main(), which
means that all of the programs also need to match its
signature. In particular, many functions need to switch to
"const char **" instead of "char **" for argv. This effect
ripples out to a few other variables and functions, as well.
This makes the patch even more invasive, but the end result
is much better. We should be treating argv strings as const
anyway, and now all programs conform to the same signature
(which also matches the way builtins are defined).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-07-01 13:58:58 +08:00
|
|
|
int cmd_main(int argc, const char **argv)
|
2005-07-14 10:45:26 +08:00
|
|
|
{
|
2006-09-26 22:47:43 +08:00
|
|
|
int listen_port = 0;
|
2010-08-30 19:30:51 +08:00
|
|
|
struct string_list listen_addr = STRING_LIST_INIT_NODUP;
|
2010-11-04 09:35:16 +08:00
|
|
|
int serve_mode = 0, 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;
|
2010-11-04 09:35:24 +08:00
|
|
|
struct credentials *cred = NULL;
|
2005-07-14 10:45:26 +08:00
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 1; i < argc; i++) {
|
add an extra level of indirection to main()
There are certain startup tasks that we expect every git
process to do. In some cases this is just to improve the
quality of the program (e.g., setting up gettext()). In
others it is a requirement for using certain functions in
libgit.a (e.g., system_path() expects that you have called
git_extract_argv0_path()).
Most commands are builtins and are covered by the git.c
version of main(). However, there are still a few external
commands that use their own main(). Each of these has to
remember to include the correct startup sequence, and we are
not always consistent.
Rather than just fix the inconsistencies, let's make this
harder to get wrong by providing a common main() that can
run this standard startup.
We basically have two options to do this:
- the compat/mingw.h file already does something like this by
adding a #define that replaces the definition of main with a
wrapper that calls mingw_startup().
The upside is that the code in each program doesn't need
to be changed at all; it's rewritten on the fly by the
preprocessor.
The downside is that it may make debugging of the startup
sequence a bit more confusing, as the preprocessor is
quietly inserting new code.
- the builtin functions are all of the form cmd_foo(),
and git.c's main() calls them.
This is much more explicit, which may make things more
obvious to somebody reading the code. It's also more
flexible (because of course we have to figure out _which_
cmd_foo() to call).
The downside is that each of the builtins must define
cmd_foo(), instead of just main().
This patch chooses the latter option, preferring the more
explicit approach, even though it is more invasive. We
introduce a new file common-main.c, with the "real" main. It
expects to call cmd_main() from whatever other objects it is
linked against.
We link common-main.o against anything that links against
libgit.a, since we know that such programs will need to do
this setup. Note that common-main.o can't actually go inside
libgit.a, as the linker would not pick up its main()
function automatically (it has no callers).
The rest of the patch is just adjusting all of the various
external programs (mostly in t/helper) to use cmd_main().
I've provided a global declaration for cmd_main(), which
means that all of the programs also need to match its
signature. In particular, many functions need to switch to
"const char **" instead of "char **" for argv. This effect
ripples out to a few other variables and functions, as well.
This makes the patch even more invasive, but the end result
is much better. We should be treating argv strings as const
anyway, and now all programs conform to the same signature
(which also matches the way builtins are defined).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-07-01 13:58:58 +08:00
|
|
|
const char *arg = argv[i];
|
use skip_prefix to avoid magic numbers
It's a common idiom to match a prefix and then skip past it
with a magic number, like:
if (starts_with(foo, "bar"))
foo += 3;
This is easy to get wrong, since you have to count the
prefix string yourself, and there's no compiler check if the
string changes. We can use skip_prefix to avoid the magic
numbers here.
Note that some of these conversions could be much shorter.
For example:
if (starts_with(arg, "--foo=")) {
bar = arg + 6;
continue;
}
could become:
if (skip_prefix(arg, "--foo=", &bar))
continue;
However, I have left it as:
if (skip_prefix(arg, "--foo=", &v)) {
bar = v;
continue;
}
to visually match nearby cases which need to actually
process the string. Like:
if (skip_prefix(arg, "--foo=", &v)) {
bar = atoi(v);
continue;
}
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-19 03:47:50 +08:00
|
|
|
const char *v;
|
2005-07-14 10:45:26 +08:00
|
|
|
|
use skip_prefix to avoid magic numbers
It's a common idiom to match a prefix and then skip past it
with a magic number, like:
if (starts_with(foo, "bar"))
foo += 3;
This is easy to get wrong, since you have to count the
prefix string yourself, and there's no compiler check if the
string changes. We can use skip_prefix to avoid the magic
numbers here.
Note that some of these conversions could be much shorter.
For example:
if (starts_with(arg, "--foo=")) {
bar = arg + 6;
continue;
}
could become:
if (skip_prefix(arg, "--foo=", &bar))
continue;
However, I have left it as:
if (skip_prefix(arg, "--foo=", &v)) {
bar = v;
continue;
}
to visually match nearby cases which need to actually
process the string. Like:
if (skip_prefix(arg, "--foo=", &v)) {
bar = atoi(v);
continue;
}
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-19 03:47:50 +08:00
|
|
|
if (skip_prefix(arg, "--listen=", &v)) {
|
|
|
|
string_list_append(&listen_addr, xstrdup_tolower(v));
|
2008-12-26 18:12:15 +08:00
|
|
|
continue;
|
2006-09-26 22:47:43 +08:00
|
|
|
}
|
use skip_prefix to avoid magic numbers
It's a common idiom to match a prefix and then skip past it
with a magic number, like:
if (starts_with(foo, "bar"))
foo += 3;
This is easy to get wrong, since you have to count the
prefix string yourself, and there's no compiler check if the
string changes. We can use skip_prefix to avoid the magic
numbers here.
Note that some of these conversions could be much shorter.
For example:
if (starts_with(arg, "--foo=")) {
bar = arg + 6;
continue;
}
could become:
if (skip_prefix(arg, "--foo=", &bar))
continue;
However, I have left it as:
if (skip_prefix(arg, "--foo=", &v)) {
bar = v;
continue;
}
to visually match nearby cases which need to actually
process the string. Like:
if (skip_prefix(arg, "--foo=", &v)) {
bar = atoi(v);
continue;
}
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-19 03:47:50 +08:00
|
|
|
if (skip_prefix(arg, "--port=", &v)) {
|
2005-07-14 10:45:26 +08:00
|
|
|
char *end;
|
|
|
|
unsigned long n;
|
use skip_prefix to avoid magic numbers
It's a common idiom to match a prefix and then skip past it
with a magic number, like:
if (starts_with(foo, "bar"))
foo += 3;
This is easy to get wrong, since you have to count the
prefix string yourself, and there's no compiler check if the
string changes. We can use skip_prefix to avoid the magic
numbers here.
Note that some of these conversions could be much shorter.
For example:
if (starts_with(arg, "--foo=")) {
bar = arg + 6;
continue;
}
could become:
if (skip_prefix(arg, "--foo=", &bar))
continue;
However, I have left it as:
if (skip_prefix(arg, "--foo=", &v)) {
bar = v;
continue;
}
to visually match nearby cases which need to actually
process the string. Like:
if (skip_prefix(arg, "--foo=", &v)) {
bar = atoi(v);
continue;
}
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-19 03:47:50 +08:00
|
|
|
n = strtoul(v, &end, 0);
|
|
|
|
if (*v && !*end) {
|
2006-09-26 22:47:43 +08:00
|
|
|
listen_port = n;
|
2005-07-14 10:45:26 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
2010-11-04 09:35:16 +08:00
|
|
|
if (!strcmp(arg, "--serve")) {
|
|
|
|
serve_mode = 1;
|
|
|
|
continue;
|
|
|
|
}
|
2005-07-16 00:32:16 +08:00
|
|
|
if (!strcmp(arg, "--inetd")) {
|
|
|
|
inetd_mode = 1;
|
|
|
|
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")) {
|
daemon: add --log-destination=(stderr|syslog|none)
This new option can be used to override the implicit --syslog of
--inetd, or to disable all logging. (While --detach also implies
--syslog, --log-destination=stderr with --detach is useless since
--detach disassociates the process from the original stderr.) --syslog
is retained as an alias for --log-destination=syslog.
--log-destination always overrides implicit --syslog regardless of
option order. This is different than the “last one wins” logic that
applies to some implicit options elsewhere in Git, but should hopefully
be less confusing. (I also don’t know if *all* implicit options in Git
follow “last one wins”.)
The combination of --inetd with --log-destination=stderr is useful, for
instance, when running `git daemon` as an instanced systemd service
(with associated socket unit). In this case, log messages sent via
syslog are received by the journal daemon, but run the risk of being
processed at a time when the `git daemon` process has already exited
(especially if the process was very short-lived, e.g. due to client
error), so that the journal daemon can no longer read its cgroup and
attach the message to the correct systemd unit (see systemd/systemd#2913
[1]). Logging to stderr instead can solve this problem, because systemd
can connect stderr directly to the journal daemon, which then already
knows which unit is associated with this stream.
[1]: https://github.com/systemd/systemd/issues/2913
Helped-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Helped-by: Junio C Hamano <gitster@pobox.com>
Helped-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Lucas Werkmeister <mail@lucaswerkmeister.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-02-05 02:30:37 +08:00
|
|
|
log_destination = LOG_DESTINATION_SYSLOG;
|
2005-09-24 22:13:01 +08:00
|
|
|
continue;
|
|
|
|
}
|
daemon: add --log-destination=(stderr|syslog|none)
This new option can be used to override the implicit --syslog of
--inetd, or to disable all logging. (While --detach also implies
--syslog, --log-destination=stderr with --detach is useless since
--detach disassociates the process from the original stderr.) --syslog
is retained as an alias for --log-destination=syslog.
--log-destination always overrides implicit --syslog regardless of
option order. This is different than the “last one wins” logic that
applies to some implicit options elsewhere in Git, but should hopefully
be less confusing. (I also don’t know if *all* implicit options in Git
follow “last one wins”.)
The combination of --inetd with --log-destination=stderr is useful, for
instance, when running `git daemon` as an instanced systemd service
(with associated socket unit). In this case, log messages sent via
syslog are received by the journal daemon, but run the risk of being
processed at a time when the `git daemon` process has already exited
(especially if the process was very short-lived, e.g. due to client
error), so that the journal daemon can no longer read its cgroup and
attach the message to the correct systemd unit (see systemd/systemd#2913
[1]). Logging to stderr instead can solve this problem, because systemd
can connect stderr directly to the journal daemon, which then already
knows which unit is associated with this stream.
[1]: https://github.com/systemd/systemd/issues/2913
Helped-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Helped-by: Junio C Hamano <gitster@pobox.com>
Helped-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Lucas Werkmeister <mail@lucaswerkmeister.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-02-05 02:30:37 +08:00
|
|
|
if (skip_prefix(arg, "--log-destination=", &v)) {
|
|
|
|
if (!strcmp(v, "syslog")) {
|
|
|
|
log_destination = LOG_DESTINATION_SYSLOG;
|
|
|
|
continue;
|
|
|
|
} else if (!strcmp(v, "stderr")) {
|
|
|
|
log_destination = LOG_DESTINATION_STDERR;
|
|
|
|
continue;
|
|
|
|
} else if (!strcmp(v, "none")) {
|
|
|
|
log_destination = LOG_DESTINATION_NONE;
|
|
|
|
continue;
|
|
|
|
} else
|
|
|
|
die("unknown log destination '%s'", v);
|
|
|
|
}
|
2005-09-27 10:10:55 +08:00
|
|
|
if (!strcmp(arg, "--export-all")) {
|
|
|
|
export_all_trees = 1;
|
|
|
|
continue;
|
|
|
|
}
|
use skip_prefix to avoid magic numbers
It's a common idiom to match a prefix and then skip past it
with a magic number, like:
if (starts_with(foo, "bar"))
foo += 3;
This is easy to get wrong, since you have to count the
prefix string yourself, and there's no compiler check if the
string changes. We can use skip_prefix to avoid the magic
numbers here.
Note that some of these conversions could be much shorter.
For example:
if (starts_with(arg, "--foo=")) {
bar = arg + 6;
continue;
}
could become:
if (skip_prefix(arg, "--foo=", &bar))
continue;
However, I have left it as:
if (skip_prefix(arg, "--foo=", &v)) {
bar = v;
continue;
}
to visually match nearby cases which need to actually
process the string. Like:
if (skip_prefix(arg, "--foo=", &v)) {
bar = atoi(v);
continue;
}
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-19 03:47:50 +08:00
|
|
|
if (skip_prefix(arg, "--access-hook=", &v)) {
|
|
|
|
access_hook = v;
|
2012-08-15 02:37:51 +08:00
|
|
|
continue;
|
|
|
|
}
|
use skip_prefix to avoid magic numbers
It's a common idiom to match a prefix and then skip past it
with a magic number, like:
if (starts_with(foo, "bar"))
foo += 3;
This is easy to get wrong, since you have to count the
prefix string yourself, and there's no compiler check if the
string changes. We can use skip_prefix to avoid the magic
numbers here.
Note that some of these conversions could be much shorter.
For example:
if (starts_with(arg, "--foo=")) {
bar = arg + 6;
continue;
}
could become:
if (skip_prefix(arg, "--foo=", &bar))
continue;
However, I have left it as:
if (skip_prefix(arg, "--foo=", &v)) {
bar = v;
continue;
}
to visually match nearby cases which need to actually
process the string. Like:
if (skip_prefix(arg, "--foo=", &v)) {
bar = atoi(v);
continue;
}
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-19 03:47:50 +08:00
|
|
|
if (skip_prefix(arg, "--timeout=", &v)) {
|
|
|
|
timeout = atoi(v);
|
2005-11-17 07:38:29 +08:00
|
|
|
continue;
|
2005-10-20 05:27:01 +08:00
|
|
|
}
|
use skip_prefix to avoid magic numbers
It's a common idiom to match a prefix and then skip past it
with a magic number, like:
if (starts_with(foo, "bar"))
foo += 3;
This is easy to get wrong, since you have to count the
prefix string yourself, and there's no compiler check if the
string changes. We can use skip_prefix to avoid the magic
numbers here.
Note that some of these conversions could be much shorter.
For example:
if (starts_with(arg, "--foo=")) {
bar = arg + 6;
continue;
}
could become:
if (skip_prefix(arg, "--foo=", &bar))
continue;
However, I have left it as:
if (skip_prefix(arg, "--foo=", &v)) {
bar = v;
continue;
}
to visually match nearby cases which need to actually
process the string. Like:
if (skip_prefix(arg, "--foo=", &v)) {
bar = atoi(v);
continue;
}
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-19 03:47:50 +08:00
|
|
|
if (skip_prefix(arg, "--init-timeout=", &v)) {
|
|
|
|
init_timeout = atoi(v);
|
2005-11-17 07:38:29 +08:00
|
|
|
continue;
|
2005-10-20 05:27:01 +08:00
|
|
|
}
|
use skip_prefix to avoid magic numbers
It's a common idiom to match a prefix and then skip past it
with a magic number, like:
if (starts_with(foo, "bar"))
foo += 3;
This is easy to get wrong, since you have to count the
prefix string yourself, and there's no compiler check if the
string changes. We can use skip_prefix to avoid the magic
numbers here.
Note that some of these conversions could be much shorter.
For example:
if (starts_with(arg, "--foo=")) {
bar = arg + 6;
continue;
}
could become:
if (skip_prefix(arg, "--foo=", &bar))
continue;
However, I have left it as:
if (skip_prefix(arg, "--foo=", &v)) {
bar = v;
continue;
}
to visually match nearby cases which need to actually
process the string. Like:
if (skip_prefix(arg, "--foo=", &v)) {
bar = atoi(v);
continue;
}
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-19 03:47:50 +08:00
|
|
|
if (skip_prefix(arg, "--max-connections=", &v)) {
|
|
|
|
max_connections = atoi(v);
|
2008-08-15 02:02:20 +08:00
|
|
|
if (max_connections < 0)
|
|
|
|
max_connections = 0; /* unlimited */
|
|
|
|
continue;
|
|
|
|
}
|
2005-11-18 03:37:14 +08:00
|
|
|
if (!strcmp(arg, "--strict-paths")) {
|
|
|
|
strict_paths = 1;
|
|
|
|
continue;
|
|
|
|
}
|
use skip_prefix to avoid magic numbers
It's a common idiom to match a prefix and then skip past it
with a magic number, like:
if (starts_with(foo, "bar"))
foo += 3;
This is easy to get wrong, since you have to count the
prefix string yourself, and there's no compiler check if the
string changes. We can use skip_prefix to avoid the magic
numbers here.
Note that some of these conversions could be much shorter.
For example:
if (starts_with(arg, "--foo=")) {
bar = arg + 6;
continue;
}
could become:
if (skip_prefix(arg, "--foo=", &bar))
continue;
However, I have left it as:
if (skip_prefix(arg, "--foo=", &v)) {
bar = v;
continue;
}
to visually match nearby cases which need to actually
process the string. Like:
if (skip_prefix(arg, "--foo=", &v)) {
bar = atoi(v);
continue;
}
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-19 03:47:50 +08:00
|
|
|
if (skip_prefix(arg, "--base-path=", &v)) {
|
|
|
|
base_path = v;
|
2005-12-23 09:27:40 +08:00
|
|
|
continue;
|
|
|
|
}
|
2007-07-28 05:00:29 +08:00
|
|
|
if (!strcmp(arg, "--base-path-relaxed")) {
|
|
|
|
base_path_relaxed = 1;
|
|
|
|
continue;
|
|
|
|
}
|
use skip_prefix to avoid magic numbers
It's a common idiom to match a prefix and then skip past it
with a magic number, like:
if (starts_with(foo, "bar"))
foo += 3;
This is easy to get wrong, since you have to count the
prefix string yourself, and there's no compiler check if the
string changes. We can use skip_prefix to avoid the magic
numbers here.
Note that some of these conversions could be much shorter.
For example:
if (starts_with(arg, "--foo=")) {
bar = arg + 6;
continue;
}
could become:
if (skip_prefix(arg, "--foo=", &bar))
continue;
However, I have left it as:
if (skip_prefix(arg, "--foo=", &v)) {
bar = v;
continue;
}
to visually match nearby cases which need to actually
process the string. Like:
if (skip_prefix(arg, "--foo=", &v)) {
bar = atoi(v);
continue;
}
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-19 03:47:50 +08:00
|
|
|
if (skip_prefix(arg, "--interpolated-path=", &v)) {
|
|
|
|
interpolated_path = v;
|
2006-09-20 09:31:51 +08:00
|
|
|
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;
|
|
|
|
}
|
use skip_prefix to avoid magic numbers
It's a common idiom to match a prefix and then skip past it
with a magic number, like:
if (starts_with(foo, "bar"))
foo += 3;
This is easy to get wrong, since you have to count the
prefix string yourself, and there's no compiler check if the
string changes. We can use skip_prefix to avoid the magic
numbers here.
Note that some of these conversions could be much shorter.
For example:
if (starts_with(arg, "--foo=")) {
bar = arg + 6;
continue;
}
could become:
if (skip_prefix(arg, "--foo=", &bar))
continue;
However, I have left it as:
if (skip_prefix(arg, "--foo=", &v)) {
bar = v;
continue;
}
to visually match nearby cases which need to actually
process the string. Like:
if (skip_prefix(arg, "--foo=", &v)) {
bar = atoi(v);
continue;
}
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-19 03:47:50 +08:00
|
|
|
if (skip_prefix(arg, "--user-path=", &v)) {
|
|
|
|
user_path = v;
|
2006-02-05 14:27:29 +08:00
|
|
|
continue;
|
|
|
|
}
|
use skip_prefix to avoid magic numbers
It's a common idiom to match a prefix and then skip past it
with a magic number, like:
if (starts_with(foo, "bar"))
foo += 3;
This is easy to get wrong, since you have to count the
prefix string yourself, and there's no compiler check if the
string changes. We can use skip_prefix to avoid the magic
numbers here.
Note that some of these conversions could be much shorter.
For example:
if (starts_with(arg, "--foo=")) {
bar = arg + 6;
continue;
}
could become:
if (skip_prefix(arg, "--foo=", &bar))
continue;
However, I have left it as:
if (skip_prefix(arg, "--foo=", &v)) {
bar = v;
continue;
}
to visually match nearby cases which need to actually
process the string. Like:
if (skip_prefix(arg, "--foo=", &v)) {
bar = atoi(v);
continue;
}
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-19 03:47:50 +08:00
|
|
|
if (skip_prefix(arg, "--pid-file=", &v)) {
|
|
|
|
pid_file = v;
|
2006-07-13 18:18:08 +08:00
|
|
|
continue;
|
|
|
|
}
|
2006-07-14 00:47:13 +08:00
|
|
|
if (!strcmp(arg, "--detach")) {
|
|
|
|
detach = 1;
|
|
|
|
continue;
|
|
|
|
}
|
use skip_prefix to avoid magic numbers
It's a common idiom to match a prefix and then skip past it
with a magic number, like:
if (starts_with(foo, "bar"))
foo += 3;
This is easy to get wrong, since you have to count the
prefix string yourself, and there's no compiler check if the
string changes. We can use skip_prefix to avoid the magic
numbers here.
Note that some of these conversions could be much shorter.
For example:
if (starts_with(arg, "--foo=")) {
bar = arg + 6;
continue;
}
could become:
if (skip_prefix(arg, "--foo=", &bar))
continue;
However, I have left it as:
if (skip_prefix(arg, "--foo=", &v)) {
bar = v;
continue;
}
to visually match nearby cases which need to actually
process the string. Like:
if (skip_prefix(arg, "--foo=", &v)) {
bar = atoi(v);
continue;
}
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-19 03:47:50 +08:00
|
|
|
if (skip_prefix(arg, "--user=", &v)) {
|
|
|
|
user_name = v;
|
2006-08-23 01:37:41 +08:00
|
|
|
continue;
|
|
|
|
}
|
use skip_prefix to avoid magic numbers
It's a common idiom to match a prefix and then skip past it
with a magic number, like:
if (starts_with(foo, "bar"))
foo += 3;
This is easy to get wrong, since you have to count the
prefix string yourself, and there's no compiler check if the
string changes. We can use skip_prefix to avoid the magic
numbers here.
Note that some of these conversions could be much shorter.
For example:
if (starts_with(arg, "--foo=")) {
bar = arg + 6;
continue;
}
could become:
if (skip_prefix(arg, "--foo=", &bar))
continue;
However, I have left it as:
if (skip_prefix(arg, "--foo=", &v)) {
bar = v;
continue;
}
to visually match nearby cases which need to actually
process the string. Like:
if (skip_prefix(arg, "--foo=", &v)) {
bar = atoi(v);
continue;
}
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-19 03:47:50 +08:00
|
|
|
if (skip_prefix(arg, "--group=", &v)) {
|
|
|
|
group_name = v;
|
2006-08-23 01:37:41 +08:00
|
|
|
continue;
|
|
|
|
}
|
use skip_prefix to avoid magic numbers
It's a common idiom to match a prefix and then skip past it
with a magic number, like:
if (starts_with(foo, "bar"))
foo += 3;
This is easy to get wrong, since you have to count the
prefix string yourself, and there's no compiler check if the
string changes. We can use skip_prefix to avoid the magic
numbers here.
Note that some of these conversions could be much shorter.
For example:
if (starts_with(arg, "--foo=")) {
bar = arg + 6;
continue;
}
could become:
if (skip_prefix(arg, "--foo=", &bar))
continue;
However, I have left it as:
if (skip_prefix(arg, "--foo=", &v)) {
bar = v;
continue;
}
to visually match nearby cases which need to actually
process the string. Like:
if (skip_prefix(arg, "--foo=", &v)) {
bar = atoi(v);
continue;
}
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-19 03:47:50 +08:00
|
|
|
if (skip_prefix(arg, "--enable=", &v)) {
|
|
|
|
enable_service(v, 1);
|
2006-08-21 10:03:13 +08:00
|
|
|
continue;
|
|
|
|
}
|
use skip_prefix to avoid magic numbers
It's a common idiom to match a prefix and then skip past it
with a magic number, like:
if (starts_with(foo, "bar"))
foo += 3;
This is easy to get wrong, since you have to count the
prefix string yourself, and there's no compiler check if the
string changes. We can use skip_prefix to avoid the magic
numbers here.
Note that some of these conversions could be much shorter.
For example:
if (starts_with(arg, "--foo=")) {
bar = arg + 6;
continue;
}
could become:
if (skip_prefix(arg, "--foo=", &bar))
continue;
However, I have left it as:
if (skip_prefix(arg, "--foo=", &v)) {
bar = v;
continue;
}
to visually match nearby cases which need to actually
process the string. Like:
if (skip_prefix(arg, "--foo=", &v)) {
bar = atoi(v);
continue;
}
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-19 03:47:50 +08:00
|
|
|
if (skip_prefix(arg, "--disable=", &v)) {
|
|
|
|
enable_service(v, 0);
|
2006-08-21 10:03:13 +08:00
|
|
|
continue;
|
|
|
|
}
|
use skip_prefix to avoid magic numbers
It's a common idiom to match a prefix and then skip past it
with a magic number, like:
if (starts_with(foo, "bar"))
foo += 3;
This is easy to get wrong, since you have to count the
prefix string yourself, and there's no compiler check if the
string changes. We can use skip_prefix to avoid the magic
numbers here.
Note that some of these conversions could be much shorter.
For example:
if (starts_with(arg, "--foo=")) {
bar = arg + 6;
continue;
}
could become:
if (skip_prefix(arg, "--foo=", &bar))
continue;
However, I have left it as:
if (skip_prefix(arg, "--foo=", &v)) {
bar = v;
continue;
}
to visually match nearby cases which need to actually
process the string. Like:
if (skip_prefix(arg, "--foo=", &v)) {
bar = atoi(v);
continue;
}
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-19 03:47:50 +08:00
|
|
|
if (skip_prefix(arg, "--allow-override=", &v)) {
|
|
|
|
make_service_overridable(v, 1);
|
2006-08-21 10:03:13 +08:00
|
|
|
continue;
|
|
|
|
}
|
use skip_prefix to avoid magic numbers
It's a common idiom to match a prefix and then skip past it
with a magic number, like:
if (starts_with(foo, "bar"))
foo += 3;
This is easy to get wrong, since you have to count the
prefix string yourself, and there's no compiler check if the
string changes. We can use skip_prefix to avoid the magic
numbers here.
Note that some of these conversions could be much shorter.
For example:
if (starts_with(arg, "--foo=")) {
bar = arg + 6;
continue;
}
could become:
if (skip_prefix(arg, "--foo=", &bar))
continue;
However, I have left it as:
if (skip_prefix(arg, "--foo=", &v)) {
bar = v;
continue;
}
to visually match nearby cases which need to actually
process the string. Like:
if (skip_prefix(arg, "--foo=", &v)) {
bar = atoi(v);
continue;
}
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-19 03:47:50 +08:00
|
|
|
if (skip_prefix(arg, "--forbid-override=", &v)) {
|
|
|
|
make_service_overridable(v, 0);
|
2006-08-21 10:03:13 +08:00
|
|
|
continue;
|
|
|
|
}
|
2013-12-20 18:53:52 +08:00
|
|
|
if (!strcmp(arg, "--informative-errors")) {
|
2011-10-15 05:19:21 +08:00
|
|
|
informative_errors = 1;
|
|
|
|
continue;
|
|
|
|
}
|
2013-12-20 18:53:52 +08:00
|
|
|
if (!strcmp(arg, "--no-informative-errors")) {
|
2011-10-15 05:19:21 +08:00
|
|
|
informative_errors = 0;
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
daemon: add --log-destination=(stderr|syslog|none)
This new option can be used to override the implicit --syslog of
--inetd, or to disable all logging. (While --detach also implies
--syslog, --log-destination=stderr with --detach is useless since
--detach disassociates the process from the original stderr.) --syslog
is retained as an alias for --log-destination=syslog.
--log-destination always overrides implicit --syslog regardless of
option order. This is different than the “last one wins” logic that
applies to some implicit options elsewhere in Git, but should hopefully
be less confusing. (I also don’t know if *all* implicit options in Git
follow “last one wins”.)
The combination of --inetd with --log-destination=stderr is useful, for
instance, when running `git daemon` as an instanced systemd service
(with associated socket unit). In this case, log messages sent via
syslog are received by the journal daemon, but run the risk of being
processed at a time when the `git daemon` process has already exited
(especially if the process was very short-lived, e.g. due to client
error), so that the journal daemon can no longer read its cgroup and
attach the message to the correct systemd unit (see systemd/systemd#2913
[1]). Logging to stderr instead can solve this problem, because systemd
can connect stderr directly to the journal daemon, which then already
knows which unit is associated with this stream.
[1]: https://github.com/systemd/systemd/issues/2913
Helped-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Helped-by: Junio C Hamano <gitster@pobox.com>
Helped-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Lucas Werkmeister <mail@lucaswerkmeister.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-02-05 02:30:37 +08:00
|
|
|
if (log_destination == LOG_DESTINATION_UNSET) {
|
|
|
|
if (inetd_mode || detach)
|
|
|
|
log_destination = LOG_DESTINATION_SYSLOG;
|
|
|
|
else
|
|
|
|
log_destination = LOG_DESTINATION_STDERR;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (log_destination == LOG_DESTINATION_SYSLOG) {
|
2008-08-15 02:02:20 +08:00
|
|
|
openlog("git-daemon", LOG_PID, LOG_DAEMON);
|
2008-02-25 21:25:20 +08:00
|
|
|
set_die_routine(daemon_die);
|
2008-08-25 04:27:10 +08:00
|
|
|
} else
|
2008-09-04 11:33:29 +08:00
|
|
|
/* avoid splitting a message in the middle */
|
2010-11-04 09:35:17 +08:00
|
|
|
setvbuf(stderr, NULL, _IOFBF, 4096);
|
2008-02-25 21:25:20 +08:00
|
|
|
|
2010-11-04 09:35:23 +08:00
|
|
|
if (inetd_mode && (detach || group_name || user_name))
|
|
|
|
die("--detach, --user and --group are incompatible with --inetd");
|
2006-08-23 01:37:41 +08:00
|
|
|
|
2010-08-30 19:30:51 +08:00
|
|
|
if (inetd_mode && (listen_port || (listen_addr.nr > 0)))
|
2006-09-26 22:47:43 +08:00
|
|
|
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");
|
|
|
|
|
2010-11-04 09:35:24 +08:00
|
|
|
if (user_name)
|
|
|
|
cred = prepare_credentials(user_name, group_name);
|
2006-08-23 01:37:41 +08:00
|
|
|
|
2006-07-13 18:02:29 +08:00
|
|
|
if (strict_paths && (!ok_paths || !*ok_paths))
|
|
|
|
die("option --strict-paths requires a whitelist");
|
|
|
|
|
2008-09-09 16:27:07 +08:00
|
|
|
if (base_path && !is_directory(base_path))
|
|
|
|
die("base-path '%s' does not exist or is not a directory",
|
|
|
|
base_path);
|
2008-02-26 20:00:55 +08:00
|
|
|
|
2018-04-04 06:13:07 +08:00
|
|
|
if (log_destination != LOG_DESTINATION_STDERR) {
|
2010-11-04 09:35:16 +08:00
|
|
|
if (!freopen("/dev/null", "w", stderr))
|
|
|
|
die_errno("failed to redirect stderr to /dev/null");
|
|
|
|
}
|
|
|
|
|
2010-11-04 09:35:19 +08:00
|
|
|
if (inetd_mode || serve_mode)
|
|
|
|
return execute();
|
2005-11-15 00:41:01 +08:00
|
|
|
|
2014-02-08 15:08:51 +08:00
|
|
|
if (detach) {
|
|
|
|
if (daemonize())
|
|
|
|
die("--detach not supported on this platform");
|
common-main: call sanitize_stdfds()
This is setup that should be done in every program for
safety, but we never got around to adding it everywhere (so
builtins benefited from the call in git.c, but any external
commands did not). Putting it in the common main() gives us
this safety everywhere.
Note that the case in daemon.c is a little funny. We wait
until we know whether we want to daemonize, and then either:
- call daemonize(), which will close stdio and reopen it to
/dev/null under the hood
- sanitize_stdfds(), to fix up any odd cases
But that is way too late; the point of sanitizing is to give
us reliable descriptors on 0/1/2, and we will already have
executed code, possibly called die(), etc. The sanitizing
should be the very first thing that happens.
With this patch, git-daemon will sanitize first, and can
remove the call in the non-daemonize case. It does mean that
daemonize() may just end up closing the descriptors we
opened, but that's not a big deal (it's not wrong to do so,
nor is it really less optimal than the case where our parent
process redirected us from /dev/null ahead of time).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-07-01 14:06:02 +08:00
|
|
|
}
|
2006-07-14 00:32:11 +08:00
|
|
|
|
2006-07-13 18:18:08 +08:00
|
|
|
if (pid_file)
|
2015-08-25 04:20:39 +08:00
|
|
|
write_file(pid_file, "%"PRIuMAX, (uintmax_t) getpid());
|
2006-07-13 18:18:08 +08:00
|
|
|
|
2010-11-04 09:35:16 +08:00
|
|
|
/* prepare argv for serving-processes */
|
2016-02-23 06:44:21 +08:00
|
|
|
argv_array_push(&cld_argv, argv[0]); /* git-daemon */
|
|
|
|
argv_array_push(&cld_argv, "--serve");
|
2011-01-04 12:04:46 +08:00
|
|
|
for (i = 1; i < argc; ++i)
|
2016-02-23 06:44:21 +08:00
|
|
|
argv_array_push(&cld_argv, argv[i]);
|
2010-11-04 09:35:16 +08:00
|
|
|
|
2010-11-04 09:35:24 +08:00
|
|
|
return serve(&listen_addr, listen_port, cred);
|
2005-07-14 10:45:26 +08:00
|
|
|
}
|