Skip to content

Commit 7d32ef5

Browse files
committed
Merge branch 'ua/os-version-capability' into jch
The value of "uname -s" is by default sent over the wire as a new capability, with an opt-out for privacy-concious folks. * ua/os-version-capability: connect: advertise OS version t5701: add setup test to remove side-effect dependency version: extend get_uname_info() to hide system details version: refactor get_uname_info() version: refactor redact_non_printables() version: replace manual ASCII checks with isprint() for clarity
2 parents 70510c4 + b1d9837 commit 7d32ef5

File tree

10 files changed

+175
-22
lines changed

10 files changed

+175
-22
lines changed

Documentation/config/transfer.adoc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,3 +125,10 @@ transfer.bundleURI::
125125
transfer.advertiseObjectInfo::
126126
When `true`, the `object-info` capability is advertised by
127127
servers. Defaults to false.
128+
129+
transfer.advertiseOSVersion::
130+
When set to `true` on the server, the server will advertise its
131+
`os-version` capability to the client. On the client side, if set
132+
to `true`, it will advertise its `os-version` capability to the
133+
server only if the server also advertises its `os-version` capability.
134+
Defaults to true.

Documentation/gitprotocol-v2.adoc

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,23 @@ printable ASCII characters except space (i.e., the byte range 32 < x <
190190
and debugging purposes, and MUST NOT be used to programmatically assume
191191
the presence or absence of particular features.
192192
193+
os-version
194+
~~~~~~~~~~
195+
196+
In the same way as the `agent` capability above, the server can
197+
advertise the `os-version` capability to notify the client the
198+
kind of operating system it is running on. The client may optionally
199+
send its own `os-version` capability, to notify the server the kind
200+
of operating system it is also running on in its request to the server
201+
(but it MUST NOT do so if the server did not advertise the os-version
202+
capability). The value of this capability may consist of ASCII printable
203+
characters(from 33 to 126 inclusive) and are typically made from the
204+
result of `uname -s`(OS name e.g Linux). The os-version capability can
205+
be disabled entirely by setting the `transfer.advertiseOSVersion` config
206+
option to `false`. The `os-version` strings are purely informative for
207+
statistics and debugging purposes, and MUST NOT be used to
208+
programmatically assume the presence or absence of particular features.
209+
193210
ls-refs
194211
~~~~~~~
195212

builtin/bugreport.c

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@
1212
#include "diagnose.h"
1313
#include "object-file.h"
1414
#include "setup.h"
15+
#include "version.h"
1516

1617
static void get_system_info(struct strbuf *sys_info)
1718
{
18-
struct utsname uname_info;
1919
char *shell = NULL;
2020

2121
/* get git version from native cmd */
@@ -24,16 +24,7 @@ static void get_system_info(struct strbuf *sys_info)
2424

2525
/* system call for other version info */
2626
strbuf_addstr(sys_info, "uname: ");
27-
if (uname(&uname_info))
28-
strbuf_addf(sys_info, _("uname() failed with error '%s' (%d)\n"),
29-
strerror(errno),
30-
errno);
31-
else
32-
strbuf_addf(sys_info, "%s %s %s %s\n",
33-
uname_info.sysname,
34-
uname_info.release,
35-
uname_info.version,
36-
uname_info.machine);
27+
get_uname_info(sys_info, 1);
3728

3829
strbuf_addstr(sys_info, _("compiler info: "));
3930
get_compiler_info(sys_info);

connect.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,9 @@ static void send_capabilities(int fd_out, struct packet_reader *reader)
491491
if (server_supports_v2("agent"))
492492
packet_write_fmt(fd_out, "agent=%s", git_user_agent_sanitized());
493493

494+
if (server_supports_v2("os-version") && advertise_os_version(the_repository))
495+
packet_write_fmt(fd_out, "os-version=%s", os_version_sanitized());
496+
494497
if (server_feature_v2("object-format", &hash_name)) {
495498
int hash_algo = hash_algo_by_name(hash_name);
496499
if (hash_algo == GIT_HASH_UNKNOWN)

serve.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,16 @@ static int agent_advertise(struct repository *r UNUSED,
2929
return 1;
3030
}
3131

32+
static int os_version_advertise(struct repository *r,
33+
struct strbuf *value)
34+
{
35+
if (!advertise_os_version(r))
36+
return 0;
37+
if (value)
38+
strbuf_addstr(value, os_version_sanitized());
39+
return 1;
40+
}
41+
3242
static int object_format_advertise(struct repository *r,
3343
struct strbuf *value)
3444
{
@@ -121,6 +131,10 @@ static struct protocol_capability capabilities[] = {
121131
.name = "agent",
122132
.advertise = agent_advertise,
123133
},
134+
{
135+
.name = "os-version",
136+
.advertise = os_version_advertise,
137+
},
124138
{
125139
.name = "ls-refs",
126140
.advertise = ls_refs_advertise,

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\n" >agent_and_osversion &&
127+
if test_have_prereq WINDOWS
128+
then
129+
git config transfer.advertiseOSVersion false
130+
else
131+
printf "os-version=%s\n" $(uname -s | test_redact_non_printables) >>agent_and_osversion
132+
fi &&
133+
126134
cat >expect <<-EOF &&
127135
version 2
128-
agent=FAKE
136+
$(cat agent_and_osversion)
129137
ls-refs=unborn
130138
fetch=shallow wait-for-done
131139
server-option

t/t5701-git-serve.sh

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,17 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
77

88
. ./test-lib.sh
99

10-
test_expect_success 'test capability advertisement' '
10+
test_expect_success 'setup to generate files with expected content' '
11+
printf "agent=git/%s\n" "$(git version | cut -d" " -f3)" >agent_and_osversion &&
12+
1113
test_oid_cache <<-EOF &&
1214
wrong_algo sha1:sha256
1315
wrong_algo sha256:sha1
1416
EOF
17+
1518
cat >expect.base <<-EOF &&
1619
version 2
17-
agent=git/$(git version | cut -d" " -f3)
20+
$(cat agent_and_osversion)
1821
ls-refs=unborn
1922
fetch=shallow wait-for-done
2023
server-option
@@ -23,7 +26,26 @@ test_expect_success 'test capability advertisement' '
2326
cat >expect.trailer <<-EOF &&
2427
0000
2528
EOF
26-
cat expect.base expect.trailer >expect &&
29+
30+
if test_have_prereq WINDOWS
31+
then
32+
git config transfer.advertiseOSVersion false
33+
else
34+
printf "os-version=%s\n" $(uname -s | test_redact_non_printables) >>agent_and_osversion
35+
fi &&
36+
37+
cat >expect_osversion.base <<-EOF
38+
version 2
39+
$(cat agent_and_osversion)
40+
ls-refs=unborn
41+
fetch=shallow wait-for-done
42+
server-option
43+
object-format=$(test_oid algo)
44+
EOF
45+
'
46+
47+
test_expect_success 'test capability advertisement' '
48+
cat expect_osversion.base expect.trailer >expect &&
2749
2850
GIT_TEST_SIDEBAND_ALL=0 test-tool serve-v2 \
2951
--advertise-capabilities >out &&
@@ -351,7 +373,7 @@ test_expect_success 'test capability advertisement with uploadpack.advertiseBund
351373
cat >expect.extra <<-EOF &&
352374
bundle-uri
353375
EOF
354-
cat expect.base \
376+
cat expect_osversion.base \
355377
expect.extra \
356378
expect.trailer >expect &&
357379

t/test-lib-functions.sh

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2043,3 +2043,11 @@ test_trailing_hash () {
20432043
test-tool hexdump |
20442044
sed "s/ //g"
20452045
}
2046+
2047+
# Trim and replace each character with ascii code below 32 or above
2048+
# 127 (included) using a dot '.' character.
2049+
# Octal intervals \001-\040 and \177-\377
2050+
# correspond to decimal intervals 1-32 and 127-255
2051+
test_redact_non_printables () {
2052+
tr -d "\n\r" | tr "[\001-\040][\177-\377]" "."
2053+
}

version.c

Lines changed: 67 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
#include "git-compat-util.h"
22
#include "version.h"
33
#include "strbuf.h"
4+
#include "sane-ctype.h"
5+
#include "gettext.h"
6+
#include "config.h"
47

58
#ifndef GIT_VERSION_H
69
# include "version-def.h"
@@ -11,6 +14,19 @@
1114
const char git_version_string[] = GIT_VERSION;
1215
const char git_built_from_commit_string[] = GIT_BUILT_FROM_COMMIT;
1316

17+
/*
18+
* Trim and replace each character with ascii code below 32 or above
19+
* 127 (included) using a dot '.' character.
20+
*/
21+
static void redact_non_printables(struct strbuf *buf)
22+
{
23+
strbuf_trim(buf);
24+
for (size_t i = 0; i < buf->len; i++) {
25+
if (!isprint(buf->buf[i]) || buf->buf[i] == ' ')
26+
buf->buf[i] = '.';
27+
}
28+
}
29+
1430
const char *git_user_agent(void)
1531
{
1632
static const char *agent = NULL;
@@ -32,13 +48,58 @@ const char *git_user_agent_sanitized(void)
3248
struct strbuf buf = STRBUF_INIT;
3349

3450
strbuf_addstr(&buf, git_user_agent());
35-
strbuf_trim(&buf);
36-
for (size_t i = 0; i < buf.len; i++) {
37-
if (buf.buf[i] <= 32 || buf.buf[i] >= 127)
38-
buf.buf[i] = '.';
39-
}
40-
agent = buf.buf;
51+
redact_non_printables(&buf);
52+
agent = strbuf_detach(&buf, NULL);
4153
}
4254

4355
return agent;
4456
}
57+
58+
int get_uname_info(struct strbuf *buf, unsigned int full)
59+
{
60+
struct utsname uname_info;
61+
62+
if (uname(&uname_info)) {
63+
strbuf_addf(buf, _("uname() failed with error '%s' (%d)\n"),
64+
strerror(errno),
65+
errno);
66+
return -1;
67+
}
68+
if (full)
69+
strbuf_addf(buf, "%s %s %s %s\n",
70+
uname_info.sysname,
71+
uname_info.release,
72+
uname_info.version,
73+
uname_info.machine);
74+
else
75+
strbuf_addf(buf, "%s\n", uname_info.sysname);
76+
return 0;
77+
}
78+
79+
const char *os_version_sanitized(void)
80+
{
81+
static const char *os = NULL;
82+
83+
if (!os) {
84+
struct strbuf buf = STRBUF_INIT;
85+
86+
get_uname_info(&buf, 0);
87+
/* Sanitize the os information immediately */
88+
redact_non_printables(&buf);
89+
os = strbuf_detach(&buf, NULL);
90+
}
91+
92+
return os;
93+
}
94+
95+
int advertise_os_version(struct repository *r)
96+
{
97+
static int transfer_advertise_os_version = -1;
98+
99+
if (transfer_advertise_os_version == -1) {
100+
repo_config_get_bool(r, "transfer.advertiseosversion", &transfer_advertise_os_version);
101+
/* enabled by default */
102+
transfer_advertise_os_version = !!transfer_advertise_os_version;
103+
}
104+
return transfer_advertise_os_version;
105+
}

version.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,32 @@
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

79
const char *git_user_agent(void);
810
const char *git_user_agent_sanitized(void);
911

12+
/*
13+
Try to get information about the system using uname(2).
14+
Return -1 and put an error message into 'buf' in case of uname()
15+
error. Return 0 and put uname info into 'buf' otherwise.
16+
*/
17+
int get_uname_info(struct strbuf *buf, unsigned int full);
18+
19+
/*
20+
Retrieve, sanitize and cache system information for subsequent
21+
calls. Return a pointer to the sanitized system information
22+
string.
23+
*/
24+
const char *os_version_sanitized(void);
25+
26+
/*
27+
Retrieve and cache whether os-version capability is enabled.
28+
Return 1 if enabled, 0 if disabled.
29+
*/
30+
int advertise_os_version(struct repository *r);
31+
1032
#endif /* VERSION_H */

0 commit comments

Comments
 (0)