Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add coredump checkup for flare/doctor using coredumpctl only #1868

Merged
merged 4 commits into from
Sep 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions ee/allowedcmd/cmd_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -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...)
Expand Down
1 change: 1 addition & 0 deletions ee/debug/checkups/checkups.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
144 changes: 144 additions & 0 deletions ee/debug/checkups/coredump_linux.go
Original file line number Diff line number Diff line change
@@ -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
}
36 changes: 36 additions & 0 deletions ee/debug/checkups/coredump_other.go
Original file line number Diff line number Diff line change
@@ -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
}
Loading