360 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			360 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			Go
		
	
	
	
| // +build darwin
 | |
| 
 | |
| package process
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"os/exec"
 | |
| 	"strconv"
 | |
| 	"strings"
 | |
| 	"syscall"
 | |
| 	"unsafe"
 | |
| 
 | |
| 	common "github.com/influxdb/telegraf/plugins/system/ps/common"
 | |
| 	cpu "github.com/influxdb/telegraf/plugins/system/ps/cpu"
 | |
| 	net "github.com/influxdb/telegraf/plugins/system/ps/net"
 | |
| )
 | |
| 
 | |
| // copied from sys/sysctl.h
 | |
| const (
 | |
| 	CTLKern          = 1  // "high kernel": proc, limits
 | |
| 	KernProc         = 14 // struct: process entries
 | |
| 	KernProcPID      = 1  // by process id
 | |
| 	KernProcProc     = 8  // only return procs
 | |
| 	KernProcAll      = 0  // everything
 | |
| 	KernProcPathname = 12 // path to executable
 | |
| )
 | |
| 
 | |
| type _Ctype_struct___0 struct {
 | |
| 	Pad uint64
 | |
| }
 | |
| 
 | |
| // MemoryInfoExStat is different between OSes
 | |
| type MemoryInfoExStat struct {
 | |
| }
 | |
| 
 | |
| type MemoryMapsStat struct {
 | |
| }
 | |
| 
 | |
| func Pids() ([]int32, error) {
 | |
| 	var ret []int32
 | |
| 
 | |
| 	pids, err := callPs("pid", 0)
 | |
| 	if err != nil {
 | |
| 		return ret, err
 | |
| 	}
 | |
| 
 | |
| 	for _, pid := range pids {
 | |
| 		v, err := strconv.Atoi(pid[0])
 | |
| 		if err != nil {
 | |
| 			return ret, err
 | |
| 		}
 | |
| 		ret = append(ret, int32(v))
 | |
| 	}
 | |
| 
 | |
| 	return ret, nil
 | |
| }
 | |
| 
 | |
| func (p *Process) Ppid() (int32, error) {
 | |
| 	r, err := callPs("ppid", p.Pid)
 | |
| 	v, err := strconv.Atoi(r[0][0])
 | |
| 	if err != nil {
 | |
| 		return 0, err
 | |
| 	}
 | |
| 
 | |
| 	return int32(v), err
 | |
| }
 | |
| func (p *Process) Name() (string, error) {
 | |
| 	k, err := p.getKProc()
 | |
| 	if err != nil {
 | |
| 		return "", err
 | |
| 	}
 | |
| 
 | |
| 	return common.IntToString(k.Proc.P_comm[:]), nil
 | |
| }
 | |
| func (p *Process) Exe() (string, error) {
 | |
| 	return "", common.NotImplementedError
 | |
| }
 | |
| func (p *Process) Cmdline() (string, error) {
 | |
| 	r, err := callPs("command", p.Pid)
 | |
| 	if err != nil {
 | |
| 		return "", err
 | |
| 	}
 | |
| 	return strings.Join(r[0], " "), err
 | |
| }
 | |
| func (p *Process) CreateTime() (int64, error) {
 | |
| 	return 0, common.NotImplementedError
 | |
| }
 | |
| func (p *Process) Cwd() (string, error) {
 | |
| 	return "", common.NotImplementedError
 | |
| }
 | |
| func (p *Process) Parent() (*Process, error) {
 | |
| 	return p, common.NotImplementedError
 | |
| }
 | |
| func (p *Process) Status() (string, error) {
 | |
| 	r, err := callPs("state", p.Pid)
 | |
| 	if err != nil {
 | |
| 		return "", err
 | |
| 	}
 | |
| 
 | |
| 	return r[0][0], err
 | |
| }
 | |
| func (p *Process) Uids() ([]int32, error) {
 | |
| 	k, err := p.getKProc()
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	uids := make([]int32, 0, 3)
 | |
| 
 | |
| 	uids = append(uids, int32(k.Eproc.Pcred.P_ruid), int32(k.Eproc.Ucred.Uid), int32(k.Eproc.Pcred.P_svuid))
 | |
| 
 | |
| 	return uids, nil
 | |
| }
 | |
| func (p *Process) Gids() ([]int32, error) {
 | |
| 	k, err := p.getKProc()
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	gids := make([]int32, 0, 3)
 | |
| 	gids = append(gids, int32(k.Eproc.Pcred.P_rgid), int32(k.Eproc.Ucred.Ngroups), int32(k.Eproc.Pcred.P_svgid))
 | |
| 
 | |
| 	return gids, nil
 | |
| }
 | |
