261 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			261 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // +build freebsd,amd64
 | |
| 
 | |
| package ps
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"encoding/binary"
 | |
| 	"syscall"
 | |
| 	"unsafe"
 | |
| )
 | |
| 
 | |
| // copied from sys/sysctl.h
 | |
| const (
 | |
| 	CTL_KERN           = 1  // "high kernel": proc, limits
 | |
| 	KERN_PROC          = 14 // struct: process entries
 | |
| 	KERN_PROC_PID      = 1  // by process id
 | |
| 	KERN_PROC_PROC     = 8  // only return procs
 | |
| 	KERN_PROC_PATHNAME = 12 // path to executable
 | |
| )
 | |
| 
 | |
| // copied from sys/user.h
 | |
| type Kinfo_proc struct {
 | |
| 	Ki_structsize   int32
 | |
| 	Ki_layout       int32
 | |
| 	Ki_args         int64
 | |
| 	Ki_paddr        int64
 | |
| 	Ki_addr         int64
 | |
| 	Ki_tracep       int64
 | |
| 	Ki_textvp       int64
 | |
| 	Ki_fd           int64
 | |
| 	Ki_vmspace      int64
 | |
| 	Ki_wchan        int64
 | |
| 	Ki_pid          int32
 | |
| 	Ki_ppid         int32
 | |
| 	Ki_pgid         int32
 | |
| 	Ki_tpgid        int32
 | |
| 	Ki_sid          int32
 | |
| 	Ki_tsid         int32
 | |
| 	Ki_jobc         [2]byte
 | |
| 	Ki_spare_short1 [2]byte
 | |
| 	Ki_tdev         int32
 | |
| 	Ki_siglist      [16]byte
 | |
| 	Ki_sigmask      [16]byte
 | |
| 	Ki_sigignore    [16]byte
 | |
| 	Ki_sigcatch     [16]byte
 | |
| 	Ki_uid          int32
 | |
| 	Ki_ruid         int32
 | |
| 	Ki_svuid        int32
 | |
| 	Ki_rgid         int32
 | |
| 	Ki_svgid        int32
 | |
| 	Ki_ngroups      [2]byte
 | |
| 	Ki_spare_short2 [2]byte
 | |
| 	Ki_groups       [64]byte
 | |
| 	Ki_size         int64
 | |
| 	Ki_rssize       int64
 | |
| 	Ki_swrss        int64
 | |
| 	Ki_tsize        int64
 | |
| 	Ki_dsize        int64
 | |
| 	Ki_ssize        int64
 | |
| 	Ki_xstat        [2]byte
 | |
| 	Ki_acflag       [2]byte
 | |
| 	Ki_pctcpu       int32
 | |
| 	Ki_estcpu       int32
 | |
| 	Ki_slptime      int32
 | |
| 	Ki_swtime       int32
 | |
| 	Ki_cow          int32
 | |
| 	Ki_runtime      int64
 | |
| 	Ki_start        [16]byte
 | |
| 	Ki_childtime    [16]byte
 | |
| 	Ki_flag         int64
 | |
| 	Ki_kiflag       int64
 | |
| 	Ki_traceflag    int32
 | |
| 	Ki_stat         [1]byte
 | |
| 	Ki_nice         [1]byte
 | |
| 	Ki_lock         [1]byte
 | |
| 	Ki_rqindex      [1]byte
 | |
| 	Ki_oncpu        [1]byte
 | |
| 	Ki_lastcpu      [1]byte
 | |
| 	Ki_ocomm        [17]byte
 | |
| 	Ki_wmesg        [9]byte
 | |
| 	Ki_login        [18]byte
 | |
| 	Ki_lockname     [9]byte
 | |
| 	Ki_comm         [20]byte
 | |
| 	Ki_emul         [17]byte
 | |
| 	Ki_sparestrings [68]byte
 | |
| 	Ki_spareints    [36]byte
 | |
| 	Ki_cr_flags     int32
 | |
| 	Ki_jid          int32
 | |
| 	Ki_numthreads   int32
 | |
| 	Ki_tid          int32
 | |
| 	Ki_pri          int32
 | |
| 	Ki_rusage       [144]byte
 | |
| 	Ki_rusage_ch    [144]byte
 | |
| 	Ki_pcb          int64
 | |
| 	Ki_kstack       int64
 | |
| 	Ki_udata        int64
 | |
| 	Ki_tdaddr       int64
 | |
| 	Ki_spareptrs    [48]byte
 | |
| 	Ki_spareint64s  [96]byte
 | |
| 	Ki_sflag        int64
 | |
| 	Ki_tdflags      int64
 | |
| }
 | |
| 
 | |
| // UnixProcess is an implementation of Process that contains Unix-specific
 | |
| // fields and information.
 | |
| type UnixProcess struct {
 | |
| 	pid   int
 | |
| 	ppid  int
 | |
| 	state rune
 | |
| 	pgrp  int
 | |
| 	sid   int
 | |
| 
 | |
| 	binary string
 | |
| }
 | |
| 
 | |
| func (p *UnixProcess) Pid() int {
 | |
| 	return p.pid
 | |
| }
 | |
| 
 | |
| func (p *UnixProcess) PPid() int {
 | |
| 	return p.ppid
 | |
| }
 | |
