-
Notifications
You must be signed in to change notification settings - Fork 30
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Determine KC's DBUS_SESSION_BUS_ADDRESS on Windows
- Loading branch information
Showing
5 changed files
with
393 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
//go:build !windows | ||
// +build !windows | ||
|
||
package main | ||
|
||
import "github.com/godbus/dbus/v5" | ||
|
||
func DBusSessionBusForPlatform() (conn *dbus.Conn, err error) { | ||
return dbus.SessionBus() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
//go:build windows | ||
// +build windows | ||
|
||
package main | ||
|
||
import ( | ||
"bytes" | ||
"crypto/sha1" | ||
"fmt" | ||
"io" | ||
"os" | ||
"reflect" | ||
"strings" | ||
"syscall" | ||
"time" | ||
"errors" | ||
"unsafe" | ||
|
||
"github.com/godbus/dbus/v5" | ||
"golang.org/x/sys/windows" | ||
) | ||
|
||
var ( | ||
modkernel32 = windows.NewLazySystemDLL("kernel32.dll") | ||
procOpenFileMappingW = modkernel32.NewProc("OpenFileMappingW") //syscall only provides CreateFileMapping | ||
|
||
cachedDbusSectionName = "" | ||
) | ||
|
||
func normaliseAndHashDbusPath(kdeConnectPath string) string { | ||
if !strings.HasSuffix(kdeConnectPath, "\\") { | ||
kdeConnectPath = kdeConnectPath + "\\" | ||
} | ||
|
||
if strings.HasSuffix(kdeConnectPath, "\\bin\\") { | ||
kdeConnectPath = strings.TrimSuffix(kdeConnectPath, "bin\\") | ||
} | ||
|
||
kdeConnectPath = strings.ToLower(kdeConnectPath) | ||
|
||
sha1 := sha1.New() | ||
io.WriteString(sha1, kdeConnectPath) | ||
kdeConnectPath = fmt.Sprintf("%x", sha1.Sum(nil)) // produces a lower-case hash, which is required | ||
|
||
return kdeConnectPath | ||
} | ||
|
||
func readStringFromSection(sectionName string) (ret string, err error) { | ||
lpSectionName, err := syscall.UTF16PtrFromString(sectionName) | ||
if err != nil { | ||
return "", err | ||
} | ||
|
||
var hSharedMem uintptr | ||
for i := 0; i < 3; i++ { | ||
hSharedMem, _, err = syscall.Syscall(procOpenFileMappingW.Addr(), 3, windows.FILE_MAP_READ, 0, uintptr(unsafe.Pointer(lpSectionName))) | ||
if hSharedMem == 0 { | ||
time.Sleep(100 * time.Millisecond) | ||
} else { | ||
break | ||
} | ||
} | ||
if hSharedMem == 0 { | ||
return "", err | ||
} | ||
defer windows.CloseHandle(windows.Handle(hSharedMem)) | ||
|
||
lpsharedAddr, err := syscall.MapViewOfFile(syscall.Handle(hSharedMem), windows.FILE_MAP_READ, 0, 0, 0) | ||
if err != nil { | ||
return "", err | ||
} | ||
defer syscall.UnmapViewOfFile(lpsharedAddr) | ||
|
||
// obtain section size - rounded up to the page size | ||
mbi := windows.MemoryBasicInformation{} | ||
err = windows.VirtualQueryEx(windows.CurrentProcess(), uintptr(lpsharedAddr), &mbi, unsafe.Sizeof(mbi)) | ||
if err != nil { | ||
return "", err | ||
} | ||
|
||
// get a byte[] representation of the mapping, using the same technique as syscall_unix.go | ||
// alternatively, unsafe.Slice((*byte)(unsafe.Pointer(sec)), mbi.RegionSize) works, with a "possible misuse of unsafe.Pointer" warning | ||
var bSharedAddr []byte | ||
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&bSharedAddr)) | ||
hdr.Data = lpsharedAddr | ||
hdr.Cap = int(mbi.RegionSize) | ||
hdr.Len = int(mbi.RegionSize) | ||
|
||
// copy section's contents into this process | ||
dbusAddress := make([]byte, len(bSharedAddr)) | ||
copy(dbusAddress, bSharedAddr) | ||
dbusAddress[len(dbusAddress) - 1] = 0 // force null-termination somewhere | ||
|
||
// assuming a valid string, get the first null-terminator and cap the new slice's length to it | ||
strlen := bytes.IndexByte(dbusAddress, 0) | ||
return string(dbusAddress[:strlen]), nil | ||
} | ||
|
||
func DBusSessionBusForPlatform() (conn *dbus.Conn, err error) { | ||
// If $DBUS_SESSION_BUS_ADDRESS is set, just use that | ||
if this_env_sess_addr := os.Getenv("DBUS_SESSION_BUS_ADDRESS"); this_env_sess_addr != "" { | ||
return dbus.SessionBus() | ||
} | ||
|
||
var sectionName string = cachedDbusSectionName | ||
if sectionName == "" { | ||
// Prioritise Store-installed copies because it's more determinable | ||
kdeConnectPath, err := KdeConnectPathFromWindowsStore() | ||
if err != nil { | ||
//fmt.Fprintln(os.Stderr, err) | ||
kdeConnectPath, err = KdeConnectPathFromRunningIndicator() | ||
} | ||
|
||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
kdeConnectPath = normaliseAndHashDbusPath(kdeConnectPath) | ||
|
||
sectionName = "Local\\DBusDaemonAddressInfo-" + kdeConnectPath | ||
} | ||
|
||
if sectionName != "" { | ||
dbusAddress, err := readStringFromSection(sectionName) | ||
if err != nil { | ||
//cachedDbusSectionName = "" | ||
return nil, err | ||
} | ||
|
||
conn, err := dbus.Connect(dbusAddress) | ||
if err == nil { | ||
// Keep a copy of the computed section name to avoid the path lookups, | ||
// but not the section contents itself, as KDE Connect could be started again | ||
// from the same path but using a different port. | ||
// The odds of its install directory being changed on the other hand are low | ||
cachedDbusSectionName = sectionName | ||
} else { | ||
cachedDbusSectionName = "" | ||
} | ||
|
||
return conn, err | ||
} | ||
|
||
if err == nil { | ||
err = errors.New("could not determine KDE Connect's D-Bus daemon location") | ||
} | ||
|
||
return nil, err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
//go:build windows | ||
// +build windows | ||
|
||
package main | ||
|
||
import ( | ||
"errors" | ||
"unsafe" | ||
"path/filepath" | ||
|
||
"golang.org/x/sys/windows" | ||
) | ||
|
||
const ( | ||
WTS_CURRENT_SERVER_HANDLE = 0 | ||
WTS_CURRENT_SESSION = 0xFFFFFFFF | ||
WTSTypeProcessInfoLevel0 = 0 | ||
) | ||
|
||
type WTS_PROCESS_INFO struct { | ||
SessionId uint32 | ||
ProcessId uint32 | ||
pProcessName *uint16 | ||
pUserSid uintptr // incomplete: avoid defining a struct for something unneeded | ||
} | ||
|
||
var ( | ||
libwtsapi32 = windows.NewLazySystemDLL("wtsapi32.dll") | ||
procWTSEnumerateProcessesExW = libwtsapi32.NewProc("WTSEnumerateProcessesExW") | ||
procWTSFreeMemoryExW = libwtsapi32.NewProc("WTSFreeMemoryExW") | ||
) | ||
|
||
func wtsFreeMemoryExW(WTSTypeClass uint32, pMemory unsafe.Pointer, NumberOfEntries uint32) { | ||
procWTSFreeMemoryExW.Call(uintptr(WTSTypeClass), uintptr(pMemory), uintptr(NumberOfEntries)) | ||
} | ||
|
||
func wtsEnumerateProcessesExW(hServer windows.Handle, pLevel *uint32, SessionId uint32, ppProcessInfo unsafe.Pointer, pCount *uint32) (ret bool, lastErr error) { | ||
r1, _, lastErr := procWTSEnumerateProcessesExW.Call(uintptr(hServer), uintptr(unsafe.Pointer(pLevel)), uintptr(SessionId), uintptr(unsafe.Pointer(ppProcessInfo)), uintptr(unsafe.Pointer(pCount))) | ||
return r1 != 0, lastErr | ||
} | ||
|
||
func KdeConnectPathFromRunningIndicator() (ret string, err error) { | ||
var ( | ||
Level uint32 = WTSTypeProcessInfoLevel0 | ||
pProcessInfo *WTS_PROCESS_INFO | ||
count uint32 | ||
|
||
hostSessionId uint32 = WTS_CURRENT_SESSION | ||
) | ||
// Only look at processes started in the same Windows session as the host is running in | ||
windows.ProcessIdToSessionId(windows.GetCurrentProcessId(), &hostSessionId) | ||
|
||
r1, lastErr := wtsEnumerateProcessesExW(WTS_CURRENT_SERVER_HANDLE, &Level, hostSessionId, unsafe.Pointer(&pProcessInfo), &count) | ||
if !r1 { | ||
return "", lastErr | ||
} | ||
defer wtsFreeMemoryExW(Level, unsafe.Pointer(pProcessInfo), count) | ||
|
||
size := unsafe.Sizeof(WTS_PROCESS_INFO{}) | ||
for i := uint32(0); i < count; i++ { | ||
p := *(*WTS_PROCESS_INFO)(unsafe.Pointer(uintptr(unsafe.Pointer(pProcessInfo)) + (uintptr(size) * uintptr(i)))) | ||
procName := windows.UTF16PtrToString(p.pProcessName) | ||
if procName == "kdeconnect-indicator.exe" || procName == "kdeconnectd.exe" || procName == "kdeconnect-app.exe" { | ||
hProcess, _ := windows.OpenProcess(windows.PROCESS_QUERY_LIMITED_INFORMATION, false, p.ProcessId) | ||
if hProcess != 0 { | ||
var exeNameBuf [261]uint16 // MAX_PATH + 1 | ||
exeNameLen := uint32(len(exeNameBuf) - 1) | ||
err = windows.QueryFullProcessImageName(hProcess, 0, &exeNameBuf[0], &exeNameLen) | ||
windows.CloseHandle(hProcess) | ||
if err == nil { | ||
exeName := windows.UTF16ToString(exeNameBuf[:exeNameLen]) | ||
return filepath.Dir(exeName), nil | ||
} | ||
} | ||
} | ||
} | ||
|
||
return "", errors.New("could not find KDE Connect processes") | ||
} |
Oops, something went wrong.