From e64354aba6f8d9f81131235483db4b3f1b66068c Mon Sep 17 00:00:00 2001 From: lindexi Date: Mon, 11 Nov 2024 10:54:32 +0800 Subject: [PATCH] Get the host name from uname method to avoid read the file --- src/Avalonia.X11/UtsName.cs | 105 ++++++++++++++++++++++++++++++++++ src/Avalonia.X11/X11Window.cs | 23 +++++--- 2 files changed, 120 insertions(+), 8 deletions(-) create mode 100644 src/Avalonia.X11/UtsName.cs diff --git a/src/Avalonia.X11/UtsName.cs b/src/Avalonia.X11/UtsName.cs new file mode 100644 index 000000000000..ae39232db516 --- /dev/null +++ b/src/Avalonia.X11/UtsName.cs @@ -0,0 +1,105 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace Avalonia.X11; + +internal struct UtsName : IDisposable +{ + // https://github.com/torvalds/linux/blob/master/include/uapi/linux/utsname.h + /* + #define __NEW_UTS_LEN 64 + + struct new_utsname + { + char sysname[__NEW_UTS_LEN + 1]; + char nodename[__NEW_UTS_LEN + 1]; + char release[__NEW_UTS_LEN + 1]; + char version[__NEW_UTS_LEN + 1]; + char machine[__NEW_UTS_LEN + 1]; + char domainname[__NEW_UTS_LEN + 1]; + }; + */ + + private UtsName(IntPtr buffer) + { + _buffer = buffer; + } + + public static UtsName GetUtsName() + { + var ntsNameStructSize = (UtsLength + 1) * FieldCount; + + IntPtr buffer = Marshal.AllocHGlobal(ntsNameStructSize); + try + { + if (uname(buffer) != 0) + { + throw new InvalidOperationException("uname failed"); + } + + return new UtsName(buffer); + } + catch + { + Marshal.FreeHGlobal(buffer); + throw; + } + } + + private const int SystemNameFieldIndex = 0; + public Span SystemNameSpan => GetValue(SystemNameFieldIndex); + public string SystemName => Encoding.UTF8.GetString(SystemNameSpan); + + private const int NodeNameFieldIndex = 1; + public Span NodeNameSpan => GetValue(NodeNameFieldIndex); + public string NodeName => Encoding.UTF8.GetString(NodeNameSpan); + + private const int ReleaseFieldIndex = 2; + public Span ReleaseSpan => GetValue(ReleaseFieldIndex); + public string Release => Encoding.UTF8.GetString(ReleaseSpan); + + private const int VersionFieldIndex = 3; + public Span VersionSpan => GetValue(VersionFieldIndex); + public string Version => Encoding.UTF8.GetString(VersionSpan); + + private const int MachineFieldIndex = 4; + public Span MachineSpan => GetValue(MachineFieldIndex); + public string Machine => Encoding.UTF8.GetString(MachineSpan); + + private const int DomainNameFieldIndex = 5; + public Span DomainNameSpan => GetValue(DomainNameFieldIndex); + public string DomainName => Encoding.UTF8.GetString(DomainNameSpan); + + private const int UtsLength = 64; + + private const int FieldCount = 6; + + private Span GetValue(int fieldIndex) + { + var startOffset = (UtsLength + 1) * fieldIndex; + var length = 0; + while (Marshal.ReadByte(_buffer, startOffset + length) != 0) + { + length++; + } + + unsafe + { + return new Span((byte*)_buffer + startOffset, length); + } + } + + [DllImport("libc")] + private static extern int uname(IntPtr buf); + + private readonly IntPtr _buffer; + + public void Dispose() + { + Marshal.FreeHGlobal(_buffer); + } +} diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs index e1638fa1dbf8..5f5ce102e487 100644 --- a/src/Avalonia.X11/X11Window.cs +++ b/src/Avalonia.X11/X11Window.cs @@ -27,6 +27,7 @@ using System.Runtime.InteropServices; using Avalonia.Dialogs; using Avalonia.Platform.Storage.FileIO; +using System.Reflection.Metadata; // ReSharper disable IdentifierTypo // ReSharper disable StringLiteralTypo @@ -343,20 +344,26 @@ private void AppendPid(IntPtr windowXId) _x11.Atoms._NET_WM_PID, _x11.Atoms.XA_CARDINAL, 32, PropertyMode.Replace, ref pid, 1); - // If _NET_WM_PID is set, the ICCCM-specified property WM_CLIENT_MACHINE MUST also be set. - var hostNameFilePath = "cat /proc/sys/kernel/hostname"; - if (File.Exists(hostNameFilePath)) - { - var WM_CLIENT_MACHINE = XInternAtom(_x11.Display, "WM_CLIENT_MACHINE", false); - var hostName = File.ReadAllText(hostNameFilePath); - var stringToHGlobalAnsi = Marshal.StringToHGlobalAnsi(hostName); + // If _NET_WM_PID is set, the ICCCM-specified property WM_CLIENT_MACHINE MUST also be set. + // the hostname can change, so we can't cache it + // gethostname(3) on Linux just calls uname(2), so do it ourselves + // and avoid a memcpy + using var utsName = UtsName.GetUtsName(); + + var WM_CLIENT_MACHINE = XInternAtom(_x11.Display, "WM_CLIENT_MACHINE", false); + var nodeNameSpan = utsName.NodeNameSpan; + fixed (byte* pNodeName = &nodeNameSpan.GetPinnableReference()) + { XChangeProperty(_x11.Display, windowXId, WM_CLIENT_MACHINE, _x11.Atoms.XA_STRING, 8, - PropertyMode.Replace, ref stringToHGlobalAnsi, (int)hostName.Length); + PropertyMode.Replace, pNodeName, nodeNameSpan.Length); } } + [DllImport("libc")] + static extern int uname(IntPtr buf); + private static readonly int s_pid = GetProcessId(); private static int GetProcessId()