89 lines
2.6 KiB
Go
89 lines
2.6 KiB
Go
//go:build windows
|
|||
|
|
|
||
|
|
package windows
|
||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"fmt"
|
||
|
|
"os/exec"
|
||
|
|
"path/filepath"
|
||
|
|
"syscall"
|
||
|
|
"time"
|
||
|
|
|
||
|
|
"golang.org/x/sys/windows"
|
||
|
|
"softbox.local/core/updater"
|
||
|
|
)
|
||
|
|
|
||
|
|
func (platform) WaitForProcessExit(ctx context.Context, pid int, timeout time.Duration) error {
|
||
|
|
return waitForProcessExit(ctx, pid, timeout, openWindowsProcess)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (platform) StartSelfUpdate(command updater.StartCommand) (int, error) {
|
||
|
|
if !filepath.IsAbs(command.Entrypoint) || !filepath.IsAbs(command.WorkingDirectory) {
|
||
|
|
return 0, fmt.Errorf("self-update launch paths must be absolute")
|
||
|
|
}
|
||
|
|
if filepath.Base(command.Entrypoint) != updater.ProductExecutableName ||
|
||
|
|
filepath.Dir(command.Entrypoint) != filepath.Clean(command.WorkingDirectory) ||
|
||
|
|
command.HealthRequestID == "" {
|
||
|
|
return 0, fmt.Errorf("invalid fixed self-update launch command")
|
||
|
|
}
|
||
|
|
commandLine := exec.Command(command.Entrypoint, updater.InternalHealthFlag, command.HealthRequestID)
|
||
|
|
commandLine.Dir = command.WorkingDirectory
|
||
|
|
if err := commandLine.Start(); err != nil {
|
||
|
|
return 0, err
|
||
|
|
}
|
||
|
|
return commandLine.Process.Pid, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (platform) SyncDirectory(path string) error {
|
||
|
|
pathPointer, err := syscall.UTF16PtrFromString(path)
|
||
|
|
if err != nil {
|
||
|
|
return fmt.Errorf("encode directory path: %w", err)
|
||
|
|
}
|
||
|
|
handle, err := syscall.CreateFile(pathPointer, syscall.GENERIC_READ|syscall.GENERIC_WRITE,
|
||
|
|
syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE, nil,
|
||
|
|
syscall.OPEN_EXISTING, syscall.FILE_FLAG_BACKUP_SEMANTICS, 0)
|
||
|
|
if err != nil {
|
||
|
|
return fmt.Errorf("open directory handle: %w", err)
|
||
|
|
}
|
||
|
|
if err := syscall.FlushFileBuffers(handle); err != nil {
|
||
|
|
_ = syscall.CloseHandle(handle)
|
||
|
|
return fmt.Errorf("flush directory handle: %w", err)
|
||
|
|
}
|
||
|
|
if err := syscall.CloseHandle(handle); err != nil {
|
||
|
|
return fmt.Errorf("close directory handle: %w", err)
|
||
|
|
}
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
type windowsPIDHandle struct{ handle windows.Handle }
|
||
|
|
|
||
|
|
func openWindowsProcess(pid int) (pidWaitHandle, error) {
|
||
|
|
handle, err := windows.OpenProcess(windows.SYNCHRONIZE, false, uint32(pid))
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
return windowsPIDHandle{handle: handle}, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (handle windowsPIDHandle) Wait(timeout time.Duration) (bool, error) {
|
||
|
|
milliseconds := uint32(timeout / time.Millisecond)
|
||
|
|
if milliseconds == 0 {
|
||
|
|
milliseconds = 1
|
||
|
|
}
|
||
|
|
result, err := windows.WaitForSingleObject(handle.handle, milliseconds)
|
||
|
|
if err != nil {
|
||
|
|
return false, err
|
||
|
|
}
|
||
|
|
switch result {
|
||
|
|
case windows.WAIT_OBJECT_0:
|
||
|
|
return true, nil
|
||
|
|
case uint32(windows.WAIT_TIMEOUT):
|
||
|
|
return false, nil
|
||
|
|
default:
|
||
|
|
return false, fmt.Errorf("WaitForSingleObject returned %d", result)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func (handle windowsPIDHandle) Close() error { return windows.CloseHandle(handle.handle) }
|