From da39359f9f1e1d20a0aa575db7180c1f52d0342f Mon Sep 17 00:00:00 2001 From: Branden Clark Date: Fri, 6 Jan 2023 18:21:04 -0500 Subject: [PATCH 01/26] move systray --- {cmd => comp/systray}/systray/systray.go | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {cmd => comp/systray}/systray/systray.go (100%) diff --git a/cmd/systray/systray.go b/comp/systray/systray/systray.go similarity index 100% rename from cmd/systray/systray.go rename to comp/systray/systray/systray.go From 82c189280d6c9e091d574375f686d26913b13ae9 Mon Sep 17 00:00:00 2001 From: Branden Clark Date: Wed, 14 Dec 2022 15:48:38 -0500 Subject: [PATCH 02/26] initial boilerplate --- cmd/systray/command/command.go | 59 +++++++++++++++++++++++++++++++ cmd/systray/main_windows.go | 24 +++++++++++++ comp/systray/bundle.go | 29 +++++++++++++++ comp/systray/systray/component.go | 20 +++++++++++ 4 files changed, 132 insertions(+) create mode 100644 cmd/systray/command/command.go create mode 100644 cmd/systray/main_windows.go create mode 100644 comp/systray/bundle.go create mode 100644 comp/systray/systray/component.go diff --git a/cmd/systray/command/command.go b/cmd/systray/command/command.go new file mode 100644 index 00000000000000..e3b63c686fe8b0 --- /dev/null +++ b/cmd/systray/command/command.go @@ -0,0 +1,59 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +// Package command implements the top-level `systray` binary, including its subcommands. +package command + +import ( + "fmt" + "os" + + "github.com/spf13/cobra" + + "github.com/DataDog/datadog-agent/cmd/agent/common" + "github.com/DataDog/datadog-agent/comp/core" + "github.com/DataDog/datadog-agent/comp/systray" + "github.com/DataDog/datadog-agent/pkg/util/fxutil" + "go.uber.org/fx" +) + +// GlobalParams contains the values of systray-global Cobra flags. +type GlobalParams struct { + LaunchGuiFlag bool + LaunchElevatedFlag bool + LaunchCommand string +} + +// MakeCommand makes the top-level Cobra command for this app. +func MakeCommand() *cobra.Command { + globalParams := GlobalParams{} + + // root command + cmd := &cobra.Command{ + Use: fmt.Sprintf("%s", os.Args[0]), + SilenceUsage: true, + RunE: func(cmd *cobra.Command, args[]string) error { + return fxutil.Run( + fx.Supply(core.CreateBundleParams( + common.DefaultConfPath, + // TODO: const/var for log path + ).LogForDaemon("TRAY", "log_file", "C:\\ProgramData\\Datadog\\Logs\\ddtray.log")), + core.Bundle, + systray.Bundle, + ) + }, + } + + cmd.PersistentFlags().BoolVar(&globalParams.LaunchGuiFlag, "launch-gui", false, "Launch browser configuration and exit") + + // launch-elev=true only means the process should have been elevated so that it will not elevate again. If the + // parameter is specified but the process is not elevated, some operation will fail due to access denied. + cmd.PersistentFlags().BoolVar(&globalParams.LaunchElevatedFlag, "launch-elev", false, "Launch program as elevated, internal use only") + + // If this parameter is specified, the process will try to carry out the command before the message loop. + cmd.PersistentFlags().StringVar(&globalParams.LaunchCommand, "launch-cmd", "", "Carry out a specific command after launch") + + return cmd +} diff --git a/cmd/systray/main_windows.go b/cmd/systray/main_windows.go new file mode 100644 index 00000000000000..578d91b822178d --- /dev/null +++ b/cmd/systray/main_windows.go @@ -0,0 +1,24 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +package main + +import ( + "os" + + "github.com/DataDog/datadog-agent/cmd/internal/runcmd" + "github.com/DataDog/datadog-agent/cmd/systray/command" + "github.com/DataDog/datadog-agent/pkg/util/log" +) + +var ( + DefaultConfPath = "c:\\programdata\\datadog" +) + +func main() { + defer log.Flush() + os.Exit(runcmd.Run(command.MakeCommand())) +} + diff --git a/comp/systray/bundle.go b/comp/systray/bundle.go new file mode 100644 index 00000000000000..a9584dc0132612 --- /dev/null +++ b/comp/systray/bundle.go @@ -0,0 +1,29 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +// Package systray implements the "systray" bundle for the systray app. +// +// Including `systray.Module` in an App will automatically start the app. +// +// This bundle depends on comp/core. +package systray + +import ( + "github.com/DataDog/datadog-agent/comp/systray/systray" + "github.com/DataDog/datadog-agent/pkg/util/fxutil" + "go.uber.org/fx" +) + +// team: agent-windows + +// Bundle defines the fx options for this bundle. +var Bundle = fxutil.Bundle( + systray.Module, + + // require the systray component, causing it to start + fx.Invoke(func(_ systray.Component) {}), +) + + diff --git a/comp/systray/systray/component.go b/comp/systray/systray/component.go new file mode 100644 index 00000000000000..9b5a47be65d257 --- /dev/null +++ b/comp/systray/systray/component.go @@ -0,0 +1,20 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +// Package systray +package systray + +import ( + "github.com/DataDog/datadog-agent/pkg/util/fxutil" + "go.uber.org/fx" +) + +type Component interface { + +} + +var Module = fxutil.Component( + fx.Provide(newSystray), +) From 62035cf7f4969699485431730d7559608f1e80cb Mon Sep 17 00:00:00 2001 From: Branden Clark Date: Thu, 15 Dec 2022 19:56:34 -0500 Subject: [PATCH 03/26] moved files into component --- {cmd => comp/systray}/systray/doconfigure.go | 2 +- {cmd => comp/systray}/systray/doflare.go | 2 +- .../systray}/systray/doservicecontrol.go | 2 +- comp/systray/systray/systray.go | 68 ++----------------- comp/systray/systray/uac.c | 66 ++++++++++++++++++ comp/systray/systray/uac.h | 3 + 6 files changed, 76 insertions(+), 67 deletions(-) rename {cmd => comp/systray}/systray/doconfigure.go (99%) rename {cmd => comp/systray}/systray/doflare.go (99%) rename {cmd => comp/systray}/systray/doservicecontrol.go (98%) create mode 100644 comp/systray/systray/uac.c create mode 100644 comp/systray/systray/uac.h diff --git a/cmd/systray/doconfigure.go b/comp/systray/systray/doconfigure.go similarity index 99% rename from cmd/systray/doconfigure.go rename to comp/systray/systray/doconfigure.go index 98fea326296806..8b7420b0fc4d4e 100644 --- a/cmd/systray/doconfigure.go +++ b/comp/systray/systray/doconfigure.go @@ -5,7 +5,7 @@ //go:build windows // +build windows -package main +package systray import ( "encoding/json" diff --git a/cmd/systray/doflare.go b/comp/systray/systray/doflare.go similarity index 99% rename from cmd/systray/doflare.go rename to comp/systray/systray/doflare.go index 33cb5b7dad1952..84b6e7aa67204b 100644 --- a/cmd/systray/doflare.go +++ b/comp/systray/systray/doflare.go @@ -5,7 +5,7 @@ //go:build windows // +build windows -package main +package systray import ( "bytes" diff --git a/cmd/systray/doservicecontrol.go b/comp/systray/systray/doservicecontrol.go similarity index 98% rename from cmd/systray/doservicecontrol.go rename to comp/systray/systray/doservicecontrol.go index 3915d981a46ff6..6e40888acd0ab2 100644 --- a/cmd/systray/doservicecontrol.go +++ b/comp/systray/systray/doservicecontrol.go @@ -6,7 +6,7 @@ //go:build windows // +build windows -package main +package systray import ( "github.com/DataDog/datadog-agent/cmd/agent/windows/controlsvc" diff --git a/comp/systray/systray/systray.go b/comp/systray/systray/systray.go index 414b03b6a59eb6..01574da4b87f0e 100644 --- a/comp/systray/systray/systray.go +++ b/comp/systray/systray/systray.go @@ -5,69 +5,9 @@ //go:build windows // +build windows -package main - -//#include -// -//BOOL LaunchUnelevated(LPCWSTR CommandLine) -//{ -// BOOL result = FALSE; -// HWND hwnd = GetShellWindow(); -// -// if (hwnd != NULL) -// { -// DWORD pid; -// if (GetWindowThreadProcessId(hwnd, &pid) != 0) -// { -// HANDLE process = OpenProcess(PROCESS_CREATE_PROCESS, FALSE, pid); -// -// if (process != NULL) -// { -// SIZE_T size; -// if ((!InitializeProcThreadAttributeList(NULL, 1, 0, &size)) && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) -// { -// LPPROC_THREAD_ATTRIBUTE_LIST p = (LPPROC_THREAD_ATTRIBUTE_LIST)malloc(size); -// if (p != NULL) -// { -// if (InitializeProcThreadAttributeList(p, 1, 0, &size)) -// { -// if (UpdateProcThreadAttribute(p, 0, -// PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, -// &process, sizeof(process), -// NULL, NULL)) -// { -// STARTUPINFOEXW siex = {0}; -// siex.lpAttributeList = p; -// siex.StartupInfo.cb = sizeof(siex); -// PROCESS_INFORMATION pi = {0}; -// -// size_t cmdlen = wcslen(CommandLine); -// size_t rawcmdlen = (cmdlen + 1) * sizeof(WCHAR); -// PWSTR cmdstr = (PWSTR)malloc(rawcmdlen); -// if (cmdstr != NULL) -// { -// memcpy(cmdstr, CommandLine, rawcmdlen); -// if (CreateProcessW(NULL, cmdstr, NULL, NULL, FALSE, -// CREATE_NEW_CONSOLE | EXTENDED_STARTUPINFO_PRESENT, -// NULL, NULL, &siex.StartupInfo, &pi)) -// { -// result = TRUE; -// CloseHandle(pi.hProcess); -// CloseHandle(pi.hThread); -// } -// free(cmdstr); -// } -// } -// } -// free(p); -// } -// } -// CloseHandle(process); -// } -// } -// } -// return result; -//} +package systray + +//#include "uac.h" import "C" import ( @@ -321,7 +261,7 @@ func open(url string) error { cmdptr := windows.StringToUTF16Ptr("rundll32.exe url.dll,FileProtocolHandler " + url) if C.LaunchUnelevated(C.LPCWSTR(unsafe.Pointer(cmdptr))) == 0 { // Failed to run process non-elevated, retry with normal launch. - log.Warnf("Failed to launch configuration page as non-elevated, will launch as current process.") + pkglog.Warnf("Failed to launch configuration page as non-elevated, will launch as current process.") return exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start() } diff --git a/comp/systray/systray/uac.c b/comp/systray/systray/uac.c new file mode 100644 index 00000000000000..f51e4db33c7c78 --- /dev/null +++ b/comp/systray/systray/uac.c @@ -0,0 +1,66 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +#include "uac.h" + +BOOL LaunchUnelevated(LPCWSTR CommandLine) +{ + BOOL result = FALSE; + HWND hwnd = GetShellWindow(); + + if (hwnd != NULL) + { + DWORD pid; + if (GetWindowThreadProcessId(hwnd, &pid) != 0) + { + HANDLE process = OpenProcess(PROCESS_CREATE_PROCESS, FALSE, pid); + + if (process != NULL) + { + SIZE_T size; + if ((!InitializeProcThreadAttributeList(NULL, 1, 0, &size)) && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) + { + LPPROC_THREAD_ATTRIBUTE_LIST p = (LPPROC_THREAD_ATTRIBUTE_LIST)malloc(size); + if (p != NULL) + { + if (InitializeProcThreadAttributeList(p, 1, 0, &size)) + { + if (UpdateProcThreadAttribute(p, 0, + PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, + &process, sizeof(process), + NULL, NULL)) + { + STARTUPINFOEXW siex = {0}; + siex.lpAttributeList = p; + siex.StartupInfo.cb = sizeof(siex); + PROCESS_INFORMATION pi = {0}; + + size_t cmdlen = wcslen(CommandLine); + size_t rawcmdlen = (cmdlen + 1) * sizeof(WCHAR); + PWSTR cmdstr = (PWSTR)malloc(rawcmdlen); + if (cmdstr != NULL) + { + memcpy(cmdstr, CommandLine, rawcmdlen); + if (CreateProcessW(NULL, cmdstr, NULL, NULL, FALSE, + CREATE_NEW_CONSOLE | EXTENDED_STARTUPINFO_PRESENT, + NULL, NULL, &siex.StartupInfo, &pi)) + { + result = TRUE; + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + } + free(cmdstr); + } + } + } + free(p); + } + } + CloseHandle(process); + } + } + } + return result; +} diff --git a/comp/systray/systray/uac.h b/comp/systray/systray/uac.h new file mode 100644 index 00000000000000..b1471820e44a26 --- /dev/null +++ b/comp/systray/systray/uac.h @@ -0,0 +1,3 @@ +#include + +BOOL LaunchUnelevated(LPCWSTR CommandLine); From 2ee7d357c25e2842b161738b8e89745bf1845de3 Mon Sep 17 00:00:00 2001 From: Branden Clark Date: Fri, 30 Dec 2022 14:37:25 -0500 Subject: [PATCH 04/26] Basic functionality --- cmd/systray/command/command.go | 32 +-- cmd/systray/main_windows.go | 12 +- comp/systray/bundle.go | 4 + comp/systray/internal/params.go | 13 ++ comp/systray/systray/systray.go | 347 ++++++++++++++++++++------------ comp/systray/systray/uac.c | 18 ++ pkg/util/winutil/users.go | 21 ++ 7 files changed, 296 insertions(+), 151 deletions(-) create mode 100644 comp/systray/internal/params.go diff --git a/cmd/systray/command/command.go b/cmd/systray/command/command.go index e3b63c686fe8b0..fa088a8abb6369 100644 --- a/cmd/systray/command/command.go +++ b/cmd/systray/command/command.go @@ -9,6 +9,7 @@ package command import ( "fmt" "os" + "path/filepath" "github.com/spf13/cobra" @@ -16,19 +17,26 @@ import ( "github.com/DataDog/datadog-agent/comp/core" "github.com/DataDog/datadog-agent/comp/systray" "github.com/DataDog/datadog-agent/pkg/util/fxutil" + "github.com/DataDog/datadog-agent/pkg/util/winutil" "go.uber.org/fx" ) -// GlobalParams contains the values of systray-global Cobra flags. -type GlobalParams struct { - LaunchGuiFlag bool - LaunchElevatedFlag bool - LaunchCommand string -} +const ( + defaultLogFile = "c:\\programdata\\datadog\\logs\\ddtray.log" +) // MakeCommand makes the top-level Cobra command for this app. func MakeCommand() *cobra.Command { - globalParams := GlobalParams{} + systrayParams := systray.BundleParams{} + + // log file path + var logFilePath string + confPath, err := winutil.GetProgramDataDir() + if err == nil { + logFilePath = filepath.Join(confPath, "logs", "ddtray.log") + } else { + logFilePath = defaultLogFile + } // root command cmd := &cobra.Command{ @@ -38,22 +46,22 @@ func MakeCommand() *cobra.Command { return fxutil.Run( fx.Supply(core.CreateBundleParams( common.DefaultConfPath, - // TODO: const/var for log path - ).LogForDaemon("TRAY", "log_file", "C:\\ProgramData\\Datadog\\Logs\\ddtray.log")), + ).LogForDaemon("TRAY", "log_file", logFilePath)), core.Bundle, + fx.Supply(systrayParams), systray.Bundle, ) }, } - cmd.PersistentFlags().BoolVar(&globalParams.LaunchGuiFlag, "launch-gui", false, "Launch browser configuration and exit") + cmd.PersistentFlags().BoolVar(&systrayParams.LaunchGuiFlag, "launch-gui", false, "Launch browser configuration and exit") // launch-elev=true only means the process should have been elevated so that it will not elevate again. If the // parameter is specified but the process is not elevated, some operation will fail due to access denied. - cmd.PersistentFlags().BoolVar(&globalParams.LaunchElevatedFlag, "launch-elev", false, "Launch program as elevated, internal use only") + cmd.PersistentFlags().BoolVar(&systrayParams.LaunchElevatedFlag, "launch-elev", false, "Launch program as elevated, internal use only") // If this parameter is specified, the process will try to carry out the command before the message loop. - cmd.PersistentFlags().StringVar(&globalParams.LaunchCommand, "launch-cmd", "", "Carry out a specific command after launch") + cmd.PersistentFlags().StringVar(&systrayParams.LaunchCommand, "launch-cmd", "", "Carry out a specific command after launch") return cmd } diff --git a/cmd/systray/main_windows.go b/cmd/systray/main_windows.go index 578d91b822178d..04fca85f15c87c 100644 --- a/cmd/systray/main_windows.go +++ b/cmd/systray/main_windows.go @@ -13,12 +13,12 @@ import ( "github.com/DataDog/datadog-agent/pkg/util/log" ) -var ( - DefaultConfPath = "c:\\programdata\\datadog" -) - func main() { - defer log.Flush() - os.Exit(runcmd.Run(command.MakeCommand())) + exitcode := 0 + defer func() { + log.Flush() + os.Exit(exitcode) + }() + exitcode = runcmd.Run(command.MakeCommand()) } diff --git a/comp/systray/bundle.go b/comp/systray/bundle.go index a9584dc0132612..4ba010d0e42379 100644 --- a/comp/systray/bundle.go +++ b/comp/systray/bundle.go @@ -12,12 +12,16 @@ package systray import ( "github.com/DataDog/datadog-agent/comp/systray/systray" + "github.com/DataDog/datadog-agent/comp/systray/internal" "github.com/DataDog/datadog-agent/pkg/util/fxutil" "go.uber.org/fx" ) // team: agent-windows +// BundleParams defines the parameters for this bundle +type BundleParams = internal.BundleParams + // Bundle defines the fx options for this bundle. var Bundle = fxutil.Bundle( systray.Module, diff --git a/comp/systray/internal/params.go b/comp/systray/internal/params.go new file mode 100644 index 00000000000000..44737b1eba1def --- /dev/null +++ b/comp/systray/internal/params.go @@ -0,0 +1,13 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +package internal + +type BundleParams struct { + LaunchGuiFlag bool + LaunchElevatedFlag bool + LaunchCommand string +} + diff --git a/comp/systray/systray/systray.go b/comp/systray/systray/systray.go index 01574da4b87f0e..1daef714a9691e 100644 --- a/comp/systray/systray/systray.go +++ b/comp/systray/systray/systray.go @@ -11,25 +11,56 @@ package systray import "C" import ( - "flag" + "context" "fmt" "os" "os/exec" "runtime" "strings" + "sync" "time" "unsafe" - seelog "github.com/cihub/seelog" - - "github.com/DataDog/datadog-agent/pkg/util/log" - + "github.com/DataDog/datadog-agent/comp/core/log" + "github.com/DataDog/datadog-agent/comp/systray/internal" + pkglog "github.com/DataDog/datadog-agent/pkg/util/log" + "github.com/DataDog/datadog-agent/pkg/util/winutil" "github.com/DataDog/datadog-agent/pkg/version" + "go.uber.org/fx" "github.com/lxn/walk" + "github.com/lxn/win" "golang.org/x/sys/windows" ) +type dependencies struct { + fx.In + + Lc fx.Lifecycle + Shutdowner fx.Shutdowner + + Log log.Component + Params internal.BundleParams +} + +type systray struct { + // For triggering Shutdown + shutdowner fx.Shutdowner + + log log.Component + params internal.BundleParams + + isUserAdmin bool + + // allocated in start, destroyed in stop + singletonEventHandle windows.Handle + + // Window management + notifyWindowToStop func() + routineWaitGroup sync.WaitGroup + +} + type menuItem struct { label string handler walk.EventHandler @@ -37,22 +68,17 @@ type menuItem struct { } const ( + launchGraceTime = 2 + eventname = "ddtray-event" cmdTextStartService = "StartService" cmdTextStopService = "StopService" cmdTextRestartService = "RestartService" cmdTextConfig = "Config" + menuSeparator = "SEPARATOR" ) var ( - separator = "SEPARATOR" - launchGraceTime = 2 - ni *walk.NotifyIcon - launchgui bool - launchelev bool - launchcmd string - eventname = windows.StringToUTF16Ptr("ddtray-event") - isUserAdmin bool - cmds = map[string]func(){ + cmds = map[string]func(){ cmdTextStartService: onStart, cmdTextStopService: onStop, cmdTextRestartService: onRestart, @@ -60,27 +86,14 @@ var ( } ) -func init() { - enableLoggingToFile() - - isAdmin, err := isUserAnAdmin() - isUserAdmin = isAdmin - - if err != nil { - log.Warnf("Failed to call isUserAnAdmin %v", err) - // If we cannot determine if the user is admin or not let the user allow to click on the buttons. - isUserAdmin = true - } -} - -func createMenuItems(notifyIcon *walk.NotifyIcon) []menuItem { - av, _ := version.Agent() - verstring := av.GetNumberAndPre() - - menuHandler := func(cmd string) func() { - return func() { - execCmdOrElevate(cmd) - } +// newSystray creates a new systray component, which will start and stop based on +// the fx Lifecycle +func newSystray(deps dependencies) (Component, error) { + // fx init + s := &systray{ + log: deps.Log, + params: deps.Params, + shutdowner: deps.Shutdowner, } menuitems := make([]menuItem, 0) @@ -94,64 +107,113 @@ func createMenuItems(notifyIcon *walk.NotifyIcon) []menuItem { menuitems = append(menuitems, menuItem{label: separator}) menuitems = append(menuitems, menuItem{label: "E&xit", handler: onExit, enabled: true}) - return menuitems + // init vars + isAdmin, err := winutil.IsUserAnAdmin() + if err != nil { + s.log.Warnf("Failed to call IsUserAnAdmin %v", err) + // If we cannot determine if the user is admin or not let the user allow to click on the buttons. + s.isUserAdmin = true + } else { + s.isUserAdmin = isAdmin + } + + return s, nil } -func isUserAnAdmin() (bool, error) { - shell32 := windows.NewLazySystemDLL("Shell32.dll") - defer windows.FreeLibrary(windows.Handle(shell32.Handle())) +func (s *systray) start(ctx context.Context) error { + var err error - isUserAnAdminProc := shell32.NewProc("IsUserAnAdmin") - ret, _, winError := isUserAnAdminProc.Call() + s.log.Debugf("launch-gui is %v, launch-elev is %v, launch-cmd is %v", s.params.LaunchGuiFlag, s.params.LaunchElevatedFlag, s.params.LaunchCommand) - if winError != windows.NTE_OP_OK { - return false, fmt.Errorf("IsUserAnAdmin returns error code %d", winError) + if s.params.LaunchGuiFlag { + s.log.Debug("Preparing to launch configuration interface...") + onConfigure() } - if ret == 0 { - return false, nil + + s.singletonEventHandle, err = acquireProcessSingleton(eventname) + if err != nil { + s.log.Errorf("Failed to acquire singleton %v", err) + return err } - return true, nil -} -func showCustomMessage(notifyIcon *walk.NotifyIcon, message string) { - if err := notifyIcon.ShowCustom("Datadog Agent Manager", message, nil); err != nil { - log.Warnf("Failed to show custom message %v", err) + s.routineWaitGroup.Add(1) + go windowRoutine(s) + + // If a command is specified in process command line, carry it out. + if s.params.LaunchCommand != "" { + go execCmdOrElevate(s, s.params.LaunchCommand) } + + return nil } -func onExit() { - walk.App().Exit(0) +func (s *systray) stop(ctx context.Context) error { + if s.notifyWindowToStop != nil { + // Send stop message to window (stops windowRoutine goroutine) + s.notifyWindowToStop() + } + + // wait for goroutine to finish + s.log.Info("starting wait") + s.routineWaitGroup.Wait() + s.log.Info("routine done!") + + // release our singleton + if s.singletonEventHandle != windows.Handle(0) { + windows.CloseHandle(s.singletonEventHandle) + } + + return nil } -func main() { +// Run window setup and message loop in a single threadlocked goroutine +// https://github.com/lxn/walk/issues/601 +// Use the notifyWindowToStop function to stop the message loop +// Use routineWaitGroup to wait until the routine exits +func windowRoutine(s *systray) { // Following https://github.com/lxn/win/commit/d9566253ae00d0a7dc7e4c9bda651dcfee029001 // it's up to the caller to lock OS threads runtime.LockOSThread() defer runtime.UnlockOSThread() - flag.BoolVar(&launchgui, "launch-gui", false, "Launch browser configuration and exit") + defer s.routineWaitGroup.Done() - // launch-elev=true only means the process should have been elevated so that it will not elevate again. If the - // parameter is specified but the process is not elevated, some operation will fail due to access denied. - flag.BoolVar(&launchelev, "launch-elev", false, "Launch program as elevated, internal use only") - - // If this parameter is specified, the process will try to carry out the command before the message loop. - flag.StringVar(&launchcmd, "launch-cmd", "", "Carry out a specific command after launch") - flag.Parse() + // We need either a walk.MainWindow or a walk.Dialog for their message loop. + mw, err := walk.NewMainWindow() + if err != nil { + s.log.Errorf("Failed to create main window %v", err) + return + } + defer mw.Dispose() - log.Debugf("launch-gui is %v, launch-elev is %v, launch-cmd is %v", launchgui, launchelev, launchcmd) + ni, err := createNotifyIcon(s, mw) + if err != nil { + s.log.Errorf("Failed to create notification tray icon %v", err) + return + } + defer ni.Dispose() - if launchgui { - //enableLoggingToConsole() - defer log.Flush() - log.Debug("Preparing to launch configuration interface...") - onConfigure() + // Provide a function that will trigger this thread to run PostQuitMessage() + // which will cause the message loop to return + s.notifyWindowToStop = func() { + mw.Synchronize(func() { + win.PostQuitMessage(0) + }) } + // Run the message loop + // use the notifyWindowToStop function to stop the message loop + mw.Run() + s.log.Info("routine exiting!") +} + +func acquireProcessSingleton(eventname string) (windows.Handle, error) { + var utf16EventName = windows.StringToUTF16Ptr(eventname) + // Check to see if the process is already running - h, _ := windows.OpenEvent(0x1F0003, // EVENT_ALL_ACCESS + h, _ := windows.OpenEvent(windows.EVENT_ALL_ACCESS, false, - eventname) + utf16EventName) if h != windows.Handle(0) { // Process already running. @@ -161,49 +223,55 @@ func main() { time.Sleep(time.Duration(launchGraceTime) * time.Second) // Try again - h, _ := windows.OpenEvent(0x1F0003, // EVENT_ALL_ACCESS + h, _ := windows.OpenEvent(windows.EVENT_ALL_ACCESS, false, - eventname) + utf16EventName) if h != windows.Handle(0) { windows.CloseHandle(h) - return + return windows.Handle(0), fmt.Errorf("systray is already running") } } // otherwise, create the handle so that nobody else will - h, _ = windows.CreateEvent(nil, 0, 0, eventname) - // should never fail; test just to make sure we don't close unopened handle - if h != windows.Handle(0) { - defer windows.CloseHandle(h) - } - // We need either a walk.MainWindow or a walk.Dialog for their message loop. - // We will not make it visible in this example, though. - mw, err := walk.NewMainWindow() + h, err := windows.CreateEvent(nil, 0, 0, utf16EventName) if err != nil { - log.Errorf("Failed to create main window %v", err) - os.Exit(1) + // can fail with ERROR_ALREADY_EXISTS if we lost a race + if h != windows.Handle(0) { + windows.CloseHandle(h) + } + return windows.Handle(0), err } - // 1 is the ID of the MAIN_ICON in systray.rc - icon, err := walk.NewIconFromResourceId(1) - if err != nil { - log.Warnf("Failed to load icon %v", err) - } - // Create the notify icon and make sure we clean it up on exit. + return h, nil +} + +func createNotifyIcon(s *systray, mw *walk.MainWindow) (ni *walk.NotifyIcon, err error) { + // Create the notify icon (must be cleaned up) ni, err = walk.NewNotifyIcon(mw) if err != nil { - log.Errorf("Failed to create newNotifyIcon %v", err) - os.Exit(2) + return nil, err } - defer ni.Dispose() + defer func () { + if err != nil { + ni.Dispose() + ni = nil + } + }() // Set the icon and a tool tip text. + // 1 is the ID of the MAIN_ICON in systray.rc + icon, err := walk.NewIconFromResourceId(1) + if err != nil { + pkglog.Warnf("Failed to load icon %v", err) + } if err := ni.SetIcon(icon); err != nil { - log.Warnf("Failed to set icon %v", err) + pkglog.Warnf("Failed to set icon %v", err) } + + // Set mouseover tooltip if err := ni.SetToolTip("Click for info or use the context menu to exit."); err != nil { - log.Warnf("Failed to set tooltip text %v", err) + pkglog.Warnf("Failed to set tooltip text %v", err) } // When the left mouse button is pressed, bring up our balloon. @@ -214,21 +282,21 @@ func main() { showCustomMessage(ni, "Please right click to display available options.") }) - menuitems := createMenuItems(ni) + menuitems := createMenuItems(s, ni) for _, item := range menuitems { var action *walk.Action - if item.label == separator { + if item.label == menuSeparator { action = walk.NewSeparatorAction() } else { action = walk.NewAction() if err := action.SetText(item.label); err != nil { - log.Warnf("Failed to set text for item %s %v", item.label, err) + pkglog.Warnf("Failed to set text for item %s %v", item.label, err) continue } err = action.SetEnabled(item.enabled) if err != nil { - log.Warnf("Failed to set enabled for item %s %v", item.label, err) + pkglog.Warnf("Failed to set enabled for item %s %v", item.label, err) continue } if item.handler != nil { @@ -237,23 +305,59 @@ func main() { } err = ni.ContextMenu().Actions().Add(action) if err != nil { - log.Warnf("Failed to add action for item %s to context menu %v", item.label, err) + pkglog.Warnf("Failed to add action for item %s to context menu %v", item.label, err) continue } } // The notify icon is hidden initially, so we have to make it visible. if err := ni.SetVisible(true); err != nil { - log.Warnf("Failed to set window visibility %v", err) + pkglog.Warnf("Failed to set window visibility %v", err) } - // If a command is specified in process command line, carry it out. - if launchcmd != "" { - execCmdOrElevate(launchcmd) + + return ni, nil +} + +func showCustomMessage(notifyIcon *walk.NotifyIcon, message string) { + if err := notifyIcon.ShowCustom("Datadog Agent Manager", message, nil); err != nil { + pkglog.Warnf("Failed to show custom message %v", err) } +} - // Run the message loop. - mw.Run() +func triggerShutdown(s *systray) { + if s != nil { + // Tell fx to begin shutdown process + s.shutdowner.Shutdown() + } +} + +func onExit(s *systray) { + triggerShutdown(s) +} + +func createMenuItems(s *systray, notifyIcon *walk.NotifyIcon) []menuItem { + av, _ := version.Agent() + verstring := av.GetNumberAndPre() + + menuHandler := func(cmd string) func() { + return func() { + execCmdOrElevate(s, cmd) + } + } + + menuitems := make([]menuItem, 0) + menuitems = append(menuitems, menuItem{label: verstring, enabled: false}) + menuitems = append(menuitems, menuItem{label: menuSeparator}) + menuitems = append(menuitems, menuItem{label: "&Start", handler: menuHandler(cmdTextStartService), enabled: true}) + menuitems = append(menuitems, menuItem{label: "S&top", handler: menuHandler(cmdTextStopService), enabled: true}) + menuitems = append(menuitems, menuItem{label: "&Restart", handler: menuHandler(cmdTextRestartService), enabled: true}) + menuitems = append(menuitems, menuItem{label: "&Configure", handler: menuHandler(cmdTextConfig), enabled: true}) + menuitems = append(menuitems, menuItem{label: "&Flare", handler: onFlare, enabled: true}) + menuitems = append(menuitems, menuItem{label: menuSeparator}) + menuitems = append(menuitems, menuItem{label: "E&xit", handler: func() { onExit(s) }, enabled: true}) + + return menuitems } // opens a browser window at the specified URL @@ -269,35 +373,12 @@ func open(url string) error { return nil } -func enableLoggingToFile() { - seeConfig := ` - - - - - ` - logger, _ := seelog.LoggerFromConfigAsBytes([]byte(seeConfig)) - log.ReplaceLogger(logger) -} - -//nolint:deadcode // for debugging -func enableLoggingToConsole() { - seeConfig := ` - - - - - ` - logger, _ := seelog.LoggerFromConfigAsBytes([]byte(seeConfig)) - log.ReplaceLogger(logger) -} - // execCmdOrElevate carries out a command. If current process is not elevated and is not supposed to be elevated, it will launch // itself as elevated and quit from the current instance. -func execCmdOrElevate(cmd string) { - if !launchelev && !isUserAdmin { +func execCmdOrElevate(s *systray, cmd string) { + if !s.params.LaunchElevatedFlag && !s.isUserAdmin { // If not launched as elevated and user is not admin, relaunch self. Use AND here to prevent from dead loop. - relaunchElevated(cmd) + relaunchElevated(s, cmd) // If elevation failed, just quit to the caller. return @@ -310,7 +391,7 @@ func execCmdOrElevate(cmd string) { // relaunchElevated launch another instance of the current process asking it to carry out a command as admin. // If the function succeeds, it will quit the process, otherwise the function will return to the caller. -func relaunchElevated(cmd string) { +func relaunchElevated(s *systray, cmd string) { verb := "runas" exe, _ := os.Executable() cwd, _ := os.Getwd() @@ -328,8 +409,8 @@ func relaunchElevated(cmd string) { err := windows.ShellExecute(0, verbPtr, exePtr, argPtr, cwdPtr, showCmd) if err != nil { - log.Warnf("Failed to launch self as elevated %v", err) + pkglog.Warnf("Failed to launch self as elevated %v", err) } else { - onExit() + triggerShutdown(s) } } diff --git a/comp/systray/systray/uac.c b/comp/systray/systray/uac.c index f51e4db33c7c78..1c0e543a9018e8 100644 --- a/comp/systray/systray/uac.c +++ b/comp/systray/systray/uac.c @@ -5,20 +5,38 @@ #include "uac.h" +// Attempts to drop privileges from an elevated process by creating a new process and +// spoofing the parent PID to be explorer.exe. This causes the new process to inherit +// its access token from explorer.exe. +// +// Technique relies on having permission to open explorer.exe with PROCESS_CREATE_PROCESS, +// this access is verified against the explorer.exe process DACL. +// Generally, +// If the current process was elevated via a consent prompt, the user account is the same and access will be granted. +// If the current process was elevated via a credential prompt, the user account is different and access will be denied. +// https://learn.microsoft.com/en-us/windows/security/identity-protection/user-account-control/how-user-account-control-works +// +// TODO: Try to enable SeDebugPrivilege. This will allow this function to support credential prompts +// if group policy has not been modified to remove SeDebugPrivilege from Administrators. BOOL LaunchUnelevated(LPCWSTR CommandLine) { BOOL result = FALSE; + // Get handle to the Shell's desktop window + // https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getshellwindow HWND hwnd = GetShellWindow(); if (hwnd != NULL) { DWORD pid; + // Get pid that created the window, this should be PID of explorer.exe if (GetWindowThreadProcessId(hwnd, &pid) != 0) { HANDLE process = OpenProcess(PROCESS_CREATE_PROCESS, FALSE, pid); if (process != NULL) { + // Create thread attribute list containing PROC_THREAD_ATTRIBUTE_PARENT_PROCESS + // to spoof the parent process. SIZE_T size; if ((!InitializeProcThreadAttributeList(NULL, 1, 0, &size)) && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) { diff --git a/pkg/util/winutil/users.go b/pkg/util/winutil/users.go index 51a89437cf6ffe..a0b48c3b96eeaf 100644 --- a/pkg/util/winutil/users.go +++ b/pkg/util/winutil/users.go @@ -8,6 +8,7 @@ package winutil import ( + "fmt" "syscall" "golang.org/x/sys/windows" @@ -40,3 +41,23 @@ func GetSidFromUser() (*windows.SID, error) { return windows.StringToSid(sidString) } + +// Returns true is a user is a member of the Administrator's group +// TODO: Microsoft does not recommend using this function, instead CheckTokenMembership should be used. +// https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-isuseranadmin +func IsUserAnAdmin() (bool, error) { + shell32 := windows.NewLazySystemDLL("Shell32.dll") + defer windows.FreeLibrary(windows.Handle(shell32.Handle())) + + isUserAnAdminProc := shell32.NewProc("IsUserAnAdmin") + ret, _, winError := isUserAnAdminProc.Call() + + if winError != windows.NTE_OP_OK { + return false, fmt.Errorf("IsUserAnAdmin returns error code %d", winError) + } + if ret == 0 { + return false, nil + } + return true, nil +} + From 741dfeaca6bd6af13dceeb97080146661a2fd93f Mon Sep 17 00:00:00 2001 From: Branden Clark Date: Tue, 3 Jan 2023 14:50:28 -0500 Subject: [PATCH 05/26] update cmd function definitions to use component --- comp/systray/systray/doconfigure.go | 26 ++++++++---------------- comp/systray/systray/doservicecontrol.go | 13 ++++++------ comp/systray/systray/systray.go | 10 ++++++--- 3 files changed, 22 insertions(+), 27 deletions(-) diff --git a/comp/systray/systray/doconfigure.go b/comp/systray/systray/doconfigure.go index 8b7420b0fc4d4e..c6321e10ae986b 100644 --- a/comp/systray/systray/doconfigure.go +++ b/comp/systray/systray/doconfigure.go @@ -11,31 +11,23 @@ import ( "encoding/json" "fmt" - "github.com/DataDog/datadog-agent/cmd/agent/common" "github.com/DataDog/datadog-agent/pkg/api/security" "github.com/DataDog/datadog-agent/pkg/api/util" - "github.com/DataDog/datadog-agent/pkg/config" - - "github.com/DataDog/datadog-agent/pkg/util/log" + pkgconfig "github.com/DataDog/datadog-agent/pkg/config" ) -func onConfigure() { +func onConfigure(s *systray) { // seems like a waste. However, the handler function doesn't expect an error code. // this just eats the error code. - err := doConfigure() + err := doConfigure(s) if err != nil { - log.Warnf("Failed to launch gui %v", err) + s.log.Warnf("Failed to launch gui %v", err) } return } -func doConfigure() error { - - err := common.SetupConfigWithoutSecrets("", "") - if err != nil { - return fmt.Errorf("unable to set up global agent configuration: %v", err) - } +func doConfigure(s *systray) error { - guiPort := config.Datadog.GetString("GUI_port") + guiPort := s.config.GetString("GUI_port") if guiPort == "-1" { return fmt.Errorf("GUI not enabled: to enable, please set an appropriate port in your datadog.yaml file") } @@ -48,11 +40,11 @@ func doConfigure() error { // Get the CSRF token from the agent c := util.GetClient(false) // FIX: get certificates right then make this true - ipcAddress, err := config.GetIPCAddress() + ipcAddress, err := pkgconfig.GetIPCAddress() if err != nil { return err } - urlstr := fmt.Sprintf("https://%v:%v/agent/gui/csrf-token", ipcAddress, config.Datadog.GetInt("cmd_port")) + urlstr := fmt.Sprintf("https://%v:%v/agent/gui/csrf-token", ipcAddress, s.config.GetInt("cmd_port")) err = util.SetAuthToken() if err != nil { return err @@ -74,6 +66,6 @@ func doConfigure() error { return fmt.Errorf("error opening GUI: " + err.Error()) } - log.Debugf("GUI opened at 127.0.0.1:" + guiPort + "\n") + s.log.Debugf("GUI opened at 127.0.0.1:" + guiPort + "\n") return nil } diff --git a/comp/systray/systray/doservicecontrol.go b/comp/systray/systray/doservicecontrol.go index 6e40888acd0ab2..a92f148ee11701 100644 --- a/comp/systray/systray/doservicecontrol.go +++ b/comp/systray/systray/doservicecontrol.go @@ -10,25 +10,24 @@ package systray import ( "github.com/DataDog/datadog-agent/cmd/agent/windows/controlsvc" - "github.com/DataDog/datadog-agent/pkg/util/log" ) -func onRestart() { +func onRestart(s *systray) { if err := controlsvc.RestartService(); err != nil { - log.Warnf("Failed to restart datadog service %v", err) + s.log.Warnf("Failed to restart datadog service %v", err) } } -func onStart() { +func onStart(s *systray) { if err := controlsvc.StartService(); err != nil { - log.Warnf("Failed to start datadog service %v", err) + s.log.Warnf("Failed to start datadog service %v", err) } } -func onStop() { +func onStop(s *systray) { if err := controlsvc.StopService(); err != nil { - log.Warnf("Failed to stop datadog service %v", err) + s.log.Warnf("Failed to stop datadog service %v", err) } } diff --git a/comp/systray/systray/systray.go b/comp/systray/systray/systray.go index 1daef714a9691e..d9aa797efe8fe0 100644 --- a/comp/systray/systray/systray.go +++ b/comp/systray/systray/systray.go @@ -21,6 +21,7 @@ import ( "time" "unsafe" + "github.com/DataDog/datadog-agent/comp/core/config" "github.com/DataDog/datadog-agent/comp/core/log" "github.com/DataDog/datadog-agent/comp/systray/internal" pkglog "github.com/DataDog/datadog-agent/pkg/util/log" @@ -40,6 +41,7 @@ type dependencies struct { Shutdowner fx.Shutdowner Log log.Component + Config config.Component Params internal.BundleParams } @@ -48,6 +50,7 @@ type systray struct { shutdowner fx.Shutdowner log log.Component + config config.Component params internal.BundleParams isUserAdmin bool @@ -78,7 +81,7 @@ const ( ) var ( - cmds = map[string]func(){ + cmds = map[string]func(*systray){ cmdTextStartService: onStart, cmdTextStopService: onStop, cmdTextRestartService: onRestart, @@ -92,6 +95,7 @@ func newSystray(deps dependencies) (Component, error) { // fx init s := &systray{ log: deps.Log, + config: deps.Config, params: deps.Params, shutdowner: deps.Shutdowner, } @@ -127,7 +131,7 @@ func (s *systray) start(ctx context.Context) error { if s.params.LaunchGuiFlag { s.log.Debug("Preparing to launch configuration interface...") - onConfigure() + onConfigure(s) } s.singletonEventHandle, err = acquireProcessSingleton(eventname) @@ -385,7 +389,7 @@ func execCmdOrElevate(s *systray, cmd string) { } if cmds[cmd] != nil { - cmds[cmd]() + cmds[cmd](s) } } From 9a8a325182574d6c2dab50489e699460d1173cae Mon Sep 17 00:00:00 2001 From: Branden Clark Date: Tue, 3 Jan 2023 15:32:25 -0500 Subject: [PATCH 06/26] rebase og params --- cmd/systray/command/command.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/cmd/systray/command/command.go b/cmd/systray/command/command.go index fa088a8abb6369..aeacde4523beb6 100644 --- a/cmd/systray/command/command.go +++ b/cmd/systray/command/command.go @@ -15,6 +15,8 @@ import ( "github.com/DataDog/datadog-agent/cmd/agent/common" "github.com/DataDog/datadog-agent/comp/core" + "github.com/DataDog/datadog-agent/comp/core/config" + "github.com/DataDog/datadog-agent/comp/core/log" "github.com/DataDog/datadog-agent/comp/systray" "github.com/DataDog/datadog-agent/pkg/util/fxutil" "github.com/DataDog/datadog-agent/pkg/util/winutil" @@ -44,9 +46,10 @@ func MakeCommand() *cobra.Command { SilenceUsage: true, RunE: func(cmd *cobra.Command, args[]string) error { return fxutil.Run( - fx.Supply(core.CreateBundleParams( - common.DefaultConfPath, - ).LogForDaemon("TRAY", "log_file", logFilePath)), + fx.Supply(core.BundleParams{ + ConfigParams: config.NewParams(common.DefaultConfPath), + LogParams: log.LogForDaemon("TRAY", "log_file", logFilePath), + }), core.Bundle, fx.Supply(systrayParams), systray.Bundle, From 067375092acb31c9ad6277870c9b4836e35f50b0 Mon Sep 17 00:00:00 2001 From: Branden Clark Date: Tue, 3 Jan 2023 16:30:25 -0500 Subject: [PATCH 07/26] use flare component --- comp/systray/systray/doflare.go | 49 ++++++++++++------------ comp/systray/systray/doservicecontrol.go | 4 +- comp/systray/systray/systray.go | 6 ++- 3 files changed, 30 insertions(+), 29 deletions(-) diff --git a/comp/systray/systray/doflare.go b/comp/systray/systray/doflare.go index 84b6e7aa67204b..d8c145d880e395 100644 --- a/comp/systray/systray/doflare.go +++ b/comp/systray/systray/doflare.go @@ -21,8 +21,8 @@ import ( "github.com/DataDog/datadog-agent/cmd/agent/common" "github.com/DataDog/datadog-agent/pkg/api/util" "github.com/DataDog/datadog-agent/pkg/config" - "github.com/DataDog/datadog-agent/pkg/flare" - "github.com/DataDog/datadog-agent/pkg/util/log" + pkgflare "github.com/DataDog/datadog-agent/pkg/flare" + pkglog "github.com/DataDog/datadog-agent/pkg/util/log" ) const ( @@ -83,11 +83,11 @@ func dialogProc(hwnd win.HWND, msg uint32, wParam, lParam uintptr) (result uintp x, y, _, _ := calcPos(wndrect, dlgrect) r, _, err = procSetWindowPos.Call(uintptr(hwnd), 0, uintptr(x), uintptr(y), uintptr(0), uintptr(0), uintptr(0x0041)) if r != 0 { - log.Debugf("failed to set window pos %v", err) + pkglog.Debugf("failed to set window pos %v", err) } } } else { - log.Debugf("failed to get pos %v", err) + pkglog.Debugf("failed to get pos %v", err) } } // set the "OK" to disabled until there's something approximating an email @@ -122,11 +122,11 @@ func dialogProc(hwnd win.HWND, msg uint32, wParam, lParam uintptr) (result uintp win.SendDlgItemMessage(hwnd, IDC_TICKET_EDIT, win.WM_GETTEXT, 255, uintptr(unsafe.Pointer(&buf[0]))) info.caseid = windows.UTF16ToString(buf) - log.Debugf("ticket number %s", info.caseid) + pkglog.Debugf("ticket number %s", info.caseid) win.SendDlgItemMessage(hwnd, IDC_EMAIL_EDIT, win.WM_GETTEXT, 255, uintptr(unsafe.Pointer(&buf[0]))) info.email = windows.UTF16ToString(buf) - log.Debugf("email %s", info.email) + pkglog.Debugf("email %s", info.email) win.EndDialog(hwnd, win.IDOK) return uintptr(1) @@ -137,21 +137,22 @@ func dialogProc(hwnd win.HWND, msg uint32, wParam, lParam uintptr) (result uintp } return uintptr(0) } -func onFlare() { +func onFlare(s *systray) { + // Create a dialog box to prompt for ticket number and email, then create and submit the flare // library will allow multiple calls (multi-threaded window proc?) // however, we're using a single instance of the info structure to // pass data around. Don't allow multiple dialogs to be displayed // (in go1.18, this could be done with sync.Mutex#TryLock) if !inProgress.CAS(false, true) { - log.Warn("Dialog already in progress, skipping") + s.log.Warn("Dialog already in progress, skipping") return } defer inProgress.Store(false) myInst := win.GetModuleHandle(nil) if myInst == win.HINSTANCE(0) { - log.Errorf("Failed to get my own module handle") + s.log.Errorf("Failed to get my own module handle") return } ret := win.DialogBoxParam(myInst, win.MAKEINTRESOURCE(uintptr(IDD_DIALOG1)), win.HWND(0), windows.NewCallback(dialogProc), uintptr(0)) @@ -161,7 +162,7 @@ func onFlare() { // got a non number, just create a new case info.caseid = "0" } - r, e := requestFlare(info.caseid, info.email) + r, e := requestFlare(s, info.caseid, info.email) caption, _ := windows.UTF16PtrFromString("Datadog Flare") var text *uint16 if e == nil { @@ -171,17 +172,14 @@ func onFlare() { } win.MessageBox(win.HWND(0), text, caption, win.MB_OK) } - log.Debugf("DialogBoxParam returns %d", ret) + s.log.Debugf("DialogBoxParam returns %d", ret) } -func requestFlare(caseID, customerEmail string) (response string, e error) { - log.Debug("Asking the agent to build the flare archive.") +func requestFlare(s *systray, caseID, customerEmail string) (response string, e error) { + // For first try, ask the agent to build the flare + s.log.Debug("Asking the agent to build the flare archive.") - e = common.SetupConfig("") - if e != nil { - return - } c := util.GetClient(false) // FIX: get certificates right then make this true ipcAddress, err := config.GetIPCAddress() if err != nil { @@ -208,28 +206,29 @@ func requestFlare(caseID, customerEmail string) (response string, e error) { r, e := util.DoPost(c, urlstr, "application/json", bytes.NewBuffer([]byte{})) var filePath string if e != nil { + // The agent could not make the flare, try create one from this context if r != nil && string(r) != "" { - log.Warnf("The agent ran into an error while making the flare: %s\n", r) + s.log.Warnf("The agent ran into an error while making the flare: %s\n", r) e = fmt.Errorf("Error getting flare from running agent: %s", r) } else { - log.Debug("The agent was unable to make the flare.") + s.log.Debug("The agent was unable to make the flare.") e = fmt.Errorf("Error getting flare from running agent: %w", e) } - log.Debug("Initiating flare locally.") + s.log.Debug("Initiating flare locally.") - filePath, e = flare.CreateArchive(true, common.GetDistPath(), common.PyChecksPath, []string{logFile, jmxLogFile}, nil, e) + filePath, e = s.flare.Create(true, common.GetDistPath(), common.PyChecksPath, []string{logFile, jmxLogFile}, nil, e) if e != nil { - log.Errorf("The flare zipfile failed to be created: %s\n", e) + s.log.Errorf("The flare zipfile failed to be created: %s\n", e) return } } else { filePath = string(r) } - log.Warnf("%s is going to be uploaded to Datadog\n", filePath) + s.log.Warnf("%s is going to be uploaded to Datadog\n", filePath) - response, e = flare.SendFlare(filePath, caseID, customerEmail) - log.Debug(response) + response, e = pkgflare.SendFlare(filePath, caseID, customerEmail) + s.log.Debug(response) if e != nil { return } diff --git a/comp/systray/systray/doservicecontrol.go b/comp/systray/systray/doservicecontrol.go index a92f148ee11701..a6e6323ef423b4 100644 --- a/comp/systray/systray/doservicecontrol.go +++ b/comp/systray/systray/doservicecontrol.go @@ -16,18 +16,16 @@ func onRestart(s *systray) { if err := controlsvc.RestartService(); err != nil { s.log.Warnf("Failed to restart datadog service %v", err) } - } + func onStart(s *systray) { if err := controlsvc.StartService(); err != nil { s.log.Warnf("Failed to start datadog service %v", err) } - } func onStop(s *systray) { if err := controlsvc.StopService(); err != nil { s.log.Warnf("Failed to stop datadog service %v", err) } - } diff --git a/comp/systray/systray/systray.go b/comp/systray/systray/systray.go index d9aa797efe8fe0..581d0b64b906bc 100644 --- a/comp/systray/systray/systray.go +++ b/comp/systray/systray/systray.go @@ -23,6 +23,7 @@ import ( "github.com/DataDog/datadog-agent/comp/core/config" "github.com/DataDog/datadog-agent/comp/core/log" + "github.com/DataDog/datadog-agent/comp/core/flare" "github.com/DataDog/datadog-agent/comp/systray/internal" pkglog "github.com/DataDog/datadog-agent/pkg/util/log" "github.com/DataDog/datadog-agent/pkg/util/winutil" @@ -42,6 +43,7 @@ type dependencies struct { Log log.Component Config config.Component + Flare flare.Component Params internal.BundleParams } @@ -51,6 +53,7 @@ type systray struct { log log.Component config config.Component + flare flare.Component params internal.BundleParams isUserAdmin bool @@ -96,6 +99,7 @@ func newSystray(deps dependencies) (Component, error) { s := &systray{ log: deps.Log, config: deps.Config, + flare: deps.Flare, params: deps.Params, shutdowner: deps.Shutdowner, } @@ -357,7 +361,7 @@ func createMenuItems(s *systray, notifyIcon *walk.NotifyIcon) []menuItem { menuitems = append(menuitems, menuItem{label: "S&top", handler: menuHandler(cmdTextStopService), enabled: true}) menuitems = append(menuitems, menuItem{label: "&Restart", handler: menuHandler(cmdTextRestartService), enabled: true}) menuitems = append(menuitems, menuItem{label: "&Configure", handler: menuHandler(cmdTextConfig), enabled: true}) - menuitems = append(menuitems, menuItem{label: "&Flare", handler: onFlare, enabled: true}) + menuitems = append(menuitems, menuItem{label: "&Flare", handler: func() { onFlare(s) }, enabled: true}) menuitems = append(menuitems, menuItem{label: menuSeparator}) menuitems = append(menuitems, menuItem{label: "E&xit", handler: func() { onExit(s) }, enabled: true}) From 0f4adadde6306dd89966f850fbc4af6cde14af47 Mon Sep 17 00:00:00 2001 From: Branden Clark Date: Wed, 4 Jan 2023 23:50:23 -0500 Subject: [PATCH 08/26] Add --debug and --console build args --- cmd/systray/command/command.go | 24 +++++++++++++++++++++++- tasks/systray.py | 18 ++++++++++++++---- 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/cmd/systray/command/command.go b/cmd/systray/command/command.go index aeacde4523beb6..09b8640fd12375 100644 --- a/cmd/systray/command/command.go +++ b/cmd/systray/command/command.go @@ -27,6 +27,15 @@ const ( defaultLogFile = "c:\\programdata\\datadog\\logs\\ddtray.log" ) +var ( + // set by the build task and used to configure the logger to output to console when debugging. + // This value should correspond to the subsystem in the PE header. + // + // There will only be console output if the PE subsystem is "console", but the GUI functions will + // also fail, so this is really only useful for debugging setup/initialization or cmdline setup. + subsystem string +) + // MakeCommand makes the top-level Cobra command for this app. func MakeCommand() *cobra.Command { systrayParams := systray.BundleParams{} @@ -39,6 +48,15 @@ func MakeCommand() *cobra.Command { } else { logFilePath = defaultLogFile } + fmt.Println(logFilePath) + + // log params + var logParams log.Params + if subsystem == "windows" { + logParams = log.LogForDaemon("TRAY", "log_file", logFilePath) + } else if subsystem == "console" { + logParams = log.LogForOneShot("TRAY", "log_file", true) + } // root command cmd := &cobra.Command{ @@ -48,7 +66,7 @@ func MakeCommand() *cobra.Command { return fxutil.Run( fx.Supply(core.BundleParams{ ConfigParams: config.NewParams(common.DefaultConfPath), - LogParams: log.LogForDaemon("TRAY", "log_file", logFilePath), + LogParams: logParams, }), core.Bundle, fx.Supply(systrayParams), @@ -57,6 +75,10 @@ func MakeCommand() *cobra.Command { }, } + // + // NOTE: The command line help/usage will not be visible in the release binary because the PE subsystem is "windows" + // + cmd.PersistentFlags().BoolVar(&systrayParams.LaunchGuiFlag, "launch-gui", false, "Launch browser configuration and exit") // launch-elev=true only means the process should have been elevated so that it will not elevate again. If the diff --git a/tasks/systray.py b/tasks/systray.py index d1bd2bf57a5ee1..c8b1b05aa164a6 100644 --- a/tasks/systray.py +++ b/tasks/systray.py @@ -16,7 +16,7 @@ @task -def build(ctx, rebuild=False, race=False, major_version='7', arch="x64", go_mod="mod"): +def build(ctx, debug=False, console=False, rebuild=False, race=False, major_version='7', arch="x64", go_mod="mod"): """ Build the agent. If the bits to include in the build are not specified, the values from `invoke.yaml` will be used. @@ -44,7 +44,14 @@ def build(ctx, rebuild=False, race=False, major_version='7', arch="x64", go_mod= command += "-i cmd/systray/systray.rc -O coff -o cmd/systray/rsrc.syso" ctx.run(command) ldflags = get_version_ldflags(ctx, major_version=major_version) - ldflags += "-s -w -linkmode external -extldflags '-Wl,--subsystem,windows' " + if not debug: + ldflags += "-s -w " + if console: + subsystem = 'console' + else: + subsystem = 'windows' + ldflags += f"-X {REPO_PATH}/cmd/systray/command/command.subsystem={subsystem} " + ldflags += f"-linkmode external -extldflags '-Wl,--subsystem,{subsystem}' " cmd = "go build -mod={go_mod} {race_opt} {build_type} -o {agent_bin} -ldflags=\"{ldflags}\" {REPO_PATH}/cmd/systray" args = { "go_mod": go_mod, @@ -68,7 +75,7 @@ def run(ctx, rebuild=False, race=False, skip_build=False): if not skip_build: build(ctx, rebuild, race) - ctx.run(os.path.join(BIN_PATH, bin_name("ddtray.exe"))) + ctx.run(os.path.join(BIN_PATH, bin_name("ddtray"))) @task @@ -82,4 +89,7 @@ def clean(ctx): # remove the bin/agent folder print("Remove systray executable") - ctx.run("rm -rf ./bin/agent/ddtray.exe") + try: + os.remove('./bin/agent/ddtray.exe') + except Exception as e: + print(e) From 4f8d9c5adb39d4c169997e5d682801807d94a4dc Mon Sep 17 00:00:00 2001 From: Branden Clark Date: Fri, 6 Jan 2023 15:57:02 -0500 Subject: [PATCH 09/26] Replace systray single dashed args with double dashed --- cmd/systray/command/command.go | 3 +-- comp/systray/systray/systray.go | 5 +---- omnibus/resources/agent/msi/source.wxs.erb | 4 ++-- omnibus/resources/iot/msi/source.wxs.erb | 4 ++-- ...ddtray-cmdline-args-compat-05fc525c6f6dea01.yaml | 13 +++++++++++++ 5 files changed, 19 insertions(+), 10 deletions(-) create mode 100644 releasenotes/notes/break-ddtray-cmdline-args-compat-05fc525c6f6dea01.yaml diff --git a/cmd/systray/command/command.go b/cmd/systray/command/command.go index 09b8640fd12375..bd89a21d54ccac 100644 --- a/cmd/systray/command/command.go +++ b/cmd/systray/command/command.go @@ -33,7 +33,7 @@ var ( // // There will only be console output if the PE subsystem is "console", but the GUI functions will // also fail, so this is really only useful for debugging setup/initialization or cmdline setup. - subsystem string + subsystem = "windows" ) // MakeCommand makes the top-level Cobra command for this app. @@ -48,7 +48,6 @@ func MakeCommand() *cobra.Command { } else { logFilePath = defaultLogFile } - fmt.Println(logFilePath) // log params var logParams log.Params diff --git a/comp/systray/systray/systray.go b/comp/systray/systray/systray.go index 581d0b64b906bc..872043e9c94be4 100644 --- a/comp/systray/systray/systray.go +++ b/comp/systray/systray/systray.go @@ -162,9 +162,7 @@ func (s *systray) stop(ctx context.Context) error { } // wait for goroutine to finish - s.log.Info("starting wait") s.routineWaitGroup.Wait() - s.log.Info("routine done!") // release our singleton if s.singletonEventHandle != windows.Handle(0) { @@ -212,7 +210,6 @@ func windowRoutine(s *systray) { // Run the message loop // use the notifyWindowToStop function to stop the message loop mw.Run() - s.log.Info("routine exiting!") } func acquireProcessSingleton(eventname string) (windows.Handle, error) { @@ -405,7 +402,7 @@ func relaunchElevated(s *systray, cmd string) { cwd, _ := os.Getwd() // Reconstruct arguments, drop launch-gui and tell the new process it should have been elevated. - xargs := []string{"-launch-elev=true", "-launch-cmd=" + cmd} + xargs := []string{"--launch-elev=true", "--launch-cmd=" + cmd} args := strings.Join(xargs, " ") verbPtr, _ := windows.UTF16PtrFromString(verb) diff --git a/omnibus/resources/agent/msi/source.wxs.erb b/omnibus/resources/agent/msi/source.wxs.erb index d9b6d66688cbce..ec5a09da306373 100644 --- a/omnibus/resources/agent/msi/source.wxs.erb +++ b/omnibus/resources/agent/msi/source.wxs.erb @@ -186,7 +186,7 @@ Directory="AGENT" Execute="immediate" Return="asyncNoWait" - ExeCommand=""[AGENT]ddtray.exe" "-launch-gui"" /> + ExeCommand=""[AGENT]ddtray.exe" "--launch-gui"" /> @@ -367,7 +367,7 @@ Name="Datadog Agent Manager" Description="Manage your Datadog Agent" Target="[AGENT]ddtray.exe" - Arguments= ""-launch-gui"" + Arguments= ""--launch-gui"" WorkingDirectory="AGENT"/> diff --git a/omnibus/resources/iot/msi/source.wxs.erb b/omnibus/resources/iot/msi/source.wxs.erb index 1218d4a75fb4b6..1e75203e026570 100644 --- a/omnibus/resources/iot/msi/source.wxs.erb +++ b/omnibus/resources/iot/msi/source.wxs.erb @@ -109,7 +109,7 @@ Directory="AGENT" Execute="immediate" Return="asyncNoWait" - ExeCommand=""[AGENT]ddtray.exe" "-launch-gui"" /> + ExeCommand=""[AGENT]ddtray.exe" "--launch-gui"" /> @@ -291,7 +291,7 @@ Name="Datadog Agent Manager" Description="Manage your Datadog Agent" Target="[AGENT]ddtray.exe" - Arguments= ""-launch-gui"" + Arguments= ""--launch-gui"" WorkingDirectory="AGENT"/> diff --git a/releasenotes/notes/break-ddtray-cmdline-args-compat-05fc525c6f6dea01.yaml b/releasenotes/notes/break-ddtray-cmdline-args-compat-05fc525c6f6dea01.yaml new file mode 100644 index 00000000000000..befe9e7b413f72 --- /dev/null +++ b/releasenotes/notes/break-ddtray-cmdline-args-compat-05fc525c6f6dea01.yaml @@ -0,0 +1,13 @@ +--- +upgrade: + - | + The command line arguments to the Datadog Agent Manager for Windows ``ddtray.exe`` + have changed from single-dash arguments to double-dash arguments. + For example, ``-launch-gui`` must now be provided as ``--launch-gui``. + The start menu shortcut created by the installer will be automatically updated. + Any custom scripts or shortcuts that launch ``ddtray.exe`` with arguments must be updated manually. +deprecations: + - | + The command line arguments to the Datadog Agent Manager for Windows ``ddtray.exe`` + have changed from single-dash arguments to double-dash arguments. + For example, ``-launch-gui`` must now be provided as ``--launch-gui``. From ae2d086cda1029f51d62a8f319c17ce82e05c4cd Mon Sep 17 00:00:00 2001 From: Branden Clark Date: Fri, 6 Jan 2023 16:21:18 -0500 Subject: [PATCH 10/26] Require admin to launch systray --- cmd/systray/ddtray.exe.manifest | 2 +- .../notes/require-admin-for-ddtray-5a087c08fc8d2f4c.yaml | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 releasenotes/notes/require-admin-for-ddtray-5a087c08fc8d2f4c.yaml diff --git a/cmd/systray/ddtray.exe.manifest b/cmd/systray/ddtray.exe.manifest index 54d6cb1df9ea35..2cd320f3d310cb 100644 --- a/cmd/systray/ddtray.exe.manifest +++ b/cmd/systray/ddtray.exe.manifest @@ -10,7 +10,7 @@ diff --git a/releasenotes/notes/require-admin-for-ddtray-5a087c08fc8d2f4c.yaml b/releasenotes/notes/require-admin-for-ddtray-5a087c08fc8d2f4c.yaml new file mode 100644 index 00000000000000..45f7e02f548340 --- /dev/null +++ b/releasenotes/notes/require-admin-for-ddtray-5a087c08fc8d2f4c.yaml @@ -0,0 +1,4 @@ +--- +other: + - | + The Datadog Agent Manager ``ddtray.exe`` now requires admin to launch. From 3b76ebfe69272fb3de21b682181f9a3634fb261c Mon Sep 17 00:00:00 2001 From: Branden Clark Date: Fri, 6 Jan 2023 16:52:31 -0500 Subject: [PATCH 11/26] disable cobra mousetrap --- cmd/systray/command/command.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cmd/systray/command/command.go b/cmd/systray/command/command.go index bd89a21d54ccac..4d4f8d39d5c840 100644 --- a/cmd/systray/command/command.go +++ b/cmd/systray/command/command.go @@ -36,6 +36,11 @@ var ( subsystem = "windows" ) +func init() { + // disable cobra mouse trap so cobra doesn't immediately kill our GUI app + cobra.MousetrapHelpText = "" +} + // MakeCommand makes the top-level Cobra command for this app. func MakeCommand() *cobra.Command { systrayParams := systray.BundleParams{} From a8c4204829deb542aec51ff04db5f23d9c0cfb9f Mon Sep 17 00:00:00 2001 From: Branden Clark Date: Fri, 6 Jan 2023 17:10:46 -0500 Subject: [PATCH 12/26] gofmt --- cmd/systray/command/command.go | 6 +++--- cmd/systray/main_windows.go | 1 - comp/systray/bundle.go | 4 +--- comp/systray/internal/params.go | 5 ++--- comp/systray/systray/component.go | 1 - comp/systray/systray/systray.go | 32 +++++++++++++++---------------- pkg/util/winutil/users.go | 1 - 7 files changed, 21 insertions(+), 29 deletions(-) diff --git a/cmd/systray/command/command.go b/cmd/systray/command/command.go index 4d4f8d39d5c840..52c06d0b2a9837 100644 --- a/cmd/systray/command/command.go +++ b/cmd/systray/command/command.go @@ -64,13 +64,13 @@ func MakeCommand() *cobra.Command { // root command cmd := &cobra.Command{ - Use: fmt.Sprintf("%s", os.Args[0]), + Use: fmt.Sprintf("%s", os.Args[0]), SilenceUsage: true, - RunE: func(cmd *cobra.Command, args[]string) error { + RunE: func(cmd *cobra.Command, args []string) error { return fxutil.Run( fx.Supply(core.BundleParams{ ConfigParams: config.NewParams(common.DefaultConfPath), - LogParams: logParams, + LogParams: logParams, }), core.Bundle, fx.Supply(systrayParams), diff --git a/cmd/systray/main_windows.go b/cmd/systray/main_windows.go index 04fca85f15c87c..6dd2b9e8f6a750 100644 --- a/cmd/systray/main_windows.go +++ b/cmd/systray/main_windows.go @@ -21,4 +21,3 @@ func main() { }() exitcode = runcmd.Run(command.MakeCommand()) } - diff --git a/comp/systray/bundle.go b/comp/systray/bundle.go index 4ba010d0e42379..f44c9a832ef5b2 100644 --- a/comp/systray/bundle.go +++ b/comp/systray/bundle.go @@ -11,8 +11,8 @@ package systray import ( - "github.com/DataDog/datadog-agent/comp/systray/systray" "github.com/DataDog/datadog-agent/comp/systray/internal" + "github.com/DataDog/datadog-agent/comp/systray/systray" "github.com/DataDog/datadog-agent/pkg/util/fxutil" "go.uber.org/fx" ) @@ -29,5 +29,3 @@ var Bundle = fxutil.Bundle( // require the systray component, causing it to start fx.Invoke(func(_ systray.Component) {}), ) - - diff --git a/comp/systray/internal/params.go b/comp/systray/internal/params.go index 44737b1eba1def..697ed601dae803 100644 --- a/comp/systray/internal/params.go +++ b/comp/systray/internal/params.go @@ -6,8 +6,7 @@ package internal type BundleParams struct { - LaunchGuiFlag bool + LaunchGuiFlag bool LaunchElevatedFlag bool - LaunchCommand string + LaunchCommand string } - diff --git a/comp/systray/systray/component.go b/comp/systray/systray/component.go index 9b5a47be65d257..cdf8b9c19da76f 100644 --- a/comp/systray/systray/component.go +++ b/comp/systray/systray/component.go @@ -12,7 +12,6 @@ import ( ) type Component interface { - } var Module = fxutil.Component( diff --git a/comp/systray/systray/systray.go b/comp/systray/systray/systray.go index 872043e9c94be4..937e064a9d7e40 100644 --- a/comp/systray/systray/systray.go +++ b/comp/systray/systray/systray.go @@ -22,28 +22,28 @@ import ( "unsafe" "github.com/DataDog/datadog-agent/comp/core/config" - "github.com/DataDog/datadog-agent/comp/core/log" "github.com/DataDog/datadog-agent/comp/core/flare" + "github.com/DataDog/datadog-agent/comp/core/log" "github.com/DataDog/datadog-agent/comp/systray/internal" pkglog "github.com/DataDog/datadog-agent/pkg/util/log" "github.com/DataDog/datadog-agent/pkg/util/winutil" "github.com/DataDog/datadog-agent/pkg/version" - "go.uber.org/fx" "github.com/lxn/walk" "github.com/lxn/win" + "go.uber.org/fx" "golang.org/x/sys/windows" ) type dependencies struct { fx.In - Lc fx.Lifecycle + Lc fx.Lifecycle Shutdowner fx.Shutdowner - Log log.Component + Log log.Component Config config.Component - Flare flare.Component + Flare flare.Component Params internal.BundleParams } @@ -51,20 +51,19 @@ type systray struct { // For triggering Shutdown shutdowner fx.Shutdowner - log log.Component + log log.Component config config.Component - flare flare.Component + flare flare.Component params internal.BundleParams - isUserAdmin bool + isUserAdmin bool // allocated in start, destroyed in stop singletonEventHandle windows.Handle // Window management notifyWindowToStop func() - routineWaitGroup sync.WaitGroup - + routineWaitGroup sync.WaitGroup } type menuItem struct { @@ -84,7 +83,7 @@ const ( ) var ( - cmds = map[string]func(*systray){ + cmds = map[string]func(*systray){ cmdTextStartService: onStart, cmdTextStopService: onStop, cmdTextRestartService: onRestart, @@ -97,10 +96,10 @@ var ( func newSystray(deps dependencies) (Component, error) { // fx init s := &systray{ - log: deps.Log, - config: deps.Config, - flare: deps.Flare, - params: deps.Params, + log: deps.Log, + config: deps.Config, + flare: deps.Flare, + params: deps.Params, shutdowner: deps.Shutdowner, } @@ -257,7 +256,7 @@ func createNotifyIcon(s *systray, mw *walk.MainWindow) (ni *walk.NotifyIcon, err if err != nil { return nil, err } - defer func () { + defer func() { if err != nil { ni.Dispose() ni = nil @@ -320,7 +319,6 @@ func createNotifyIcon(s *systray, mw *walk.MainWindow) (ni *walk.NotifyIcon, err pkglog.Warnf("Failed to set window visibility %v", err) } - return ni, nil } diff --git a/pkg/util/winutil/users.go b/pkg/util/winutil/users.go index a0b48c3b96eeaf..b04fe6908f269a 100644 --- a/pkg/util/winutil/users.go +++ b/pkg/util/winutil/users.go @@ -60,4 +60,3 @@ func IsUserAnAdmin() (bool, error) { } return true, nil } - From 2f92a8aa111d91add9fa2ec2cb651f32cb388def Mon Sep 17 00:00:00 2001 From: Branden Clark Date: Fri, 6 Jan 2023 17:15:31 -0500 Subject: [PATCH 13/26] handle --launch-gui in goroutine --- comp/systray/systray/systray.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/comp/systray/systray/systray.go b/comp/systray/systray/systray.go index 937e064a9d7e40..bc8dde208d4ad6 100644 --- a/comp/systray/systray/systray.go +++ b/comp/systray/systray/systray.go @@ -134,7 +134,7 @@ func (s *systray) start(ctx context.Context) error { if s.params.LaunchGuiFlag { s.log.Debug("Preparing to launch configuration interface...") - onConfigure(s) + go onConfigure(s) } s.singletonEventHandle, err = acquireProcessSingleton(eventname) From 3b07820c30f75dcbb4dc278f2cb1163b1dbf7544 Mon Sep 17 00:00:00 2001 From: Branden Clark Date: Fri, 6 Jan 2023 17:24:25 -0500 Subject: [PATCH 14/26] license header --- comp/systray/systray/uac.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/comp/systray/systray/uac.h b/comp/systray/systray/uac.h index b1471820e44a26..5b591464c2eb1f 100644 --- a/comp/systray/systray/uac.h +++ b/comp/systray/systray/uac.h @@ -1,3 +1,8 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + #include BOOL LaunchUnelevated(LPCWSTR CommandLine); From d889bc9cadb3dcce6146f9506bddb2320643aa7a Mon Sep 17 00:00:00 2001 From: Branden Clark Date: Fri, 6 Jan 2023 17:44:46 -0500 Subject: [PATCH 15/26] misc --- comp/systray/systray/systray.go | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/comp/systray/systray/systray.go b/comp/systray/systray/systray.go index bc8dde208d4ad6..d0432469f60d4a 100644 --- a/comp/systray/systray/systray.go +++ b/comp/systray/systray/systray.go @@ -103,16 +103,8 @@ func newSystray(deps dependencies) (Component, error) { shutdowner: deps.Shutdowner, } - menuitems := make([]menuItem, 0) - menuitems = append(menuitems, menuItem{label: verstring, enabled: false}) - menuitems = append(menuitems, menuItem{label: separator}) - menuitems = append(menuitems, menuItem{label: "&Start", handler: menuHandler(cmdTextStartService), enabled: true}) - menuitems = append(menuitems, menuItem{label: "S&top", handler: menuHandler(cmdTextStopService), enabled: true}) - menuitems = append(menuitems, menuItem{label: "&Restart", handler: menuHandler(cmdTextRestartService), enabled: true}) - menuitems = append(menuitems, menuItem{label: "&Configure", handler: menuHandler(cmdTextConfig), enabled: true}) - menuitems = append(menuitems, menuItem{label: "&Flare", handler: onFlare, enabled: true}) - menuitems = append(menuitems, menuItem{label: separator}) - menuitems = append(menuitems, menuItem{label: "E&xit", handler: onExit, enabled: true}) + // fx lifecycle hooks + deps.Lc.Append(fx.Hook{OnStart: s.start, OnStop: s.stop}) // init vars isAdmin, err := winutil.IsUserAnAdmin() @@ -127,6 +119,7 @@ func newSystray(deps dependencies) (Component, error) { return s, nil } +// start hook has a fx enforced timeout, so don't do long running things func (s *systray) start(ctx context.Context) error { var err error @@ -250,6 +243,8 @@ func acquireProcessSingleton(eventname string) (windows.Handle, error) { return h, nil } +// this function must be called from and the NotifyIcon used from a single thread locked goroutine +// https://github.com/lxn/walk/issues/601 func createNotifyIcon(s *systray, mw *walk.MainWindow) (ni *walk.NotifyIcon, err error) { // Create the notify icon (must be cleaned up) ni, err = walk.NewNotifyIcon(mw) @@ -267,15 +262,15 @@ func createNotifyIcon(s *systray, mw *walk.MainWindow) (ni *walk.NotifyIcon, err // 1 is the ID of the MAIN_ICON in systray.rc icon, err := walk.NewIconFromResourceId(1) if err != nil { - pkglog.Warnf("Failed to load icon %v", err) + s.log.Warnf("Failed to load icon %v", err) } if err := ni.SetIcon(icon); err != nil { - pkglog.Warnf("Failed to set icon %v", err) + s.log.Warnf("Failed to set icon %v", err) } // Set mouseover tooltip if err := ni.SetToolTip("Click for info or use the context menu to exit."); err != nil { - pkglog.Warnf("Failed to set tooltip text %v", err) + s.log.Warnf("Failed to set tooltip text %v", err) } // When the left mouse button is pressed, bring up our balloon. @@ -295,12 +290,12 @@ func createNotifyIcon(s *systray, mw *walk.MainWindow) (ni *walk.NotifyIcon, err } else { action = walk.NewAction() if err := action.SetText(item.label); err != nil { - pkglog.Warnf("Failed to set text for item %s %v", item.label, err) + s.log.Warnf("Failed to set text for item %s %v", item.label, err) continue } err = action.SetEnabled(item.enabled) if err != nil { - pkglog.Warnf("Failed to set enabled for item %s %v", item.label, err) + s.log.Warnf("Failed to set enabled for item %s %v", item.label, err) continue } if item.handler != nil { @@ -309,14 +304,14 @@ func createNotifyIcon(s *systray, mw *walk.MainWindow) (ni *walk.NotifyIcon, err } err = ni.ContextMenu().Actions().Add(action) if err != nil { - pkglog.Warnf("Failed to add action for item %s to context menu %v", item.label, err) + s.log.Warnf("Failed to add action for item %s to context menu %v", item.label, err) continue } } // The notify icon is hidden initially, so we have to make it visible. if err := ni.SetVisible(true); err != nil { - pkglog.Warnf("Failed to set window visibility %v", err) + s.log.Warnf("Failed to set window visibility %v", err) } return ni, nil @@ -412,7 +407,7 @@ func relaunchElevated(s *systray, cmd string) { err := windows.ShellExecute(0, verbPtr, exePtr, argPtr, cwdPtr, showCmd) if err != nil { - pkglog.Warnf("Failed to launch self as elevated %v", err) + s.log.Warnf("Failed to launch self as elevated %v", err) } else { triggerShutdown(s) } From af6a231110f4a5fc6e5c2cfd00369d7498ba0312 Mon Sep 17 00:00:00 2001 From: Branden Clark Date: Fri, 6 Jan 2023 18:52:07 -0500 Subject: [PATCH 16/26] go build windows tags --- cmd/systray/command/command.go | 2 ++ comp/systray/bundle.go | 2 ++ comp/systray/internal/params.go | 2 ++ comp/systray/systray/component.go | 2 ++ 4 files changed, 8 insertions(+) diff --git a/cmd/systray/command/command.go b/cmd/systray/command/command.go index 52c06d0b2a9837..bd48c93ccb0981 100644 --- a/cmd/systray/command/command.go +++ b/cmd/systray/command/command.go @@ -2,6 +2,8 @@ // under the Apache License Version 2.0. // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2016-present Datadog, Inc. +//go:build windows +// +build windows // Package command implements the top-level `systray` binary, including its subcommands. package command diff --git a/comp/systray/bundle.go b/comp/systray/bundle.go index f44c9a832ef5b2..52509128bc610a 100644 --- a/comp/systray/bundle.go +++ b/comp/systray/bundle.go @@ -2,6 +2,8 @@ // under the Apache License Version 2.0. // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2016-present Datadog, Inc. +//go:build windows +// +build windows // Package systray implements the "systray" bundle for the systray app. // diff --git a/comp/systray/internal/params.go b/comp/systray/internal/params.go index 697ed601dae803..275f84ca2bf1a4 100644 --- a/comp/systray/internal/params.go +++ b/comp/systray/internal/params.go @@ -2,6 +2,8 @@ // under the Apache License Version 2.0. // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2016-present Datadog, Inc. +//go:build windows +// +build windows package internal diff --git a/comp/systray/systray/component.go b/comp/systray/systray/component.go index cdf8b9c19da76f..2507ffa11ed884 100644 --- a/comp/systray/systray/component.go +++ b/comp/systray/systray/component.go @@ -2,6 +2,8 @@ // under the Apache License Version 2.0. // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2016-present Datadog, Inc. +//go:build windows +// +build windows // Package systray package systray From 3b0f7fb9f734fe336e9c4ebc06e3d45732780092 Mon Sep 17 00:00:00 2001 From: Branden Clark Date: Fri, 6 Jan 2023 19:12:58 -0500 Subject: [PATCH 17/26] codeowners --- .github/CODEOWNERS | 1 + comp/README.md | 10 ++++++++++ comp/systray/bundle.go | 2 +- comp/systray/systray/component.go | 2 ++ 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 83d456fc3f8e3e..c6ad3895668fba 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -177,6 +177,7 @@ /comp @DataDog/agent-shared-components /comp/core @DataDog/agent-shared-components /comp/process @DataDog/processes +/comp/systray @DataDog/agent-windows # END COMPONENTS # pkg diff --git a/comp/README.md b/comp/README.md index e1be85a271b4a4..9e6bfc0373ff4b 100644 --- a/comp/README.md +++ b/comp/README.md @@ -46,3 +46,13 @@ Package runner implements a component to run data collection checks in the Proce Package submitter implements a component to submit collected data in the Process Agent to supported Datadog intakes. + +## [comp/systray](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/systray) (Component Bundle) + +*Datadog Team*: agent-windows + +Package systray implements the "systray" bundle for the Datadog Agent Manager tray application. + +### [comp/systray/systray](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/systray/systray) + +Package systray diff --git a/comp/systray/bundle.go b/comp/systray/bundle.go index 52509128bc610a..6c6046c1d18af3 100644 --- a/comp/systray/bundle.go +++ b/comp/systray/bundle.go @@ -5,7 +5,7 @@ //go:build windows // +build windows -// Package systray implements the "systray" bundle for the systray app. +// Package systray implements the "systray" bundle for the Datadog Agent Manager tray application. // // Including `systray.Module` in an App will automatically start the app. // diff --git a/comp/systray/systray/component.go b/comp/systray/systray/component.go index 2507ffa11ed884..ef6570c0ee3e50 100644 --- a/comp/systray/systray/component.go +++ b/comp/systray/systray/component.go @@ -13,6 +13,8 @@ import ( "go.uber.org/fx" ) +// team: agent-windows + type Component interface { } From 2699277a9dd480f517891e39313715058fc50d4a Mon Sep 17 00:00:00 2001 From: Branden Clark Date: Fri, 6 Jan 2023 19:15:46 -0500 Subject: [PATCH 18/26] fix team name --- .github/CODEOWNERS | 2 +- comp/README.md | 2 +- comp/systray/bundle.go | 2 +- comp/systray/systray/component.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index c6ad3895668fba..de60a62e534ddf 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -177,7 +177,7 @@ /comp @DataDog/agent-shared-components /comp/core @DataDog/agent-shared-components /comp/process @DataDog/processes -/comp/systray @DataDog/agent-windows +/comp/systray @DataDog/windows-agent # END COMPONENTS # pkg diff --git a/comp/README.md b/comp/README.md index 9e6bfc0373ff4b..4b0edefd9b395e 100644 --- a/comp/README.md +++ b/comp/README.md @@ -49,7 +49,7 @@ supported Datadog intakes. ## [comp/systray](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/systray) (Component Bundle) -*Datadog Team*: agent-windows +*Datadog Team*: windows-agent Package systray implements the "systray" bundle for the Datadog Agent Manager tray application. diff --git a/comp/systray/bundle.go b/comp/systray/bundle.go index 6c6046c1d18af3..c60a2541c656a4 100644 --- a/comp/systray/bundle.go +++ b/comp/systray/bundle.go @@ -19,7 +19,7 @@ import ( "go.uber.org/fx" ) -// team: agent-windows +// team: windows-agent // BundleParams defines the parameters for this bundle type BundleParams = internal.BundleParams diff --git a/comp/systray/systray/component.go b/comp/systray/systray/component.go index ef6570c0ee3e50..35809655d7ecce 100644 --- a/comp/systray/systray/component.go +++ b/comp/systray/systray/component.go @@ -13,7 +13,7 @@ import ( "go.uber.org/fx" ) -// team: agent-windows +// team: windows-agent type Component interface { } From 4b18e8e631a344ff57a1a1638edf0b5e31daaf35 Mon Sep 17 00:00:00 2001 From: Branden Clark Date: Mon, 9 Jan 2023 14:15:26 -0500 Subject: [PATCH 19/26] Update uac.c --- comp/systray/systray/uac.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/comp/systray/systray/uac.c b/comp/systray/systray/uac.c index 1c0e543a9018e8..360b1c93f6619a 100644 --- a/comp/systray/systray/uac.c +++ b/comp/systray/systray/uac.c @@ -35,8 +35,7 @@ BOOL LaunchUnelevated(LPCWSTR CommandLine) if (process != NULL) { - // Create thread attribute list containing PROC_THREAD_ATTRIBUTE_PARENT_PROCESS - // to spoof the parent process. + // To set the parent process, create a thread attribute list containing PROC_THREAD_ATTRIBUTE_PARENT_PROCESS SIZE_T size; if ((!InitializeProcThreadAttributeList(NULL, 1, 0, &size)) && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) { From 397e44e864679593bfe87d91fc2f2bf21772d0ff Mon Sep 17 00:00:00 2001 From: Branden Clark Date: Mon, 9 Jan 2023 14:16:48 -0500 Subject: [PATCH 20/26] Update uac.c --- comp/systray/systray/uac.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/comp/systray/systray/uac.c b/comp/systray/systray/uac.c index 360b1c93f6619a..cf4245d7628630 100644 --- a/comp/systray/systray/uac.c +++ b/comp/systray/systray/uac.c @@ -6,8 +6,8 @@ #include "uac.h" // Attempts to drop privileges from an elevated process by creating a new process and -// spoofing the parent PID to be explorer.exe. This causes the new process to inherit -// its access token from explorer.exe. +// setting the parent process to be the user's explorer.exe. This causes the new process to +// inherit its access token from explorer.exe. // // Technique relies on having permission to open explorer.exe with PROCESS_CREATE_PROCESS, // this access is verified against the explorer.exe process DACL. From 90d18c7bee8cc515e31eb227453dc2db717b2488 Mon Sep 17 00:00:00 2001 From: Branden Clark Date: Mon, 9 Jan 2023 14:34:49 -0500 Subject: [PATCH 21/26] Add DeleteProcThreadAttributeList call --- comp/systray/systray/uac.c | 1 + 1 file changed, 1 insertion(+) diff --git a/comp/systray/systray/uac.c b/comp/systray/systray/uac.c index cf4245d7628630..4f90235728a5e9 100644 --- a/comp/systray/systray/uac.c +++ b/comp/systray/systray/uac.c @@ -72,6 +72,7 @@ BOOL LaunchUnelevated(LPCWSTR CommandLine) } } } + DeleteProcThreadAttributeList(p); free(p); } } From 5274412e83c46ec189cedaebe7fd5889854ce3f1 Mon Sep 17 00:00:00 2001 From: Branden Clark Date: Tue, 10 Jan 2023 18:47:58 -0500 Subject: [PATCH 22/26] review comments, remove systray bundle --- cmd/systray/command/command.go | 15 +++++++++----- comp/systray/bundle.go | 33 ------------------------------- comp/systray/internal/params.go | 14 ------------- comp/systray/systray/component.go | 6 ++++++ comp/systray/systray/systray.go | 5 ++--- 5 files changed, 18 insertions(+), 55 deletions(-) delete mode 100644 comp/systray/bundle.go delete mode 100644 comp/systray/internal/params.go diff --git a/cmd/systray/command/command.go b/cmd/systray/command/command.go index bd48c93ccb0981..9b7fa5b6a73d6c 100644 --- a/cmd/systray/command/command.go +++ b/cmd/systray/command/command.go @@ -19,7 +19,7 @@ import ( "github.com/DataDog/datadog-agent/comp/core" "github.com/DataDog/datadog-agent/comp/core/config" "github.com/DataDog/datadog-agent/comp/core/log" - "github.com/DataDog/datadog-agent/comp/systray" + "github.com/DataDog/datadog-agent/comp/systray/systray" "github.com/DataDog/datadog-agent/pkg/util/fxutil" "github.com/DataDog/datadog-agent/pkg/util/winutil" "go.uber.org/fx" @@ -33,8 +33,9 @@ var ( // set by the build task and used to configure the logger to output to console when debugging. // This value should correspond to the subsystem in the PE header. // - // There will only be console output if the PE subsystem is "console", but the GUI functions will - // also fail, so this is really only useful for debugging setup/initialization or cmdline setup. + // In normal circumstances, we don't want the systray to launch a console window when it runs + // so the default subsystem is "windows". However, console output can be useful for debugging. + // Console output can be viewd by setting the PE subsystem to "console". subsystem = "windows" ) @@ -45,7 +46,7 @@ func init() { // MakeCommand makes the top-level Cobra command for this app. func MakeCommand() *cobra.Command { - systrayParams := systray.BundleParams{} + systrayParams := systray.Params{} // log file path var logFilePath string @@ -70,13 +71,17 @@ func MakeCommand() *cobra.Command { SilenceUsage: true, RunE: func(cmd *cobra.Command, args []string) error { return fxutil.Run( + // core fx.Supply(core.BundleParams{ ConfigParams: config.NewParams(common.DefaultConfPath), LogParams: logParams, }), core.Bundle, + // systray fx.Supply(systrayParams), - systray.Bundle, + systray.Module, + // require the systray component, causing it to start + fx.Invoke(func(_ systray.Component) {}), ) }, } diff --git a/comp/systray/bundle.go b/comp/systray/bundle.go deleted file mode 100644 index c60a2541c656a4..00000000000000 --- a/comp/systray/bundle.go +++ /dev/null @@ -1,33 +0,0 @@ -// Unless explicitly stated otherwise all files in this repository are licensed -// under the Apache License Version 2.0. -// This product includes software developed at Datadog (https://www.datadoghq.com/). -// Copyright 2016-present Datadog, Inc. -//go:build windows -// +build windows - -// Package systray implements the "systray" bundle for the Datadog Agent Manager tray application. -// -// Including `systray.Module` in an App will automatically start the app. -// -// This bundle depends on comp/core. -package systray - -import ( - "github.com/DataDog/datadog-agent/comp/systray/internal" - "github.com/DataDog/datadog-agent/comp/systray/systray" - "github.com/DataDog/datadog-agent/pkg/util/fxutil" - "go.uber.org/fx" -) - -// team: windows-agent - -// BundleParams defines the parameters for this bundle -type BundleParams = internal.BundleParams - -// Bundle defines the fx options for this bundle. -var Bundle = fxutil.Bundle( - systray.Module, - - // require the systray component, causing it to start - fx.Invoke(func(_ systray.Component) {}), -) diff --git a/comp/systray/internal/params.go b/comp/systray/internal/params.go deleted file mode 100644 index 275f84ca2bf1a4..00000000000000 --- a/comp/systray/internal/params.go +++ /dev/null @@ -1,14 +0,0 @@ -// Unless explicitly stated otherwise all files in this repository are licensed -// under the Apache License Version 2.0. -// This product includes software developed at Datadog (https://www.datadoghq.com/). -// Copyright 2016-present Datadog, Inc. -//go:build windows -// +build windows - -package internal - -type BundleParams struct { - LaunchGuiFlag bool - LaunchElevatedFlag bool - LaunchCommand string -} diff --git a/comp/systray/systray/component.go b/comp/systray/systray/component.go index 35809655d7ecce..93c34786c545d5 100644 --- a/comp/systray/systray/component.go +++ b/comp/systray/systray/component.go @@ -15,6 +15,12 @@ import ( // team: windows-agent +type Params struct { + LaunchGuiFlag bool + LaunchElevatedFlag bool + LaunchCommand string +} + type Component interface { } diff --git a/comp/systray/systray/systray.go b/comp/systray/systray/systray.go index d0432469f60d4a..3f82e873514da9 100644 --- a/comp/systray/systray/systray.go +++ b/comp/systray/systray/systray.go @@ -24,7 +24,6 @@ import ( "github.com/DataDog/datadog-agent/comp/core/config" "github.com/DataDog/datadog-agent/comp/core/flare" "github.com/DataDog/datadog-agent/comp/core/log" - "github.com/DataDog/datadog-agent/comp/systray/internal" pkglog "github.com/DataDog/datadog-agent/pkg/util/log" "github.com/DataDog/datadog-agent/pkg/util/winutil" "github.com/DataDog/datadog-agent/pkg/version" @@ -44,7 +43,7 @@ type dependencies struct { Log log.Component Config config.Component Flare flare.Component - Params internal.BundleParams + Params Params } type systray struct { @@ -54,7 +53,7 @@ type systray struct { log log.Component config config.Component flare flare.Component - params internal.BundleParams + params Params isUserAdmin bool From 17f234541ebcbd41566f2785f2ea41c4bf5890e1 Mon Sep 17 00:00:00 2001 From: Branden Clark Date: Tue, 10 Jan 2023 19:06:08 -0500 Subject: [PATCH 23/26] Fix LogForOneShot args --- cmd/systray/command/command.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/systray/command/command.go b/cmd/systray/command/command.go index 9b7fa5b6a73d6c..2503942ac9e605 100644 --- a/cmd/systray/command/command.go +++ b/cmd/systray/command/command.go @@ -62,7 +62,7 @@ func MakeCommand() *cobra.Command { if subsystem == "windows" { logParams = log.LogForDaemon("TRAY", "log_file", logFilePath) } else if subsystem == "console" { - logParams = log.LogForOneShot("TRAY", "log_file", true) + logParams = log.LogForOneShot("TRAY", "info", true) } // root command From a348c2de4e62422ec1d6e13588f228b1e6eeec31 Mon Sep 17 00:00:00 2001 From: Branden Clark Date: Tue, 10 Jan 2023 19:16:44 -0500 Subject: [PATCH 24/26] Add config section with log_file option --- cmd/systray/command/command.go | 2 +- pkg/config/config.go | 3 +++ pkg/config/config_template.yaml | 15 +++++++++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/cmd/systray/command/command.go b/cmd/systray/command/command.go index 2503942ac9e605..a793be32e76c38 100644 --- a/cmd/systray/command/command.go +++ b/cmd/systray/command/command.go @@ -60,7 +60,7 @@ func MakeCommand() *cobra.Command { // log params var logParams log.Params if subsystem == "windows" { - logParams = log.LogForDaemon("TRAY", "log_file", logFilePath) + logParams = log.LogForDaemon("TRAY", "system_tray.log_file", logFilePath) } else if subsystem == "console" { logParams = log.LogForOneShot("TRAY", "info", true) } diff --git a/pkg/config/config.go b/pkg/config/config.go index b031f660fe2491..4df7b4f42b6734 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -1226,6 +1226,9 @@ func InitConfig(config Config) { bindVectorOptions(config, Metrics) bindVectorOptions(config, Logs) + // Datadog Agent Manager System Tray + config.BindEnvAndSetDefault("system_tray.log_file", "") + setupAPM(config) SetupOTLP(config) setupProcesses(config) diff --git a/pkg/config/config_template.yaml b/pkg/config/config_template.yaml index 4f8a589e32a16c..72aa9df42b6785 100644 --- a/pkg/config/config_template.yaml +++ b/pkg/config/config_template.yaml @@ -3450,3 +3450,18 @@ api_key: # # verbosity: normal {{end -}} +{{- if (eq .OS "windows")}} +##################################################### +## Datadog Agent Manager System Tray Configuration ## +##################################################### + +## @param system_tray - custom object - optional +## This section configures the Datadog Agent Manager System Tray +# +# system_tray: + ## @param log_file - string - optional - default: %ProgramData%\Datadog\logs\ddtray.log + ## @env DD_TRAY_LOG_FILE - string - optional + ## The full path to the file where Datadog Agent Manager System Tray logs are written. + # + # log_file: +{{end -}} From 53f90904ef927219989652e3c347c2848f6c6b29 Mon Sep 17 00:00:00 2001 From: Branden Clark Date: Tue, 10 Jan 2023 19:31:49 -0500 Subject: [PATCH 25/26] review comments --- comp/systray/systray/systray.go | 4 ++-- tasks/systray.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/comp/systray/systray/systray.go b/comp/systray/systray/systray.go index 3f82e873514da9..8f809082a76db0 100644 --- a/comp/systray/systray/systray.go +++ b/comp/systray/systray/systray.go @@ -92,7 +92,7 @@ var ( // newSystray creates a new systray component, which will start and stop based on // the fx Lifecycle -func newSystray(deps dependencies) (Component, error) { +func newSystray(deps dependencies) Component { // fx init s := &systray{ log: deps.Log, @@ -115,7 +115,7 @@ func newSystray(deps dependencies) (Component, error) { s.isUserAdmin = isAdmin } - return s, nil + return s } // start hook has a fx enforced timeout, so don't do long running things diff --git a/tasks/systray.py b/tasks/systray.py index c8b1b05aa164a6..98c1d898014b72 100644 --- a/tasks/systray.py +++ b/tasks/systray.py @@ -90,6 +90,6 @@ def clean(ctx): # remove the bin/agent folder print("Remove systray executable") try: - os.remove('./bin/agent/ddtray.exe') + os.remove(os.path.join(BIN_PATH, bin_name("ddtray"))) except Exception as e: print(e) From a755fd4651011f91fef27cbf048be42e51103dcd Mon Sep 17 00:00:00 2001 From: Branden Clark Date: Tue, 10 Jan 2023 19:39:25 -0500 Subject: [PATCH 26/26] lint-components fix --- comp/README.md | 2 +- comp/systray/bundle.go | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 comp/systray/bundle.go diff --git a/comp/README.md b/comp/README.md index 4b0edefd9b395e..bbb2b04e33c4aa 100644 --- a/comp/README.md +++ b/comp/README.md @@ -51,7 +51,7 @@ supported Datadog intakes. *Datadog Team*: windows-agent -Package systray implements the "systray" bundle for the Datadog Agent Manager tray application. +Package systray implements the Datadog Agent Manager System Tray ### [comp/systray/systray](https://pkg.go.dev/github.com/DataDog/dd-agent-comp-experiments/comp/systray/systray) diff --git a/comp/systray/bundle.go b/comp/systray/bundle.go new file mode 100644 index 00000000000000..64ad9d8fb9b242 --- /dev/null +++ b/comp/systray/bundle.go @@ -0,0 +1,11 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. +//go:build windows +// +build windows + +// Package systray implements the Datadog Agent Manager System Tray +package systray + +// team: windows-agent