Skip to content

Commit

Permalink
Add coredump checkup for flare/doctor using coredumpctl only
Browse files Browse the repository at this point in the history
  • Loading branch information
RebeccaMahany committed Sep 17, 2024
1 parent fbecac9 commit 86aae7c
Show file tree
Hide file tree
Showing 4 changed files with 184 additions and 0 deletions.
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
143 changes: 143 additions & 0 deletions ee/debug/checkups/coredump_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
//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 = Failing
c.summary += fmt.Sprintf("%s has at least one coredump; ", binaryName)
c.data[binaryName] = 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 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 err != nil {
return nil, fmt.Errorf("running coredumpctl list %s: %w", binaryName, err)
}

// No coredumps!
if strings.Contains(string(out), "No coredumps found") {
return nil, nil
}

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: %w", binaryName, 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
}

0 comments on commit 86aae7c

Please sign in to comment.