2006-01-26 04:38:36 +08:00
|
|
|
#include "git-compat-util.h"
|
2005-07-05 02:57:58 +08:00
|
|
|
#include "cache.h"
|
2005-07-06 06:44:09 +08:00
|
|
|
#include "pkt-line.h"
|
2005-07-08 15:02:52 +08:00
|
|
|
#include "quote.h"
|
2005-10-16 15:25:26 +08:00
|
|
|
#include "refs.h"
|
2007-03-13 07:00:19 +08:00
|
|
|
#include "run-command.h"
|
2007-05-12 23:45:59 +08:00
|
|
|
#include "remote.h"
|
2013-07-09 04:56:53 +08:00
|
|
|
#include "connect.h"
|
2010-05-23 17:19:44 +08:00
|
|
|
#include "url.h"
|
2013-09-18 10:10:31 +08:00
|
|
|
#include "string-list.h"
|
2013-12-05 21:02:29 +08:00
|
|
|
#include "sha1-array.h"
|
2005-07-05 02:57:58 +08:00
|
|
|
|
2006-08-16 01:23:48 +08:00
|
|
|
static char *server_capabilities;
|
2013-09-18 07:29:28 +08:00
|
|
|
static const char *parse_feature_value(const char *, const char *, int *);
|
2005-10-28 10:48:54 +08:00
|
|
|
|
Improve git-peek-remote
This makes git-peek-remote able to basically do everything that
git-ls-remote does (but obviously just for the native protocol, so no
http[s]: or rsync: support).
The default behaviour is the same, but you can now give a mixture of
"--refs", "--tags" and "--heads" flags, where "--refs" forces
git-peek-remote to only show real refs (ie none of the fakey tag lookups,
but also not the special pseudo-refs like HEAD and MERGE_HEAD).
The "--tags" and "--heads" flags respectively limit the output to just
regular tags and heads, of course.
You can still also ask to limit them by name too.
You can combine the flags, so
git peek-remote --refs --tags .
will show all local _true_ tags, without the generated tag lookups
(compare the output without the "--refs" flag).
And "--tags --heads" will show both tags and heads, but will avoid (for
example) any special refs outside of the standard locations.
I'm also planning on adding a "--ignore-local" flag that allows us to ask
it to ignore any refs that we already have in the local tree, but that's
an independent thing.
All this is obviously gearing up to making "git fetch" cheaper.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2006-07-05 03:29:10 +08:00
|
|
|
static int check_ref(const char *name, int len, unsigned int flags)
|
|
|
|
{
|
|
|
|
if (!flags)
|
|
|
|
return 1;
|
|
|
|
|
2006-09-06 03:00:17 +08:00
|
|
|
if (len < 5 || memcmp(name, "refs/", 5))
|
Improve git-peek-remote
This makes git-peek-remote able to basically do everything that
git-ls-remote does (but obviously just for the native protocol, so no
http[s]: or rsync: support).
The default behaviour is the same, but you can now give a mixture of
"--refs", "--tags" and "--heads" flags, where "--refs" forces
git-peek-remote to only show real refs (ie none of the fakey tag lookups,
but also not the special pseudo-refs like HEAD and MERGE_HEAD).
The "--tags" and "--heads" flags respectively limit the output to just
regular tags and heads, of course.
You can still also ask to limit them by name too.
You can combine the flags, so
git peek-remote --refs --tags .
will show all local _true_ tags, without the generated tag lookups
(compare the output without the "--refs" flag).
And "--tags --heads" will show both tags and heads, but will avoid (for
example) any special refs outside of the standard locations.
I'm also planning on adding a "--ignore-local" flag that allows us to ask
it to ignore any refs that we already have in the local tree, but that's
an independent thing.
All this is obviously gearing up to making "git fetch" cheaper.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2006-07-05 03:29:10 +08:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Skip the "refs/" part */
|
|
|
|
name += 5;
|
|
|
|
len -= 5;
|
|
|
|
|
|
|
|
/* REF_NORMAL means that we don't want the magic fake tag refs */
|
2011-09-16 05:10:25 +08:00
|
|
|
if ((flags & REF_NORMAL) && check_refname_format(name, 0))
|
Improve git-peek-remote
This makes git-peek-remote able to basically do everything that
git-ls-remote does (but obviously just for the native protocol, so no
http[s]: or rsync: support).
The default behaviour is the same, but you can now give a mixture of
"--refs", "--tags" and "--heads" flags, where "--refs" forces
git-peek-remote to only show real refs (ie none of the fakey tag lookups,
but also not the special pseudo-refs like HEAD and MERGE_HEAD).
The "--tags" and "--heads" flags respectively limit the output to just
regular tags and heads, of course.
You can still also ask to limit them by name too.
You can combine the flags, so
git peek-remote --refs --tags .
will show all local _true_ tags, without the generated tag lookups
(compare the output without the "--refs" flag).
And "--tags --heads" will show both tags and heads, but will avoid (for
example) any special refs outside of the standard locations.
I'm also planning on adding a "--ignore-local" flag that allows us to ask
it to ignore any refs that we already have in the local tree, but that's
an independent thing.
All this is obviously gearing up to making "git fetch" cheaper.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2006-07-05 03:29:10 +08:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* REF_HEADS means that we want regular branch heads */
|
|
|
|
if ((flags & REF_HEADS) && !memcmp(name, "heads/", 6))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
/* REF_TAGS means that we want tags */
|
|
|
|
if ((flags & REF_TAGS) && !memcmp(name, "tags/", 5))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
/* All type bits clear means that we are ok with anything */
|
|
|
|
return !(flags & ~REF_NORMAL);
|
|
|
|
}
|
|
|
|
|
2007-10-30 09:05:40 +08:00
|
|
|
int check_ref_type(const struct ref *ref, int flags)
|
|
|
|
{
|
|
|
|
return check_ref(ref->name, strlen(ref->name), flags);
|
|
|
|
}
|
|
|
|
|
2012-06-20 02:24:50 +08:00
|
|
|
static void die_initial_contact(int got_at_least_one_head)
|
|
|
|
{
|
|
|
|
if (got_at_least_one_head)
|
|
|
|
die("The remote end hung up upon initial contact");
|
|
|
|
else
|
|
|
|
die("Could not read from remote repository.\n\n"
|
|
|
|
"Please make sure you have the correct access rights\n"
|
|
|
|
"and the repository exists.");
|
|
|
|
}
|
|
|
|
|
2013-09-18 10:10:31 +08:00
|
|
|
static void parse_one_symref_info(struct string_list *symref, const char *val, int len)
|
|
|
|
{
|
|
|
|
char *sym, *target;
|
|
|
|
struct string_list_item *item;
|
|
|
|
|
|
|
|
if (!len)
|
|
|
|
return; /* just "symref" */
|
|
|
|
/* e.g. "symref=HEAD:refs/heads/master" */
|
|
|
|
sym = xmalloc(len + 1);
|
|
|
|
memcpy(sym, val, len);
|
|
|
|
sym[len] = '\0';
|
|
|
|
target = strchr(sym, ':');
|
|
|
|
if (!target)
|
|
|
|
/* just "symref=something" */
|
|
|
|
goto reject;
|
|
|
|
*(target++) = '\0';
|
|
|
|
if (check_refname_format(sym, REFNAME_ALLOW_ONELEVEL) ||
|
|
|
|
check_refname_format(target, REFNAME_ALLOW_ONELEVEL))
|
|
|
|
/* "symref=bogus:pair */
|
|
|
|
goto reject;
|
|
|
|
item = string_list_append(symref, sym);
|
|
|
|
item->util = target;
|
|
|
|
return;
|
|
|
|
reject:
|
|
|
|
free(sym);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void annotate_refs_with_symref_info(struct ref *ref)
|
|
|
|
{
|
|
|
|
struct string_list symref = STRING_LIST_INIT_DUP;
|
|
|
|
const char *feature_list = server_capabilities;
|
|
|
|
|
|
|
|
while (feature_list) {
|
|
|
|
int len;
|
|
|
|
const char *val;
|
|
|
|
|
|
|
|
val = parse_feature_value(feature_list, "symref", &len);
|
|
|
|
if (!val)
|
|
|
|
break;
|
|
|
|
parse_one_symref_info(&symref, val, len);
|
|
|
|
feature_list = val + 1;
|
|
|
|
}
|
|
|
|
sort_string_list(&symref);
|
|
|
|
|
|
|
|
for (; ref; ref = ref->next) {
|
|
|
|
struct string_list_item *item;
|
|
|
|
item = string_list_lookup(&symref, ref->name);
|
|
|
|
if (!item)
|
|
|
|
continue;
|
|
|
|
ref->symref = xstrdup((char *)item->util);
|
|
|
|
}
|
|
|
|
string_list_clear(&symref, 0);
|
|
|
|
}
|
|
|
|
|
2005-07-17 04:55:50 +08:00
|
|
|
/*
|
|
|
|
* Read all the refs from the other end
|
|
|
|
*/
|
2013-02-21 04:06:45 +08:00
|
|
|
struct ref **get_remote_heads(int in, char *src_buf, size_t src_len,
|
|
|
|
struct ref **list, unsigned int flags,
|
2013-12-05 21:02:33 +08:00
|
|
|
struct sha1_array *extra_have,
|
|
|
|
struct sha1_array *shallow_points)
|
2005-07-17 04:55:50 +08:00
|
|
|
{
|
2013-09-18 10:10:31 +08:00
|
|
|
struct ref **orig_list = list;
|
2012-06-20 02:24:50 +08:00
|
|
|
int got_at_least_one_head = 0;
|
|
|
|
|
2005-07-17 04:55:50 +08:00
|
|
|
*list = NULL;
|
|
|
|
for (;;) {
|
|
|
|
struct ref *ref;
|
|
|
|
unsigned char old_sha1[20];
|
|
|
|
char *name;
|
2005-10-28 10:48:54 +08:00
|
|
|
int len, name_len;
|
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 *buffer = packet_buffer;
|
2005-07-17 04:55:50 +08:00
|
|
|
|
2013-02-21 04:06:45 +08:00
|
|
|
len = packet_read(in, &src_buf, &src_len,
|
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
|
|
|
packet_buffer, sizeof(packet_buffer),
|
pkt-line: teach packet_read_line to chomp newlines
The packets sent during ref negotiation are all terminated
by newline; even though the code to chomp these newlines is
short, we end up doing it in a lot of places.
This patch teaches packet_read_line to auto-chomp the
trailing newline; this lets us get rid of a lot of inline
chomping code.
As a result, some call-sites which are not reading
line-oriented data (e.g., when reading chunks of packfiles
alongside sideband) transition away from packet_read_line to
the generic packet_read interface. This patch converts all
of the existing callsites.
Since the function signature of packet_read_line does not
change (but its behavior does), there is a possibility of
new callsites being introduced in later commits, silently
introducing an incompatibility. However, since a later
patch in this series will change the signature, such a
commit would have to be merged directly into this commit,
not to the tip of the series; we can therefore ignore the
issue.
This is an internal cleanup and should produce no change of
behavior in the normal case. However, there is one corner
case to note. Callers of packet_read_line have never been
able to tell the difference between a flush packet ("0000")
and an empty packet ("0004"), as both cause packet_read_line
to return a length of 0. Readers treat them identically,
even though Documentation/technical/protocol-common.txt says
we must not; it also says that implementations should not
send an empty pkt-line.
By stripping out the newline before the result gets to the
caller, we will now treat the newline-only packet ("0005\n")
the same as an empty packet, which in turn gets treated like
a flush packet. In practice this doesn't matter, as neither
empty nor newline-only packets are part of git's protocols
(at least not for the line-oriented bits, and readers who
are not expecting line-oriented packets will be calling
packet_read directly, anyway). But even if we do decide to
care about the distinction later, it is orthogonal to this
patch. The right place to tighten would be to stop treating
empty packets as flush packets, and this change does not
make doing so any harder.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-02-21 04:02:28 +08:00
|
|
|
PACKET_READ_GENTLE_ON_EOF |
|
|
|
|
PACKET_READ_CHOMP_NEWLINE);
|
2012-06-20 02:24:50 +08:00
|
|
|
if (len < 0)
|
|
|
|
die_initial_contact(got_at_least_one_head);
|
|
|
|
|
2005-07-17 04:55:50 +08:00
|
|
|
if (!len)
|
|
|
|
break;
|
|
|
|
|
2013-12-01 04:55:40 +08:00
|
|
|
if (len > 4 && starts_with(buffer, "ERR "))
|
2008-11-02 02:44:45 +08:00
|
|
|
die("remote error: %s", buffer + 4);
|
|
|
|
|
2014-01-18 04:21:14 +08:00
|
|
|
if (len == 48 && starts_with(buffer, "shallow ")) {
|
2013-12-05 21:02:33 +08:00
|
|
|
if (get_sha1_hex(buffer + 8, old_sha1))
|
|
|
|
die("protocol error: expected shallow sha-1, got '%s'", buffer + 8);
|
|
|
|
if (!shallow_points)
|
|
|
|
die("repository on the other end cannot be shallow");
|
|
|
|
sha1_array_append(shallow_points, old_sha1);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2005-07-17 04:55:50 +08:00
|
|
|
if (len < 42 || get_sha1_hex(buffer, old_sha1) || buffer[40] != ' ')
|
|
|
|
die("protocol error: expected sha/ref, got '%s'", buffer);
|
|
|
|
name = buffer + 41;
|
2005-10-14 09:57:40 +08:00
|
|
|
|
2005-10-28 10:48:54 +08:00
|
|
|
name_len = strlen(name);
|
|
|
|
if (len != name_len + 41) {
|
Avoid unnecessary "if-before-free" tests.
This change removes all obvious useless if-before-free tests.
E.g., it replaces code like this:
if (some_expression)
free (some_expression);
with the now-equivalent:
free (some_expression);
It is equivalent not just because POSIX has required free(NULL)
to work for a long time, but simply because it has worked for
so long that no reasonable porting target fails the test.
Here's some evidence from nearly 1.5 years ago:
http://www.winehq.org/pipermail/wine-patches/2006-October/031544.html
FYI, the change below was prepared by running the following:
git ls-files -z | xargs -0 \
perl -0x3b -pi -e \
's/\bif\s*\(\s*(\S+?)(?:\s*!=\s*NULL)?\s*\)\s+(free\s*\(\s*\1\s*\))/$2/s'
Note however, that it doesn't handle brace-enclosed blocks like
"if (x) { free (x); }". But that's ok, since there were none like
that in git sources.
Beware: if you do use the above snippet, note that it can
produce syntactically invalid C code. That happens when the
affected "if"-statement has a matching "else".
E.g., it would transform this
if (x)
free (x);
else
foo ();
into this:
free (x);
else
foo ();
There were none of those here, either.
If you're interested in automating detection of the useless
tests, you might like the useless-if-before-free script in gnulib:
[it *does* detect brace-enclosed free statements, and has a --name=S
option to make it detect free-like functions with different names]
http://git.sv.gnu.org/gitweb/?p=gnulib.git;a=blob;f=build-aux/useless-if-before-free
Addendum:
Remove one more (in imap-send.c), spotted by Jean-Luc Herren <jlh@gmx.ch>.
Signed-off-by: Jim Meyering <meyering@redhat.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-02-01 01:26:32 +08:00
|
|
|
free(server_capabilities);
|
2006-09-02 12:16:31 +08:00
|
|
|
server_capabilities = xstrdup(name + name_len + 1);
|
2005-10-28 10:48:54 +08:00
|
|
|
}
|
|
|
|
|
2008-09-09 16:27:09 +08:00
|
|
|
if (extra_have &&
|
|
|
|
name_len == 5 && !memcmp(".have", name, 5)) {
|
2013-12-05 21:02:29 +08:00
|
|
|
sha1_array_append(extra_have, old_sha1);
|
2008-09-09 16:27:09 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
Improve git-peek-remote
This makes git-peek-remote able to basically do everything that
git-ls-remote does (but obviously just for the native protocol, so no
http[s]: or rsync: support).
The default behaviour is the same, but you can now give a mixture of
"--refs", "--tags" and "--heads" flags, where "--refs" forces
git-peek-remote to only show real refs (ie none of the fakey tag lookups,
but also not the special pseudo-refs like HEAD and MERGE_HEAD).
The "--tags" and "--heads" flags respectively limit the output to just
regular tags and heads, of course.
You can still also ask to limit them by name too.
You can combine the flags, so
git peek-remote --refs --tags .
will show all local _true_ tags, without the generated tag lookups
(compare the output without the "--refs" flag).
And "--tags --heads" will show both tags and heads, but will avoid (for
example) any special refs outside of the standard locations.
I'm also planning on adding a "--ignore-local" flag that allows us to ask
it to ignore any refs that we already have in the local tree, but that's
an independent thing.
All this is obviously gearing up to making "git fetch" cheaper.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2006-07-05 03:29:10 +08:00
|
|
|
if (!check_ref(name, name_len, flags))
|
2005-12-26 15:18:37 +08:00
|
|
|
continue;
|
2008-10-18 16:44:18 +08:00
|
|
|
ref = alloc_ref(buffer + 41);
|
2006-08-23 14:49:00 +08:00
|
|
|
hashcpy(ref->old_sha1, old_sha1);
|
2005-07-17 04:55:50 +08:00
|
|
|
*list = ref;
|
|
|
|
list = &ref->next;
|
2012-06-20 02:24:50 +08:00
|
|
|
got_at_least_one_head = 1;
|
2005-07-17 04:55:50 +08:00
|
|
|
}
|
2013-09-18 10:10:31 +08:00
|
|
|
|
|
|
|
annotate_refs_with_symref_info(*orig_list);
|
|
|
|
|
2005-07-17 04:55:50 +08:00
|
|
|
return list;
|
|
|
|
}
|
|
|
|
|
2013-09-18 07:29:28 +08:00
|
|
|
static const char *parse_feature_value(const char *feature_list, const char *feature, int *lenp)
|
2012-01-09 05:06:19 +08:00
|
|
|
{
|
|
|
|
int len;
|
|
|
|
|
|
|
|
if (!feature_list)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
len = strlen(feature);
|
|
|
|
while (*feature_list) {
|
|
|
|
const char *found = strstr(feature_list, feature);
|
|
|
|
if (!found)
|
|
|
|
return NULL;
|
parse_feature_request: make it easier to see feature values
We already take care to parse key/value capabilities like
"foo=bar", but the code does not provide a good way of
actually finding out what is on the right-hand side of the
"=".
A server using "parse_feature_request" could accomplish this
with some extra parsing. You must skip past the "key"
portion manually, check for "=" versus NUL or space, and
then find the length by searching for the next space (or
NUL). But clients can't even do that, since the
"server_supports" interface does not even return the
pointer.
Instead, let's have our parser share more information by
providing a pointer to the value and its length. The
"parse_feature_value" function returns a pointer to the
feature's value portion, along with the length of the value.
If the feature is missing, NULL is returned. If it does not
have an "=", then a zero-length value is returned.
Similarly, "server_feature_value" behaves in the same way,
but always checks the static server_feature_list variable.
We can then implement "server_supports" in terms of
"server_feature_value". We cannot implement the original
"parse_feature_request" in terms of our new function,
because it returned a pointer to the beginning of the
feature. However, no callers actually cared about the value
of the returned pointer, so we can simplify it to a boolean
just as we do for "server_supports".
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2012-08-14 09:59:27 +08:00
|
|
|
if (feature_list == found || isspace(found[-1])) {
|
|
|
|
const char *value = found + len;
|
|
|
|
/* feature with no value (e.g., "thin-pack") */
|
|
|
|
if (!*value || isspace(*value)) {
|
|
|
|
if (lenp)
|
|
|
|
*lenp = 0;
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
/* feature with a value (e.g., "agent=git/1.2.3") */
|
|
|
|
else if (*value == '=') {
|
|
|
|
value++;
|
|
|
|
if (lenp)
|
|
|
|
*lenp = strcspn(value, " \t\n");
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* otherwise we matched a substring of another feature;
|
|
|
|
* keep looking
|
|
|
|
*/
|
|
|
|
}
|
2012-01-09 05:06:19 +08:00
|
|
|
feature_list = found + 1;
|
|
|
|
}
|
|
|
|
return NULL;
|
2005-10-28 10:48:54 +08:00
|
|
|
}
|
|
|
|
|
parse_feature_request: make it easier to see feature values
We already take care to parse key/value capabilities like
"foo=bar", but the code does not provide a good way of
actually finding out what is on the right-hand side of the
"=".
A server using "parse_feature_request" could accomplish this
with some extra parsing. You must skip past the "key"
portion manually, check for "=" versus NUL or space, and
then find the length by searching for the next space (or
NUL). But clients can't even do that, since the
"server_supports" interface does not even return the
pointer.
Instead, let's have our parser share more information by
providing a pointer to the value and its length. The
"parse_feature_value" function returns a pointer to the
feature's value portion, along with the length of the value.
If the feature is missing, NULL is returned. If it does not
have an "=", then a zero-length value is returned.
Similarly, "server_feature_value" behaves in the same way,
but always checks the static server_feature_list variable.
We can then implement "server_supports" in terms of
"server_feature_value". We cannot implement the original
"parse_feature_request" in terms of our new function,
because it returned a pointer to the beginning of the
feature. However, no callers actually cared about the value
of the returned pointer, so we can simplify it to a boolean
just as we do for "server_supports".
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2012-08-14 09:59:27 +08:00
|
|
|
int parse_feature_request(const char *feature_list, const char *feature)
|
|
|
|
{
|
|
|
|
return !!parse_feature_value(feature_list, feature, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *server_feature_value(const char *feature, int *len)
|
|
|
|
{
|
|
|
|
return parse_feature_value(server_capabilities, feature, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
int server_supports(const char *feature)
|
|
|
|
{
|
|
|
|
return !!server_feature_value(feature, NULL);
|
|
|
|
}
|
|
|
|
|
2005-07-14 09:46:20 +08:00
|
|
|
enum protocol {
|
|
|
|
PROTO_LOCAL = 1,
|
2013-11-29 03:50:03 +08:00
|
|
|
PROTO_FILE,
|
2005-07-14 09:46:20 +08:00
|
|
|
PROTO_SSH,
|
2010-05-14 17:31:35 +08:00
|
|
|
PROTO_GIT
|
2005-07-14 09:46:20 +08:00
|
|
|
};
|
|
|
|
|
2013-11-29 03:50:03 +08:00
|
|
|
int url_is_local_not_ssh(const char *url)
|
|
|
|
{
|
|
|
|
const char *colon = strchr(url, ':');
|
|
|
|
const char *slash = strchr(url, '/');
|
|
|
|
return !colon || (slash && slash < colon) ||
|
|
|
|
has_dos_drive_prefix(url);
|
|
|
|
}
|
|
|
|
|
2013-11-29 03:49:17 +08:00
|
|
|
static const char *prot_name(enum protocol protocol)
|
|
|
|
{
|
|
|
|
switch (protocol) {
|
|
|
|
case PROTO_LOCAL:
|
2013-11-29 03:50:03 +08:00
|
|
|
case PROTO_FILE:
|
2013-11-29 03:49:17 +08:00
|
|
|
return "file";
|
|
|
|
case PROTO_SSH:
|
|
|
|
return "ssh";
|
|
|
|
case PROTO_GIT:
|
|
|
|
return "git";
|
|
|
|
default:
|
|
|
|
return "unkown protocol";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-07-14 09:46:20 +08:00
|
|
|
static enum protocol get_protocol(const char *name)
|
|
|
|
{
|
|
|
|
if (!strcmp(name, "ssh"))
|
|
|
|
return PROTO_SSH;
|
|
|
|
if (!strcmp(name, "git"))
|
|
|
|
return PROTO_GIT;
|
2005-10-15 08:14:56 +08:00
|
|
|
if (!strcmp(name, "git+ssh"))
|
|
|
|
return PROTO_SSH;
|
|
|
|
if (!strcmp(name, "ssh+git"))
|
|
|
|
return PROTO_SSH;
|
2007-08-02 01:03:37 +08:00
|
|
|
if (!strcmp(name, "file"))
|
2013-11-29 03:50:03 +08:00
|
|
|
return PROTO_FILE;
|
2005-07-14 09:46:20 +08:00
|
|
|
die("I don't handle protocol '%s'", name);
|
|
|
|
}
|
|
|
|
|
2005-07-21 21:10:36 +08:00
|
|
|
#define STR_(s) # s
|
|
|
|
#define STR(s) STR_(s)
|
2005-07-14 09:46:20 +08:00
|
|
|
|
2010-02-18 04:56:02 +08:00
|
|
|
static void get_host_and_port(char **host, const char **port)
|
|
|
|
{
|
|
|
|
char *colon, *end;
|
|
|
|
|
|
|
|
if (*host[0] == '[') {
|
|
|
|
end = strchr(*host + 1, ']');
|
|
|
|
if (end) {
|
|
|
|
*end = 0;
|
|
|
|
end++;
|
|
|
|
(*host)++;
|
|
|
|
} else
|
|
|
|
end = *host;
|
|
|
|
} else
|
|
|
|
end = *host;
|
|
|
|
colon = strchr(end, ':');
|
|
|
|
|
|
|
|
if (colon) {
|
|
|
|
*colon = 0;
|
|
|
|
*port = colon + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-12-06 12:39:36 +08:00
|
|
|
static void enable_keepalive(int sockfd)
|
|
|
|
{
|
|
|
|
int ka = 1;
|
|
|
|
|
|
|
|
if (setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &ka, sizeof(ka)) < 0)
|
|
|
|
fprintf(stderr, "unable to set SO_KEEPALIVE on socket: %s\n",
|
|
|
|
strerror(errno));
|
|
|
|
}
|
|
|
|
|
2005-09-29 07:52:21 +08:00
|
|
|
#ifndef NO_IPV6
|
2005-09-29 07:37:58 +08:00
|
|
|
|
2007-05-24 05:34:27 +08:00
|
|
|
static const char *ai_name(const struct addrinfo *ai)
|
|
|
|
{
|
2009-04-24 20:16:41 +08:00
|
|
|
static char addr[NI_MAXHOST];
|
|
|
|
if (getnameinfo(ai->ai_addr, ai->ai_addrlen, addr, sizeof(addr), NULL, 0,
|
|
|
|
NI_NUMERICHOST) != 0)
|
2007-05-24 05:34:27 +08:00
|
|
|
strcpy(addr, "(unknown)");
|
2009-04-24 20:16:41 +08:00
|
|
|
|
2007-05-24 05:34:27 +08:00
|
|
|
return addr;
|
|
|
|
}
|
|
|
|
|
2006-06-07 11:58:41 +08:00
|
|
|
/*
|
|
|
|
* Returns a connected socket() fd, or else die()s.
|
|
|
|
*/
|
2007-05-17 01:09:41 +08:00
|
|
|
static int git_tcp_connect_sock(char *host, int flags)
|
2005-07-14 09:46:20 +08:00
|
|
|
{
|
2011-07-13 00:28:34 +08:00
|
|
|
struct strbuf error_message = STRBUF_INIT;
|
|
|
|
int sockfd = -1;
|
2006-06-28 17:04:39 +08:00
|
|
|
const char *port = STR(DEFAULT_GIT_PORT);
|
2005-07-21 21:10:36 +08:00
|
|
|
struct addrinfo hints, *ai0, *ai;
|
|
|
|
int gai;
|
2007-05-24 05:34:27 +08:00
|
|
|
int cnt = 0;
|
2005-07-21 21:10:36 +08:00
|
|
|
|
2010-02-18 04:56:02 +08:00
|
|
|
get_host_and_port(&host, &port);
|
|
|
|
if (!*port)
|
|
|
|
port = "<none>";
|
2005-07-21 21:10:36 +08:00
|
|
|
|
|
|
|
memset(&hints, 0, sizeof(hints));
|
|
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
|
|
hints.ai_protocol = IPPROTO_TCP;
|
|
|
|
|
2007-05-17 01:09:41 +08:00
|
|
|
if (flags & CONNECT_VERBOSE)
|
|
|
|
fprintf(stderr, "Looking up %s ... ", host);
|
|
|
|
|
2005-07-21 21:10:36 +08:00
|
|
|
gai = getaddrinfo(host, port, &hints, &ai);
|
|
|
|
if (gai)
|
Fix "getaddrinfo()" buglet
At least in Linux glibc, "getaddrinfo()" has a very irritating feature (or
bug, who knows..).
Namely if you pass it in an empty string for the service name, it will
happily and quietly consider it identical to a NULL port pointer, and
return port number zero and no errors. Which obviously will not work.
Maybe that's what it's really expected to do, although the man-page for
getaddrinfo() certainly implies that it's a bug.
So when somebody passes me a "please pull" request pointing to something
like the following
git://git.kernel.org:/pub/scm/linux/kernel/git/mchehab/v4l-dvb.git
(note the extraneous colon at the end of the host name), git would happily
try to connect to port 0, which would generally just cause the remote to
not even answer, and the "connect()" will take a long time to time out.
So to work around the glibc feature/bug, just notice this empty port case
automatically. Also, add the port information to the error information
when it fails to look up (maybe it's the host-name that fails, maybe it's
the port-name - we should print out both).
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-03-28 00:50:20 +08:00
|
|
|
die("Unable to look up %s (port %s) (%s)", host, port, gai_strerror(gai));
|
2005-07-21 21:10:36 +08:00
|
|
|
|
2007-05-17 01:09:41 +08:00
|
|
|
if (flags & CONNECT_VERBOSE)
|
|
|
|
fprintf(stderr, "done.\nConnecting to %s (port %s) ... ", host, port);
|
|
|
|
|
2011-08-01 19:16:09 +08:00
|
|
|
for (ai0 = ai; ai; ai = ai->ai_next, cnt++) {
|
2006-06-07 11:58:41 +08:00
|
|
|
sockfd = socket(ai->ai_family,
|
|
|
|
ai->ai_socktype, ai->ai_protocol);
|
2011-07-13 00:28:34 +08:00
|
|
|
if ((sockfd < 0) ||
|
|
|
|
(connect(sockfd, ai->ai_addr, ai->ai_addrlen) < 0)) {
|
|
|
|
strbuf_addf(&error_message, "%s[%d: %s]: errno=%s\n",
|
|
|
|
host, cnt, ai_name(ai), strerror(errno));
|
|
|
|
if (0 <= sockfd)
|
|
|
|
close(sockfd);
|
2005-07-21 21:10:36 +08:00
|
|
|
sockfd = -1;
|
|
|
|
continue;
|
2005-07-14 09:46:20 +08:00
|
|
|
}
|
2007-05-24 05:34:27 +08:00
|
|
|
if (flags & CONNECT_VERBOSE)
|
|
|
|
fprintf(stderr, "%s ", ai_name(ai));
|
2005-07-21 21:10:36 +08:00
|
|
|
break;
|
2005-07-14 09:46:20 +08:00
|
|
|
}
|
|
|
|
|
2005-07-21 21:10:36 +08:00
|
|
|
freeaddrinfo(ai0);
|
2005-07-14 09:46:20 +08:00
|
|
|
|
|
|
|
if (sockfd < 0)
|
2011-07-13 00:28:34 +08:00
|
|
|
die("unable to connect to %s:\n%s", host, error_message.buf);
|
2005-07-21 21:10:36 +08:00
|
|
|
|
2011-12-06 12:39:36 +08:00
|
|
|
enable_keepalive(sockfd);
|
|
|
|
|
2007-05-17 01:09:41 +08:00
|
|
|
if (flags & CONNECT_VERBOSE)
|
|
|
|
fprintf(stderr, "done.\n");
|
|
|
|
|
2011-07-13 00:28:34 +08:00
|
|
|
strbuf_release(&error_message);
|
|
|
|
|
2006-06-07 11:58:41 +08:00
|
|
|
return sockfd;
|
2005-07-14 09:46:20 +08:00
|
|
|
}
|
|
|
|
|
2005-09-29 07:52:21 +08:00
|
|
|
#else /* NO_IPV6 */
|
2005-09-29 07:37:58 +08:00
|
|
|
|
2006-06-07 11:58:41 +08:00
|
|
|
/*
|
|
|
|
* Returns a connected socket() fd, or else die()s.
|
|
|
|
*/
|
2007-05-17 01:09:41 +08:00
|
|
|
static int git_tcp_connect_sock(char *host, int flags)
|
2005-09-29 07:37:58 +08:00
|
|
|
{
|
2011-08-01 19:16:10 +08:00
|
|
|
struct strbuf error_message = STRBUF_INIT;
|
|
|
|
int sockfd = -1;
|
2010-02-18 04:56:02 +08:00
|
|
|
const char *port = STR(DEFAULT_GIT_PORT);
|
|
|
|
char *ep;
|
2005-09-29 07:37:58 +08:00
|
|
|
struct hostent *he;
|
|
|
|
struct sockaddr_in sa;
|
|
|
|
char **ap;
|
|
|
|
unsigned int nport;
|
2007-05-24 05:34:27 +08:00
|
|
|
int cnt;
|
2005-09-29 07:37:58 +08:00
|
|
|
|
2010-02-18 04:56:02 +08:00
|
|
|
get_host_and_port(&host, &port);
|
2005-09-29 07:37:58 +08:00
|
|
|
|
2007-05-17 01:09:41 +08:00
|
|
|
if (flags & CONNECT_VERBOSE)
|
|
|
|
fprintf(stderr, "Looking up %s ... ", host);
|
|
|
|
|
2005-09-29 07:37:58 +08:00
|
|
|
he = gethostbyname(host);
|
|
|
|
if (!he)
|
|
|
|
die("Unable to look up %s (%s)", host, hstrerror(h_errno));
|
|
|
|
nport = strtoul(port, &ep, 10);
|
|
|
|
if ( ep == port || *ep ) {
|
|
|
|
/* Not numeric */
|
|
|
|
struct servent *se = getservbyname(port,"tcp");
|
|
|
|
if ( !se )
|
2009-01-05 02:38:41 +08:00
|
|
|
die("Unknown port %s", port);
|
2005-09-29 07:37:58 +08:00
|
|
|
nport = se->s_port;
|
|
|
|
}
|
|
|
|
|
2007-05-17 01:09:41 +08:00
|
|
|
if (flags & CONNECT_VERBOSE)
|
|
|
|
fprintf(stderr, "done.\nConnecting to %s (port %s) ... ", host, port);
|
|
|
|
|
2007-05-24 05:34:27 +08:00
|
|
|
for (cnt = 0, ap = he->h_addr_list; *ap; ap++, cnt++) {
|
2005-09-29 07:37:58 +08:00
|
|
|
memset(&sa, 0, sizeof sa);
|
|
|
|
sa.sin_family = he->h_addrtype;
|
2005-09-29 08:26:44 +08:00
|
|
|
sa.sin_port = htons(nport);
|
2005-11-22 21:54:23 +08:00
|
|
|
memcpy(&sa.sin_addr, *ap, he->h_length);
|
2005-09-29 07:37:58 +08:00
|
|
|
|
2011-08-01 19:16:10 +08:00
|
|
|
sockfd = socket(he->h_addrtype, SOCK_STREAM, 0);
|
|
|
|
if ((sockfd < 0) ||
|
|
|
|
connect(sockfd, (struct sockaddr *)&sa, sizeof sa) < 0) {
|
|
|
|
strbuf_addf(&error_message, "%s[%d: %s]: errno=%s\n",
|
2007-05-24 05:34:27 +08:00
|
|
|
host,
|
|
|
|
cnt,
|
|
|
|
inet_ntoa(*(struct in_addr *)&sa.sin_addr),
|
2011-08-01 19:16:10 +08:00
|
|
|
strerror(errno));
|
|
|
|
if (0 <= sockfd)
|
|
|
|
close(sockfd);
|
2005-09-29 07:37:58 +08:00
|
|
|
sockfd = -1;
|
|
|
|
continue;
|
|
|
|
}
|
2007-05-24 05:34:27 +08:00
|
|
|
if (flags & CONNECT_VERBOSE)
|
|
|
|
fprintf(stderr, "%s ",
|
|
|
|
inet_ntoa(*(struct in_addr *)&sa.sin_addr));
|
2005-09-29 07:37:58 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sockfd < 0)
|
2011-08-01 19:16:10 +08:00
|
|
|
die("unable to connect to %s:\n%s", host, error_message.buf);
|
2005-09-29 07:37:58 +08:00
|
|
|
|
2011-12-06 12:39:36 +08:00
|
|
|
enable_keepalive(sockfd);
|
|
|
|
|
2007-05-17 01:09:41 +08:00
|
|
|
if (flags & CONNECT_VERBOSE)
|
|
|
|
fprintf(stderr, "done.\n");
|
|
|
|
|
2006-06-07 11:58:41 +08:00
|
|
|
return sockfd;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* NO_IPV6 */
|
|
|
|
|
|
|
|
|
2007-05-17 01:09:41 +08:00
|
|
|
static void git_tcp_connect(int fd[2], char *host, int flags)
|
2006-06-07 11:58:41 +08:00
|
|
|
{
|
2007-05-17 01:09:41 +08:00
|
|
|
int sockfd = git_tcp_connect_sock(host, flags);
|
2006-06-07 11:58:41 +08:00
|
|
|
|
2005-09-29 07:37:58 +08:00
|
|
|
fd[0] = sockfd;
|
2007-01-22 09:10:51 +08:00
|
|
|
fd[1] = dup(sockfd);
|
2005-09-29 07:37:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-08-16 01:23:48 +08:00
|
|
|
static char *git_proxy_command;
|
2005-11-04 22:57:16 +08:00
|
|
|
|
2008-05-15 01:46:53 +08:00
|
|
|
static int git_proxy_command_options(const char *var, const char *value,
|
|
|
|
void *cb)
|
2005-11-04 22:57:16 +08:00
|
|
|
{
|
2005-11-19 19:48:56 +08:00
|
|
|
if (!strcmp(var, "core.gitproxy")) {
|
2005-11-22 11:18:23 +08:00
|
|
|
const char *for_pos;
|
|
|
|
int matchlen = -1;
|
|
|
|
int hostlen;
|
2009-03-11 10:38:12 +08:00
|
|
|
const char *rhost_name = cb;
|
|
|
|
int rhost_len = strlen(rhost_name);
|
2005-11-22 11:18:23 +08:00
|
|
|
|
2005-11-19 19:48:56 +08:00
|
|
|
if (git_proxy_command)
|
2005-11-04 22:57:16 +08:00
|
|
|
return 0;
|
2008-02-12 02:52:15 +08:00
|
|
|
if (!value)
|
|
|
|
return config_error_nonbool(var);
|
2005-11-19 19:48:56 +08:00
|
|
|
/* [core]
|
|
|
|
* ;# matches www.kernel.org as well
|
|
|
|
* gitproxy = netcatter-1 for kernel.org
|
|
|
|
* gitproxy = netcatter-2 for sample.xz
|
|
|
|
* gitproxy = netcatter-default
|
|
|
|
*/
|
2005-11-22 11:18:23 +08:00
|
|
|
for_pos = strstr(value, " for ");
|
2005-11-19 19:48:56 +08:00
|
|
|
if (!for_pos)
|
|
|
|
/* matches everybody */
|
|
|
|
matchlen = strlen(value);
|
|
|
|
else {
|
|
|
|
hostlen = strlen(for_pos + 5);
|
|
|
|
if (rhost_len < hostlen)
|
|
|
|
matchlen = -1;
|
|
|
|
else if (!strncmp(for_pos + 5,
|
|
|
|
rhost_name + rhost_len - hostlen,
|
|
|
|
hostlen) &&
|
|
|
|
((rhost_len == hostlen) ||
|
|
|
|
rhost_name[rhost_len - hostlen -1] == '.'))
|
|
|
|
matchlen = for_pos - value;
|
|
|
|
else
|
|
|
|
matchlen = -1;
|
|
|
|
}
|
|
|
|
if (0 <= matchlen) {
|
|
|
|
/* core.gitproxy = none for kernel.org */
|
2007-06-07 15:04:01 +08:00
|
|
|
if (matchlen == 4 &&
|
2005-11-19 19:48:56 +08:00
|
|
|
!memcmp(value, "none", 4))
|
|
|
|
matchlen = 0;
|
2007-09-16 06:32:36 +08:00
|
|
|
git_proxy_command = xmemdupz(value, matchlen);
|
2005-11-04 22:57:16 +08:00
|
|
|
}
|
2005-11-19 19:48:56 +08:00
|
|
|
return 0;
|
2005-11-04 22:57:16 +08:00
|
|
|
}
|
|
|
|
|
2008-05-15 01:46:53 +08:00
|
|
|
return git_default_config(var, value, cb);
|
2005-11-04 22:57:16 +08:00
|
|
|
}
|
|
|
|
|
2005-11-19 19:48:56 +08:00
|
|
|
static int git_use_proxy(const char *host)
|
2005-11-04 22:57:16 +08:00
|
|
|
{
|
|
|
|
git_proxy_command = getenv("GIT_PROXY_COMMAND");
|
2009-03-11 10:38:12 +08:00
|
|
|
git_config(git_proxy_command_options, (void*)host);
|
2005-11-19 19:48:56 +08:00
|
|
|
return (git_proxy_command && *git_proxy_command);
|
2005-11-04 22:57:16 +08:00
|
|
|
}
|
|
|
|
|
connect: treat generic proxy processes like ssh processes
The git_connect function returns two ends of a pipe for
talking with a remote, plus a struct child_process
representing the other end of the pipe. If we have a direct
socket connection, then this points to a special "no_fork"
child process.
The code path for doing git-over-pipes or git-over-ssh sets
up this child process to point to the child git command or
the ssh process. When we call finish_connect eventually, we
check wait() on the command and report its return value.
The code path for git://, on the other hand, always sets it
to no_fork. In the case of a direct TCP connection, this
makes sense; we have no child process. But in the case of a
proxy command (configured by core.gitproxy), we do have a
child process, but we throw away its pid, and therefore
ignore its return code.
Instead, let's keep that information in the proxy case, and
respect its return code, which can help catch some errors
(though depending on your proxy command, it will be errors
reported by the proxy command itself, and not propagated
from git commands. Still, it is probably better to propagate
such errors than to ignore them).
It also means that the child_process field can reliably be
used to determine whether the returned descriptors are
actually a full-duplex socket, which means we should be
using shutdown() instead of a simple close.
Signed-off-by: Jeff King <peff@peff.net>
Helped-by: Johannes Sixt <j6t@kdbg.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2011-05-16 14:46:07 +08:00
|
|
|
static struct child_process *git_proxy_connect(int fd[2], char *host)
|
2005-11-04 22:57:16 +08:00
|
|
|
{
|
2006-06-28 17:04:39 +08:00
|
|
|
const char *port = STR(DEFAULT_GIT_PORT);
|
connect: treat generic proxy processes like ssh processes
The git_connect function returns two ends of a pipe for
talking with a remote, plus a struct child_process
representing the other end of the pipe. If we have a direct
socket connection, then this points to a special "no_fork"
child process.
The code path for doing git-over-pipes or git-over-ssh sets
up this child process to point to the child git command or
the ssh process. When we call finish_connect eventually, we
check wait() on the command and report its return value.
The code path for git://, on the other hand, always sets it
to no_fork. In the case of a direct TCP connection, this
makes sense; we have no child process. But in the case of a
proxy command (configured by core.gitproxy), we do have a
child process, but we throw away its pid, and therefore
ignore its return code.
Instead, let's keep that information in the proxy case, and
respect its return code, which can help catch some errors
(though depending on your proxy command, it will be errors
reported by the proxy command itself, and not propagated
from git commands. Still, it is probably better to propagate
such errors than to ignore them).
It also means that the child_process field can reliably be
used to determine whether the returned descriptors are
actually a full-duplex socket, which means we should be
using shutdown() instead of a simple close.
Signed-off-by: Jeff King <peff@peff.net>
Helped-by: Johannes Sixt <j6t@kdbg.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2011-05-16 14:46:07 +08:00
|
|
|
const char **argv;
|
|
|
|
struct child_process *proxy;
|
2005-11-04 22:57:16 +08:00
|
|
|
|
2010-02-18 04:56:02 +08:00
|
|
|
get_host_and_port(&host, &port);
|
2005-11-04 22:57:16 +08:00
|
|
|
|
connect: treat generic proxy processes like ssh processes
The git_connect function returns two ends of a pipe for
talking with a remote, plus a struct child_process
representing the other end of the pipe. If we have a direct
socket connection, then this points to a special "no_fork"
child process.
The code path for doing git-over-pipes or git-over-ssh sets
up this child process to point to the child git command or
the ssh process. When we call finish_connect eventually, we
check wait() on the command and report its return value.
The code path for git://, on the other hand, always sets it
to no_fork. In the case of a direct TCP connection, this
makes sense; we have no child process. But in the case of a
proxy command (configured by core.gitproxy), we do have a
child process, but we throw away its pid, and therefore
ignore its return code.
Instead, let's keep that information in the proxy case, and
respect its return code, which can help catch some errors
(though depending on your proxy command, it will be errors
reported by the proxy command itself, and not propagated
from git commands. Still, it is probably better to propagate
such errors than to ignore them).
It also means that the child_process field can reliably be
used to determine whether the returned descriptors are
actually a full-duplex socket, which means we should be
using shutdown() instead of a simple close.
Signed-off-by: Jeff King <peff@peff.net>
Helped-by: Johannes Sixt <j6t@kdbg.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2011-05-16 14:46:07 +08:00
|
|
|
argv = xmalloc(sizeof(*argv) * 4);
|
2007-03-13 07:00:19 +08:00
|
|
|
argv[0] = git_proxy_command;
|
|
|
|
argv[1] = host;
|
|
|
|
argv[2] = port;
|
|
|
|
argv[3] = NULL;
|
connect: treat generic proxy processes like ssh processes
The git_connect function returns two ends of a pipe for
talking with a remote, plus a struct child_process
representing the other end of the pipe. If we have a direct
socket connection, then this points to a special "no_fork"
child process.
The code path for doing git-over-pipes or git-over-ssh sets
up this child process to point to the child git command or
the ssh process. When we call finish_connect eventually, we
check wait() on the command and report its return value.
The code path for git://, on the other hand, always sets it
to no_fork. In the case of a direct TCP connection, this
makes sense; we have no child process. But in the case of a
proxy command (configured by core.gitproxy), we do have a
child process, but we throw away its pid, and therefore
ignore its return code.
Instead, let's keep that information in the proxy case, and
respect its return code, which can help catch some errors
(though depending on your proxy command, it will be errors
reported by the proxy command itself, and not propagated
from git commands. Still, it is probably better to propagate
such errors than to ignore them).
It also means that the child_process field can reliably be
used to determine whether the returned descriptors are
actually a full-duplex socket, which means we should be
using shutdown() instead of a simple close.
Signed-off-by: Jeff King <peff@peff.net>
Helped-by: Johannes Sixt <j6t@kdbg.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2011-05-16 14:46:07 +08:00
|
|
|
proxy = xcalloc(1, sizeof(*proxy));
|
|
|
|
proxy->argv = argv;
|
|
|
|
proxy->in = -1;
|
|
|
|
proxy->out = -1;
|
|
|
|
if (start_command(proxy))
|
2007-03-13 07:00:19 +08:00
|
|
|
die("cannot start proxy %s", argv[0]);
|
connect: treat generic proxy processes like ssh processes
The git_connect function returns two ends of a pipe for
talking with a remote, plus a struct child_process
representing the other end of the pipe. If we have a direct
socket connection, then this points to a special "no_fork"
child process.
The code path for doing git-over-pipes or git-over-ssh sets
up this child process to point to the child git command or
the ssh process. When we call finish_connect eventually, we
check wait() on the command and report its return value.
The code path for git://, on the other hand, always sets it
to no_fork. In the case of a direct TCP connection, this
makes sense; we have no child process. But in the case of a
proxy command (configured by core.gitproxy), we do have a
child process, but we throw away its pid, and therefore
ignore its return code.
Instead, let's keep that information in the proxy case, and
respect its return code, which can help catch some errors
(though depending on your proxy command, it will be errors
reported by the proxy command itself, and not propagated
from git commands. Still, it is probably better to propagate
such errors than to ignore them).
It also means that the child_process field can reliably be
used to determine whether the returned descriptors are
actually a full-duplex socket, which means we should be
using shutdown() instead of a simple close.
Signed-off-by: Jeff King <peff@peff.net>
Helped-by: Johannes Sixt <j6t@kdbg.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2011-05-16 14:46:07 +08:00
|
|
|
fd[0] = proxy->out; /* read from proxy stdout */
|
|
|
|
fd[1] = proxy->in; /* write to proxy stdin */
|
|
|
|
return proxy;
|
2005-11-04 22:57:16 +08:00
|
|
|
}
|
|
|
|
|
2013-11-29 03:49:54 +08:00
|
|
|
static const char *get_port_numeric(const char *p)
|
2007-09-01 17:36:31 +08:00
|
|
|
{
|
|
|
|
char *end;
|
|
|
|
if (p) {
|
2008-12-21 09:12:11 +08:00
|
|
|
long port = strtol(p + 1, &end, 10);
|
|
|
|
if (end != p + 1 && *end == '\0' && 0 <= port && port < 65536) {
|
2013-11-29 03:49:54 +08:00
|
|
|
return p;
|
2007-09-01 17:36:31 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2005-07-05 02:57:58 +08:00
|
|
|
/*
|
2013-11-29 03:49:01 +08:00
|
|
|
* Extract protocol and relevant parts from the specified connection URL.
|
|
|
|
* The caller must free() the returned strings.
|
2005-07-05 02:57:58 +08:00
|
|
|
*/
|
2013-11-29 03:49:01 +08:00
|
|
|
static enum protocol parse_connect_url(const char *url_orig, char **ret_host,
|
2013-11-29 03:49:54 +08:00
|
|
|
char **ret_path)
|
2005-07-05 02:57:58 +08:00
|
|
|
{
|
2010-05-23 17:19:44 +08:00
|
|
|
char *url;
|
2009-03-13 20:51:33 +08:00
|
|
|
char *host, *path;
|
2005-12-21 18:23:42 +08:00
|
|
|
char *end;
|
2013-11-29 03:50:03 +08:00
|
|
|
int separator = '/';
|
2005-11-18 03:37:14 +08:00
|
|
|
enum protocol protocol = PROTO_LOCAL;
|
2006-06-20 09:25:21 +08:00
|
|
|
|
2010-05-23 17:19:44 +08:00
|
|
|
if (is_url(url_orig))
|
|
|
|
url = url_decode(url_orig);
|
|
|
|
else
|
|
|
|
url = xstrdup(url_orig);
|
|
|
|
|
2005-11-18 03:37:14 +08:00
|
|
|
host = strstr(url, "://");
|
2009-09-01 13:35:10 +08:00
|
|
|
if (host) {
|
2005-11-18 03:37:14 +08:00
|
|
|
*host = '\0';
|
|
|
|
protocol = get_protocol(url);
|
|
|
|
host += 3;
|
2005-12-21 18:23:42 +08:00
|
|
|
} else {
|
2005-07-05 02:57:58 +08:00
|
|
|
host = url;
|
2013-11-29 03:50:03 +08:00
|
|
|
if (!url_is_local_not_ssh(url)) {
|
|
|
|
protocol = PROTO_SSH;
|
|
|
|
separator = ':';
|
|
|
|
}
|
2005-12-21 18:23:42 +08:00
|
|
|
}
|
|
|
|
|
2010-01-27 02:24:42 +08:00
|
|
|
/*
|
2013-11-29 03:49:54 +08:00
|
|
|
* Don't do destructive transforms as protocol code does
|
|
|
|
* '[]' unwrapping in get_host_and_port()
|
2010-01-27 02:24:42 +08:00
|
|
|
*/
|
2005-12-21 18:23:42 +08:00
|
|
|
if (host[0] == '[') {
|
|
|
|
end = strchr(host + 1, ']');
|
|
|
|
if (end) {
|
|
|
|
end++;
|
|
|
|
} else
|
|
|
|
end = host;
|
|
|
|
} else
|
|
|
|
end = host;
|
|
|
|
|
2013-11-29 03:50:03 +08:00
|
|
|
if (protocol == PROTO_LOCAL)
|
2007-08-02 01:03:37 +08:00
|
|
|
path = end;
|
2013-11-29 03:50:03 +08:00
|
|
|
else if (protocol == PROTO_FILE && has_dos_drive_prefix(end))
|
|
|
|
path = end; /* "file://$(pwd)" may be "file://C:/projects/repo" */
|
|
|
|
else
|
|
|
|
path = strchr(end, separator);
|
2005-07-14 09:46:20 +08:00
|
|
|
|
2005-11-18 03:37:14 +08:00
|
|
|
if (!path || !*path)
|
|
|
|
die("No path specified. See 'man git-pull' for valid url syntax");
|
|
|
|
|
|
|
|
/*
|
|
|
|
* null-terminate hostname and point path to ~ for URL's like this:
|
|
|
|
* ssh://host.xz/~user/repo
|
|
|
|
*/
|
2013-11-29 03:50:03 +08:00
|
|
|
|
|
|
|
end = path; /* Need to \0 terminate host here */
|
|
|
|
if (separator == ':')
|
|
|
|
path++; /* path starts after ':' */
|
|
|
|
if (protocol == PROTO_GIT || protocol == PROTO_SSH) {
|
2005-11-18 03:37:14 +08:00
|
|
|
if (path[1] == '~')
|
|
|
|
path++;
|
|
|
|
}
|
|
|
|
|
2013-11-29 03:50:03 +08:00
|
|
|
path = xstrdup(path);
|
|
|
|
*end = '\0';
|
|
|
|
|
2013-11-29 03:49:01 +08:00
|
|
|
*ret_host = xstrdup(host);
|
2013-11-29 03:50:03 +08:00
|
|
|
*ret_path = path;
|
2013-11-29 03:49:01 +08:00
|
|
|
free(url);
|
|
|
|
return protocol;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct child_process no_fork;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This returns a dummy child_process if the transport protocol does not
|
|
|
|
* need fork(2), or a struct child_process object if it does. Once done,
|
|
|
|
* finish the connection with finish_connect() with the value returned from
|
|
|
|
* this function (it is safe to call finish_connect() with NULL to support
|
|
|
|
* the former case).
|
|
|
|
*
|
|
|
|
* If it returns, the connect is successful; it just dies on errors (this
|
|
|
|
* will hopefully be changed in a libification effort, to return NULL when
|
|
|
|
* the connection failed).
|
|
|
|
*/
|
|
|
|
struct child_process *git_connect(int fd[2], const char *url,
|
|
|
|
const char *prog, int flags)
|
|
|
|
{
|
2013-11-29 03:50:15 +08:00
|
|
|
char *hostandport, *path;
|
2013-11-29 03:49:01 +08:00
|
|
|
struct child_process *conn = &no_fork;
|
|
|
|
enum protocol protocol;
|
|
|
|
const char **arg;
|
|
|
|
struct strbuf cmd = STRBUF_INIT;
|
|
|
|
|
|
|
|
/* Without this we cannot rely on waitpid() to tell
|
|
|
|
* what happened to our children.
|
2007-09-01 17:36:31 +08:00
|
|
|
*/
|
2013-11-29 03:49:01 +08:00
|
|
|
signal(SIGCHLD, SIG_DFL);
|
2007-09-01 17:36:31 +08:00
|
|
|
|
2013-11-29 03:50:15 +08:00
|
|
|
protocol = parse_connect_url(url, &hostandport, &path);
|
2013-11-29 03:49:17 +08:00
|
|
|
if (flags & CONNECT_DIAG_URL) {
|
|
|
|
printf("Diag: url=%s\n", url ? url : "NULL");
|
|
|
|
printf("Diag: protocol=%s\n", prot_name(protocol));
|
2013-11-29 03:50:15 +08:00
|
|
|
printf("Diag: hostandport=%s\n", hostandport ? hostandport : "NULL");
|
2013-11-29 03:49:17 +08:00
|
|
|
printf("Diag: path=%s\n", path ? path : "NULL");
|
2013-11-29 03:50:15 +08:00
|
|
|
conn = NULL;
|
|
|
|
} else if (protocol == PROTO_GIT) {
|
2006-06-07 11:58:41 +08:00
|
|
|
/* These underlying connection commands die() if they
|
|
|
|
* cannot connect.
|
|
|
|
*/
|
2013-11-29 03:50:15 +08:00
|
|
|
char *target_host = xstrdup(hostandport);
|
|
|
|
if (git_use_proxy(hostandport))
|
|
|
|
conn = git_proxy_connect(fd, hostandport);
|
2006-04-17 23:14:47 +08:00
|
|
|
else
|
2013-11-29 03:50:15 +08:00
|
|
|
git_tcp_connect(fd, hostandport, flags);
|
2006-06-07 11:58:41 +08:00
|
|
|
/*
|
|
|
|
* Separate original protocol components prog and path
|
2009-06-05 09:33:32 +08:00
|
|
|
* from extended host header with a NUL byte.
|
|
|
|
*
|
|
|
|
* Note: Do not add any other headers here! Doing so
|
|
|
|
* will cause older git-daemon servers to crash.
|
2006-06-07 11:58:41 +08:00
|
|
|
*/
|
|
|
|
packet_write(fd[1],
|
|
|
|
"%s %s%chost=%s%c",
|
|
|
|
prog, path, 0,
|
|
|
|
target_host, 0);
|
|
|
|
free(target_host);
|
2013-11-29 03:50:15 +08:00
|
|
|
} else {
|
|
|
|
conn = xcalloc(1, sizeof(*conn));
|
|
|
|
|
|
|
|
strbuf_addstr(&cmd, prog);
|
|
|
|
strbuf_addch(&cmd, ' ');
|
|
|
|
sq_quote_buf(&cmd, path);
|
|
|
|
|
|
|
|
conn->in = conn->out = -1;
|
|
|
|
conn->argv = arg = xcalloc(7, sizeof(*arg));
|
|
|
|
if (protocol == PROTO_SSH) {
|
|
|
|
const char *ssh = getenv("GIT_SSH");
|
|
|
|
int putty = ssh && strcasestr(ssh, "plink");
|
|
|
|
char *ssh_host = hostandport;
|
|
|
|
const char *port = NULL;
|
|
|
|
get_host_and_port(&ssh_host, &port);
|
|
|
|
port = get_port_numeric(port);
|
|
|
|
|
|
|
|
if (!ssh) ssh = "ssh";
|
|
|
|
|
|
|
|
*arg++ = ssh;
|
|
|
|
if (putty && !strcasestr(ssh, "tortoiseplink"))
|
|
|
|
*arg++ = "-batch";
|
|
|
|
if (port) {
|
|
|
|
/* P is for PuTTY, p is for OpenSSH */
|
|
|
|
*arg++ = putty ? "-P" : "-p";
|
|
|
|
*arg++ = port;
|
|
|
|
}
|
|
|
|
*arg++ = ssh_host;
|
|
|
|
} else {
|
|
|
|
/* remove repo-local variables from the environment */
|
|
|
|
conn->env = local_repo_env;
|
|
|
|
conn->use_shell = 1;
|
2005-08-03 23:15:42 +08:00
|
|
|
}
|
2013-11-29 03:50:15 +08:00
|
|
|
*arg++ = cmd.buf;
|
|
|
|
*arg = NULL;
|
2007-10-20 03:47:54 +08:00
|
|
|
|
2013-11-29 03:50:15 +08:00
|
|
|
if (start_command(conn))
|
|
|
|
die("unable to fork");
|
2007-10-20 03:47:54 +08:00
|
|
|
|
2013-11-29 03:50:15 +08:00
|
|
|
fd[0] = conn->out; /* read from child's stdout */
|
|
|
|
fd[1] = conn->in; /* write to child's stdin */
|
|
|
|
strbuf_release(&cmd);
|
|
|
|
}
|
|
|
|
free(hostandport);
|
2013-11-29 03:49:01 +08:00
|
|
|
free(path);
|
2007-10-20 03:47:53 +08:00
|
|
|
return conn;
|
2005-07-05 02:57:58 +08:00
|
|
|
}
|
|
|
|
|
2011-05-16 14:52:11 +08:00
|
|
|
int git_connection_is_socket(struct child_process *conn)
|
|
|
|
{
|
|
|
|
return conn == &no_fork;
|
|
|
|
}
|
|
|
|
|
2007-10-20 03:47:53 +08:00
|
|
|
int finish_connect(struct child_process *conn)
|
2005-07-05 02:57:58 +08:00
|
|
|
{
|
2007-10-20 03:47:54 +08:00
|
|
|
int code;
|
2011-05-16 14:52:11 +08:00
|
|
|
if (!conn || git_connection_is_socket(conn))
|
2006-09-12 17:00:13 +08:00
|
|
|
return 0;
|
|
|
|
|
2007-10-20 03:47:54 +08:00
|
|
|
code = finish_command(conn);
|
|
|
|
free(conn->argv);
|
2007-10-20 03:47:53 +08:00
|
|
|
free(conn);
|
2007-10-20 03:47:54 +08:00
|
|
|
return code;
|
2005-07-05 02:57:58 +08:00
|
|
|
}
|