598 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			598 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Go
		
	
	
	
| // +build linux
 | |
| 
 | |
| package process
 | |
| 
 | |
| import (
 | |
| 	"encoding/json"
 | |
| 	"io/ioutil"
 | |
| 	"os"
 | |
| 	"path/filepath"
 | |
| 	"strconv"
 | |
| 	"strings"
 | |
| 	"syscall"
 | |
| 
 | |
| 	common "github.com/influxdb/telegraf/plugins/system/ps/common"
 | |
| 	cpu "github.com/influxdb/telegraf/plugins/system/ps/cpu"
 | |
| 	host "github.com/influxdb/telegraf/plugins/system/ps/host"
 | |
| 	net "github.com/influxdb/telegraf/plugins/system/ps/net"
 | |
| )
 | |
| 
 | |
| const (
 | |
| 	PrioProcess = 0 // linux/resource.h
 | |
| )
 | |
| 
 | |
| // MemoryInfoExStat is different between OSes
 | |
| type MemoryInfoExStat struct {
 | |
| 	RSS    uint64 `json:"rss"`    // bytes
 | |
| 	VMS    uint64 `json:"vms"`    // bytes
 | |
| 	Shared uint64 `json:"shared"` // bytes
 | |
| 	Text   uint64 `json:"text"`   // bytes
 | |
| 	Lib    uint64 `json:"lib"`    // bytes
 | |
| 	Data   uint64 `json:"data"`   // bytes
 | |
| 	Dirty  uint64 `json:"dirty"`  // bytes
 | |
| }
 | |
| 
 | |
| func (m MemoryInfoExStat) String() string {
 | |
| 	s, _ := json.Marshal(m)
 | |
| 	return string(s)
 | |
| }
 | |
| 
 | |
| type MemoryMapsStat struct {
 | |
| 	Path         string `json:"path"`
 | |
| 	Rss          uint64 `json:"rss"`
 | |
| 	Size         uint64 `json:"size"`
 | |
| 	Pss          uint64 `json:"pss"`
 | |
| 	SharedClean  uint64 `json:"shared_clean"`
 | |
| 	SharedDirty  uint64 `json:"shared_dirty"`
 | |
| 	PrivateClean uint64 `json:"private_clean"`
 | |
| 	PrivateDirty uint64 `json:"private_dirty"`
 | |
| 	Referenced   uint64 `json:"referenced"`
 | |
| 	Anonymous    uint64 `json:"anonymous"`
 | |
| 	Swap         uint64 `json:"swap"`
 | |
| }
 | |
| 
 | |
| func (m MemoryMapsStat) String() string {
 | |
| 	s, _ := json.Marshal(m)
 | |
| 	return string(s)
 | |
| }
 | |
| 
 | |
| // Create new Process instance
 | |
| // This only stores Pid
 | |
| func NewProcess(pid int32) (*Process, error) {
 | |
| 	p := &Process{
 | |
| 		Pid: int32(pid),
 | |
| 	}
 | |
| 	err := p.fillFromStatus()
 | |
| 	return p, err
 | |
| }
 | |
| 
 | |
| func (p *Process) Ppid() (int32, error) {
 | |
| 	_, ppid, _, _, _, err := p.fillFromStat()
 | |
| 	if err != nil {
 | |
| 		return -1, err
 | |
| 	}
 | |
| 	return ppid, nil
 | |
| }
 | |
| func (p *Process) Name() (string, error) {
 | |
| 	return p.name, nil
 | |
| }
 | |
| func (p *Process) Exe() (string, error) {
 | |
| 	return p.fillFromExe()
 | |
| }
 | |
| func (p *Process) Cmdline() (string, error) {
 | |
| 	return p.fillFromCmdline()
 | |
| }
 | |
| func (p *Process) CreateTime() (int64, error) {
 | |
| 	_, _, _, createTime, _, err := p.fillFromStat()
 | |
| 	if err != nil {
 | |
| 		return 0, err
 | |
| 	}
 | |
| 	return createTime, nil
 | |
| }
 | |
