mirror of
https://github.com/JustArchiNET/ArchiSteamFarm.git
synced 2025-12-15 22:10:30 +00:00
Make --system-required work on Linux (systemd) (#3526)
* Initial POC * Extract the parts that interest us from auto-generated file * Add support for no dbus being available * Add final bits
This commit is contained in:
committed by
GitHub
parent
d4691ced51
commit
8ac4efe392
@@ -24,6 +24,7 @@
|
|||||||
<PackageReference Include="SteamKit2" />
|
<PackageReference Include="SteamKit2" />
|
||||||
<PackageReference Include="System.Composition" />
|
<PackageReference Include="System.Composition" />
|
||||||
<PackageReference Include="System.Security.Cryptography.ProtectedData" />
|
<PackageReference Include="System.Security.Cryptography.ProtectedData" />
|
||||||
|
<PackageReference Include="Tmds.DBus.Protocol" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ using System.Threading.Tasks;
|
|||||||
using ArchiSteamFarm.Localization;
|
using ArchiSteamFarm.Localization;
|
||||||
using ArchiSteamFarm.Storage;
|
using ArchiSteamFarm.Storage;
|
||||||
using ArchiSteamFarm.Web;
|
using ArchiSteamFarm.Web;
|
||||||
|
using Microsoft.Win32.SafeHandles;
|
||||||
|
using Tmds.DBus.Protocol;
|
||||||
|
|
||||||
namespace ArchiSteamFarm.Core;
|
namespace ArchiSteamFarm.Core;
|
||||||
|
|
||||||
@@ -69,13 +71,20 @@ internal static class OS {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static SafeHandle? InhibitLock;
|
||||||
private static Mutex? SingleInstance;
|
private static Mutex? SingleInstance;
|
||||||
|
|
||||||
internal static void CoreInit(bool minimized, bool systemRequired) {
|
internal static async Task CoreInit(bool minimized, bool systemRequired) {
|
||||||
if (minimized) {
|
if (minimized) {
|
||||||
MinimizeConsoleWindow();
|
MinimizeConsoleWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (OperatingSystem.IsLinux()) {
|
||||||
|
if (systemRequired) {
|
||||||
|
await LinuxKeepSystemActive().ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (OperatingSystem.IsWindows()) {
|
if (OperatingSystem.IsWindows()) {
|
||||||
if (systemRequired) {
|
if (systemRequired) {
|
||||||
WindowsKeepSystemActive();
|
WindowsKeepSystemActive();
|
||||||
@@ -181,6 +190,12 @@ internal static class OS {
|
|||||||
// Instead, we'll dispose the mutex which should automatically release it by the CLR
|
// Instead, we'll dispose the mutex which should automatically release it by the CLR
|
||||||
SingleInstance.Dispose();
|
SingleInstance.Dispose();
|
||||||
SingleInstance = null;
|
SingleInstance = null;
|
||||||
|
|
||||||
|
// Release the inhibit lock as well, if needed
|
||||||
|
if (InhibitLock != null) {
|
||||||
|
InhibitLock.Dispose();
|
||||||
|
InhibitLock = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static bool VerifyEnvironment() {
|
internal static bool VerifyEnvironment() {
|
||||||
@@ -261,6 +276,79 @@ internal static class OS {
|
|||||||
NativeMethods.FlashWindowEx(ref flashInfo);
|
NativeMethods.FlashWindowEx(ref flashInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[SupportedOSPlatform("Linux")]
|
||||||
|
private static async Task LinuxKeepSystemActive() {
|
||||||
|
if (!OperatingSystem.IsLinux()) {
|
||||||
|
throw new PlatformNotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Docs: https://systemd.io/INHIBITOR_LOCKS
|
||||||
|
string? systemAddress = Address.System;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(systemAddress)) {
|
||||||
|
ASF.ArchiLogger.LogGenericError(Strings.FormatWarningFailedWithError(nameof(systemAddress)));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
using Connection connection = new(systemAddress);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await connection.ConnectAsync().ConfigureAwait(false);
|
||||||
|
} catch (ConnectException e) {
|
||||||
|
// Possible if no DBus is available at all
|
||||||
|
ASF.ArchiLogger.LogGenericDebuggingException(e);
|
||||||
|
ASF.ArchiLogger.LogGenericError(Strings.FormatWarningFailedWithError(nameof(connection)));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageWriter writer = connection.GetMessageWriter();
|
||||||
|
|
||||||
|
writer.WriteMethodCallHeader(
|
||||||
|
"org.freedesktop.login1",
|
||||||
|
"/org/freedesktop/login1",
|
||||||
|
"org.freedesktop.login1.Manager",
|
||||||
|
"Inhibit",
|
||||||
|
"ssss"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Colon-separated list of lock types
|
||||||
|
writer.WriteString("idle");
|
||||||
|
|
||||||
|
// Human-readable, descriptive string of who is taking the lock
|
||||||
|
writer.WriteString(SharedInfo.PublicIdentifier);
|
||||||
|
|
||||||
|
// Human-readable, descriptive string of why the lock is taken
|
||||||
|
writer.WriteString("--system-required");
|
||||||
|
|
||||||
|
// Mode
|
||||||
|
writer.WriteString("block");
|
||||||
|
|
||||||
|
MessageBuffer message = writer.CreateMessage();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Inhibit() returns a single value, a file descriptor that encapsulates the lock
|
||||||
|
InhibitLock = await connection.CallMethodAsync(
|
||||||
|
message, static (response, _) => {
|
||||||
|
Reader reader = response.GetBodyReader();
|
||||||
|
|
||||||
|
return reader.ReadHandle<SafeFileHandle>();
|
||||||
|
}
|
||||||
|
).ConfigureAwait(false);
|
||||||
|
} catch (DBusException e) {
|
||||||
|
// Possible if login manager does not support inhibit, although that should be super rare
|
||||||
|
ASF.ArchiLogger.LogGenericDebuggingException(e);
|
||||||
|
ASF.ArchiLogger.LogGenericError(Strings.FormatWarningFailedWithError(nameof(connection)));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (InhibitLock == null) {
|
||||||
|
ASF.ArchiLogger.LogGenericError(Strings.FormatWarningFailedWithError(nameof(InhibitLock)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void MinimizeConsoleWindow() {
|
private static void MinimizeConsoleWindow() {
|
||||||
(_, int top) = Console.GetCursorPosition();
|
(_, int top) = Console.GetCursorPosition();
|
||||||
|
|
||||||
|
|||||||
@@ -278,7 +278,7 @@ internal static class Program {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
OS.CoreInit(Minimized, SystemRequired);
|
await OS.CoreInit(Minimized, SystemRequired).ConfigureAwait(false);
|
||||||
|
|
||||||
Console.Title = SharedInfo.ProgramIdentifier;
|
Console.Title = SharedInfo.ProgramIdentifier;
|
||||||
ASF.ArchiLogger.LogGenericInfo(SharedInfo.ProgramIdentifier);
|
ASF.ArchiLogger.LogGenericInfo(SharedInfo.ProgramIdentifier);
|
||||||
|
|||||||
@@ -21,5 +21,6 @@
|
|||||||
<PackageVersion Include="System.Composition" Version="10.0.1" />
|
<PackageVersion Include="System.Composition" Version="10.0.1" />
|
||||||
<PackageVersion Include="System.Composition.AttributedModel" Version="10.0.1" />
|
<PackageVersion Include="System.Composition.AttributedModel" Version="10.0.1" />
|
||||||
<PackageVersion Include="System.Security.Cryptography.ProtectedData" Version="10.0.1" />
|
<PackageVersion Include="System.Security.Cryptography.ProtectedData" Version="10.0.1" />
|
||||||
|
<PackageVersion Include="Tmds.DBus.Protocol" Version="0.21.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
Reference in New Issue
Block a user