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  }