| 
 | |
| func (p *Process) Cwd() (string, error) {
 | |
| 	return p.fillFromCwd()
 | |
| }
 | |
| func (p *Process) Parent() (*Process, error) {
 | |
| 	return nil, common.NotImplementedError
 | |
| }
 | |
| func (p *Process) Status() (string, error) {
 | |
| 	return p.status, nil
 | |
| }
 | |
| func (p *Process) Uids() ([]int32, error) {
 | |
| 	return p.uids, nil
 | |
| }
 | |
| func (p *Process) Gids() ([]int32, error) {
 | |
| 	return p.gids, nil
 | |
| }
 | |
| func (p *Process) Terminal() (string, error) {
 | |
| 	terminal, _, _, _, _, err := p.fillFromStat()
 | |
| 	if err != nil {
 | |
| 		return "", err
 | |
| 	}
 | |
| 	return terminal, nil
 | |
| }
 | |
| func (p *Process) Nice() (int32, error) {
 | |
| 	_, _, _, _, nice, err := p.fillFromStat()
 | |
| 	if err != nil {
 | |
| 		return 0, err
 | |
| 	}
 | |
| 	return nice, nil
 | |
| }
 | |
| func (p *Process) IOnice() (int32, error) {
 | |
| 	return 0, common.NotImplementedError
 | |
| }
 | |
| func (p *Process) Rlimit() ([]RlimitStat, error) {
 | |
| 	return nil, common.NotImplementedError
 | |
| }
 | |
| func (p *Process) IOCounters() (*IOCountersStat, error) {
 | |
| 	return p.fillFromIO()
 | |
| }
 | |
| func (p *Process) NumCtxSwitches() (*NumCtxSwitchesStat, error) {
 | |
| 	return p.numCtxSwitches, nil
 | |
| }
 | |
| func (p *Process) NumFDs() (int32, error) {
 | |
| 	return 0, common.NotImplementedError
 | |
| }
 | |
| func (p *Process) NumThreads() (int32, error) {
 | |
| 	return p.numThreads, nil
 | |
| }
 | |
| func (p *Process) Threads() (map[string]string, error) {
 | |
| 	ret := make(map[string]string, 0)
 | |
| 	return ret, nil
 | |
| }
 | |
| func (p *Process) CPUTimes() (*cpu.CPUTimesStat, error) {
 | |
| 	_, _, cpuTimes, _, _, err := p.fillFromStat()
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	return cpuTimes, nil
 | |
| }
 | |
| func (p *Process) CPUAffinity() ([]int32, error) {
 | |
| 	return nil, common.NotImplementedError
 | |
| }
 | |
| func (p *Process) MemoryInfo() (*MemoryInfoStat, error) {
 | |
| 	return p.memInfo, nil
 | |
| }
 | |
| func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) {
 | |
| 	_, memInfoEx, err := p.fillFromStatm()
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	return memInfoEx, nil
 | |
| }
 | |
| 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
 | |
| }
 | |
| 
 | |
| // MemoryMaps get memory maps from /proc/(pid)/smaps
 | |
| func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) {
 | |
| 	pid := p.Pid
 | |
| 	var ret []MemoryMapsStat
 | |
| 	smapsPath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "smaps")
 | |
| 	contents, err := ioutil.ReadFile(smapsPath)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	lines := strings.Split(string(contents), "\n")
 | |
| 
 | |
| 	// function of parsing a block
 | |
