From 426a569c8995f032757b1a587eacf54f831e7f72 Mon Sep 17 00:00:00 2001 From: Andrey Pshenkin Date: Sun, 5 Nov 2023 03:10:01 +0000 Subject: [PATCH] Fix single instance lock for macOS sandbox app (#3029) * fix single instance lock for sandbox app * fix single instance lock for sandbox app --- .../frontend/desktop/darwin/AppDelegate.h | 2 ++ .../frontend/desktop/darwin/AppDelegate.m | 17 +++++++++++++---- .../desktop/darwin/single_instance.go | 19 ++++++++++++++++++- 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/v2/internal/frontend/desktop/darwin/AppDelegate.h b/v2/internal/frontend/desktop/darwin/AppDelegate.h index 4fa32233e..a8d10f647 100644 --- a/v2/internal/frontend/desktop/darwin/AppDelegate.h +++ b/v2/internal/frontend/desktop/darwin/AppDelegate.h @@ -28,4 +28,6 @@ extern void HandleSecondInstanceData(char * message); void SendDataToFirstInstance(char * singleInstanceUniqueId, char * text); +char* GetMacOsNativeTempDir(); + #endif /* AppDelegate_h */ diff --git a/v2/internal/frontend/desktop/darwin/AppDelegate.m b/v2/internal/frontend/desktop/darwin/AppDelegate.m index 55bc2b4a8..18ecfbcff 100644 --- a/v2/internal/frontend/desktop/darwin/AppDelegate.m +++ b/v2/internal/frontend/desktop/darwin/AppDelegate.m @@ -48,17 +48,26 @@ } void SendDataToFirstInstance(char * singleInstanceUniqueId, char * message) { + // we pass message in object because otherwise sandboxing will prevent us from sending it https://developer.apple.com/forums/thread/129437 + NSString * myString = [NSString stringWithUTF8String:message]; [[NSDistributedNotificationCenter defaultCenter] postNotificationName:[NSString stringWithUTF8String:singleInstanceUniqueId] - object:nil - userInfo:@{@"message": [NSString stringWithUTF8String:message]} + object:(__bridge const void *)(myString) + userInfo:nil deliverImmediately:YES]; } +char* GetMacOsNativeTempDir() { + NSString *tempDir = NSTemporaryDirectory(); + char *copy = strdup([tempDir UTF8String]); + + return copy; +} + - (void)handleSecondInstanceNotification:(NSNotification *)note; { - if (note.userInfo[@"message"] != nil) { - NSString *message = note.userInfo[@"message"]; + if (note.object != nil) { + NSString * message = (__bridge NSString *)note.object; const char* utf8Message = message.UTF8String; HandleSecondInstanceData((char*)utf8Message); } diff --git a/v2/internal/frontend/desktop/darwin/single_instance.go b/v2/internal/frontend/desktop/darwin/single_instance.go index 6f61ed173..012baef02 100644 --- a/v2/internal/frontend/desktop/darwin/single_instance.go +++ b/v2/internal/frontend/desktop/darwin/single_instance.go @@ -12,13 +12,16 @@ package darwin import "C" import ( "encoding/json" + "fmt" "github.com/wailsapp/wails/v2/pkg/options" "os" + "strings" "syscall" + "unsafe" ) func SetupSingleInstance(uniqueID string) { - lockFilePath := os.TempDir() + lockFilePath := getTempDir() lockFileName := uniqueID + ".lock" _, err := createLockFile(lockFilePath + "/" + lockFileName) @@ -62,14 +65,28 @@ func HandleSecondInstanceData(secondInstanceMessage *C.char) { func createLockFile(filename string) (*os.File, error) { file, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE, 0600) if err != nil { + fmt.Printf("Failed to open lockfile %s: %s", filename, err) return nil, err } err = syscall.Flock(int(file.Fd()), syscall.LOCK_EX|syscall.LOCK_NB) if err != nil { + // Flock failed for some other reason than other instance already lock it. Print it in logs for possible debugging. + if !strings.Contains(err.Error(), "resource temporarily unavailable") { + fmt.Printf("Failed to lock lockfile %s: %s", filename, err) + } file.Close() return nil, err } return file, nil } + +// If app is sandboxed, golang os.TempDir() will return path that will not be accessible. So use native macOS temp dir function. +func getTempDir() string { + cstring := C.GetMacOsNativeTempDir() + path := C.GoString(cstring) + C.free(unsafe.Pointer(cstring)) + + return path +}