diff --git a/go.mod b/go.mod index 5e5dd001eb..343609d638 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.21 require ( cloud.google.com/go/pubsub v1.33.0 - fyne.io/systray v1.10.0 + fyne.io/systray v1.10.1-0.20240111184411-11c585fff98d github.com/AbGuthrie/goquery/v2 v2.0.1 github.com/DATA-DOG/go-sqlmock v1.5.0 github.com/Masterminds/semver v1.5.0 @@ -289,7 +289,6 @@ require ( github.com/subosito/gotenv v1.2.0 // indirect github.com/tchap/go-patricia/v2 v2.3.1 // indirect github.com/technoweenie/multipartstreamer v1.0.1 // indirect - github.com/tevino/abool v1.2.0 // indirect github.com/tklauser/go-sysconf v0.3.11 // indirect github.com/tklauser/numcpus v0.6.0 // indirect github.com/trivago/tgo v1.0.7 // indirect diff --git a/go.sum b/go.sum index c565feddf3..af6f40b4f2 100644 --- a/go.sum +++ b/go.sum @@ -84,8 +84,8 @@ contrib.go.opencensus.io/integrations/ocsql v0.1.7/go.mod h1:8DsSdjz3F+APR+0z0Wk dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -fyne.io/systray v1.10.0 h1:Yr1D9Lxeiw3+vSuZWPlaHC8BMjIHZXJKkek706AfYQk= -fyne.io/systray v1.10.0/go.mod h1:oM2AQqGJ1AMo4nNqZFYU8xYygSBZkW2hmdJ7n4yjedE= +fyne.io/systray v1.10.1-0.20240111184411-11c585fff98d h1:NjHwOOuOgGswUOPzDlsEDJOqKdjOjwL8Vi1mj9qx9+o= +fyne.io/systray v1.10.1-0.20240111184411-11c585fff98d/go.mod h1:RVwqP9nYMo7h5zViCBHri2FgjXF7H2cub7MAq4NSoLs= github.com/AbGuthrie/goquery/v2 v2.0.1 h1:h0tIhmeRroyqYjT9zxXPXOrheNp1xqNTV+XFWuDI+eA= github.com/AbGuthrie/goquery/v2 v2.0.1/go.mod h1:xpDLF4kUr+TRFXogclRa7Zzc8bMAB/fYm1zG/XX1WOA= github.com/AlekSi/pointer v1.2.0 h1:glcy/gc4h8HnG2Z3ZECSzZ1IX1x2JxRVuDzaJwQE0+w= @@ -1171,8 +1171,6 @@ github.com/tchap/go-patricia/v2 v2.3.1 h1:6rQp39lgIYZ+MHmdEq4xzuk1t7OdC35z/xm0BG github.com/tchap/go-patricia/v2 v2.3.1/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k= github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM= github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog= -github.com/tevino/abool v1.2.0 h1:heAkClL8H6w+mK5md9dzsuohKeXHUpY7Vw0ZCKW+huA= -github.com/tevino/abool v1.2.0/go.mod h1:qc66Pna1RiIsPa7O4Egxxs9OqkuxDX55zznh9K07Tzg= github.com/theupdateframework/go-tuf v0.5.2 h1:habfDzTmpbzBLIFGWa2ZpVhYvFBoK0C1onC3a4zuPRA= github.com/theupdateframework/go-tuf v0.5.2/go.mod h1:SyMV5kg5n4uEclsyxXJZI2UxPFJNDc4Y+r7wv+MlvTA= github.com/throttled/throttled/v2 v2.8.0 h1:B5VfdM8BE+ClI2Ji238SbNOTWfYcocvuAhgT27lvwrE= diff --git a/orbit/changes/15821-fix-desktop-windows-tray-icon b/orbit/changes/15821-fix-desktop-windows-tray-icon new file mode 100644 index 0000000000..435a336f3d --- /dev/null +++ b/orbit/changes/15821-fix-desktop-windows-tray-icon @@ -0,0 +1,2 @@ +* Fixed bug on Windows where Fleet Desktop tray icon was not showing in the task bar. +* Fixed bug on Windows where Orbit was not bringing the Fleet Desktop process up (when it was detected as not running). diff --git a/orbit/cmd/desktop/desktop.go b/orbit/cmd/desktop/desktop.go index 6d07d05016..0044a9a0d2 100644 --- a/orbit/cmd/desktop/desktop.go +++ b/orbit/cmd/desktop/desktop.go @@ -11,6 +11,7 @@ import ( "fyne.io/systray" "github.com/fleetdm/fleet/v4/orbit/pkg/constant" + "github.com/fleetdm/fleet/v4/orbit/pkg/go-paniclog" "github.com/fleetdm/fleet/v4/orbit/pkg/profiles" "github.com/fleetdm/fleet/v4/orbit/pkg/token" "github.com/fleetdm/fleet/v4/orbit/pkg/update" @@ -59,6 +60,7 @@ func setupRunners() { func main() { setupLogs() + setupStderr() // Our TUF provided targets must support launching with "--help". if len(os.Args) > 1 && os.Args[1] == "--help" { @@ -101,25 +103,11 @@ func main() { log.Info().Msg("ready") systray.SetTooltip("Fleet Desktop") + // Default to dark theme icon because this seems to be a better fit on Linux (Ubuntu at // least). On macOS this is used as a template icon anyway. systray.SetTemplateIcon(iconDark, iconDark) - // Theme detection is currently only on Windows. On macOS we use template icons (which - // automatically change), and on Linux we don't handle it yet (Ubuntu doesn't seem to change - // systray colors in the default configuration when toggling light/dark). - if runtime.GOOS == "windows" { - // Set the initial theme, and watch for theme changes. - theme, err := getSystemTheme() - if err != nil { - log.Error().Err(err).Msg("get system theme") - } - iconManager := newIconManager(theme) - go func() { - watchSystemTheme(iconManager) - }() - } - // Add a disabled menu item with the current version versionItem := systray.AddMenuItem(fmt.Sprintf("Fleet Desktop v%s", version), "") versionItem.Disable() @@ -438,14 +426,12 @@ func (m *mdmMigrationHandler) ShowInstructions() error { return nil } -// setupLogs configures our logging system to write logs to rolling files and -// stderr, if for some reason we can't write a log file the logs are still -// printed to stderr. +// setupLogs configures our logging system to write logs to rolling files, if for some +// reason we can't write a log file the logs are still printed to stderr. func setupLogs() { - stderrOut := zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339Nano, NoColor: true} - dir, err := logDir() if err != nil { + stderrOut := zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339Nano, NoColor: true} log.Logger = log.Output(stderrOut) log.Error().Err(err).Msg("find directory for logs") return @@ -454,6 +440,7 @@ func setupLogs() { dir = filepath.Join(dir, "Fleet") if err := os.MkdirAll(dir, 0o755); err != nil { + stderrOut := zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339Nano, NoColor: true} log.Logger = log.Output(stderrOut) log.Error().Err(err).Msg("make directories for log files") return @@ -466,10 +453,40 @@ func setupLogs() { MaxAge: 28, // days } - log.Logger = log.Output(zerolog.MultiLevelWriter( - zerolog.ConsoleWriter{Out: logFile, TimeFormat: time.RFC3339Nano, NoColor: true}, - stderrOut, - )) + consoleWriter := zerolog.ConsoleWriter{Out: logFile, TimeFormat: time.RFC3339Nano, NoColor: true} + log.Logger = log.Output(consoleWriter) +} + +// setupStderr redirects stderr output to a file. +func setupStderr() { + dir, err := logDir() + if err != nil { + log.Error().Err(err).Msg("find directory for stderr") + return + } + + if err := os.MkdirAll(dir, 0o755); err != nil { + log.Error().Err(err).Msg("make directories for stderr") + return + } + + stderrFile, err := os.OpenFile(filepath.Join(dir, "Fleet", "fleet-desktop.err"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o666) + if err != nil { + log.Error().Err(err).Msg("create file to redirect stderr") + return + } + defer stderrFile.Close() + + if _, err := stderrFile.Write([]byte(time.Now().UTC().Format("2006-01-02T15-04-05") + "\n")); err != nil { + log.Error().Err(err).Msg("write to stderr file") + } + + // We need to use this method to properly capture golang's panic stderr output. + // Just setting os.Stderr to a file doesn't work (Go's runtime is probably using os.Stderr + // very early). + if _, err := paniclog.RedirectStderr(stderrFile); err != nil { + log.Error().Err(err).Msg("redirect stderr to file") + } } // logDir returns the default root directory to use for application-level logs. @@ -512,29 +529,3 @@ func logDir() (string, error) { return dir, nil } - -type iconManager struct { - theme theme -} - -func newIconManager(theme theme) *iconManager { - m := &iconManager{ - theme: theme, - } - m.UpdateTheme(theme) - return m -} - -func (m *iconManager) UpdateTheme(theme theme) { - m.theme = theme - switch theme { - case themeDark: - systray.SetIcon(iconDark) - case themeLight: - systray.SetIcon(iconLight) - case themeUnknown: - log.Debug().Msg("theme unknown, using dark theme") - default: - log.Error().Str("theme", string(theme)).Msg("tried to set invalid theme") - } -} diff --git a/orbit/cmd/desktop/desktop_unix.go b/orbit/cmd/desktop/desktop_unix.go index 394994dbf6..5ad36300f1 100644 --- a/orbit/cmd/desktop/desktop_unix.go +++ b/orbit/cmd/desktop/desktop_unix.go @@ -9,21 +9,9 @@ import ( "github.com/rs/zerolog/log" ) -//go:embed icon_light.png -var iconLight []byte - //go:embed icon_dark.png var iconDark []byte -func getSystemTheme() (theme, error) { - log.Debug().Msg("get system theme not implemented for this platform") - return themeUnknown, nil -} - -func watchSystemTheme(_ *iconManager) { - log.Debug().Msg("watch system theme not implemented for this platform") -} - func blockWaitForStopEvent(channelId string) error { log.Debug().Msg("communication channel helpers are not implemented for this platform") return nil diff --git a/orbit/cmd/desktop/desktop_windows.go b/orbit/cmd/desktop/desktop_windows.go index a4a8405f5c..aeb92d1fd4 100644 --- a/orbit/cmd/desktop/desktop_windows.go +++ b/orbit/cmd/desktop/desktop_windows.go @@ -7,103 +7,25 @@ import ( _ "embed" "errors" "fmt" - "syscall" "time" "github.com/rs/zerolog/log" "golang.org/x/sys/windows" - "golang.org/x/sys/windows/registry" ) // For Windows we must use ico format for the icon, // see https://github.com/getlantern/systray/blob/6065fda28be8c8d91aeb5e20de25e1600b8664a3/systray_windows.go#L850-L856. -// since watchSystemTheme is currently buggy, we are using the same icon for both themes -// +// In the past we implemented some logic to detect the Windows theme but it was buggy, +// so as a temporary fix we are using the same colored icon for both themes. +// Such theme detection logic was removed in this PR: https://github.com/fleetdm/fleet/pull/16402. + //go:embed windows_app.ico var iconLight []byte //go:embed windows_app.ico var iconDark []byte -// Adapted from MIT licensed code in -// https://github.com/WireGuard/wireguard-go/commit/7e962a9932667f4a161b20aba5ff1c75ab8e578a -// and https://gist.github.com/jerblack/1d05bbcebb50ad55c312e4d7cf1bc909 - -// mkwinsyscall generates the Window syscall from the //sys comment below. -//go:generate go run golang.org/x/sys/windows/mkwinsyscall -output generated_syscall_windows.go desktop_windows.go -//sys regNotifyChangeKeyValue(key windows.Handle, watchSubtree bool, notifyFilter uint32, event windows.Handle, asynchronous bool) (regerrno error) = advapi32.RegNotifyChangeKeyValue - -const ( - // Registry path where theme value is stored. - registryPath = `Software\Microsoft\Windows\CurrentVersion\Themes\Personalize` - registryKey = "AppsUseLightTheme" - // REG_NOTIFY_CHANGE_LAST_SET notifies the caller of changes to a value of the key. This can include adding or deleting a value, or changing an existing value. - REG_NOTIFY_CHANGE_LAST_SET uint32 = 0x00000004 -) - -func getSystemTheme() (theme, error) { - // Adapted from https://stackoverflow.com/a/58494769/491710 - key, err := registry.OpenKey(registry.CURRENT_USER, registryPath, registry.QUERY_VALUE) - if err != nil { - return themeDark, err - } - defer key.Close() - - val, _, err := key.GetIntegerValue(registryKey) - if err != nil { - return themeDark, err - } - - switch val { - case 0: - return themeDark, nil - case 1: - return themeLight, nil - default: - return themeUnknown, fmt.Errorf("unknown theme value %d", val) - } -} - -// since this logic is currently buggy, we are currently using the same icon for both themes -func watchSystemTheme(iconManager *iconManager) { - for { - // Function call for proper defer semantics. - func() { - // Open the key within the loop, because "If the specified key is closed, the event is - // signaled. This means that an application should not depend on the key being open after - // returning from a wait operation on the event." - - // https://docs.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regnotifychangekeyvalue - key, err := registry.OpenKey(registry.CURRENT_USER, registryPath, syscall.KEY_NOTIFY) - if err != nil { - log.Error().Err(err).Msg("open registry key") - return - } - defer key.Close() - - err = regNotifyChangeKeyValue(windows.Handle(key), false, REG_NOTIFY_CHANGE_LAST_SET, windows.Handle(0), false) - if err != nil { - log.Error().Err(err).Msg("change notification on registry value") - } - - theme, err := getSystemTheme() - if err != nil { - log.Error().Err(err).Msg("get system theme") - return - } - log.Debug().Str("theme", string(theme)).Msg("got theme update") - - // The systray library has a timeout issue trying to change the icon sometimes. As a - // cheap workaround, do this update a handful of times hoping one will work. Sadly the - // API doesn't return an error in these cases. - for i := 0; i < 10; i++ { - iconManager.UpdateTheme(theme) - time.Sleep(1 * time.Second) - } - }() - } -} - // blockWaitForStopEvent waits for the named event kernel object to be signalled func blockWaitForStopEvent(channelId string) error { if channelId == "" { diff --git a/orbit/cmd/desktop/theme.go b/orbit/cmd/desktop/theme.go deleted file mode 100644 index 34b6867e7f..0000000000 --- a/orbit/cmd/desktop/theme.go +++ /dev/null @@ -1,9 +0,0 @@ -package main - -type theme string - -const ( - themeDark theme = "dark" - themeLight = "light" - themeUnknown = "unknown" -) diff --git a/orbit/pkg/go-paniclog/LICENSE b/orbit/pkg/go-paniclog/LICENSE new file mode 100644 index 0000000000..8ff8a854db --- /dev/null +++ b/orbit/pkg/go-paniclog/LICENSE @@ -0,0 +1,21 @@ +Copyright (C) 2019 by Dustin Spicuzza (dustin@virtualroadside.com) +Copyright (C) 2012 by Nick Craig-Wood http://www.craig-wood.com/nick/ + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/orbit/pkg/go-paniclog/README.md b/orbit/pkg/go-paniclog/README.md new file mode 100644 index 0000000000..53ea30b532 --- /dev/null +++ b/orbit/pkg/go-paniclog/README.md @@ -0,0 +1,34 @@ +> This package was copied from https://github.com/virtuald/go-paniclog + +go-paniclog +=========== + +By default, panics in golang are sent to stderr. Unfortunately, there isn't a +direct builtin global mechanism to capture/send the output of the panic to +a file or really do anything with it other than to write to stderr. + +One possible solution is that you can redirect stderr to a file, and that's +all that this package does. Of course, once you redirect stderr to file, +anything else you write to stderr will also end up in that file. v2.0 now +includes a function you can use to undo the redirection if you wanted to do +that for some reason. + +Reference: https://stackoverflow.com/questions/34772012/capturing-panic-in-golang + + +Alternatives +------------ + +* [panicwrap](https://github.com/mitchellh/panicwrap) may be a better solution + for many programs + +Author +------ + +I can't claim any credit for this idea or the code, it is entirely taken from +the [rclone](https://github.com/ncw/rclone.git) program by Nick Craig-Wood. + +License +------- + +MIT License diff --git a/orbit/pkg/go-paniclog/example/main.go b/orbit/pkg/go-paniclog/example/main.go new file mode 100644 index 0000000000..a42e290c43 --- /dev/null +++ b/orbit/pkg/go-paniclog/example/main.go @@ -0,0 +1,31 @@ +package main + +import ( + "fmt" + "os" + + "github.com/fleetdm/fleet/v4/orbit/pkg/go-paniclog" +) + +func main() { + f, err := os.Create("test.log") + if err != nil { + fmt.Println("Error creating file:", err) + os.Exit(1) + } + + undo, err := paniclog.RedirectStderr(f) + if err != nil { + fmt.Println("Error redirecting stderr:", err) + os.Exit(1) + } + + f.Close() + + if os.Getenv("UNDO_PANICLOG") != "" { + // demonstrates undoing the stderr redirect + undo() //nolint:errcheck + } + + panic("this should end up in the file instead of the console") +} diff --git a/orbit/pkg/go-paniclog/paniclog.go b/orbit/pkg/go-paniclog/paniclog.go new file mode 100644 index 0000000000..ac4be9a63e --- /dev/null +++ b/orbit/pkg/go-paniclog/paniclog.go @@ -0,0 +1,19 @@ +package paniclog + +import "os" + +// UndoFunction will reverse the redirection +type UndoFunction func() error + +// RedirectStderr to the file passed in, so that the output of any panics that +// occur will be sent to that file. The caller may close the file after +// this function returns. +// +// Of course, anything else written to stderr will also be sent to that file, +// so don't do that unless that's your intent. +// +// Returns a function that can be used to revert stderr back to the console, +// or an error if stderr could not be redirected +func RedirectStderr(f *os.File) (UndoFunction, error) { + return redirectStderr(f) +} diff --git a/orbit/pkg/go-paniclog/paniclog_other.go b/orbit/pkg/go-paniclog/paniclog_other.go new file mode 100644 index 0000000000..2aa778fd97 --- /dev/null +++ b/orbit/pkg/go-paniclog/paniclog_other.go @@ -0,0 +1,15 @@ +// Log the panic to the log file - for oses which can't do this + +//go:build !windows && !darwin && !dragonfly && !freebsd && !linux && !nacl && !netbsd && !openbsd +// +build !windows,!darwin,!dragonfly,!freebsd,!linux,!nacl,!netbsd,!openbsd + +package paniclog + +import ( + "errors" + "os" +) + +func redirectStderr(f *os.File) (UndoFunction, error) { + return nil, errors.New("Can't redirect stderr to file") +} diff --git a/orbit/pkg/go-paniclog/paniclog_unix.go b/orbit/pkg/go-paniclog/paniclog_unix.go new file mode 100644 index 0000000000..08bf190b0e --- /dev/null +++ b/orbit/pkg/go-paniclog/paniclog_unix.go @@ -0,0 +1,39 @@ +// Log the panic under unix to the log file + +//go:build !windows && !solaris && !plan9 +// +build !windows,!solaris,!plan9 + +package paniclog + +import ( + "errors" + "os" + + "golang.org/x/sys/unix" +) + +func redirectStderr(f *os.File) (UndoFunction, error) { + stderrFd := int(os.Stderr.Fd()) + oldfd, err := unix.Dup(stderrFd) + if err != nil { + return nil, errors.New("Failed to redirect stderr to file: " + err.Error()) + } + + err = unix.Dup2(int(f.Fd()), stderrFd) + if err != nil { + return nil, errors.New("Failed to redirect stderr to file: " + err.Error()) + } + + undo := func() error { + undoErr := unix.Dup2(oldfd, stderrFd) + unix.Close(oldfd) + + if undoErr != nil { + return errors.New("Failed to reverse stderr redirection: " + err.Error()) + } + + return nil + } + + return undo, nil +} diff --git a/orbit/pkg/go-paniclog/paniclog_windows.go b/orbit/pkg/go-paniclog/paniclog_windows.go new file mode 100644 index 0000000000..5380f33f80 --- /dev/null +++ b/orbit/pkg/go-paniclog/paniclog_windows.go @@ -0,0 +1,84 @@ +// Log the panic under windows to the log file +// +// Code from minix, via +// +// https://play.golang.org/p/kLtct7lSUg + +//go:build windows +// +build windows + +package paniclog + +import ( + "errors" + "os" + "syscall" +) + +var ( + kernel32 = syscall.MustLoadDLL("kernel32.dll") + procSetStdHandle = kernel32.MustFindProc("SetStdHandle") + procGetStdHandle = kernel32.MustFindProc("GetStdHandle") +) + +func dupFD(fd uintptr) (syscall.Handle, error) { + // Cribbed from https://github.com/golang/go/blob/go1.8/src/syscall/exec_windows.go#L303. + p, err := syscall.GetCurrentProcess() + if err != nil { + return 0, err + } + var h syscall.Handle + return h, syscall.DuplicateHandle(p, syscall.Handle(fd), p, &h, 0, true, syscall.DUPLICATE_SAME_ACCESS) +} + +func getStdHandle(stdHandle int32) (syscall.Handle, error) { + r0, _, e1 := syscall.Syscall(procGetStdHandle.Addr(), 2, uintptr(stdHandle), 0, 0) + rh0 := syscall.Handle(r0) + if rh0 == syscall.InvalidHandle { + if e1 != 0 { + return syscall.InvalidHandle, error(e1) + } + return syscall.InvalidHandle, syscall.EINVAL + } + return syscall.Handle(r0), nil +} + +func setStdHandle(stdhandle int32, handle syscall.Handle) error { + r0, _, e1 := syscall.Syscall(procSetStdHandle.Addr(), 2, uintptr(stdhandle), uintptr(handle), 0) + if r0 == 0 { + if e1 != 0 { + return error(e1) + } + return syscall.EINVAL + } + return nil +} + +func redirectStderr(f *os.File) (UndoFunction, error) { + stderrFd, err := getStdHandle(syscall.STD_ERROR_HANDLE) + if err != nil { + return nil, errors.New("Failed to redirect stderr to file: " + err.Error()) + } + + // duplicate the handle to match unix behavior + fHandle, err := dupFD(f.Fd()) + if err != nil { + return nil, errors.New("Failed to duplicate file: " + err.Error()) + } + + err = setStdHandle(syscall.STD_ERROR_HANDLE, fHandle) + if err != nil { + return nil, errors.New("Failed to redirect stderr to file: " + err.Error()) + } + + undo := func() error { + err := setStdHandle(syscall.STD_ERROR_HANDLE, stderrFd) + if err != nil { + return errors.New("Failed to redirect stderr to file: " + err.Error()) + } + syscall.CloseHandle(fHandle) + return nil + } + + return undo, nil +} diff --git a/orbit/pkg/platform/platform_notwindows.go b/orbit/pkg/platform/platform_notwindows.go index 5de106ed47..930e6dc28a 100644 --- a/orbit/pkg/platform/platform_notwindows.go +++ b/orbit/pkg/platform/platform_notwindows.go @@ -54,7 +54,8 @@ func SignalProcessBeforeTerminate(processName string) error { return nil } -// GetProcessByName gets a single process object by its name +// GetProcessByName gets a single running process object by its name. +// Returns ErrProcessNotFound if the process was not found running. func GetProcessByName(name string) (*gopsutil_process.Process, error) { if name == "" { return nil, errors.New("process name should not be empty") diff --git a/orbit/pkg/platform/platform_windows.go b/orbit/pkg/platform/platform_windows.go index 14ce164739..a0fc336b07 100644 --- a/orbit/pkg/platform/platform_windows.go +++ b/orbit/pkg/platform/platform_windows.go @@ -136,16 +136,14 @@ func SignalProcessBeforeTerminate(processName string) error { return fmt.Errorf("get process: %w", err) } - isRunning, err := foundProcess.IsRunning() - if (err == nil) && (isRunning) { - if err := foundProcess.Kill(); err != nil { - return fmt.Errorf("kill process %d: %w", foundProcess.Pid, err) - } + if err := foundProcess.Kill(); err != nil { + return fmt.Errorf("kill process %d: %w", foundProcess.Pid, err) } return nil } -// GetProcessByName gets a single process object by its name +// GetProcessByName gets a single running process object by its name. +// Returns ErrProcessNotFound if the process was not found running. func GetProcessByName(name string) (*gopsutil_process.Process, error) { if name == "" { return nil, errors.New("process name should not be empty") @@ -198,6 +196,11 @@ func GetProcessByName(name string) (*gopsutil_process.Process, error) { return nil, fmt.Errorf("NewProcess: %w", err) } + isRunning, err := process.IsRunning() + if err != nil || !isRunning { + return nil, ErrProcessNotFound + } + return process, nil }