diff --git a/pkg/osquery/runtime/osqueryinstance.go b/pkg/osquery/runtime/osqueryinstance.go index 15c0180c4..af180eb6a 100644 --- a/pkg/osquery/runtime/osqueryinstance.go +++ b/pkg/osquery/runtime/osqueryinstance.go @@ -29,6 +29,7 @@ import ( "github.com/osquery/osquery-go/plugin/config" "github.com/osquery/osquery-go/plugin/distributed" osquerylogger "github.com/osquery/osquery-go/plugin/logger" + "github.com/shirou/gopsutil/v3/process" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" @@ -731,7 +732,7 @@ func (i *OsqueryInstance) detectStaleDatabaseLock(ctx context.Context, paths *os infoToLog := []any{ "lockfile_path", lockFilePath, - "lockfile_modtime", lockFileInfo.ModTime().String(), + "lockfile_modtime", lockFileInfo.ModTime().UTC().String(), } defer func() { @@ -742,37 +743,42 @@ func (i *OsqueryInstance) detectStaleDatabaseLock(ctx context.Context, paths *os }() // Check to see whether the process holding the file still exists - p, err := getProcessHoldingFile(ctx, lockFilePath) + processes, err := getProcessesHoldingFile(ctx, lockFilePath) if err != nil { infoToLog = append(infoToLog, "err", err) return false, fmt.Errorf("getting process holding file: %w", err) } - // Grab more info to log from the process using the lockfile - infoToLog = append(infoToLog, "pid", p.Pid) - if name, err := p.NameWithContext(ctx); err == nil { - infoToLog = append(infoToLog, "process_name", name) + // Grab more info to log from the processes using the lockfile + processStrs := make([]string, len(processes)) + for i, p := range processes { + processStrs[i] = processStr(ctx, p) } - if cmdline, err := p.CmdlineWithContext(ctx); err == nil { - infoToLog = append(infoToLog, "process_cmdline", cmdline) + infoToLog = append(infoToLog, "processes", processStrs) + + return true, nil +} + +func processStr(ctx context.Context, p *process.Process) string { + name := "unknown" + uids := "unknown" + status := "unknown" + cmdline := "unknown" + + if gotName, err := p.NameWithContext(ctx); err == nil { + name = gotName } - if status, err := p.StatusWithContext(ctx); err == nil { - infoToLog = append(infoToLog, "process_status", status) + if gotUids, err := p.UidsWithContext(ctx); err == nil { + uids = fmt.Sprintf("%v", gotUids) } - if isRunning, err := p.IsRunningWithContext(ctx); err == nil { - infoToLog = append(infoToLog, "process_is_running", isRunning) + if gotStatus, err := p.StatusWithContext(ctx); err == nil { + status = strings.Join(gotStatus, " ") } - if parent, err := p.ParentWithContext(ctx); err == nil { - infoToLog = append(infoToLog, "process_parent_pid", parent.Pid) - if parentCmdline, err := parent.CmdlineWithContext(ctx); err == nil { - infoToLog = append(infoToLog, "process_parent_cmdline", parentCmdline) - } - if parentStatus, err := p.StatusWithContext(ctx); err == nil { - infoToLog = append(infoToLog, "process_parent_status", parentStatus) - } + if gotCmdline, err := p.CmdlineWithContext(ctx); err == nil { + cmdline = gotCmdline } - return true, nil + return fmt.Sprintf("process with name `%s` and PID %d belonging to UIDs %s has current status `%s` (%s)", name, p.Pid, uids, status, cmdline) } // createOsquerydCommand uses osqueryOptions to return an *exec.Cmd diff --git a/pkg/osquery/runtime/runtime_helpers.go b/pkg/osquery/runtime/runtime_helpers.go index 392e4a4c5..500a4c7e2 100644 --- a/pkg/osquery/runtime/runtime_helpers.go +++ b/pkg/osquery/runtime/runtime_helpers.go @@ -41,7 +41,7 @@ func isExitOk(_ error) bool { return false } -func getProcessHoldingFile(ctx context.Context, pathToFile string) (*process.Process, error) { +func getProcessesHoldingFile(ctx context.Context, pathToFile string) ([]*process.Process, error) { cmd, err := allowedcmd.Lsof(ctx, "-t", pathToFile) if err != nil { return nil, fmt.Errorf("creating lsof command: %w", err) @@ -57,10 +57,24 @@ func getProcessHoldingFile(ctx context.Context, pathToFile string) (*process.Pro return nil, errors.New("no process found using file via lsof") } - pid, err := strconv.ParseInt(pidStr, 10, 32) - if err != nil { - return nil, fmt.Errorf("invalid pid %s: %w", pidStr, err) + pidStrs := strings.Split(strings.TrimSpace(string(out)), "\n") + if len(pidStrs) == 0 { + return nil, errors.New("no processes found using file via lsof") + } + + processes := make([]*process.Process, 0) + for _, pidStr := range pidStrs { + pid, err := strconv.ParseInt(pidStr, 10, 32) + if err != nil { + return nil, fmt.Errorf("invalid pid %s: %w", pidStr, err) + } + + p, err := process.NewProcess(int32(pid)) + if err != nil { + return nil, fmt.Errorf("getting process for %d: %w", pid, err) + } + processes = append(processes, p) } - return process.NewProcess(int32(pid)) + return processes, nil } diff --git a/pkg/osquery/runtime/runtime_helpers_windows.go b/pkg/osquery/runtime/runtime_helpers_windows.go index bc50363e7..6e9895e7c 100644 --- a/pkg/osquery/runtime/runtime_helpers_windows.go +++ b/pkg/osquery/runtime/runtime_helpers_windows.go @@ -82,12 +82,13 @@ func isExitOk(err error) bool { return false } -func getProcessHoldingFile(ctx context.Context, pathToFile string) (*process.Process, error) { +func getProcessesHoldingFile(ctx context.Context, pathToFile string) ([]*process.Process, error) { allProcesses, err := process.ProcessesWithContext(ctx) if err != nil { return nil, fmt.Errorf("getting process list: %w", err) } + processes := make([]*process.Process, 0) for _, p := range allProcesses { openFiles, err := p.OpenFilesWithContext(ctx) if err != nil { @@ -100,9 +101,14 @@ func getProcessHoldingFile(ctx context.Context, pathToFile string) (*process.Pro continue } - return p, nil + processes = append(processes, p) + break } } - return nil, errors.New("no process found using file") + if len(processes) == 0 { + return nil, errors.New("no processes found using file") + } + + return processes, nil }