]> git.ipfire.org Git - thirdparty/git.git/commitdiff
agent: advertise OS name via agent capability
authorUsman Akinyemi <usmanakinyemi202@gmail.com>
Sat, 15 Feb 2025 15:50:52 +0000 (21:20 +0530)
committerJunio C Hamano <gitster@pobox.com>
Wed, 19 Feb 2025 17:48:37 +0000 (09:48 -0800)
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. 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 <chriscool@tuxfamily.org>
Signed-off-by: Usman Akinyemi <usmanakinyemi202@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Documentation/gitprotocol-v2.txt
connect.c
t/t5701-git-serve.sh
t/test-lib-functions.sh
version.c
version.h

index 1652fef3aeb166412e525811a634436229afd459..9f6350bbf2f72a29ab1d9042c73f488f9692a1a3 100644 (file)
@@ -184,9 +184,13 @@ form `agent=X`) to notify the client that the server is running version
 the `agent` capability with a value `Y` (in the form `agent=Y`) in its
 request to the server (but it MUST NOT do so if the server did not
 advertise the agent 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 of the form "package/version" (e.g.,
-"git/1.8.3.1"). The agent strings are purely informative for statistics
+printable ASCII characters except space (i.e., the byte range 33 <= x <=
+126), and are typically of the form "package/version-os" (e.g.,
+"git/1.8.3.1-Linux") where `os` is the operating system name (e.g.,
+"Linux"). `X` and `Y` can be configured using the GIT_USER_AGENT
+environment variable and it takes priority. The `os` is
+retrieved using the 'sysname' field of the `uname(2)` system call
+or its equivalent. The agent strings are purely informative for statistics
 and debugging purposes, and MUST NOT be used to programmatically assume
 the presence or absence of particular features.
 
index 10fad43e98652f8c441866c982103fc2a5a01321..4d8547907575cdd77f4c685e0c0fb1ec7c926930 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -625,7 +625,7 @@ const char *parse_feature_value(const char *feature_list, const char *feature, s
                                        *offset = found + len - orig_start;
                                return value;
                        }
-                       /* feature with a value (e.g., "agent=git/1.2.3") */
+                       /* feature with a value (e.g., "agent=git/1.2.3-Linux") */
                        else if (*value == '=') {
                                size_t end;
 
index 4c24a188b988208520737b9db35175ea4386368c..678a346ed06780e389d8d3e4c06bd61c92966d2f 100755 (executable)
@@ -8,13 +8,19 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 . ./test-lib.sh
 
 test_expect_success 'setup to generate files with expected content' '
-       printf "agent=git/%s\n" "$(git version | cut -d" " -f3)" >agent_capability &&
+       printf "agent=git/%s" "$(git version | cut -d" " -f3)" >agent_capability &&
 
        test_oid_cache <<-EOF &&
        wrong_algo sha1:sha256
        wrong_algo sha256:sha1
        EOF
 
+       if test_have_prereq WINDOWS
+       then
+               printf "agent=FAKE\n" >agent_capability
+       else
+               printf -- "-%s\n" $(uname -s | test_redact_non_printables) >>agent_capability
+       fi &&
        cat >expect.base <<-EOF &&
        version 2
        $(cat agent_capability)
@@ -31,6 +37,10 @@ test_expect_success 'setup to generate files with expected content' '
 test_expect_success 'test capability advertisement' '
        cat expect.base expect.trailer >expect &&
 
+       if test_have_prereq WINDOWS
+       then
+               GIT_USER_AGENT=FAKE && export GIT_USER_AGENT
+       fi &&
        GIT_TEST_SIDEBAND_ALL=0 test-tool serve-v2 \
                --advertise-capabilities >out &&
        test-tool pkt-line unpack <out >actual &&
@@ -361,6 +371,10 @@ test_expect_success 'test capability advertisement with uploadpack.advertiseBund
            expect.extra \
            expect.trailer >expect &&
 
+       if test_have_prereq WINDOWS
+       then
+               GIT_USER_AGENT=FAKE && export GIT_USER_AGENT
+       fi &&
        GIT_TEST_SIDEBAND_ALL=0 test-tool serve-v2 \
                --advertise-capabilities >out &&
        test-tool pkt-line unpack <out >actual &&
index 78e054ab503a6573ca9810a19d89afff334c6ba2..346590432360f32c4bd6d989d66d3e3ad77f4473 100644 (file)
@@ -2007,3 +2007,11 @@ test_trailing_hash () {
                test-tool hexdump |
                sed "s/ //g"
 }
+
+# Trim and replace each character with ascii code below 32 or above
+# 127 (included) using a dot '.' character.
+# Octal intervals \001-\040 and \177-\377
+# correspond to decimal intervals 1-32 and 127-255
+test_redact_non_printables () {
+    tr -d "\n\r" | tr "[\001-\040][\177-\377]" "."
+}
index d95221a72a6d6397ec30c5575c10a1d5f9cecf16..8e927cf1ebc8e0c477bf615b183441a0d18ed10f 100644 (file)
--- a/version.c
+++ b/version.c
@@ -1,8 +1,9 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
 #include "git-compat-util.h"
 #include "version.h"
 #include "version-def.h"
 #include "strbuf.h"
-#include "sane-ctype.h"
 #include "gettext.h"
 
 const char git_version_string[] = GIT_VERSION;
@@ -34,6 +35,27 @@ const char *git_user_agent(void)
        return agent;
 }
 
+/*
+  Retrieve, sanitize and cache operating system info for subsequent
+  calls. Return a pointer to the sanitized operating system info
+  string.
+*/
+static const char *os_info(void)
+{
+       static const char *os = NULL;
+
+       if (!os) {
+               struct strbuf buf = STRBUF_INIT;
+
+               get_uname_info(&buf, 0);
+               /* Sanitize the os information immediately */
+               redact_non_printables(&buf);
+               os = strbuf_detach(&buf, NULL);
+       }
+
+       return os;
+}
+
 const char *git_user_agent_sanitized(void)
 {
        static const char *agent = NULL;
@@ -42,6 +64,11 @@ const char *git_user_agent_sanitized(void)
                struct strbuf buf = STRBUF_INIT;
 
                strbuf_addstr(&buf, git_user_agent());
+
+               if (!getenv("GIT_USER_AGENT")) {
+                       strbuf_addch(&buf, '-');
+                       strbuf_addstr(&buf, os_info());
+               }
                redact_non_printables(&buf);
                agent = strbuf_detach(&buf, NULL);
        }
index 5eb586c0bdd25c05ac89af12ce010838497b7964..bbde6d371affb7d9f54b83431ae3769253a0c3e7 100644 (file)
--- a/version.h
+++ b/version.h
@@ -1,6 +1,8 @@
 #ifndef VERSION_H
 #define VERSION_H
 
+struct repository;
+
 extern const char git_version_string[];
 extern const char git_built_from_commit_string[];
 
@@ -14,4 +16,5 @@ const char *git_user_agent_sanitized(void);
 */
 int get_uname_info(struct strbuf *buf, unsigned int full);
 
+
 #endif /* VERSION_H */