| 	getBlock := func(first_line []string, block []string) (MemoryMapsStat, error) {
 | |
| 		m := MemoryMapsStat{}
 | |
| 		m.Path = first_line[len(first_line)-1]
 | |
| 
 | |
| 		for _, line := range block {
 | |
| 			if strings.Contains(line, "VmFlags") {
 | |
| 				continue
 | |
| 			}
 | |
| 			field := strings.Split(line, ":")
 | |
| 			if len(field) < 2 {
 | |
| 				continue
 | |
| 			}
 | |
| 			v := strings.Trim(field[1], " kB") // remove last "kB"
 | |
| 			t, err := strconv.ParseUint(v, 10, 64)
 | |
| 			if err != nil {
 | |
| 				return m, err
 | |
| 			}
 | |
| 
 | |
| 			switch field[0] {
 | |
| 			case "Size":
 | |
| 				m.Size = t
 | |
| 			case "Rss":
 | |
| 				m.Rss = t
 | |
| 			case "Pss":
 | |
| 				m.Pss = t
 | |
| 			case "Shared_Clean":
 | |
| 				m.SharedClean = t
 | |
| 			case "Shared_Dirty":
 | |
| 				m.SharedDirty = t
 | |
| 			case "Private_Clean":
 | |
| 				m.PrivateClean = t
 | |
| 			case "Private_Dirty":
 | |
| 				m.PrivateDirty = t
 | |
| 			case "Referenced":
 | |
| 				m.Referenced = t
 | |
| 			case "Anonymous":
 | |
| 				m.Anonymous = t
 | |
| 			case "Swap":
 | |
| 				m.Swap = t
 | |
| 			}
 | |
| 		}
 | |
| 		return m, nil
 | |
| 	}
 | |
| 
 | |
| 	blocks := make([]string, 16)
 | |
| 	for _, line := range lines {
 | |
| 		field := strings.Split(line, " ")
 | |
| 		if strings.HasSuffix(field[0], ":") == false {
 | |
| 			// new block section
 | |
| 			if len(blocks) > 0 {
 | |
| 				g, err := getBlock(field, blocks)
 | |
| 				if err != nil {
 | |
| 					return &ret, err
 | |
| 				}
 | |
| 				ret = append(ret, g)
 | |
| 			}
 | |
| 			// starts new block
 | |
| 			blocks = make([]string, 16)
 | |
| 		} else {
 | |
| 			blocks = append(blocks, line)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return &ret, nil
 | |
| }
 | |
| 
 | |
| /**
 | |
| ** Internal functions
 | |
| **/
 | |
| 
 | |
| // Get num_fds from /proc/(pid)/fd
 | |
| func (p *Process) fillFromfd() (int32, []*OpenFilesStat, error) {
 | |
| 	pid := p.Pid
 | |
| 	statPath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "fd")
 | |
| 	d, err := os.Open(statPath)
 | |
| 	if err != nil {
 | |
| 		return 0, nil, err
 | |
| 	}
 | |
| 	defer d.Close()
 | |
| 	fnames, err := d.Readdirnames(-1)
 | |
| 	numFDs := int32(len(fnames))
 | |
| 
 | |
| 	openfiles := make([]*OpenFilesStat, numFDs)
 | |
| 	for _, fd := range fnames {
 | |
| 		fpath := filepath.Join(statPath, fd)
 | |
| 		filepath, err := os.Readlink(fpath)
 | |
| 		if err != nil {
 | |
| 			continue
 | |
| 		}
 | |
| 		t, err := strconv.ParseUint(fd, 10, 64)
 | |
| 		if err != nil {
 | |
| 			return numFDs, openfiles, err
 | |
| 		}
 | |
| 		o := &OpenFilesStat{
 | |
| 			Path: filepath,
 | |
| 			Fd:   t,
 | |
| 		}
 | |
| 		openfiles = append(openfiles, o)
 | |
| 	}
 | |
| 
 | |
| 	return numFDs, openfiles, nil
 | |
| }
 | |
| 
 | |
| // Get cwd from /proc/(pid)/cwd
 | |
| func (p *Process) fillFromCwd() (string, error) {
 | |
| 	pid := p.Pid
 | |
| 	cwdPath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "cwd")
 | |
