AJ ONeal
5 years ago
26 changed files with 1383 additions and 61 deletions
@ -1,10 +1,23 @@ |
|||||
package runner |
package runner |
||||
|
|
||||
import ( |
import ( |
||||
|
"fmt" |
||||
"os/exec" |
"os/exec" |
||||
|
"strconv" |
||||
"syscall" |
"syscall" |
||||
) |
) |
||||
|
|
||||
func backgroundCmd(cmd *exec.Cmd) { |
func backgroundCmd(cmd *exec.Cmd) { |
||||
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true} |
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true} |
||||
} |
} |
||||
|
|
||||
|
func kill(pid int) error { |
||||
|
// Kill the whole processes tree (all children and grandchildren)
|
||||
|
cmd := exec.Command("taskkill", "/pid", strconv.Itoa(pid), "/T", "/F") |
||||
|
b, err := cmd.CombinedOutput() |
||||
|
if nil != err { |
||||
|
return fmt.Errorf("%s: %s", err.Error(), string(b)) |
||||
|
} |
||||
|
|
||||
|
return nil |
||||
|
} |
||||
|
@ -0,0 +1 @@ |
|||||
|
.vagrant/ |
@ -0,0 +1,4 @@ |
|||||
|
language: go |
||||
|
|
||||
|
go: |
||||
|
- 1.2.1 |
@ -0,0 +1,21 @@ |
|||||
|
The MIT License (MIT) |
||||
|
|
||||
|
Copyright (c) 2014 Mitchell Hashimoto |
||||
|
|
||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
|
of this software and associated documentation files (the "Software"), to deal |
||||
|
in the Software without restriction, including without limitation the rights |
||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
|
copies of the Software, and to permit persons to whom the Software is |
||||
|
furnished to do so, subject to the following conditions: |
||||
|
|
||||
|
The above copyright notice and this permission notice shall be included in |
||||
|
all copies or substantial portions of the Software. |
||||
|
|
||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
||||
|
THE SOFTWARE. |
@ -0,0 +1,34 @@ |
|||||
|
# Process List Library for Go |
||||
|
|
||||
|
go-ps is a library for Go that implements OS-specific APIs to list and |
||||
|
manipulate processes in a platform-safe way. The library can find and |
||||
|
list processes on Linux, Mac OS X, Solaris, and Windows. |
||||
|
|
||||
|
If you're new to Go, this library has a good amount of advanced Go educational |
||||
|
value as well. It uses some advanced features of Go: build tags, accessing |
||||
|
DLL methods for Windows, cgo for Darwin, etc. |
||||
|
|
||||
|
How it works: |
||||
|
|
||||
|
* **Darwin** uses the `sysctl` syscall to retrieve the process table. |
||||
|
* **Unix** uses the procfs at `/proc` to inspect the process tree. |
||||
|
* **Windows** uses the Windows API, and methods such as |
||||
|
`CreateToolhelp32Snapshot` to get a point-in-time snapshot of |
||||
|
the process table. |
||||
|
|
||||
|
## Installation |
||||
|
|
||||
|
Install using standard `go get`: |
||||
|
|
||||
|
``` |
||||
|
$ go get github.com/mitchellh/go-ps |
||||
|
... |
||||
|
``` |
||||
|
|
||||
|
## TODO |
||||
|
|
||||
|
Want to contribute? Here is a short TODO list of things that aren't |
||||
|
implemented for this library that would be nice: |
||||
|
|
||||
|
* FreeBSD support |
||||
|
* Plan9 support |
@ -0,0 +1,43 @@ |
|||||
|
# -*- mode: ruby -*- |
||||
|
# vi: set ft=ruby : |
||||
|
|
||||
|
# Vagrantfile API/syntax version. Don't touch unless you know what you're doing! |
||||
|
VAGRANTFILE_API_VERSION = "2" |
||||
|
|
||||
|
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| |
||||
|
config.vm.box = "chef/ubuntu-12.04" |
||||
|
|
||||
|
config.vm.provision "shell", inline: $script |
||||
|
|
||||
|
["vmware_fusion", "vmware_workstation"].each do |p| |
||||
|
config.vm.provider "p" do |v| |
||||
|
v.vmx["memsize"] = "1024" |
||||
|
v.vmx["numvcpus"] = "2" |
||||
|
v.vmx["cpuid.coresPerSocket"] = "1" |
||||
|
end |
||||
|
end |
||||
|
end |
||||
|
|
||||
|
$script = <<SCRIPT |
||||
|
SRCROOT="/opt/go" |
||||
|
|
||||
|
# Install Go |
||||
|
sudo apt-get update |
||||
|
sudo apt-get install -y build-essential mercurial |
||||
|
sudo hg clone -u release https://code.google.com/p/go ${SRCROOT} |
||||
|
cd ${SRCROOT}/src |
||||
|
sudo ./all.bash |
||||
|
|
||||
|
# Setup the GOPATH |
||||
|
sudo mkdir -p /opt/gopath |
||||
|
cat <<EOF >/tmp/gopath.sh |
||||
|
export GOPATH="/opt/gopath" |
||||
|
export PATH="/opt/go/bin:\$GOPATH/bin:\$PATH" |
||||
|
EOF |
||||
|
sudo mv /tmp/gopath.sh /etc/profile.d/gopath.sh |
||||
|
sudo chmod 0755 /etc/profile.d/gopath.sh |
||||
|
|
||||
|
# Make sure the gopath is usable by bamboo |
||||
|
sudo chown -R vagrant:vagrant $SRCROOT |
||||
|
sudo chown -R vagrant:vagrant /opt/gopath |
||||
|
SCRIPT |
@ -0,0 +1,40 @@ |
|||||
|
// ps provides an API for finding and listing processes in a platform-agnostic
|
||||
|
// way.
|
||||
|
//
|
||||
|
// NOTE: If you're reading these docs online via GoDocs or some other system,
|
||||
|
// you might only see the Unix docs. This project makes heavy use of
|
||||
|
// platform-specific implementations. We recommend reading the source if you
|
||||
|
// are interested.
|
||||
|
package ps |
||||
|
|
||||
|
// Process is the generic interface that is implemented on every platform
|
||||
|
// and provides common operations for processes.
|
||||
|
type Process interface { |
||||
|
// Pid is the process ID for this process.
|
||||
|
Pid() int |
||||
|
|
||||
|
// PPid is the parent process ID for this process.
|
||||
|
PPid() int |
||||
|
|
||||
|
// Executable name running this process. This is not a path to the
|
||||
|
// executable.
|
||||
|
Executable() string |
||||
|
} |
||||
|
|
||||
|
// Processes returns all processes.
|
||||
|
//
|
||||
|
// This of course will be a point-in-time snapshot of when this method was
|
||||
|
// called. Some operating systems don't provide snapshot capability of the
|
||||
|
// process table, in which case the process table returned might contain
|
||||
|
// ephemeral entities that happened to be running when this was called.
|
||||
|
func Processes() ([]Process, error) { |
||||
|
return processes() |
||||
|
} |
||||
|
|
||||
|
// FindProcess looks up a single process by pid.
|
||||
|
//
|
||||
|
// Process will be nil and error will be nil if a matching process is
|
||||
|
// not found.
|
||||
|
func FindProcess(pid int) (Process, error) { |
||||
|
return findProcess(pid) |
||||
|
} |
@ -0,0 +1,138 @@ |
|||||
|
// +build darwin
|
||||
|
|
||||
|
package ps |
||||
|
|
||||
|
import ( |
||||
|
"bytes" |
||||
|
"encoding/binary" |
||||
|
"syscall" |
||||
|
"unsafe" |
||||
|
) |
||||
|
|
||||
|
type DarwinProcess struct { |
||||
|
pid int |
||||
|
ppid int |
||||
|
binary string |
||||
|
} |
||||
|
|
||||
|
func (p *DarwinProcess) Pid() int { |
||||
|
return p.pid |
||||
|
} |
||||
|
|
||||
|
func (p *DarwinProcess) PPid() int { |
||||
|
return p.ppid |
||||
|
} |
||||
|
|
||||
|
func (p *DarwinProcess) Executable() string { |
||||
|
return p.binary |
||||
|
} |
||||
|
|
||||
|
func findProcess(pid int) (Process, error) { |
||||
|
ps, err := processes() |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
for _, p := range ps { |
||||
|
if p.Pid() == pid { |
||||
|
return p, nil |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return nil, nil |
||||
|
} |
||||
|
|
||||
|
func processes() ([]Process, error) { |
||||
|
buf, err := darwinSyscall() |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
procs := make([]*kinfoProc, 0, 50) |
||||
|
k := 0 |
||||
|
for i := _KINFO_STRUCT_SIZE; i < buf.Len(); i += _KINFO_STRUCT_SIZE { |
||||
|
proc := &kinfoProc{} |
||||
|
err = binary.Read(bytes.NewBuffer(buf.Bytes()[k:i]), binary.LittleEndian, proc) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
k = i |
||||
|
procs = append(procs, proc) |
||||
|
} |
||||
|
|
||||
|
darwinProcs := make([]Process, len(procs)) |
||||
|
for i, p := range procs { |
||||
|
darwinProcs[i] = &DarwinProcess{ |
||||
|
pid: int(p.Pid), |
||||
|
ppid: int(p.PPid), |
||||
|
binary: darwinCstring(p.Comm), |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return darwinProcs, nil |
||||
|
} |
||||
|
|
||||
|
func darwinCstring(s [16]byte) string { |
||||
|
i := 0 |
||||
|
for _, b := range s { |
||||
|
if b != 0 { |
||||
|
i++ |
||||
|
} else { |
||||
|
break |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return string(s[:i]) |
||||
|
} |
||||
|
|
||||
|
func darwinSyscall() (*bytes.Buffer, error) { |
||||
|
mib := [4]int32{_CTRL_KERN, _KERN_PROC, _KERN_PROC_ALL, 0} |
||||
|
size := uintptr(0) |
||||
|
|
||||
|
_, _, errno := syscall.Syscall6( |
||||
|
syscall.SYS___SYSCTL, |
||||
|
uintptr(unsafe.Pointer(&mib[0])), |
||||
|
4, |
||||
|
0, |
||||
|
uintptr(unsafe.Pointer(&size)), |
||||
|
0, |
||||
|
0) |
||||
|
|
||||
|
if errno != 0 { |
||||
|
return nil, errno |
||||
|
} |
||||
|
|
||||
|
bs := make([]byte, size) |
||||
|
_, _, errno = syscall.Syscall6( |
||||
|
syscall.SYS___SYSCTL, |
||||
|
uintptr(unsafe.Pointer(&mib[0])), |
||||
|
4, |
||||
|
uintptr(unsafe.Pointer(&bs[0])), |
||||
|
uintptr(unsafe.Pointer(&size)), |
||||
|
0, |
||||
|
0) |
||||
|
|
||||
|
if errno != 0 { |
||||
|
return nil, errno |
||||
|
} |
||||
|
|
||||
|
return bytes.NewBuffer(bs[0:size]), nil |
||||
|
} |
||||
|
|
||||
|
const ( |
||||
|
_CTRL_KERN = 1 |
||||
|
_KERN_PROC = 14 |
||||
|
_KERN_PROC_ALL = 0 |
||||
|
_KINFO_STRUCT_SIZE = 648 |
||||
|
) |
||||
|
|
||||
|
type kinfoProc struct { |
||||
|
_ [40]byte |
||||
|
Pid int32 |
||||
|
_ [199]byte |
||||
|
Comm [16]byte |
||||
|
_ [301]byte |
||||
|
PPid int32 |
||||
|
_ [84]byte |
||||
|
} |
@ -0,0 +1,260 @@ |
|||||
|
// +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() |
||||
|
} |
@ -0,0 +1,35 @@ |
|||||
|
// +build linux
|
||||
|
|
||||
|
package ps |
||||
|
|
||||
|
import ( |
||||
|
"fmt" |
||||
|
"io/ioutil" |
||||
|
"strings" |
||||
|
) |
||||
|
|
||||
|
// Refresh reloads all the data associated with this process.
|
||||
|
func (p *UnixProcess) Refresh() error { |
||||
|
statPath := fmt.Sprintf("/proc/%d/stat", p.pid) |
||||
|
dataBytes, err := ioutil.ReadFile(statPath) |
||||
|
if err != nil { |
||||
|
return err |
||||
|
} |
||||
|
|
||||
|
// First, parse out the image name
|
||||
|
data := string(dataBytes) |
||||
|
binStart := strings.IndexRune(data, '(') + 1 |
||||
|
binEnd := strings.IndexRune(data[binStart:], ')') |
||||
|
p.binary = data[binStart : binStart+binEnd] |
||||
|
|
||||
|
// Move past the image name and start parsing the rest
|
||||
|
data = data[binStart+binEnd+2:] |
||||
|
_, err = fmt.Sscanf(data, |
||||
|
"%c %d %d %d", |
||||
|
&p.state, |
||||
|
&p.ppid, |
||||
|
&p.pgrp, |
||||
|
&p.sid) |
||||
|
|
||||
|
return err |
||||
|
} |
@ -0,0 +1,96 @@ |
|||||
|
// +build solaris
|
||||
|
|
||||
|
package ps |
||||
|
|
||||
|
import ( |
||||
|
"encoding/binary" |
||||
|
"fmt" |
||||
|
"os" |
||||
|
) |
||||
|
|
||||
|
type ushort_t uint16 |
||||
|
|
||||
|
type id_t int32 |
||||
|
type pid_t int32 |
||||
|
type uid_t int32 |
||||
|
type gid_t int32 |
||||
|
|
||||
|
type dev_t uint64 |
||||
|
type size_t uint64 |
||||
|
type uintptr_t uint64 |
||||
|
|
||||
|
type timestruc_t [16]byte |
||||
|
|
||||
|
// This is copy from /usr/include/sys/procfs.h
|
||||
|
type psinfo_t struct { |
||||
|
Pr_flag int32 /* process flags (DEPRECATED; do not use) */ |
||||
|
Pr_nlwp int32 /* number of active lwps in the process */ |
||||
|
Pr_pid pid_t /* unique process id */ |
||||
|
Pr_ppid pid_t /* process id of parent */ |
||||
|
Pr_pgid pid_t /* pid of process group leader */ |
||||
|
Pr_sid pid_t /* session id */ |
||||
|
Pr_uid uid_t /* real user id */ |
||||
|
Pr_euid uid_t /* effective user id */ |
||||
|
Pr_gid gid_t /* real group id */ |
||||
|
Pr_egid gid_t /* effective group id */ |
||||
|
Pr_addr uintptr_t /* address of process */ |
||||
|
Pr_size size_t /* size of process image in Kbytes */ |
||||
|
Pr_rssize size_t /* resident set size in Kbytes */ |
||||
|
Pr_pad1 size_t |
||||
|
Pr_ttydev dev_t /* controlling tty device (or PRNODEV) */ |
||||
|
|
||||
|
// Guess this following 2 ushort_t values require a padding to properly
|
||||
|
// align to the 64bit mark.
|
||||
|
Pr_pctcpu ushort_t /* % of recent cpu time used by all lwps */ |
||||
|
Pr_pctmem ushort_t /* % of system memory used by process */ |
||||
|
Pr_pad64bit [4]byte |
||||
|
|
||||
|
Pr_start timestruc_t /* process start time, from the epoch */ |
||||
|
Pr_time timestruc_t /* usr+sys cpu time for this process */ |
||||
|
Pr_ctime timestruc_t /* usr+sys cpu time for reaped children */ |
||||
|
Pr_fname [16]byte /* name of execed file */ |
||||
|
Pr_psargs [80]byte /* initial characters of arg list */ |
||||
|
Pr_wstat int32 /* if zombie, the wait() status */ |
||||
|
Pr_argc int32 /* initial argument count */ |
||||
|
Pr_argv uintptr_t /* address of initial argument vector */ |
||||
|
Pr_envp uintptr_t /* address of initial environment vector */ |
||||
|
Pr_dmodel [1]byte /* data model of the process */ |
||||
|
Pr_pad2 [3]byte |
||||
|
Pr_taskid id_t /* task id */ |
||||
|
Pr_projid id_t /* project id */ |
||||
|
Pr_nzomb int32 /* number of zombie lwps in the process */ |
||||
|
Pr_poolid id_t /* pool id */ |
||||
|
Pr_zoneid id_t /* zone id */ |
||||
|
Pr_contract id_t /* process contract */ |
||||
|
Pr_filler int32 /* reserved for future use */ |
||||
|
Pr_lwp [128]byte /* information for representative lwp */ |
||||
|
} |
||||
|
|
||||
|
func (p *UnixProcess) Refresh() error { |
||||
|
var psinfo psinfo_t |
||||
|
|
||||
|
path := fmt.Sprintf("/proc/%d/psinfo", p.pid) |
||||
|
fh, err := os.Open(path) |
||||
|
if err != nil { |
||||
|
return err |
||||
|
} |
||||
|
defer fh.Close() |
||||
|
|
||||
|
err = binary.Read(fh, binary.LittleEndian, &psinfo) |
||||
|
if err != nil { |
||||
|
return err |
||||
|
} |
||||
|
|
||||
|
p.ppid = int(psinfo.Pr_ppid) |
||||
|
p.binary = toString(psinfo.Pr_fname[:], 16) |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
func toString(array []byte, len int) string { |
||||
|
for i := 0; i < len; i++ { |
||||
|
if array[i] == 0 { |
||||
|
return string(array[:i]) |
||||
|
} |
||||
|
} |
||||
|
return string(array[:]) |
||||
|
} |
@ -0,0 +1,101 @@ |
|||||
|
// +build linux solaris
|
||||
|
|
||||
|
package ps |
||||
|
|
||||
|
import ( |
||||
|
"fmt" |
||||
|
"io" |
||||
|
"os" |
||||
|
"strconv" |
||||
|
) |
||||
|
|
||||
|
// 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 |
||||
|
} |
||||
|
|
||||
|
func findProcess(pid int) (Process, error) { |
||||
|
dir := fmt.Sprintf("/proc/%d", pid) |
||||
|
_, err := os.Stat(dir) |
||||
|
if err != nil { |
||||
|
if os.IsNotExist(err) { |
||||
|
return nil, nil |
||||
|
} |
||||
|
|
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
return newUnixProcess(pid) |
||||
|
} |
||||
|
|
||||
|
func processes() ([]Process, error) { |
||||
|
d, err := os.Open("/proc") |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
defer d.Close() |
||||
|
|
||||
|
results := make([]Process, 0, 50) |
||||
|
for { |
||||
|
fis, err := d.Readdir(10) |
||||
|
if err == io.EOF { |
||||
|
break |
||||
|
} |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
for _, fi := range fis { |
||||
|
// We only care about directories, since all pids are dirs
|
||||
|
if !fi.IsDir() { |
||||
|
continue |
||||
|
} |
||||
|
|
||||
|
// We only care if the name starts with a numeric
|
||||
|
name := fi.Name() |
||||
|
if name[0] < '0' || name[0] > '9' { |
||||
|
continue |
||||
|
} |
||||
|
|
||||
|
// From this point forward, any errors we just ignore, because
|
||||
|
// it might simply be that the process doesn't exist anymore.
|
||||
|
pid, err := strconv.ParseInt(name, 10, 0) |
||||
|
if err != nil { |
||||
|
continue |
||||
|
} |
||||
|
|
||||
|
p, err := newUnixProcess(int(pid)) |
||||
|
if err != nil { |
||||
|
continue |
||||
|
} |
||||
|
|
||||
|
results = append(results, p) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return results, nil |
||||
|
} |
||||
|
|
||||
|
func newUnixProcess(pid int) (*UnixProcess, error) { |
||||
|
p := &UnixProcess{pid: pid} |
||||
|
return p, p.Refresh() |
||||
|
} |
@ -0,0 +1,119 @@ |
|||||
|
// +build windows
|
||||
|
|
||||
|
package ps |
||||
|
|
||||
|
import ( |
||||
|
"fmt" |
||||
|
"syscall" |
||||
|
"unsafe" |
||||
|
) |
||||
|
|
||||
|
// Windows API functions
|
||||
|
var ( |
||||
|
modKernel32 = syscall.NewLazyDLL("kernel32.dll") |
||||
|
procCloseHandle = modKernel32.NewProc("CloseHandle") |
||||
|
procCreateToolhelp32Snapshot = modKernel32.NewProc("CreateToolhelp32Snapshot") |
||||
|
procProcess32First = modKernel32.NewProc("Process32FirstW") |
||||
|
procProcess32Next = modKernel32.NewProc("Process32NextW") |
||||
|
) |
||||
|
|
||||
|
// Some constants from the Windows API
|
||||
|
const ( |
||||
|
ERROR_NO_MORE_FILES = 0x12 |
||||
|
MAX_PATH = 260 |
||||
|
) |
||||
|
|
||||
|
// PROCESSENTRY32 is the Windows API structure that contains a process's
|
||||
|
// information.
|
||||
|
type PROCESSENTRY32 struct { |
||||
|
Size uint32 |
||||
|
CntUsage uint32 |
||||
|
ProcessID uint32 |
||||
|
DefaultHeapID uintptr |
||||
|
ModuleID uint32 |
||||
|
CntThreads uint32 |
||||
|
ParentProcessID uint32 |
||||
|
PriorityClassBase int32 |
||||
|
Flags uint32 |
||||
|
ExeFile [MAX_PATH]uint16 |
||||
|
} |
||||
|
|
||||
|
// WindowsProcess is an implementation of Process for Windows.
|
||||
|
type WindowsProcess struct { |
||||
|
pid int |
||||
|
ppid int |
||||
|
exe string |
||||
|
} |
||||
|
|
||||
|
func (p *WindowsProcess) Pid() int { |
||||
|
return p.pid |
||||
|
} |
||||
|
|
||||
|
func (p *WindowsProcess) PPid() int { |
||||
|
return p.ppid |
||||
|
} |
||||
|
|
||||
|
func (p *WindowsProcess) Executable() string { |
||||
|
return p.exe |
||||
|
} |
||||
|
|
||||
|
func newWindowsProcess(e *PROCESSENTRY32) *WindowsProcess { |
||||
|
// Find when the string ends for decoding
|
||||
|
end := 0 |
||||
|
for { |
||||
|
if e.ExeFile[end] == 0 { |
||||
|
break |
||||
|
} |
||||
|
end++ |
||||
|
} |
||||
|
|
||||
|
return &WindowsProcess{ |
||||
|
pid: int(e.ProcessID), |
||||
|
ppid: int(e.ParentProcessID), |
||||
|
exe: syscall.UTF16ToString(e.ExeFile[:end]), |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func findProcess(pid int) (Process, error) { |
||||
|
ps, err := processes() |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
for _, p := range ps { |
||||
|
if p.Pid() == pid { |
||||
|
return p, nil |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return nil, nil |
||||
|
} |
||||
|
|
||||
|
func processes() ([]Process, error) { |
||||
|
handle, _, _ := procCreateToolhelp32Snapshot.Call( |
||||
|
0x00000002, |
||||
|
0) |
||||
|
if handle < 0 { |
||||
|
return nil, syscall.GetLastError() |
||||
|
} |
||||
|
defer procCloseHandle.Call(handle) |
||||
|
|
||||
|
var entry PROCESSENTRY32 |
||||
|
entry.Size = uint32(unsafe.Sizeof(entry)) |
||||
|
ret, _, _ := procProcess32First.Call(handle, uintptr(unsafe.Pointer(&entry))) |
||||
|
if ret == 0 { |
||||
|
return nil, fmt.Errorf("Error retrieving process info.") |
||||
|
} |
||||
|
|
||||
|
results := make([]Process, 0, 50) |
||||
|
for { |
||||
|
results = append(results, newWindowsProcess(&entry)) |
||||
|
|
||||
|
ret, _, _ := procProcess32Next.Call(handle, uintptr(unsafe.Pointer(&entry))) |
||||
|
if ret == 0 { |
||||
|
break |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return results, nil |
||||
|
} |
Loading…
Reference in new issue