go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/providers/os/resources/processes.go (about) 1 // Copyright (c) Mondoo, Inc. 2 // SPDX-License-Identifier: BUSL-1.1 3 4 package resources 5 6 import ( 7 "errors" 8 "fmt" 9 "strconv" 10 "sync" 11 12 "github.com/rs/zerolog/log" 13 "go.mondoo.com/cnquery/llx" 14 "go.mondoo.com/cnquery/providers-sdk/v1/plugin" 15 "go.mondoo.com/cnquery/providers/os/connection/shared" 16 "go.mondoo.com/cnquery/providers/os/resources/processes" 17 ) 18 19 type mqlProcessInternal struct { 20 SocketInodesError error 21 SocketInodes plugin.TValue[[]int64] 22 lock sync.Mutex 23 } 24 25 func initProcess(runtime *plugin.Runtime, args map[string]*llx.RawData) (map[string]*llx.RawData, plugin.Resource, error) { 26 // do not try to resolve the process if we already go all parameters 27 // NOTE: this happens for a call like processes.list 28 if len(args) > 2 { 29 return args, nil, nil 30 } 31 32 pidValue, ok := args["pid"] 33 if ok { 34 pid, ok := pidValue.Value.(int64) 35 if !ok { 36 return nil, nil, errors.New("pid has invalid type") 37 } 38 39 // lets do minimal IO in initialize 40 conn := runtime.Connection.(shared.Connection) 41 opm, err := processes.ResolveManager(conn) 42 if err != nil { 43 return nil, nil, errors.New("cannot find process manager") 44 } 45 46 // check that the PID exists 47 exists, err := opm.Exists(pid) 48 if err != nil || !exists { 49 return nil, nil, errors.New("process " + strconv.FormatInt(pid, 10) + " does not exist") 50 } 51 } 52 return args, nil, nil 53 } 54 55 func (p *mqlProcess) id() (string, error) { 56 return strconv.FormatInt(p.Pid.Data, 10), nil 57 } 58 59 func (p *mqlProcess) state() (string, error) { 60 return "", p.gatherProcessInfo() 61 } 62 63 func (p *mqlProcess) executable() (string, error) { 64 return "", p.gatherProcessInfo() 65 } 66 67 func (p *mqlProcess) command() (string, error) { 68 return "", p.gatherProcessInfo() 69 } 70 71 func (p *mqlProcess) flags() (map[string]interface{}, error) { 72 cmd := p.GetCommand() 73 if cmd.Error != nil { 74 return nil, cmd.Error 75 } 76 77 fs := processes.FlagSet{} 78 err := fs.ParseCommand(cmd.Data) 79 if err != nil { 80 return nil, err 81 } 82 flags := fs.Map() 83 84 res := map[string]interface{}{} 85 for k := range flags { 86 res[k] = flags[k] 87 } 88 return res, nil 89 } 90 91 type ProcessCallbackTrigger func() 92 93 func (p *mqlProcess) gatherProcessInfo() error { 94 p.lock.Lock() 95 defer p.lock.Unlock() 96 97 conn := p.MqlRuntime.Connection.(shared.Connection) 98 opm, err := processes.ResolveManager(conn) 99 if err != nil { 100 return errors.New("cannot find process manager") 101 } 102 103 process, err := opm.Process(p.Pid.Data) 104 if err != nil { 105 return errors.New("cannot gather process details") 106 } 107 108 p.State = plugin.TValue[string]{Data: process.State, State: plugin.StateIsSet} 109 p.Executable = plugin.TValue[string]{Data: process.Executable, State: plugin.StateIsSet} 110 p.Command = plugin.TValue[string]{Data: process.Command, State: plugin.StateIsSet} 111 p.SocketInodes = plugin.TValue[[]int64]{Data: process.SocketInodes, State: plugin.StateIsSet} 112 113 return nil 114 } 115 116 type mqlProcessesInternal struct { 117 ByPID map[int64]*mqlProcess 118 BySocketID map[int64]*mqlProcess 119 } 120 121 func (p *mqlProcesses) list() ([]interface{}, error) { 122 conn := p.MqlRuntime.Connection.(shared.Connection) 123 opm, err := processes.ResolveManager(conn) 124 if opm == nil || err != nil { 125 log.Debug().Err(err).Msg("mql[processes]> could not retrieve process resolver") 126 return nil, errors.New("cannot find process manager") 127 } 128 129 // retrieve all system processes 130 processes, err := opm.List() 131 if err != nil { 132 log.Warn().Err(err).Msg("mql[processes]> could not retrieve process list") 133 return nil, fmt.Errorf("could not retrieve process list") 134 } 135 log.Debug().Int("processes", len(processes)).Msg("mql[processes]> running processes") 136 137 procs := make([]interface{}, len(processes)) 138 139 for i := range processes { 140 proc := processes[i] 141 142 o, err := CreateResource(p.MqlRuntime, "process", map[string]*llx.RawData{ 143 "pid": llx.IntData(proc.Pid), 144 "executable": llx.StringData(proc.Executable), 145 "command": llx.StringData(proc.Command), 146 "state": llx.StringData(proc.State), 147 }) 148 if err != nil { 149 return nil, err 150 } 151 152 process := o.(*mqlProcess) 153 process.SocketInodes = plugin.TValue[[]int64]{ 154 Data: proc.SocketInodes, 155 Error: proc.SocketInodesError, 156 State: plugin.StateIsSet, 157 } 158 159 procs[i] = o 160 } 161 162 return procs, p.refreshCache(procs) 163 } 164 165 func (p *mqlProcesses) refreshCache(all []interface{}) error { 166 if all == nil { 167 raw := p.GetList() 168 if raw.Error != nil { 169 return raw.Error 170 } 171 all = raw.Data 172 } 173 174 processesMap := make(map[int64]*mqlProcess, len(all)) 175 socketsMap := map[int64]*mqlProcess{} 176 177 for i := range all { 178 process := all[i].(*mqlProcess) 179 processesMap[process.Pid.Data] = process 180 for i := range process.SocketInodes.Data { 181 socketsMap[process.SocketInodes.Data[i]] = process 182 } 183 } 184 185 p.ByPID = processesMap 186 p.BySocketID = socketsMap 187 return nil 188 }