Skip to content

Commit 5510e6c

Browse files
committed
refactor: process lock file storage path
- On Windows - Portable mode: it stores in `data/process.lock` - Non-portable mode: it stores in `%APPDATA%/SourceGit/process.lock` - On macOS, it stores in `~/Library/Application Support/SourceGit/process.lock` - On Linux - Portable mode: it stores in `data/process.lock` - Non-portable mode: it stores in `$XDG_RUNTIME_DIR/sourcegit.instance.lock` Signed-off-by: leo <longshuang@msn.cn>
1 parent 3d61055 commit 5510e6c

1 file changed

Lines changed: 66 additions & 9 deletions

File tree

src/Models/IpcChannel.cs

Lines changed: 66 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,52 @@ namespace SourceGit.Models
1010
{
1111
public class IpcChannel : IDisposable
1212
{
13-
public bool IsFirstInstance { get; }
13+
public bool IsFirstInstance { get; private set; }
1414

1515
public event Action<string> MessageReceived;
1616

1717
public IpcChannel()
1818
{
19-
try
19+
IsFirstInstance = false;
20+
21+
var lockFile = GetLockFilePath();
22+
if (OperatingSystem.IsLinux() && lockFile.NeedChangePermissions)
23+
{
24+
// On Linux, if the lock file is created in the XDG_RUNTIME_DIR, we need to set the permissions to 700 (rwx------) to ensure that only the current user can access it.
25+
try
26+
{
27+
_singletonLock = new FileStream(lockFile.Path, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None);
28+
File.SetUnixFileMode(_singletonLock.SafeFileHandle, UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute | UnixFileMode.StickyBit);
29+
IsFirstInstance = true;
30+
}
31+
catch
32+
{
33+
try
34+
{
35+
_singletonLock = new FileStream(lockFile.Path, FileMode.Open, FileAccess.ReadWrite, FileShare.None);
36+
IsFirstInstance = true;
37+
}
38+
catch
39+
{
40+
// Just ignore the exception and assume that another instance is running.
41+
}
42+
}
43+
}
44+
else
45+
{
46+
try
47+
{
48+
_singletonLock = File.Open(lockFile.Path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);
49+
IsFirstInstance = true;
50+
}
51+
catch
52+
{
53+
// Just ignore the exception and assume that another instance is running.
54+
}
55+
}
56+
57+
if (IsFirstInstance)
2058
{
21-
_singletonLock = File.Open(Path.Combine(Native.OS.BasicDirectories.CacheDir, "process.lock"), FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);
22-
IsFirstInstance = true;
2359
_server = new NamedPipeServerStream(
2460
GetPipeName(),
2561
PipeDirection.In,
@@ -29,10 +65,6 @@ public IpcChannel()
2965
_cancellationTokenSource = new CancellationTokenSource();
3066
Task.Run(StartServer);
3167
}
32-
catch
33-
{
34-
IsFirstInstance = false;
35-
}
3668
}
3769

3870
public void SendToFirstInstance(string cmd)
@@ -69,7 +101,30 @@ public void Dispose()
69101
_singletonLock?.Dispose();
70102
}
71103

72-
private static string GetPipeName()
104+
private LockFile GetLockFilePath()
105+
{
106+
// On Windows and macOS, we can use the cache directory for the lock file.
107+
if (!OperatingSystem.IsLinux())
108+
return new LockFile(Path.Combine(Native.OS.BasicDirectories.CacheDir, "process.lock"), false);
109+
110+
// On Linux, we should first check if the app is running in portable mode.
111+
var appImage = Environment.GetEnvironmentVariable("APPIMAGE");
112+
if (!string.IsNullOrEmpty(appImage) && File.Exists(appImage))
113+
{
114+
var portableDir = Path.Combine(Path.GetDirectoryName(appImage)!, "data");
115+
if (Directory.Exists(portableDir))
116+
return new LockFile(Path.Combine(portableDir, "process.lock"), false);
117+
}
118+
119+
// If not in portable mode, we should use the XDG_RUNTIME_DIR environment variable for the lock file.
120+
var runtimeDir = Environment.GetEnvironmentVariable("XDG_RUNTIME_DIR");
121+
if (string.IsNullOrEmpty(runtimeDir) || !Directory.Exists(runtimeDir))
122+
return new LockFile(Path.Combine(Native.OS.BasicDirectories.CacheDir, "process.lock"), false);
123+
124+
return new LockFile(Path.Combine(runtimeDir, "sourcegit.instance.lock"), true);
125+
}
126+
127+
private string GetPipeName()
73128
{
74129
// SourceGit does not support multiple instances on macOS, so we can use a fixed pipe name for macOS.
75130
if (OperatingSystem.IsMacOS())
@@ -108,6 +163,8 @@ private async void StartServer()
108163
}
109164
}
110165

166+
private record LockFile(string Path, bool NeedChangePermissions);
167+
111168
private FileStream _singletonLock = null;
112169
private NamedPipeServerStream _server = null;
113170
private CancellationTokenSource _cancellationTokenSource = null;

0 commit comments

Comments
 (0)