Limit ASF to a single instance per directory

This commit is contained in:
JustArchi
2019-03-29 23:07:06 +01:00
parent ed80eefd37
commit 07b8978866
5 changed files with 79 additions and 11 deletions

View File

@@ -963,6 +963,15 @@ namespace ArchiSteamFarm.Localization {
}
}
/// <summary>
/// Wyszukuje zlokalizowany ciąg podobny do ciągu ASF process is already running for this working directory, aborting!.
/// </summary>
public static string ErrorSingleInstanceRequired {
get {
return ResourceManager.GetString("ErrorSingleInstanceRequired", resourceCulture);
}
}
/// <summary>
/// Wyszukuje zlokalizowany ciąg podobny do ciągu Could not check latest version!.
/// </summary>

View File

@@ -717,4 +717,7 @@ StackTrace:
<value>Bot has {0} games remaining in its background queue.</value>
<comment>{0} will be replaced by remaining number of games in BGR's queue</comment>
</data>
<data name="ErrorSingleInstanceRequired" xml:space="preserve">
<value>ASF process is already running for this working directory, aborting!</value>
</data>
</root>

View File

@@ -139,7 +139,7 @@ namespace ArchiSteamFarm.NLog {
return !string.IsNullOrEmpty(result) ? result.Trim() : null;
}
internal static void InitCoreLoggers() {
internal static void InitCoreLoggers(bool uniqueInstance) {
if (LogManager.Configuration != null) {
IsUsingCustomConfiguration = true;
InitConsoleLoggers();
@@ -156,7 +156,10 @@ namespace ArchiSteamFarm.NLog {
config.AddTarget(coloredConsoleTarget);
config.LoggingRules.Add(new LoggingRule("*", LogLevel.Debug, coloredConsoleTarget));
if (uniqueInstance) {
FileTarget fileTarget = new FileTarget("File") {
CleanupFileName = false,
ConcurrentWrites = false,
DeleteOldFileOnStartup = true,
FileName = SharedInfo.LogFile,
Layout = GeneralLayout
@@ -164,6 +167,7 @@ namespace ArchiSteamFarm.NLog {
config.AddTarget(fileTarget);
config.LoggingRules.Add(new LoggingRule("*", LogLevel.Debug, fileTarget));
}
LogManager.Configuration = config;
InitConsoleLoggers();

View File

@@ -22,7 +22,9 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using ArchiSteamFarm.Localization;
using JetBrains.Annotations;
@@ -33,6 +35,8 @@ namespace ArchiSteamFarm {
[NotNull]
internal static string Variant => RuntimeInformation.OSDescription.Trim();
private static Mutex SingleInstance;
internal static void Init(bool systemRequired, GlobalConfig.EOptimizationMode optimizationMode) {
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) {
DisableQuickEditMode();
@@ -60,6 +64,24 @@ namespace ArchiSteamFarm {
}
}
internal static bool RegisterProcess() {
if (SingleInstance != null) {
return false;
}
string uniqueName = "Global\\" + SharedInfo.AssemblyName + "-" + Convert.ToBase64String(Encoding.UTF8.GetBytes(Directory.GetCurrentDirectory()));
Mutex singleInstance = new Mutex(true, uniqueName, out bool result);
if (!result) {
return false;
}
SingleInstance = singleInstance;
return true;
}
internal static void UnixSetFileAccessExecutable(string path) {
if (string.IsNullOrEmpty(path) || !File.Exists(path)) {
ASF.ArchiLogger.LogNullError(nameof(path));
@@ -73,6 +95,16 @@ namespace ArchiSteamFarm {
}
}
internal static void UnregisterProcess() {
if (SingleInstance == null) {
return;
}
SingleInstance.ReleaseMutex();
SingleInstance.Dispose();
SingleInstance = null;
}
private static void DisableQuickEditMode() {
if (Console.IsOutputRedirected) {
return;

View File

@@ -118,7 +118,12 @@ namespace ArchiSteamFarm {
Target.Register<HistoryTarget>(HistoryTarget.TargetName);
Target.Register<SteamTarget>(SteamTarget.TargetName);
InitCore(args);
if (!await InitCore(args).ConfigureAwait(false)) {
await Exit(1).ConfigureAwait(false);
return;
}
await InitASF(args).ConfigureAwait(false);
}
@@ -141,7 +146,7 @@ namespace ArchiSteamFarm {
await ASF.Init().ConfigureAwait(false);
}
private static void InitCore(IReadOnlyCollection<string> args) {
private static async Task<bool> InitCore(IReadOnlyCollection<string> args) {
Directory.SetCurrentDirectory(SharedInfo.HomeDirectory);
// Allow loading configs from source tree if it's a debug build
@@ -166,7 +171,17 @@ namespace ArchiSteamFarm {
ParsePreInitArgs(args);
}
Logging.InitCoreLoggers();
bool uniqueInstance = OS.RegisterProcess();
Logging.InitCoreLoggers(uniqueInstance);
if (!uniqueInstance) {
ASF.ArchiLogger.LogGenericError(Strings.ErrorSingleInstanceRequired);
await Task.Delay(5000).ConfigureAwait(false);
return false;
}
return true;
}
private static async Task InitGlobalConfigAndLanguage() {
@@ -320,6 +335,7 @@ namespace ArchiSteamFarm {
// Ensure that IPC is stopped before we finalize shutdown sequence
await ArchiKestrel.Stop().ConfigureAwait(false);
// Stop all the active bots so they can disconnect cleanly
if (Bot.Bots?.Count > 0) {
// Stop() function can block due to SK2 sockets, don't forget a maximum delay
await Task.WhenAny(Utilities.InParallel(Bot.Bots.Values.Select(bot => Task.Run(() => bot.Stop(true)))), Task.Delay(Bot.Bots.Count * WebBrowser.MaxTries * 1000)).ConfigureAwait(false);
@@ -328,8 +344,12 @@ namespace ArchiSteamFarm {
await Task.Delay(1000).ConfigureAwait(false);
}
// Flush all the pending writes to log files
LogManager.Flush();
// Unregister the process from single instancing
OS.UnregisterProcess();
return true;
}