| 	cwd, err := os.Readlink(cwdPath)
 | |
| 	if err != nil {
 | |
| 		return "", err
 | |
| 	}
 | |
| 	return string(cwd), nil
 | |
| }
 | |
| 
 | |
| // Get exe from /proc/(pid)/exe
 | |
| func (p *Process) fillFromExe() (string, error) {
 | |
| 	pid := p.Pid
 | |
| 	exePath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "exe")
 | |
| 	exe, err := os.Readlink(exePath)
 | |
| 	if err != nil {
 | |
| 		return "", err
 | |
| 	}
 | |
| 	return string(exe), nil
 | |
| }
 | |
| 
 | |
| // Get cmdline from /proc/(pid)/cmdline
 | |
| func (p *Process) fillFromCmdline() (string, error) {
 | |
| 	pid := p.Pid
 | |
| 	cmdPath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "cmdline")
 | |
| 	cmdline, err := ioutil.ReadFile(cmdPath)
 | |
| 	if err != nil {
 | |
| 		return "", err
 | |
| 	}
 | |
| 	ret := strings.FieldsFunc(string(cmdline), func(r rune) bool {
 | |
| 		if r == '\u0000' {
 | |
| 			return true
 | |
| 		}
 | |
| 		return false
 | |
| 	})
 | |
| 
 | |
| 	return strings.Join(ret, " "), nil
 | |
| }
 | |
| 
 | |
| // Get IO status from /proc/(pid)/io
 | |
| func (p *Process) fillFromIO() (*IOCountersStat, error) {
 | |
| 	pid := p.Pid
 | |
| 	ioPath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "io")
 | |
| 	ioline, err := ioutil.ReadFile(ioPath)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	lines := strings.Split(string(ioline), "\n")
 | |
| 	ret := &IOCountersStat{}
 | |
| 
 | |
| 	for _, line := range lines {
 | |
| 		field := strings.Fields(line)
 | |
| 		if len(field) < 2 {
 | |
| 			continue
 | |
| 		}
 | |
| 		t, err := strconv.ParseUint(field[1], 10, 64)
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		param := field[0]
 | |
| 		if strings.HasSuffix(param, ":") {
 | |
| 			param = param[:len(param)-1]
 | |
| 		}
 | |
| 		switch param {
 | |
| 		case "syscr":
 | |
| 			ret.ReadCount = t
 | |
| 		case "syscw":
 | |
| 			ret.WriteCount = t
 | |
| 		case "read_bytes":
 | |
| 			ret.ReadBytes = t
 | |
| 		case "write_bytes":
 | |
| 			ret.WriteBytes = t
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return ret, nil
 | |
| }
 | |
| 
 | |
| // Get memory info from /proc/(pid)/statm
 | |
| func (p *Process) fillFromStatm() (*MemoryInfoStat, *MemoryInfoExStat, error) {
 | |
| 	pid := p.Pid
 | |
| 	memPath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "statm")
 | |
| 	contents, err := ioutil.ReadFile(memPath)
 | |
| 	if err != nil {
 | |
| 		return nil, nil, err
 | |
| 	}
 | |
| 	fields := strings.Split(string(contents), " ")
 | |
| 
 | |
| 	vms, err := strconv.ParseUint(fields[0], 10, 64)
 | |
| 	if err != nil {
 | |
| 		return nil, nil, err
 | |
| 	}
 | |
| 	rss, err := strconv.ParseUint(fields[1], 10, 64)
 | |
| 	if err != nil {
 | |
| 		return nil, nil, err
 | |
| 	}
 | |
| 	memInfo := &MemoryInfoStat{
 | |
| 		RSS: rss * PageSize,
 | |
| 		VMS: vms * PageSize,
 | |
| 	}
 | |
| 
 | |
| 	shared, err := strconv.ParseUint(fields[2], 10, 64)
 | |
| 	if err != nil {
 | |
| 		return nil, nil, err
 | |
| 	}
 | |
| 	text, err := strconv.ParseUint(fields[3], 10, 64)
 | |
| 	if err != nil {
 | |
| 		return nil, nil, err
 | |
| 	}
 | |
| 	lib, err := strconv.ParseUint(fields[4], 10, 64)
 | |
| 	if err != nil {
 | |
| 		return nil, nil, err
 | |
| 	}
 | |
| 	dirty, err := strconv.ParseUint(fields[5], 10, 64)
 | |
| 	if err != nil {
 | |
| 		return nil, nil, err
 | |
| 	}
 | |
| 
 | |
| 	memInfoEx := &MemoryInfoExStat{
 | |
| 		RSS:    rss * PageSize,
 | |
| 		VMS:    vms * PageSize,
 | |
| 		Shared: shared * PageSize,
 | |
| 		Text:   text * PageSize,
 | |
| 		Lib:    lib * PageSize,
 | |
| 		Dirty:  dirty * PageSize,
 | |
| 	}
 | |
| 
 | |
| 	return memInfo, memInfoEx, nil
 | |
| }
 | |