| 
 | |
| func (p *UnixProcess) Executable() string {
 | |
| 	return p.binary
 | |
| }
 | |
| 
 | |
| // Refresh reloads all the data associated with this process.
 | |
| func (p *UnixProcess) Refresh() error {
 | |
| 
 | |
| 	mib := []int32{CTL_KERN, KERN_PROC, KERN_PROC_PID, int32(p.pid)}
 | |
| 
 | |
| 	buf, length, err := call_syscall(mib)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	proc_k := Kinfo_proc{}
 | |
| 	if length != uint64(unsafe.Sizeof(proc_k)) {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	k, err := parse_kinfo_proc(buf)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	p.ppid, p.pgrp, p.sid, p.binary = copy_params(&k)
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func copy_params(k *Kinfo_proc) (int, int, int, string) {
 | |
| 	n := -1
 | |
| 	for i, b := range k.Ki_comm {
 | |
| 		if b == 0 {
 | |
| 			break
 | |
| 		}
 | |
| 		n = i + 1
 | |
| 	}
 | |
| 	comm := string(k.Ki_comm[:n])
 | |
| 
 | |
| 	return int(k.Ki_ppid), int(k.Ki_pgid), int(k.Ki_sid), comm
 | |
| }
 | |
| 
 | |
| func findProcess(pid int) (Process, error) {
 | |
| 	mib := []int32{CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, int32(pid)}
 | |
| 
 | |
| 	_, _, err := call_syscall(mib)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	return newUnixProcess(pid)
 | |
| }
 | |
| 
 | |
| func processes() ([]Process, error) {
 | |
| 	results := make([]Process, 0, 50)
 | |
| 
 | |
| 	mib := []int32{CTL_KERN, KERN_PROC, KERN_PROC_PROC, 0}
 | |
| 	buf, length, err := call_syscall(mib)
 | |
| 	if err != nil {
 | |
| 		return results, err
 | |
| 	}
 | |
| 
 | |
| 	// get kinfo_proc size
 | |
| 	k := Kinfo_proc{}
 | |
| 	procinfo_len := int(unsafe.Sizeof(k))
 | |
| 	count := int(length / uint64(procinfo_len))
 | |
| 
 | |
| 	// parse buf to procs
 | |
| 	for i := 0; i < count; i++ {
 | |
| 		b := buf[i*procinfo_len : i*procinfo_len+procinfo_len]
 | |
| 		k, err := parse_kinfo_proc(b)
 | |
| 		if err != nil {
 | |
| 			continue
 | |
| 		}
 | |
| 		p, err := newUnixProcess(int(k.Ki_pid))
 | |
| 		if err != nil {
 | |
| 			continue
 | |
| 		}
 | |
| 		p.ppid, p.pgrp, p.sid, p.binary = copy_params(&k)
 | |
| 
 | |
| 		results = append(results, p)
 | |
| 	}
 | |
| 
 | |
| 	return results, nil
 | |
| }
 | |
| 
 | |
| func parse_kinfo_proc(buf []byte) (Kinfo_proc, error) {
 | |
| 	var k Kinfo_proc
 | |
| 	br := bytes.NewReader(buf)
 | |
| 	err := binary.Read(br, binary.LittleEndian, &k)
 | |
| 	if err != nil {
 | |
| 		return k, err
 | |
| 	}
 | |
| 
 | |
| 	return k, nil
 | |
| }
 | |
| 
 | |
| func call_syscall(mib []int32) ([]byte, uint64, error) {
 | |
| 	miblen := uint64(len(mib))
 | |
| 
 | |
| 	// get required buffer size
 | |
| 	length := uint64(0)
 | |
| 	_, _, err := syscall.RawSyscall6(
 | |
| 		syscall.SYS___SYSCTL,
 | |
| 		uintptr(unsafe.Pointer(&mib[0])),
 | |
| 		uintptr(miblen),
 | |
| 		0,
 | |
| 		uintptr(unsafe.Pointer(&length)),
 | |
| 		0,
 | |
| 		0)
 | |
| 	if err != 0 {
 | |
| 		b := make([]byte, 0)
 | |
| 		return b, length, err
 | |
| 	}
 | |
| 	if length == 0 {
 | |
| 		b := make([]byte, 0)
 | |
| 		return b, length, err
 | |
| 	}
 | |
| 	// get proc info itself
 | |
| 	buf := make([]byte, length)
 | |
| 	_, _, err = syscall.RawSyscall6(
 | |
| 		syscall.SYS___SYSCTL,
 | |
| 		uintptr(unsafe.Pointer(&mib[0])),
 | |
| 		uintptr(miblen),
 | |
| 		uintptr(unsafe.Pointer(&buf[0])),
 | |
| 		uintptr(unsafe.Pointer(&length)),
 | |
| 		0,
 | |
| 		0)
 | |
| 	if err != 0 {
 | |
| 		return buf, length, err
 | |
| 	}
 | |
| 
 | |
| 	return buf, length, nil
 | |
| }
 | |
| 
 | |
| func newUnixProcess(pid int) (*UnixProcess, error) {
 | |
| 	p := &UnixProcess{pid: pid}
 | |
| 	return p, p.Refresh()
 | |
| }
 |