Skip to content

Commit 77908e7

Browse files
Unique-Usmangitster
authored andcommitted
agent: advertise OS name via agent capability
As some issues that can happen with a Git client can be operating system specific, it can be useful for a server to know which OS a client is using. In the same way it can be useful for a client to know which OS a server is using. Our current agent capability is in the form of "package/version" (e.g., "git/1.8.3.1"). Let's extend it to include the operating system name (os) i.e in the form "package/version os" (e.g., "git/1.8.3.1 Linux"). Including OS details in the agent capability simplifies implementation, maintains backward compatibility, avoids introducing a new capability, encourages adoption across Git-compatible software, and enhances debugging by providing complete environment information without affecting functionality. Add the `transfer.advertiseOSInfo` config option to address privacy concerns. It defaults to `true` and can be changed to `false`. When `true`, both the client and server independently append their operating system name(os) to the `agent` capability value. The `agent` capability will now be in form of "package/version os" (e.g., "git/1.8.3.1 Linux"). When `false`, the `agent` capability will be in the form of "package/version" e.g "git/1.8.3.1". The server's configuration is independent of the client's. Defaults to `true`. The operating system name is retrieved using the 'sysname' field of the `uname(2)` system call or its equivalent. However, there are differences between `uname(1)` (command-line utility) and `uname(2)` (system call) outputs on Windows. These discrepancies complicate testing on Windows platforms. For example: - `uname(1)` output: MINGW64_NT-10.0-20348.3.4.10-87d57229.x86_64\ .2024-02-14.20:17.UTC.x86_64 - `uname(2)` output: Windows.10.0.20348 On Windows, uname(2) is not actually system-supplied but is instead already faked up by Git itself. We could have overcome the test issue on Windows by implementing a new `uname` subcommand in `test-tool` using uname(2), but except uname(2), which would be tested against itself, there would be nothing platform specific, so it's just simpler to disable the tests on Windows. Mentored-by: Christian Couder <[email protected]> Signed-off-by: Usman Akinyemi <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent b462386 commit 77908e7

File tree

7 files changed

+95
-7
lines changed

7 files changed

+95
-7
lines changed

Documentation/config/transfer.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,3 +125,11 @@ transfer.bundleURI::
125125
transfer.advertiseObjectInfo::
126126
When `true`, the `object-info` capability is advertised by
127127
servers. Defaults to false.
128+
129+
transfer.advertiseOSInfo::
130+
When `true`, both the client and server independently append their
131+
operating system name (os) to the `agent` capability value. The `agent`
132+
capability will now be in form of "package/version os" (e.g.,
133+
"git/1.8.3.1 Linux"). When `false`, the `agent` capability will be
134+
in the form of "package/version" e.g "git/1.8.3.1". The server's
135+
configuration is independent of the client's. Defaults to `true`.

Documentation/gitprotocol-v2.txt

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -184,11 +184,16 @@ form `agent=X`) to notify the client that the server is running version
184184
the `agent` capability with a value `Y` (in the form `agent=Y`) in its
185185
request to the server (but it MUST NOT do so if the server did not
186186
advertise the agent capability). The `X` and `Y` strings may contain any
187-
printable ASCII characters except space (i.e., the byte range 32 < x <
188-
127), and are typically of the form "package/version" (e.g.,
189-
"git/1.8.3.1"). The agent strings are purely informative for statistics
190-
and debugging purposes, and MUST NOT be used to programmatically assume
191-
the presence or absence of particular features.
187+
printable ASCII characters (i.e., the byte range 32 < x < 127), and are
188+
typically of the form "package/version os" (e.g., "git/1.8.3.1 Linux")
189+
where `os` is the operating system name (e.g., "Linux"). `X` and `Y` can
190+
be configured using the GIT_USER_AGENT environment variable and it takes
191+
priority. If `transfer.advertiseOSInfo` is `false` on the server, the server
192+
omits the `os` from X. If it is `false` on the client, the client omits the
193+
`os` from `Y`. The `os` is retrieved using the 'sysname' field of the `uname(2)`
194+
system call or its equivalent. The agent strings are purely informative for
195+
statistics and debugging purposes, and MUST NOT be used to programmatically
196+
assume the presence or absence of particular features.
192197

193198
ls-refs
194199
~~~~~~~

t/t5555-http-smart-common.sh

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,9 +123,17 @@ test_expect_success 'git receive-pack --advertise-refs: v1' '
123123
'
124124

125125
test_expect_success 'git upload-pack --advertise-refs: v2' '
126+
printf "agent=FAKE" >agent_capability &&
127+
if test_have_prereq WINDOWS
128+
then
129+
printf "\n" >>agent_capability &&
130+
git config transfer.advertiseOSInfo false
131+
else
132+
printf " %s\n" $(uname -s | test_redact_non_printables) >>agent_capability
133+
fi &&
126134
cat >expect <<-EOF &&
127135
version 2
128-
agent=FAKE
136+
$(cat agent_capability)
129137
ls-refs=unborn
130138
fetch=shallow wait-for-done
131139
server-option

