diff --git a/cmd/launcher/desktop.go b/cmd/launcher/desktop.go index a397fadb0..7e05bd08f 100644 --- a/cmd/launcher/desktop.go +++ b/cmd/launcher/desktop.go @@ -111,7 +111,8 @@ func runDesktop(_ *multislogger.MultiSlogger, args []string) error { ) } - runGroup := rungroup.NewRunGroup(slogger) + runGroup := rungroup.NewRunGroup() + runGroup.SetSlogger(slogger) // listen for signals runGroup.Add("desktopSignalListener", func() error { diff --git a/cmd/launcher/launcher.go b/cmd/launcher/launcher.go index 9d39daa44..c67a1c6f7 100644 --- a/cmd/launcher/launcher.go +++ b/cmd/launcher/launcher.go @@ -206,7 +206,7 @@ func runLauncher(ctx context.Context, cancel func(), multiSlogger, systemMultiSl } // create a rungroup for all the actors we create to allow for easy start/stop - runGroup := rungroup.NewRunGroup(slogger) + runGroup := rungroup.NewRunGroup() // Need to set up the log shipper so that we can get the logger early // and pass it to the various systems. @@ -246,6 +246,10 @@ func runLauncher(ctx context.Context, cancel func(), multiSlogger, systemMultiSl startupSpan.AddEvent("log_shipper_init_completed") } + // Now that log shipping is set up, set the slogger on the rungroup so that rungroup logs + // will also be shipped. + runGroup.SetSlogger(k.Slogger()) + startupSettingsWriter, err := startupsettings.OpenWriter(ctx, k) if err != nil { return fmt.Errorf("creating startup db: %w", err) diff --git a/ee/agent/flags/flag_controller.go b/ee/agent/flags/flag_controller.go index f7d68b250..e968c76f0 100644 --- a/ee/agent/flags/flag_controller.go +++ b/ee/agent/flags/flag_controller.go @@ -657,3 +657,15 @@ func (fc *FlagController) LocalDevelopmentPath() string { WithDefaultString(fc.cmdLineOpts.LocalDevelopmentPath), ).get(nil) } + +func (fc *FlagController) Identifier() string { + identifier := NewStringFlagValue( + WithDefaultString(fc.cmdLineOpts.Identifier), + ).get(nil) + + if strings.TrimSpace(identifier) == "" { + identifier = launcher.DefaultLauncherIdentifier + } + + return identifier +} diff --git a/ee/agent/timemachine/timemachine_darwin.go b/ee/agent/timemachine/timemachine_darwin.go index fe431235f..a12e68549 100644 --- a/ee/agent/timemachine/timemachine_darwin.go +++ b/ee/agent/timemachine/timemachine_darwin.go @@ -33,10 +33,17 @@ func AddExclusions(ctx context.Context, k types.Knapsack) { launcher.pid */ - exclusionPatterns := []string{ + exclusionPatternsFirstBatch := []string{ "*.json", "*.json.gz", "*.db", + } + + addExclusionsFromPathPatterns(ctx, k, exclusionPatternsFirstBatch) + + // Attempting to run this with a single tmutil call we see a lot of tmutil failures logged with error "argument list too long". + // To avoid this we run in two separate batches, attempting to cut the post-glob argument list roughly in half + exclusionPatternsSecondBatch := []string{ "*.sqlite", "desktop_*", "*.pid", @@ -45,6 +52,10 @@ func AddExclusions(ctx context.Context, k types.Knapsack) { "osquery*", } + addExclusionsFromPathPatterns(ctx, k, exclusionPatternsSecondBatch) +} + +func addExclusionsFromPathPatterns(ctx context.Context, k types.Knapsack, exclusionPatterns []string) { var exclusionPaths []string for _, pattern := range exclusionPatterns { matches, err := filepath.Glob(filepath.Join(k.RootDirectory(), pattern)) diff --git a/ee/agent/types/flags.go b/ee/agent/types/flags.go index 3c6cda0b2..42e1141f6 100644 --- a/ee/agent/types/flags.go +++ b/ee/agent/types/flags.go @@ -230,4 +230,7 @@ type Flags interface { // SystrayRestartEnabled controls whether launcher's desktop runner will restart systray on error SetSystrayRestartEnabled(enabled bool) error SystrayRestartEnabled() bool + + // Identifier is the package build identifier used to namespace our paths and service names + Identifier() string } diff --git a/ee/agent/types/mocks/flags.go b/ee/agent/types/mocks/flags.go index 8dda3e646..941d18397 100644 --- a/ee/agent/types/mocks/flags.go +++ b/ee/agent/types/mocks/flags.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.45.0. DO NOT EDIT. +// Code generated by mockery v2.46.0. DO NOT EDIT. package mocks @@ -378,6 +378,24 @@ func (_m *Flags) IAmBreakingEELicense() bool { return r0 } +// Identifier provides a mock function with given fields: +func (_m *Flags) Identifier() string { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Identifier") + } + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + // InModernStandby provides a mock function with given fields: func (_m *Flags) InModernStandby() bool { ret := _m.Called() diff --git a/ee/agent/types/mocks/knapsack.go b/ee/agent/types/mocks/knapsack.go index 339866056..8386393f2 100644 --- a/ee/agent/types/mocks/knapsack.go +++ b/ee/agent/types/mocks/knapsack.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.45.0. DO NOT EDIT. +// Code generated by mockery v2.46.0. DO NOT EDIT. package mocks @@ -526,6 +526,24 @@ func (_m *Knapsack) IAmBreakingEELicense() bool { return r0 } +// Identifier provides a mock function with given fields: +func (_m *Knapsack) Identifier() string { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Identifier") + } + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + // InModernStandby provides a mock function with given fields: func (_m *Knapsack) InModernStandby() bool { ret := _m.Called() diff --git a/ee/allowedcmd/cmd_linux.go b/ee/allowedcmd/cmd_linux.go index ed511e812..064d9da20 100644 --- a/ee/allowedcmd/cmd_linux.go +++ b/ee/allowedcmd/cmd_linux.go @@ -24,6 +24,10 @@ func Brew(ctx context.Context, arg ...string) (*exec.Cmd, error) { return validatedCmd, nil } +func Coredumpctl(ctx context.Context, arg ...string) (*exec.Cmd, error) { + return validatedCommand(ctx, "/usr/bin/coredumpctl", arg...) +} + func Cryptsetup(ctx context.Context, arg ...string) (*exec.Cmd, error) { for _, p := range []string{"/usr/sbin/cryptsetup", "/sbin/cryptsetup"} { validatedCmd, err := validatedCommand(ctx, p, arg...) diff --git a/ee/debug/checkups/checkups.go b/ee/debug/checkups/checkups.go index 49c7e076b..199ecee87 100644 --- a/ee/debug/checkups/checkups.go +++ b/ee/debug/checkups/checkups.go @@ -124,6 +124,7 @@ func checkupsFor(k types.Knapsack, target targetBits) []checkupInt { {&osqRestartCheckup{k: k}, doctorSupported | flareSupported}, {&uninstallHistoryCheckup{k: k}, flareSupported}, {&desktopMenu{k: k}, flareSupported}, + {&coredumpCheckup{}, doctorSupported | flareSupported}, } checkupsToRun := make([]checkupInt, 0) diff --git a/ee/debug/checkups/coredump_linux.go b/ee/debug/checkups/coredump_linux.go new file mode 100644 index 000000000..6cb745344 --- /dev/null +++ b/ee/debug/checkups/coredump_linux.go @@ -0,0 +1,144 @@ +//go:build linux +// +build linux + +package checkups + +import ( + "archive/zip" + "context" + "fmt" + "io" + "os" + "path/filepath" + "strings" + + "github.com/kolide/launcher/ee/agent" + "github.com/kolide/launcher/ee/allowedcmd" +) + +type coredumpCheckup struct { + status Status + summary string + data map[string]any +} + +func (c *coredumpCheckup) Name() string { + return "Coredump Report" +} + +func (c *coredumpCheckup) Run(ctx context.Context, extraWriter io.Writer) error { + c.data = make(map[string]any) + + c.status = Passing + for _, binaryName := range []string{"launcher", "osqueryd"} { + coredumpListRaw, err := c.coredumpList(ctx, binaryName) + if err != nil { + c.summary += fmt.Sprintf("could not get coredump data for %s; ", binaryName) + c.data[binaryName] = fmt.Sprintf("listing coredumps: %v", err) + continue + } + + if coredumpListRaw == nil { + c.summary += fmt.Sprintf("%s does not have any coredumps; ", binaryName) + c.data[binaryName] = "N/A" + continue + } + + // At least one coredump exists for at least one binary + c.status = Warning + c.summary += fmt.Sprintf("%s has at least one coredump; ", binaryName) + c.data[binaryName] = strings.TrimSpace(string(coredumpListRaw)) + } + c.summary = strings.TrimSuffix(strings.TrimSpace(c.summary), ";") + + if extraWriter == io.Discard || c.status == Passing { + // Either not a flare, or we don't have any coredumps to grab info about + return nil + } + + // Gather extra information about the coredumps + extraZip := zip.NewWriter(extraWriter) + defer extraZip.Close() + for _, binaryName := range []string{"launcher", "osqueryd"} { + if c.data[binaryName] == "N/A" { + continue + } + if err := c.writeCoredumpInfo(ctx, binaryName, extraZip); err != nil { + fmt.Fprintf(extraWriter, "Writing coredump info for %s: %v", binaryName, err) + } + } + + return nil +} + +func (c *coredumpCheckup) coredumpList(ctx context.Context, binaryName string) ([]byte, error) { + coredumpctlListCmd, err := allowedcmd.Coredumpctl(ctx, "--no-pager", "--no-legend", "--json=short", "list", binaryName) + if err != nil { + return nil, fmt.Errorf("could not create coredumpctl command: %w", err) + } + + out, err := coredumpctlListCmd.CombinedOutput() + if strings.Contains(string(out), "No coredumps found") { + return nil, nil + } + if err != nil { + return nil, fmt.Errorf("running coredumpctl list %s: out `%s`; %w", binaryName, string(out), err) + } + + return out, nil +} + +func (c *coredumpCheckup) writeCoredumpInfo(ctx context.Context, binaryName string, z *zip.Writer) error { + // Print info about all matching coredumps + coredumpctlInfoCmd, err := allowedcmd.Coredumpctl(ctx, "--no-pager", "info", binaryName) + if err != nil { + return fmt.Errorf("could not create coredumpctl info command: %w", err) + } + infoOut, err := coredumpctlInfoCmd.CombinedOutput() + if err != nil { + return fmt.Errorf("running coredumpctl info %s: out `%s`; %w", binaryName, string(infoOut), err) + } + coredumpctlInfoFile, err := z.Create(filepath.Join(".", fmt.Sprintf("coredumpctl-info-%s.txt", binaryName))) + if err != nil { + return fmt.Errorf("creating coredumpctl-info.txt for %s in zip: %w", binaryName, err) + } + if _, err := coredumpctlInfoFile.Write(infoOut); err != nil { + return fmt.Errorf("writing coredumpctl-info.txt in %s zip: %w", binaryName, err) + } + + // Now, try to get a coredump -- this will grab the most recent one + tempDir, err := agent.MkdirTemp("coredump-flare") + if err != nil { + return fmt.Errorf("making temporary directory for coredump from %s: %w", binaryName, err) + } + defer os.RemoveAll(tempDir) + tempDumpFile := filepath.Join(tempDir, fmt.Sprintf("coredump-%s.dump", binaryName)) + coredumpctlDumpCmd, err := allowedcmd.Coredumpctl(ctx, "--no-pager", fmt.Sprintf("--output=%s", tempDumpFile), "dump", binaryName) + if err != nil { + return fmt.Errorf("could not create coredumpctl dump command: %w", err) + } + if err := coredumpctlDumpCmd.Run(); err != nil { + return fmt.Errorf("running coredumpctl dump %s: %w", binaryName, err) + } + if err := addFileToZip(z, tempDumpFile); err != nil { + return fmt.Errorf("adding coredumpctl dump %s output file to zip: %w", binaryName, err) + } + + return nil +} + +func (c *coredumpCheckup) ExtraFileName() string { + return "coredumps.zip" +} + +func (c *coredumpCheckup) Status() Status { + return c.status +} + +func (c *coredumpCheckup) Summary() string { + return c.summary +} + +func (c *coredumpCheckup) Data() any { + return c.data +} diff --git a/ee/debug/checkups/coredump_other.go b/ee/debug/checkups/coredump_other.go new file mode 100644 index 000000000..93f8da19f --- /dev/null +++ b/ee/debug/checkups/coredump_other.go @@ -0,0 +1,36 @@ +//go:build !linux +// +build !linux + +package checkups + +import ( + "context" + "io" +) + +type coredumpCheckup struct { +} + +func (c *coredumpCheckup) Name() string { + return "" +} + +func (c *coredumpCheckup) ExtraFileName() string { + return "" +} + +func (c *coredumpCheckup) Run(_ context.Context, _ io.Writer) error { + return nil +} + +func (c *coredumpCheckup) Status() Status { + return Informational +} + +func (c *coredumpCheckup) Summary() string { + return "" +} + +func (c *coredumpCheckup) Data() any { + return nil +} diff --git a/ee/secureenclavesigner/secureenclavesigner_darwin.go b/ee/secureenclavesigner/secureenclavesigner_darwin.go index 163faae61..130eef9b7 100644 --- a/ee/secureenclavesigner/secureenclavesigner_darwin.go +++ b/ee/secureenclavesigner/secureenclavesigner_darwin.go @@ -114,6 +114,16 @@ func (ses *secureEnclaveSigner) Public() crypto.PublicKey { return nil } + // currentConsoleUserKey may return no error and a nil pointer where the inability + // to get the key is expected (see logic around calling firstConsoleUser). In this case, + // k will be a "typed" nil, as an uninitialized pointer to a ecdsa.PublicKey. We're returning + // this typed nil assigned as the crypto.PublicKey interface. This means that the interface's value + // will be nil, but it's underlying type will not be - so it will pass nil checks but panic + // when typecasted later. Explicitly return an untyped nil in this case to prevent confusion and panics later + if k == nil { + return nil + } + return k } diff --git a/ee/tuf/library_lookup.go b/ee/tuf/library_lookup.go index 157235616..19db1f33f 100644 --- a/ee/tuf/library_lookup.go +++ b/ee/tuf/library_lookup.go @@ -230,7 +230,7 @@ func CheckOutLatest(ctx context.Context, binary autoupdatableBinary, rootDirecto // If we can't find the specific release version that we should be on, then just return the executable // with the most recent version in the library - return mostRecentVersion(ctx, binary, updateDirectory, channel) + return mostRecentVersion(ctx, slogger, binary, updateDirectory, channel) } // findExecutable looks at our local TUF repository to find the release for our @@ -270,12 +270,12 @@ func findExecutable(ctx context.Context, binary autoupdatableBinary, tufReposito // mostRecentVersion returns the path to the most recent, valid version available in the library for the // given binary, along with its version. -func mostRecentVersion(ctx context.Context, binary autoupdatableBinary, baseUpdateDirectory, channel string) (*BinaryUpdateInfo, error) { +func mostRecentVersion(ctx context.Context, slogger *slog.Logger, binary autoupdatableBinary, baseUpdateDirectory, channel string) (*BinaryUpdateInfo, error) { ctx, span := traces.StartSpan(ctx) defer span.End() // Pull all available versions from library - validVersionsInLibrary, _, err := sortedVersionsInLibrary(ctx, binary, baseUpdateDirectory) + validVersionsInLibrary, _, err := sortedVersionsInLibrary(ctx, slogger, binary, baseUpdateDirectory) if err != nil { return nil, fmt.Errorf("could not get sorted versions in library for %s: %w", binary, err) } diff --git a/ee/tuf/library_lookup_test.go b/ee/tuf/library_lookup_test.go index 130af5fd9..5bcd93bb3 100644 --- a/ee/tuf/library_lookup_test.go +++ b/ee/tuf/library_lookup_test.go @@ -170,7 +170,7 @@ func Test_mostRecentVersion(t *testing.T) { tufci.CopyBinary(t, secondVersionPath) require.NoError(t, os.Chmod(secondVersionPath, 0755)) - latest, err := mostRecentVersion(context.TODO(), binary, testBaseDir, "nightly") + latest, err := mostRecentVersion(context.TODO(), multislogger.NewNopLogger(), binary, testBaseDir, "nightly") require.NoError(t, err, "did not expect error getting most recent version") require.Equal(t, secondVersionPath, latest.Path) require.Equal(t, secondVersion, latest.Version) @@ -202,7 +202,7 @@ func Test_mostRecentVersion_DoesNotReturnInvalidExecutables(t *testing.T) { require.NoError(t, os.MkdirAll(filepath.Dir(secondVersionPath), 0755)) os.WriteFile(secondVersionPath, []byte{}, 0755) - latest, err := mostRecentVersion(context.TODO(), binary, testBaseDir, "nightly") + latest, err := mostRecentVersion(context.TODO(), multislogger.NewNopLogger(), binary, testBaseDir, "nightly") require.NoError(t, err, "did not expect error getting most recent version") require.Equal(t, firstVersionPath, latest.Path) require.Equal(t, firstVersion, latest.Version) @@ -221,7 +221,7 @@ func Test_mostRecentVersion_ReturnsErrorOnNoUpdatesDownloaded(t *testing.T) { // Create update directories testBaseDir := t.TempDir() - _, err := mostRecentVersion(context.TODO(), binary, testBaseDir, "nightly") + _, err := mostRecentVersion(context.TODO(), multislogger.NewNopLogger(), binary, testBaseDir, "nightly") require.Error(t, err, "should have returned error when there are no available updates") }) } @@ -239,7 +239,7 @@ func Test_mostRecentVersion_requiresLauncher_v1_4_1(t *testing.T) { tufci.CopyBinary(t, firstVersionPath) require.NoError(t, os.Chmod(firstVersionPath, 0755)) - _, err := mostRecentVersion(context.TODO(), binaryLauncher, testBaseDir, "stable") + _, err := mostRecentVersion(context.TODO(), multislogger.NewNopLogger(), binaryLauncher, testBaseDir, "stable") require.Error(t, err, "should not select launcher version under v1.4.1") } @@ -255,7 +255,7 @@ func Test_mostRecentVersion_acceptsLauncher_v1_4_1(t *testing.T) { tufci.CopyBinary(t, firstVersionPath) require.NoError(t, os.Chmod(firstVersionPath, 0755)) - latest, err := mostRecentVersion(context.TODO(), binaryLauncher, testBaseDir, "stable") + latest, err := mostRecentVersion(context.TODO(), multislogger.NewNopLogger(), binaryLauncher, testBaseDir, "stable") require.NoError(t, err, "should be able to select launcher version equal to v1.4.1") require.Equal(t, firstVersionPath, latest.Path) } diff --git a/ee/tuf/library_manager.go b/ee/tuf/library_manager.go index 62cb27393..7e8da672d 100644 --- a/ee/tuf/library_manager.go +++ b/ee/tuf/library_manager.go @@ -366,7 +366,7 @@ func (ulm *updateLibraryManager) TidyLibrary(binary autoupdatableBinary, current const numberOfVersionsToKeep = 3 - versionsInLibrary, invalidVersionsInLibrary, err := sortedVersionsInLibrary(context.Background(), binary, ulm.baseDir) + versionsInLibrary, invalidVersionsInLibrary, err := sortedVersionsInLibrary(context.Background(), ulm.slogger, binary, ulm.baseDir) if err != nil { ulm.slogger.Log(context.TODO(), slog.LevelWarn, "could not get versions in library to tidy update library", @@ -410,7 +410,7 @@ func (ulm *updateLibraryManager) TidyLibrary(binary autoupdatableBinary, current // sortedVersionsInLibrary looks through the update library for the given binary to validate and sort all // available versions. It returns a sorted list of the valid versions, a list of invalid versions, and // an error only when unable to glob for versions. -func sortedVersionsInLibrary(ctx context.Context, binary autoupdatableBinary, baseUpdateDirectory string) ([]string, []string, error) { +func sortedVersionsInLibrary(ctx context.Context, slogger *slog.Logger, binary autoupdatableBinary, baseUpdateDirectory string) ([]string, []string, error) { ctx, span := traces.StartSpan(ctx) defer span.End() @@ -425,6 +425,13 @@ func sortedVersionsInLibrary(ctx context.Context, binary autoupdatableBinary, ba rawVersion := filepath.Base(rawVersionWithPath) v, err := semver.NewVersion(rawVersion) if err != nil { + slogger.Log(ctx, slog.LevelWarn, + "detected invalid binary version while parsing raw version", + "raw_version", rawVersion, + "binary", binary, + "err", err, + ) + invalidVersions = append(invalidVersions, rawVersion) continue } @@ -432,6 +439,13 @@ func sortedVersionsInLibrary(ctx context.Context, binary autoupdatableBinary, ba versionDir := filepath.Join(updatesDirectory(binary, baseUpdateDirectory), rawVersion) if err := CheckExecutable(ctx, executableLocation(versionDir, binary), "--version"); err != nil { traces.SetError(span, err) + slogger.Log(ctx, slog.LevelWarn, + "detected invalid binary version while checking executable", + "version_dir", versionDir, + "binary", binary, + "err", err, + ) + invalidVersions = append(invalidVersions, rawVersion) continue } diff --git a/ee/tuf/library_manager_test.go b/ee/tuf/library_manager_test.go index 1a26c04ea..02072a6fc 100644 --- a/ee/tuf/library_manager_test.go +++ b/ee/tuf/library_manager_test.go @@ -685,7 +685,7 @@ func Test_sortedVersionsInLibrary(t *testing.T) { } // Get sorted versions - validVersions, invalidVersions, err := sortedVersionsInLibrary(context.TODO(), binaryLauncher, testBaseDir) + validVersions, invalidVersions, err := sortedVersionsInLibrary(context.TODO(), multislogger.NewNopLogger(), binaryLauncher, testBaseDir) require.NoError(t, err, "expected no error on sorting versions in library") // Confirm invalid versions are the ones we expect @@ -725,7 +725,7 @@ func Test_sortedVersionsInLibrary_devBuilds(t *testing.T) { } // Get sorted versions - validVersions, invalidVersions, err := sortedVersionsInLibrary(context.TODO(), binaryLauncher, testBaseDir) + validVersions, invalidVersions, err := sortedVersionsInLibrary(context.TODO(), multislogger.NewNopLogger(), binaryLauncher, testBaseDir) require.NoError(t, err, "expected no error on sorting versions in library") // Confirm we don't have any invalid versions diff --git a/ee/uninstall/uninstall.go b/ee/uninstall/uninstall.go index 03f2394c1..dfc7e56b2 100644 --- a/ee/uninstall/uninstall.go +++ b/ee/uninstall/uninstall.go @@ -49,7 +49,7 @@ func Uninstall(ctx context.Context, k types.Knapsack, exitOnCompletion bool) { return } - if err := disableAutoStart(ctx); err != nil { + if err := disableAutoStart(ctx, k); err != nil { k.Slogger().Log(ctx, slog.LevelError, "disabling auto start", "err", err, diff --git a/ee/uninstall/uninstall_darwin.go b/ee/uninstall/uninstall_darwin.go index 5a419b2dc..2d6ffc6ae 100644 --- a/ee/uninstall/uninstall_darwin.go +++ b/ee/uninstall/uninstall_darwin.go @@ -5,11 +5,12 @@ import ( "fmt" "time" + "github.com/kolide/launcher/ee/agent/types" "github.com/kolide/launcher/ee/allowedcmd" ) -func disableAutoStart(ctx context.Context) error { - launchDaemonPList := "/Library/LaunchDaemons/com.kolide-k2.launcher.plist" +func disableAutoStart(ctx context.Context, k types.Knapsack) error { + launchDaemonPList := fmt.Sprintf("/Library/LaunchDaemons/com.%s.launcher.plist", k.Identifier()) launchCtlArgs := []string{"unload", launchDaemonPList} launchctlCtx, cancel := context.WithTimeout(ctx, 30*time.Second) diff --git a/ee/uninstall/uninstall_linux.go b/ee/uninstall/uninstall_linux.go index 2f3a6354b..815b11665 100644 --- a/ee/uninstall/uninstall_linux.go +++ b/ee/uninstall/uninstall_linux.go @@ -4,12 +4,14 @@ import ( "context" "fmt" + "github.com/kolide/launcher/ee/agent/types" "github.com/kolide/launcher/ee/allowedcmd" ) -func disableAutoStart(ctx context.Context) error { +func disableAutoStart(ctx context.Context, k types.Knapsack) error { + serviceName := fmt.Sprintf("launcher.%s.service", k.Identifier()) // the --now flag will disable and stop the service - cmd, err := allowedcmd.Systemctl(ctx, "disable", "--now", "launcher.kolide-k2.service") + cmd, err := allowedcmd.Systemctl(ctx, "disable", "--now", serviceName) if err != nil { return fmt.Errorf("creating systemctl cmd: %w", err) } diff --git a/ee/uninstall/uninstall_windows.go b/ee/uninstall/uninstall_windows.go index 41e968217..d1bb33a79 100644 --- a/ee/uninstall/uninstall_windows.go +++ b/ee/uninstall/uninstall_windows.go @@ -4,18 +4,21 @@ import ( "context" "fmt" + "github.com/kolide/launcher/ee/agent/types" "github.com/kolide/launcher/ee/watchdog" + "github.com/kolide/launcher/pkg/launcher" "golang.org/x/sys/windows/svc/mgr" ) -func disableAutoStart(ctx context.Context) error { +func disableAutoStart(ctx context.Context, k types.Knapsack) error { svcMgr, err := mgr.Connect() if err != nil { return fmt.Errorf("connecting to windows service manager: %w", err) } defer svcMgr.Disconnect() - launcherSvc, err := svcMgr.OpenService("LauncherKolideK2Svc") + serviceName := launcher.ServiceName(k.Identifier()) + launcherSvc, err := svcMgr.OpenService(serviceName) if err != nil { return fmt.Errorf("opening launcher service: %w", err) } diff --git a/ee/watchdog/watchdog_service_windows.go b/ee/watchdog/watchdog_service_windows.go index d3e670974..de2cfb780 100644 --- a/ee/watchdog/watchdog_service_windows.go +++ b/ee/watchdog/watchdog_service_windows.go @@ -227,7 +227,8 @@ func (w *winWatchdogSvc) checkLauncherStatus(ctx context.Context) error { func runLauncherWatchdogService(ctx context.Context, w *winWatchdogSvc) error { // create a rungroup for all the actors we create to allow for easy start/stop - runGroup := rungroup.NewRunGroup(w.slogger.Logger) + runGroup := rungroup.NewRunGroup() + runGroup.SetSlogger(w.slogger.Logger) powerEventWatcher, err := powereventwatcher.New(ctx, w.slogger.Logger, w.sleepStateUpdater) if err != nil { w.slogger.Log(ctx, slog.LevelDebug, diff --git a/pkg/rungroup/rungroup.go b/pkg/rungroup/rungroup.go index 16a92d747..6da0a58ff 100644 --- a/pkg/rungroup/rungroup.go +++ b/pkg/rungroup/rungroup.go @@ -12,6 +12,7 @@ import ( "time" "github.com/kolide/launcher/ee/gowrapper" + "github.com/kolide/launcher/pkg/log/multislogger" "golang.org/x/sync/semaphore" ) @@ -38,9 +39,9 @@ const ( executeReturnTimeout = 5 * time.Second // After interrupted, how long for all actors to exit their `execute` functions ) -func NewRunGroup(slogger *slog.Logger) *Group { +func NewRunGroup() *Group { return &Group{ - slogger: slogger.With("component", "run_group"), + slogger: multislogger.NewNopLogger(), actors: make([]rungroupActor, 0), } } @@ -49,6 +50,10 @@ func (g *Group) Add(name string, execute func() error, interrupt func(error)) { g.actors = append(g.actors, rungroupActor{name, execute, interrupt}) } +func (g *Group) SetSlogger(slogger *slog.Logger) { + g.slogger = slogger.With("component", "run_group") +} + func (g *Group) Run() error { if len(g.actors) == 0 { return nil diff --git a/pkg/rungroup/rungroup_test.go b/pkg/rungroup/rungroup_test.go index 942fa83dc..6ec4b4bfa 100644 --- a/pkg/rungroup/rungroup_test.go +++ b/pkg/rungroup/rungroup_test.go @@ -7,7 +7,6 @@ import ( "testing" "time" - "github.com/kolide/launcher/pkg/log/multislogger" "github.com/kolide/launcher/pkg/threadsafebuffer" "github.com/stretchr/testify/require" ) @@ -15,14 +14,14 @@ import ( func TestRun_NoActors(t *testing.T) { t.Parallel() - testRunGroup := NewRunGroup(multislogger.NewNopLogger()) + testRunGroup := NewRunGroup() require.NoError(t, testRunGroup.Run()) } func TestRun_MultipleActors(t *testing.T) { t.Parallel() - testRunGroup := NewRunGroup(multislogger.NewNopLogger()) + testRunGroup := NewRunGroup() groupReceivedInterrupts := make(chan struct{}, 3) @@ -93,7 +92,7 @@ func TestRun_MultipleActors(t *testing.T) { func TestRun_MultipleActors_InterruptTimeout(t *testing.T) { t.Parallel() - testRunGroup := NewRunGroup(multislogger.NewNopLogger()) + testRunGroup := NewRunGroup() groupReceivedInterrupts := make(chan struct{}, 3) @@ -166,7 +165,7 @@ func TestRun_MultipleActors_InterruptTimeout(t *testing.T) { func TestRun_MultipleActors_ExecuteReturnTimeout(t *testing.T) { t.Parallel() - testRunGroup := NewRunGroup(multislogger.NewNopLogger()) + testRunGroup := NewRunGroup() groupReceivedInterrupts := make(chan struct{}, 3) // Keep track of when `execute`s return so we give testRunGroup.Run enough time to do its thing @@ -250,7 +249,8 @@ func TestRun_RecoversAndLogsPanic(t *testing.T) { Level: slog.LevelDebug, })) - testRunGroup := NewRunGroup(slogger) + testRunGroup := NewRunGroup() + testRunGroup.SetSlogger(slogger) // Actor that will panic in its execute function testRunGroup.Add("panickingActor", func() error {