package system import ( "fmt" "io/ioutil" "path/filepath" "strconv" "strings" ) // State is the status of a process. type State rune const ( // Only values for Linux 3.14 and later are listed here Dead State = 'X' DiskSleep State = 'D' Running State = 'R' Sleeping State = 'S' Stopped State = 'T' TracingStop State = 't' Zombie State = 'Z' ) // String forms of the state from proc(5)'s documentation for // /proc/[pid]/status' "State" field. func (s State) String() string { switch s { case Dead: return "dead" case DiskSleep: return "disk sleep" case Running: return "running" case Sleeping: return "sleeping" case Stopped: return "stopped" case TracingStop: return "tracing stop" case Zombie: return "zombie" default: return fmt.Sprintf("unknown (%c)", s) } } // Stat_t represents the information from /proc/[pid]/stat, as // described in proc(5) with names based on the /proc/[pid]/status // fields. type Stat_t struct { // PID is the process ID. PID uint // Name is the command run by the process. Name string // State is the state of the process. State State // StartTime is the number of clock ticks after system boot (since // Linux 2.6). StartTime uint64 } // Stat returns a Stat_t instance for the specified process. func Stat(pid int) (stat Stat_t, err error) { bytes, err := ioutil.ReadFile(filepath.Join("/proc", strconv.Itoa(pid), "stat")) if err != nil { return stat, err } return parseStat(string(bytes)) } // GetProcessStartTime is deprecated. Use Stat(pid) and // Stat_t.StartTime instead. func GetProcessStartTime(pid int) (string, error) { stat, err := Stat(pid) if err != nil { return "", err } return fmt.Sprintf("%d", stat.StartTime), nil } func parseStat(data string) (stat Stat_t, err error) { // From proc(5), field 2 could contain space and is inside `(` and `)`. // The following is an example: // 89653 (gunicorn: maste) S 89630 89653 89653 0 -1 4194560 29689 28896 0 3 146 32 76 19 20 0 1 0 2971844 52965376 3920 18446744073709551615 1 1 0 0 0 0 0 16781312 137447943 0 0 0 17 1 0 0 0 0 0 0 0 0 0 0 0 0 0 i := strings.LastIndex(data, ")") if i <= 2 || i >= len(data)-1 { return stat, fmt.Errorf("invalid stat data: %q", data) } parts := strings.SplitN(data[:i], "(", 2) if len(parts) != 2 { return stat, fmt.Errorf("invalid stat data: %q", data) } stat.Name = parts[1] _, err = fmt.Sscanf(parts[0], "%d", &stat.PID) if err != nil { return stat, err } // parts indexes should be offset by 3 from the field number given // proc(5), because parts is zero-indexed and we've removed fields // one (PID) and two (Name) in the paren-split. parts = strings.Split(data[i+2:], " ") var state int fmt.Sscanf(parts[3-3], "%c", &state) stat.State = State(state) fmt.Sscanf(parts[22-3], "%d", &stat.StartTime) return stat, nil }