go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/providers/os/resources/procfs/processes.go (about) 1 // Copyright (c) Mondoo, Inc. 2 // SPDX-License-Identifier: BUSL-1.1 3 4 package procfs 5 6 import ( 7 "bufio" 8 "bytes" 9 "io" 10 "regexp" 11 "strconv" 12 "strings" 13 14 "github.com/rs/zerolog/log" 15 ) 16 17 type LinuxProcess struct { 18 Pid int64 `json:"pid"` 19 Cmdline string `json:"cmdline"` 20 Status *LinuxProcessStatus `json:"status"` 21 } 22 23 type LinuxProcessStatus struct { 24 // lets assume pids are always unsigned, linux returns -1 on unsuccessful forks but that 25 // is never becoming a real process 26 Pid int64 `json:"pid"` // process id 27 PPid int64 `json:"ppid"` // process id of the parent process 28 Executable string `json:"executable"` // filename of the executable 29 State string `json:"state"` 30 Tgid int64 `json:"tgid"` // thread group ID 31 Ngid int64 `json:"ngid"` // NUMA group ID (0 if none) 32 } 33 34 var LINUX_PROCES_STATUS_REGEX = regexp.MustCompile(`^(.*):\s*(.*)$`) 35 36 func ParseProcessStatus(input io.Reader) (*LinuxProcessStatus, error) { 37 lps := &LinuxProcessStatus{} 38 var err error 39 scanner := bufio.NewScanner(input) 40 for scanner.Scan() { 41 line := scanner.Text() 42 m := LINUX_PROCES_STATUS_REGEX.FindStringSubmatch(line) 43 44 if m == nil { 45 log.Warn().Str("entry", line).Msg("ignore process status entry") 46 continue 47 } 48 49 key := m[1] 50 value := m[2] 51 switch key { 52 case "Name": 53 lps.Executable = value 54 case "State": // state (R is running, S is sleeping, D is sleeping 55 // in an uninterruptible wait, Z is zombie, T is traced or stopped) 56 lps.State = value 57 case "Pid": 58 if lps.Pid, err = strconv.ParseInt(value, 10, 64); err != nil { 59 log.Warn().Err(err).Str("key", key).Msg("process> could not parse value") 60 continue 61 } 62 case "PPid": 63 if lps.Pid, err = strconv.ParseInt(value, 10, 64); err != nil { 64 log.Warn().Err(err).Str("key", key).Msg("process> could not parse value") 65 continue 66 } 67 case "Tgid": 68 if lps.Tgid, err = strconv.ParseInt(value, 10, 64); err != nil { 69 log.Warn().Err(err).Str("key", key).Msg("process> could not parse value") 70 continue 71 } 72 case "Ngid": 73 if lps.Ngid, err = strconv.ParseInt(value, 10, 64); err != nil { 74 log.Warn().Err(err).Str("key", key).Msg("process> could not parse value") 75 continue 76 } 77 case "Uid": // Real, effective, saved set, and file system UIDs 78 79 case "Gid": // Real, effective, saved set, and file system GIDs 80 81 case "Umask", "TracerPid", "FDSize", "Groups", "VmPeak", "VmSize", "VmLck", "VmPin", 82 "VmHWM", "VmRSS", "RssAnon", "RssFile", "RssShmem", "VmData", "VmStk", "VmExe", "VmLib", 83 "VmPTE", "VmSwap", "Threads", "SigQ", "SigPnd", "ShdPnd", "SigBlk", "SigIgn", "SigCgt", 84 "CapInh", "CapPrm", "CapEff", "CapBnd", "CapAmb", "Seccomp", "Cpus_allowed", "Cpus_allowed_list", 85 "Mems_allowed", "Mems_allowed_list", "voluntary_ctxt_switches", "nonvoluntary_ctxt_switches": 86 // known, nothing to do yet 87 default: 88 log.Debug().Str("key", key).Msg("process status key is not handled") 89 } 90 } 91 92 // parse status via 93 return lps, nil 94 } 95 96 func ParseProcessCmdline(content io.Reader) (string, error) { 97 data, err := io.ReadAll(content) 98 if err != nil { 99 return "", err 100 } 101 102 parts := bytes.Split(data, []byte{0}) 103 var strParts []string 104 for _, p := range parts { 105 strParts = append(strParts, strings.TrimSpace(string(p))) 106 } 107 108 return strings.Join(strParts, " "), nil 109 }