| 
 | |
| // Get various status from /proc/(pid)/status
 | |
| func (p *Process) fillFromStatus() error {
 | |
| 	pid := p.Pid
 | |
| 	statPath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "status")
 | |
| 	contents, err := ioutil.ReadFile(statPath)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	lines := strings.Split(string(contents), "\n")
 | |
| 	p.numCtxSwitches = &NumCtxSwitchesStat{}
 | |
| 	p.memInfo = &MemoryInfoStat{}
 | |
| 	for _, line := range lines {
 | |
| 		tabParts := strings.SplitN(line, "\t", 2)
 | |
| 		if len(tabParts) < 2 {
 | |
| 			continue
 | |
| 		}
 | |
| 		value := tabParts[1]
 | |
| 		switch strings.TrimRight(tabParts[0], ":") {
 | |
| 		case "Name":
 | |
| 			p.name = strings.Trim(value, " \t")
 | |
| 		case "State":
 | |
| 			// get between "(" and ")"
 | |
| 			s := strings.Index(value, "(") + 1
 | |
| 			e := strings.Index(value, ")")
 | |
| 			p.status = value[s:e]
 | |
| 		case "Uid":
 | |
| 			p.uids = make([]int32, 0, 4)
 | |
| 			for _, i := range strings.Split(value, "\t") {
 | |
| 				v, err := strconv.ParseInt(i, 10, 32)
 | |
| 				if err != nil {
 | |
| 					return err
 | |
| 				}
 | |
| 				p.uids = append(p.uids, int32(v))
 | |
| 			}
 | |
| 		case "Gid":
 | |
| 			p.gids = make([]int32, 0, 4)
 | |
| 			for _, i := range strings.Split(value, "\t") {
 | |
| 				v, err := strconv.ParseInt(i, 10, 32)
 | |
| 				if err != nil {
 | |
| 					return err
 | |
| 				}
 | |
| 				p.gids = append(p.gids, int32(v))
 | |
| 			}
 | |
| 		case "Threads":
 | |
| 			v, err := strconv.ParseInt(value, 10, 32)
 | |
| 			if err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			p.numThreads = int32(v)
 | |
| 		case "voluntary_ctxt_switches":
 | |
| 			v, err := strconv.ParseInt(value, 10, 64)
 | |
| 			if err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			p.numCtxSwitches.Voluntary = v
 | |
| 		case "nonvoluntary_ctxt_switches":
 | |
| 			v, err := strconv.ParseInt(value, 10, 64)
 | |
| 			if err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			p.numCtxSwitches.Involuntary = v
 | |
| 		case "VmRSS":
 | |
| 			value := strings.Trim(value, " kB") // remove last "kB"
 | |
| 			v, err := strconv.ParseUint(value, 10, 64)
 | |
| 			if err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			p.memInfo.RSS = v * 1024
 | |
| 		case "VmSize":
 | |
| 			value := strings.Trim(value, " kB") // remove last "kB"
 | |
| 			v, err := strconv.ParseUint(value, 10, 64)
 | |
| 			if err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			p.memInfo.VMS = v * 1024
 | |
| 		case "VmSwap":
 | |
| 			value := strings.Trim(value, " kB") // remove last "kB"
 | |
| 			v, err := strconv.ParseUint(value, 10, 64)
 | |
| 			if err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			p.memInfo.Swap = v * 1024
 | |
| 		}
 | |
| 
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (p *Process) fillFromStat() (string, int32, *cpu.CPUTimesStat, int64, int32, error) {
 | |
| 	pid := p.Pid
 | |
| 	statPath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "stat")
 | |
| 	contents, err := ioutil.ReadFile(statPath)
 | |
| 	if err != nil {
 | |
| 		return "", 0, nil, 0, 0, err
 | |
| 	}
 | |
| 	fields := strings.Fields(string(contents))
 | |
| 
 | |
| 	termmap, err := getTerminalMap()
 | |
| 	terminal := ""
 | |
| 	if err == nil {
 | |
| 		t, err := strconv.ParseUint(fields[6], 10, 64)
 | |
| 		if err != nil {
 | |
| 			return "", 0, nil, 0, 0, err
 | |
| 		}
 | |
| 		terminal = termmap[t]
 | |
| 	}
 | |
| 
 | |
| 	ppid, err := strconv.ParseInt(fields[3], 10, 32)
 | |
| 	if err != nil {
 | |
| 		return "", 0, nil, 0, 0, err
 | |
| 	}
 | |
| 	utime, err := strconv.ParseFloat(fields[13], 64)
 | |
| 	if err != nil {
 | |
| 		return "", 0, nil, 0, 0, err
 | |
| 	}
 | |
| 
 | |
| 	stime, err := strconv.ParseFloat(fields[14], 64)
 | |
| 	if err != nil {
 | |
| 		return "", 0, nil, 0, 0, err
 | |
| 	}
 | |
| 
 | |
| 	cpuTimes := &cpu.CPUTimesStat{
 | |
| 		CPU:    "cpu",
 | |
| 		User:   float64(utime / ClockTicks),
 | |
| 		System: float64(stime / ClockTicks),
 | |
| 	}
 | |
| 
 | |
| 	bootTime, _ := host.BootTime()
 | |
| 	t, err := strconv.ParseUint(fields[21], 10, 64)
 | |
| 	if err != nil {
 | |
| 		return "", 0, nil, 0, 0, err
 | |
| 	}
 | |
| 
 | |
| 	ctime := ((t / uint64(ClockTicks)) + uint64(bootTime)) * 1000
 | |
| 	createTime := int64(ctime)
 | |
| 
 | |
| 	//	p.Nice = mustParseInt32(fields[18])
 | |
| 	// use syscall instead of parse Stat file
 | |
| 	snice, _ := syscall.Getpriority(PrioProcess, int(pid))
 | |
| 	nice := int32(snice) // FIXME: is this true?
 | |
| 
 | |
| 	return terminal, int32(ppid), cpuTimes, createTime, nice, nil
 | |
| }
 | |
| 
 | |
| func Pids() ([]int32, error) {
 | |
| 	var ret []int32
 | |
| 
 | |
| 	d, err := os.Open("/proc")
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	defer d.Close()
 | |
| 
 | |
| 	fnames, err := d.Readdirnames(-1)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	for _, fname := range fnames {
 | |
| 		pid, err := strconv.ParseInt(fname, 10, 32)
 | |
| 		if err != nil {
 | |
| 			// if not numeric name, just skip
 | |
| 			continue
 | |
| 		}
 | |
| 		ret = append(ret, int32(pid))
 | |
| 	}
 | |
| 
 | |
| 	return ret, nil
 | |
| }
 |