@@ -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 ( lockFile . Path , File . GetUnixFileMode ( lockFile . Path ) | 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