// +build linux netbsd package ps import ( "fmt" "io" "io/ioutil" "os" "path/filepath" "strconv" "strings" ) // 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 // binary name might be truncated } // Pid returns process id func (p *UnixProcess) Pid() int { return p.pid } // PPid returns parent process id func (p *UnixProcess) PPid() int { return p.ppid } // Executable returns process executable name func (p *UnixProcess) Executable() string { path, err := p.Path() if err != nil { // Fall back to binary name which might be truncated return p.binary } return filepath.Base(path) } // Path returns path to process executable func (p *UnixProcess) Path() (string, error) { return filepath.EvalSymlinks(fmt.Sprintf("/proc/%d/exe", p.pid)) } // 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 // The name here might not be the full name 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) 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() }