| func (p *Process) Terminal() (string, error) {
 | |
| 	return "", common.NotImplementedError
 | |
| 	/*
 | |
| 		k, err := p.getKProc()
 | |
| 		if err != nil {
 | |
| 			return "", err
 | |
| 		}
 | |
| 
 | |
| 		ttyNr := uint64(k.Eproc.Tdev)
 | |
| 		termmap, err := getTerminalMap()
 | |
| 		if err != nil {
 | |
| 			return "", err
 | |
| 		}
 | |
| 
 | |
| 		return termmap[ttyNr], nil
 | |
| 	*/
 | |
| }
 | |
| func (p *Process) Nice() (int32, error) {
 | |
| 	k, err := p.getKProc()
 | |
| 	if err != nil {
 | |
| 		return 0, err
 | |
| 	}
 | |
| 	return int32(k.Proc.P_nice), nil
 | |
| }
 | |
| func (p *Process) IOnice() (int32, error) {
 | |
| 	return 0, common.NotImplementedError
 | |
| }
 | |
| func (p *Process) Rlimit() ([]RlimitStat, error) {
 | |
| 	var rlimit []RlimitStat
 | |
| 	return rlimit, common.NotImplementedError
 | |
| }
 | |
| func (p *Process) IOCounters() (*IOCountersStat, error) {
 | |
| 	return nil, common.NotImplementedError
 | |
| }
 | |
| func (p *Process) NumCtxSwitches() (*NumCtxSwitchesStat, error) {
 | |
| 	return nil, common.NotImplementedError
 | |
| }
 | |
| func (p *Process) NumFDs() (int32, error) {
 | |
| 	return 0, common.NotImplementedError
 | |
| }
 | |
| func (p *Process) NumThreads() (int32, error) {
 | |
| 	return 0, common.NotImplementedError
 | |
| 
 | |
| 	/*
 | |
| 		k, err := p.getKProc()
 | |
| 		if err != nil {
 | |
| 			return 0, err
 | |
| 		}
 | |
| 
 | |
| 			return k.KiNumthreads, nil
 | |
| 	*/
 | |
| }
 | |
| func (p *Process) Threads() (map[string]string, error) {
 | |
| 	ret := make(map[string]string, 0)
 | |
| 	return ret, common.NotImplementedError
 | |
| }
 | |
| func (p *Process) CPUTimes() (*cpu.CPUTimesStat, error) {
 | |
| 	return nil, common.NotImplementedError
 | |
| }
 | |
| func (p *Process) CPUAffinity() ([]int32, error) {
 | |
| 	return nil, common.NotImplementedError
 | |
| }
 | |
| func (p *Process) MemoryInfo() (*MemoryInfoStat, error) {
 | |
| 	r, err := callPs("rss,vsize,pagein", p.Pid)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	rss, err := strconv.Atoi(r[0][0])
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	vms, err := strconv.Atoi(r[0][1])
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	pagein, err := strconv.Atoi(r[0][2])
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	ret := &MemoryInfoStat{
 | |
| 		RSS:  uint64(rss),
 | |
| 		VMS:  uint64(vms),
 | |
| 		Swap: uint64(pagein),
 | |
| 	}
 | |
| 
 | |
| 	return ret, nil
 | |
| }
 | |
| func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) {
 | |
| 	return nil, common.NotImplementedError
 | |
| }
 | |
| func (p *Process) MemoryPercent() (float32, error) {
 | |
| 	return 0, common.NotImplementedError
 | |
| }
 | |
| 
 | |
| func (p *Process) Children() ([]*Process, error) {
 | |
| 	return nil, common.NotImplementedError
 | |
| }
 | |
| 
 | |
| func (p *Process) OpenFiles() ([]OpenFilesStat, error) {
 | |
| 	return nil, common.NotImplementedError
 | |
| }
 | |
| 
 | |
| func (p *Process) Connections() ([]net.NetConnectionStat, error) {
 | |
| 	return nil, common.NotImplementedError
 | |
| }
 | |
| 
 | |
| func (p *Process) IsRunning() (bool, error) {
 | |
| 	return true, common.NotImplementedError
 | |
| }
 | |
| func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) {
 | |
| 	var ret []MemoryMapsStat
 | |
| 	return &ret, common.NotImplementedError
 | |
| }
 | |
| 
 | |