t/t5701-git-serve.sh

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,20 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
88
. ./test-lib.sh
99

1010
test_expect_success 'setup to generate files with expected content' '
11-
printf "agent=git/%s\n" "$(git version | cut -d" " -f3)" >agent_capability &&
11+
printf "agent=git/%s" "$(git version | cut -d" " -f3)" >agent_capability &&
1212
1313
test_oid_cache <<-EOF &&
1414
wrong_algo sha1:sha256
1515
wrong_algo sha256:sha1
1616
EOF
1717
18+
if test_have_prereq WINDOWS
19+
then
20+
printf "\n" >>agent_capability &&
21+
git config transfer.advertiseOSInfo false
22+
else
23+
printf " %s\n" $(uname -s | test_redact_non_printables) >>agent_capability
24+
fi &&
1825
cat >expect.base <<-EOF &&
1926
version 2
2027
$(cat agent_capability)

t/test-lib-functions.sh

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2007,3 +2007,11 @@ test_trailing_hash () {
20072007
test-tool hexdump |
20082008
sed "s/ //g"
20092009
}
2010+
2011+
# Trim and replace each character with ascii code below 32 or above
2012+
# 127 (included) using a dot '.' character.
2013+
# Octal intervals \001-\040 and \177-\377
2014+
# correspond to decimal intervals 1-32 and 127-255
2015+
test_redact_non_printables () {
2016+
tr -d "\n\r" | tr "[\001-\040][\177-\377]" "."
2017+
}

version.c

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1+
#define USE_THE_REPOSITORY_VARIABLE
2+
13
#include "git-compat-util.h"
24
#include "version.h"
35
#include "version-def.h"
46
#include "strbuf.h"
57
#include "sane-ctype.h"
68
#include "gettext.h"
9+
#include "config.h"
710

811
const char git_version_string[] = GIT_VERSION;
912
const char git_built_from_commit_string[] = GIT_BUILT_FROM_COMMIT;
@@ -43,6 +46,12 @@ const char *git_user_agent_sanitized(void)
4346

4447
strbuf_addstr(&buf, git_user_agent());
4548
redact_non_printables(&buf);
49+
/* Add os name if the transfer.advertiseosinfo config is true */
50+
if (advertise_os_info()) {
51+
/* Add space to space character after git version string */
52+
strbuf_addch(&buf, ' ');
53+
strbuf_addstr(&buf, os_info_sanitized());
54+
}
4655
agent = strbuf_detach(&buf, NULL);
4756
}
4857

@@ -69,3 +78,31 @@ int get_uname_info(struct strbuf *buf, unsigned int full)
6978
strbuf_addf(buf, "%s\n", uname_info.sysname);
7079
return 0;
7180
}
81+
82+
const char *os_info_sanitized(void)
83+
{
84+
static const char *os = NULL;
85+
86+
if (!os) {
87+
struct strbuf buf = STRBUF_INIT;
88+
89+
get_uname_info(&buf, 0);
90+
/* Sanitize the os information immediately */
91+
redact_non_printables(&buf);
92+
os = strbuf_detach(&buf, NULL);
93+
}
94+
95+
return os;
96+
}
97+
98+
int advertise_os_info(void)
99+
{
100+
static int transfer_advertise_os_info= -1;
101+
102+
if (transfer_advertise_os_info == -1) {
103+
repo_config_get_bool(the_repository, "transfer.advertiseosinfo", &transfer_advertise_os_info);
104+
/* enabled by default */
105+
transfer_advertise_os_info = !!transfer_advertise_os_info;
106+
}
107+
return transfer_advertise_os_info;
108+
}

version.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#ifndef VERSION_H
22
#define VERSION_H
33

4+
struct repository;
5+
46
extern const char git_version_string[];
57
extern const char git_built_from_commit_string[];
68

@@ -14,4 +16,17 @@ const char *git_user_agent_sanitized(void);
1416
*/
1517
int get_uname_info(struct strbuf *buf, unsigned int full);
1618

19+
/*
20+
Retrieve, sanitize and cache operating system info for subsequent
21+
calls. Return a pointer to the sanitized operating system info
22+
string.
23+
*/
24+
const char *os_info_sanitized(void);
25+
26+
/*
27+
Retrieve and cache transfer.advertiseosinfo config value. Return 1
28+
if true, 0 if false.
29+
*/
30+
int advertise_os_info(void);
31+
1732
#endif /* VERSION_H */

0 commit comments

Comments
 (0)