diff --git a/README.md b/README.md index 881ad32..8e8baf9 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ 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, and Windows. +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 diff --git a/process_linux.go b/process_linux.go new file mode 100644 index 0000000..c1558f7 --- /dev/null +++ b/process_linux.go @@ -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 +} diff --git a/process_solaris.go b/process_solaris.go new file mode 100644 index 0000000..014c416 --- /dev/null +++ b/process_solaris.go @@ -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[:]) +} diff --git a/process_unix.go b/process_unix.go index 578215a..3b733ce 100644 --- a/process_unix.go +++ b/process_unix.go @@ -1,14 +1,12 @@ -// +build linux +// +build linux solaris package ps import ( "fmt" "io" - "io/ioutil" "os" "strconv" - "strings" ) // UnixProcess is an implementation of Process that contains Unix-specific @@ -35,32 +33,6 @@ func (p *UnixProcess) Executable() string { return p.binary } -// 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 -} - func findProcess(pid int) (Process, error) { dir := fmt.Sprintf("/proc/%d", pid) _, err := os.Stat(dir) diff --git a/process_unix_test.go b/process_unix_test.go index debbc1b..754073e 100644 --- a/process_unix_test.go +++ b/process_unix_test.go @@ -1,4 +1,4 @@ -// +build linux +// +build linux solaris package ps