diff --git a/process.go b/process.go index a5ddae4..04e3685 100644 --- a/process.go +++ b/process.go @@ -1,5 +1,11 @@ package gopsutil +import ( + "strings" + "os" + "syscall" +) + type Process struct { Pid int32 `json:"pid"` Ppid int32 `json:"ppid"` @@ -80,3 +86,42 @@ func Pid_exists(pid int32) (bool, error) { return false, err } + + +// POSIX +func getTerminalMap() (map[uint64]string, error){ + ret := make(map[uint64]string) + termfiles := make([]string, 0) + + d, err := os.Open("/dev") + if err != nil { + return nil, err + } + defer d.Close() + + devnames, err := d.Readdirnames(-1) + for _, devname := range devnames{ + if strings.HasPrefix(devname, "/dev/tty"){ + termfiles = append(termfiles, "/dev/tty/" + devname) + } + } + + ptsd, err := os.Open("/dev/pts") + if err != nil { + return nil, err + } + defer ptsd.Close() + + ptsnames, err := ptsd.Readdirnames(-1) + for _, ptsname := range ptsnames{ + termfiles = append(termfiles, "/dev/pts/" + ptsname) + } + + for _, name := range termfiles{ + stat := syscall.Stat_t{} + syscall.Stat(name, &stat) + rdev := stat.Rdev + ret[rdev] = strings.Replace(name, "/dev", "", -1) + } + return ret, nil +} diff --git a/process_linux.go b/process_linux.go index d8ac609..dc839be 100644 --- a/process_linux.go +++ b/process_linux.go @@ -8,13 +8,38 @@ import ( "path/filepath" "strconv" "strings" + "sync" + "syscall" +) + +const ( + PRIO_PROCESS = 0 // linux/resource.h ) func NewProcess(pid int32) (*Process, error) { p := &Process{ Pid: int32(pid), } - go fillFromStat(pid, p) + + var wg sync.WaitGroup + wg.Add(4) + go func() { + wg.Done() + fillFromStat(pid, p) + }() + go func() { + defer wg.Done() + fillFromStatus(pid, p) + }() + go func() { + defer wg.Done() + go fillFromfd(pid, p) + }() + go func() { + defer wg.Done() + go fillFromCmdline(pid, p) + }() + wg.Wait() /* // user := parseInt32(fields[13]) @@ -33,22 +58,88 @@ func NewProcess(pid int32) (*Process, error) { return p, nil } +// Parse to int32 without error func parseInt32(val string) int32 { vv, _ := strconv.ParseInt(val, 10, 32) return int32(vv) } +// Parse to uint64 without error +func parseUint64(val string) uint64 { + vv, _ := strconv.ParseInt(val, 10, 64) + return uint64(vv) +} + +// Get num_fds from /proc/(pid)/fd +func fillFromfd(pid int32, p *Process) error { + statPath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "fd") + d, err := os.Open(statPath) + if err != nil { + return err + } + defer d.Close() + fnames, err := d.Readdirnames(-1) + p.Num_fds = int32(len(fnames)) -/* -func fillFromStatm(pid int32, p *Process) error{ - statPath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "statm") + return nil +} + +// Get cmdline from /proc/(pid)/cmdline +func fillFromCmdline(pid int32, p *Process) error { + cmdPath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "cmdline") + cmdline, err := ioutil.ReadFile(cmdPath) + if err != nil { + return err + } + // remove \u0000 + p.Cmdline = strings.TrimFunc(string(cmdline), func(r rune) bool { + if r == '\u0000' { + return true + } + return false + }) + + return nil +} + +// get various status from /proc/(pid)/status +func fillFromStatus(pid int32, p *Process) error { + statPath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "status") contents, err := ioutil.ReadFile(statPath) if err != nil { return err } - fields := strings.Fields(string(contents)) + lines := strings.Split(string(contents), "\n") + for _, line := range lines { + field := strings.Split(line, ":") + if len(field) < 2 { + continue + } +// fmt.Printf("%s ->__%s__\n", field[0], strings.Trim(field[1], " \t")) + switch field[0] { + case "Name": + p.Name = strings.Trim(field[1], " \t") + case "State": + // get between "(" and ")" + s := strings.Index(field[1], "(") + 1 + e := strings.Index(field[1], "(") + 1 + p.Status = field[1][s:e] + // case "PPid": // filled by fillFromStat + case "Uid": + for _, i := range strings.Split(field[1], "\t") { + p.Uids = append(p.Uids, parseInt32(i)) + } + case "Gid": + for _, i := range strings.Split(field[1], "\t") { + p.Gids = append(p.Uids, parseInt32(i)) + } + case "Threads": + p.Num_Threads = parseInt32(field[1]) + } + } + + return nil } -*/ func fillFromStat(pid int32, p *Process) error { statPath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "stat") @@ -58,47 +149,26 @@ func fillFromStat(pid int32, p *Process) error { } fields := strings.Fields(string(contents)) - p.Name = strings.Trim(fields[1], "()") // remove "(" and ")" - p.Status, _ = getState(fields[2][0]) + termmap, err := getTerminalMap() + if err == nil{ + p.Terminal = termmap[parseUint64(fields[6])] + } + p.Ppid = parseInt32(fields[3]) - // p.Terminal, _ = strconv.Atoi(fields[6]) - // p.Priority, _ = strconv.Atoi(fields[17]) - p.Nice = parseInt32(fields[18]) - // p.Processor, _ = strconv.Atoi(fields[38]) - return nil -} + utime, _ := strconv.ParseFloat(fields[11], 64) + stime, _ := strconv.ParseFloat(fields[11], 64) -func getState(status uint8) (string, error) { + p.Cpu_times = CPU_TimesStat{ + User: float32(utime / CLOCK_TICKS), + System: float32(stime / CLOCK_TICKS), + } - /* - >>> psutil.STATUS_RUNNING - 'running' - >>> psutil.STATUS_SLEEPING - 'sleeping' - >>> psutil.STATUS_DISK_SLEEP - 'disk-sleep' - >>> psutil.STATUS_STOPPED - 'stopped' - >>> psutil.STATUS_TRACING_STOP - 'tracing-stop' - >>> psutil.STATUS_ZOMBIE - 'zombie' - >>> psutil.STATUS_DEAD - 'dead' - >>> psutil.STATUS_WAKE_KILL - Traceback (most recent call last): - File "", line 1, in - AttributeError: 'ModuleWrapper' object has no attribute 'STATUS_WAKE_KILL' - >>> psutil.STATUS_WAKING - 'waking' - >>> psutil.STATUS_IDLE - 'idle' - >>> psutil.STATUS_LOCKED - 'locked' - >>> psutil.STATUS_WAITING - 'waiting' - */ - return "running", nil + // p.Nice = parseInt32(fields[18]) + // use syscall instead of parse Stat file + nice, _ := syscall.Getpriority(PRIO_PROCESS, int(pid)) + p.Nice = int32(nice) // FIXME: is this true? + + return nil } func processes() ([]*Process, error) { diff --git a/process_linux_amd64.go b/process_linux_amd64.go new file mode 100644 index 0000000..82e542d --- /dev/null +++ b/process_linux_amd64.go @@ -0,0 +1,8 @@ +// +build linux +// +build amd64 + +package gopsutil + +const( + CLOCK_TICKS = 2 // FIXME: /usr/include/x86_64-linux-gnu/bits/time.h +)