diff mbox series

[3/3] connect: advertise OS version

Message ID 20240619125708.3719150-4-christian.couder@gmail.com (mailing list archive)
State New, archived
Headers show
Series Advertise OS version | expand

Commit Message

Christian Couder June 19, 2024, 12:57 p.m. UTC
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.

Let's add OS information exchange to the protocol in the same way some
git version exchange is performed.

Signed-off-by: Christian Couder <chriscool@tuxfamily.org>
---
 Documentation/gitprotocol-v2.txt | 18 ++++++++++++++++++
 connect.c                        |  3 +++
 serve.c                          | 12 ++++++++++++
 t/t5555-http-smart-common.sh     |  3 +++
 t/t5701-git-serve.sh             |  3 +++
 version.c                        | 29 +++++++++++++++++++++++++++++
 version.h                        |  3 +++
 7 files changed, 71 insertions(+)
diff mbox series

Patch

diff --git a/Documentation/gitprotocol-v2.txt b/Documentation/gitprotocol-v2.txt
index 414bc625d5..f676b2dc7a 100644
--- a/Documentation/gitprotocol-v2.txt
+++ b/Documentation/gitprotocol-v2.txt
@@ -190,6 +190,24 @@  printable ASCII characters except space (i.e., the byte range 32 < x <
 and debugging purposes, and MUST NOT be used to programmatically assume
 the presence or absence of particular features.
 
+os-version
+~~~~~~~~~~
+
+In the same way as the `agent` capability above, the server can
+advertise the `os-version` capability with a value `X` (in the form
+`os-version=X`) to notify the client that the server is running an
+operating system that can be identified by `X`. The client may
+optionally send its own `os-version` string by including the
+`os-version` capability with a value `Y` (in the form `os-version=Y`)
+in its request to the server (but it MUST NOT do so if the server did
+not advertise the os-version capability). The `X` and `Y` strings may
+contain any printable ASCII characters except space (i.e., the byte
+range 32 < x < 127), and are typically made from the result of
+`uname -srvm`. The os-version strings are purely informative for
+statistics and debugging purposes, and MUST NOT be used to
+programmatically assume the presence or absence of particular
+features.
+
 ls-refs
 ~~~~~~~
 
diff --git a/connect.c b/connect.c
index 0d77737a53..3a48806ddc 100644
--- a/connect.c
+++ b/connect.c
@@ -489,6 +489,9 @@  static void send_capabilities(int fd_out, struct packet_reader *reader)
 	if (server_supports_v2("agent"))
 		packet_write_fmt(fd_out, "agent=%s", git_user_agent_sanitized());
 
+	if (server_supports_v2("os-version"))
+		packet_write_fmt(fd_out, "os-version=%s", os_version_sanitized());
+
 	if (server_feature_v2("object-format", &hash_name)) {
 		int hash_algo = hash_algo_by_name(hash_name);
 		if (hash_algo == GIT_HASH_UNKNOWN)
diff --git a/serve.c b/serve.c
index aa651b73e9..77eb5ebdaa 100644
--- a/serve.c
+++ b/serve.c
@@ -29,6 +29,14 @@  static int agent_advertise(struct repository *r UNUSED,
 	return 1;
 }
 
+static int os_version_advertise(struct repository *r UNUSED,
+			   struct strbuf *value)
+{
+	if (value)
+		strbuf_addstr(value, os_version_sanitized());
+	return 1;
+}
+
 static int object_format_advertise(struct repository *r,
 				   struct strbuf *value)
 {
@@ -121,6 +129,10 @@  static struct protocol_capability capabilities[] = {
 		.name = "agent",
 		.advertise = agent_advertise,
 	},
+	{
+		.name = "os-version",
+		.advertise = os_version_advertise,
+	},
 	{
 		.name = "ls-refs",
 		.advertise = ls_refs_advertise,
diff --git a/t/t5555-http-smart-common.sh b/t/t5555-http-smart-common.sh
index 3dcb3340a3..c67739236f 100755
--- a/t/t5555-http-smart-common.sh
+++ b/t/t5555-http-smart-common.sh
@@ -124,9 +124,12 @@  test_expect_success 'git receive-pack --advertise-refs: v1' '
 '
 
 test_expect_success 'git upload-pack --advertise-refs: v2' '
+	# Octal intervals \001-\040 and \177-\377
+	# corresponds to decimal intervals 1-32 and 127-255
 	cat >expect <<-EOF &&
 	version 2
 	agent=FAKE
+	os-version=$(uname -srvm | tr -d "\n" | tr "[\001-\040][\177-\377]" ".")
 	ls-refs=unborn
 	fetch=shallow wait-for-done
 	server-option
diff --git a/t/t5701-git-serve.sh b/t/t5701-git-serve.sh
index c48830de8f..9c9a707e6a 100755
--- a/t/t5701-git-serve.sh
+++ b/t/t5701-git-serve.sh
@@ -13,9 +13,12 @@  test_expect_success 'test capability advertisement' '
 	wrong_algo sha1:sha256
 	wrong_algo sha256:sha1
 	EOF
+	# Octal intervals \001-\040 and \177-\377
+	# corresponds to decimal intervals 1-32 and 127-255
 	cat >expect.base <<-EOF &&
 	version 2
 	agent=git/$(git version | cut -d" " -f3)
+	os-version=$(uname -srvm | tr -d "\n" | tr "[\001-\040][\177-\377]" ".")
 	ls-refs=unborn
 	fetch=shallow wait-for-done
 	server-option
diff --git a/version.c b/version.c
index 10b9fa77d1..5b20ea0d7c 100644
--- a/version.c
+++ b/version.c
@@ -61,3 +61,32 @@  int get_uname_info(struct strbuf *buf)
 		    uname_info.machine);
 	return 0;
 }
+
+const char *os_version(void)
+{
+	static const char *os = NULL;
+
+	if (!os) {
+		struct strbuf buf = STRBUF_INIT;
+
+		get_uname_info(&buf);
+		os = strbuf_detach(&buf, NULL);
+	}
+
+	return os;
+}
+
+const char *os_version_sanitized(void)
+{
+	static const char *os_sanitized = NULL;
+
+	if (!os_sanitized) {
+		struct strbuf buf = STRBUF_INIT;
+
+		strbuf_addstr(&buf, os_version());
+		strbuf_sanitize(&buf);
+		os_sanitized = strbuf_detach(&buf, NULL);
+	}
+
+	return os_sanitized;
+}
diff --git a/version.h b/version.h
index afe3dbbab7..349952c8f2 100644
--- a/version.h
+++ b/version.h
@@ -14,4 +14,7 @@  const char *git_user_agent_sanitized(void);
 */
 int get_uname_info(struct strbuf *buf);
 
+const char *os_version(void);
+const char *os_version_sanitized(void);
+
 #endif /* VERSION_H */