| func copyParams(k *KinfoProc, p *Process) error {
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func processes() ([]Process, error) {
 | |
| 	results := make([]Process, 0, 50)
 | |
| 
 | |
| 	mib := []int32{CTLKern, KernProc, KernProcAll, 0}
 | |
| 	buf, length, err := common.CallSyscall(mib)
 | |
| 	if err != nil {
 | |
| 		return results, err
 | |
| 	}
 | |
| 
 | |
| 	// get kinfo_proc size
 | |
| 	k := KinfoProc{}
 | |
| 	procinfoLen := int(unsafe.Sizeof(k))
 | |
| 	count := int(length / uint64(procinfoLen))
 | |
| 	/*
 | |
| 		fmt.Println(length, procinfoLen, count)
 | |
| 		b := buf[0*procinfoLen : 0*procinfoLen+procinfoLen]
 | |
| 		fmt.Println(b)
 | |
| 		kk, err := parseKinfoProc(b)
 | |
| 		fmt.Printf("%#v", kk)
 | |
| 	*/
 | |
| 
 | |
| 	// parse buf to procs
 | |
| 	for i := 0; i < count; i++ {
 | |
| 		b := buf[i*procinfoLen : i*procinfoLen+procinfoLen]
 | |
| 		k, err := parseKinfoProc(b)
 | |
| 		if err != nil {
 | |
| 			continue
 | |
| 		}
 | |
| 		p, err := NewProcess(int32(k.Proc.P_pid))
 | |
| 		if err != nil {
 | |
| 			continue
 | |
| 		}
 | |
| 		copyParams(&k, p)
 | |
| 
 | |
| 		results = append(results, *p)
 | |
| 	}
 | |
| 
 | |
| 	return results, nil
 | |
| }
 | |
| 
 | |
| func parseKinfoProc(buf []byte) (KinfoProc, error) {
 | |
| 	var k KinfoProc
 | |
| 	br := bytes.NewReader(buf)
 | |
| 
 | |
| 	err := Read(br, LittleEndian, &k)
 | |
| 	if err != nil {
 | |
| 		return k, err
 | |
| 	}
 | |
| 
 | |
| 	return k, nil
 | |
| }
 | |
| 
 | |
| func (p *Process) getKProc() (*KinfoProc, error) {
 | |
| 	mib := []int32{CTLKern, KernProc, KernProcPID, p.Pid}
 | |
| 	procK := KinfoProc{}
 | |
| 	length := uint64(unsafe.Sizeof(procK))
 | |
| 	buf := make([]byte, length)
 | |
| 	_, _, syserr := syscall.Syscall6(
 | |
| 		syscall.SYS___SYSCTL,
 | |
| 		uintptr(unsafe.Pointer(&mib[0])),
 | |
| 		uintptr(len(mib)),
 | |
| 		uintptr(unsafe.Pointer(&buf[0])),
 | |
| 		uintptr(unsafe.Pointer(&length)),
 | |
| 		0,
 | |
| 		0)
 | |
| 	if syserr != 0 {
 | |
| 		return nil, syserr
 | |
| 	}
 | |
| 	k, err := parseKinfoProc(buf)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	return &k, nil
 | |
| }
 | |
| 
 | |
| func NewProcess(pid int32) (*Process, error) {
 | |
| 	p := &Process{Pid: pid}
 | |
| 
 | |
| 	return p, nil
 | |
| }
 | |
| 
 | |
| // call ps command.
 | |
| // Return value deletes Header line(you must not input wrong arg).
 | |
| // And splited by Space. Caller have responsibility to manage.
 | |
| // If passed arg pid is 0, get information from all process.
 | |
| func callPs(arg string, pid int32) ([][]string, error) {
 | |
| 	var cmd []string
 | |
| 	if pid == 0 { // will get from all processes.
 | |
| 		cmd = []string{"-x", "-o", arg}
 | |
| 	} else {
 | |
| 		cmd = []string{"-x", "-o", arg, "-p", strconv.Itoa(int(pid))}
 | |
| 	}
 | |
| 	out, err := exec.Command("/bin/ps", cmd...).Output()
 | |
| 	if err != nil {
 | |
| 		return [][]string{}, err
 | |
| 	}
 | |
| 	lines := strings.Split(string(out), "\n")
 | |
| 
 | |
| 	var ret [][]string
 | |
| 	for _, l := range lines[1:] {
 | |
| 		var lr []string
 | |
| 		for _, r := range strings.Split(l, " ") {
 | |
| 			if r == "" {
 | |
| 				continue
 | |
| 			}
 | |
| 			lr = append(lr, strings.TrimSpace(r))
 | |
| 		}
 | |
| 		if len(lr) != 0 {
 | |
| 			ret = append(ret, lr)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return ret, nil
 | |
| }
 |