github.com/ratrocket/u-root@v0.0.0-20180201221235-1cf9f48ee2cf/cmds/ps/ps_linux.go (about) 1 // Copyright 2016 the u-root Authors. All rights reserved 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package main 6 7 import ( 8 "fmt" 9 "io/ioutil" 10 "os" 11 "path/filepath" 12 "reflect" 13 "strconv" 14 "strings" 15 ) 16 17 const ( 18 proc = "/proc" 19 USER_HZ = 100 20 ) 21 22 // Portable way to implement ps cross-plataform 23 // Like the os.File 24 type Process struct { 25 process 26 } 27 28 // table content of stat file defined by: 29 // https://www.kernel.org/doc/Documentation/filesystems/proc.txt (2009) 30 // Section (ctrl + f) : Table 1-4: Contents of the stat files (as of 2.6.30-rc7) 31 type process struct { 32 Pid string // process id 33 Cmd string // filename of the executable 34 State string // state (R is running, S is sleeping, D is sleeping in an uninterruptible wait, Z is zombie, T is traced or stopped) 35 Ppid string // process id of the parent process 36 Pgrp string // pgrp of the process 37 Sid string // session id 38 TTYNr string // tty the process uses 39 TTYPgrp string // pgrp of the tty 40 Flags string // task flags 41 MinFlt string // number of minor faults 42 CminFlt string // number of minor faults with child's 43 MajFlt string // number of major faults 44 CmajFlt string // number of major faults with child's 45 Utime string // user mode jiffies 46 Stime string // kernel mode jiffies 47 Cutime string // user mode jiffies with child's 48 Cstime string // kernel mode jiffies with child's 49 Priority string // priority level 50 Nice string // nice level 51 NumThreads string // number of threads 52 ItRealValue string // (obsolete, always 0) 53 StartTime string // time the process started after system boot 54 Vsize string // virtual memory size 55 Rss string // resident set memory size 56 Rsslim string // current limit in bytes on the rss 57 StartCode string // address above which program text can run 58 EndCode string // address below which program text can run 59 StartStack string // address of the start of the main process stack 60 Esp string // current value of ESP 61 Eip string // current value of EIP 62 Pending string // bitmap of pending signals 63 Blocked string // bitmap of blocked signals 64 Sigign string // bitmap of ignored signals 65 Sigcatch string // bitmap of caught signals 66 Wchan string // place holder, used to be the wchan address, use /proc/PID/wchan 67 Zero1 string // ignored 68 Zero2 string // ignored 69 ExitSignal string // signal to send to parent thread on exit 70 TaskCpu string // which CPU the task is scheduled on 71 RtPriority string // realtime priority 72 Policy string // scheduling policy (man sched_setscheduler) 73 BlkioTicks string // time spent waiting for block IO 74 Gtime string // guest time of the task in jiffies 75 Cgtime string // guest time of the task children in jiffies 76 StartData string // address above which program data+bss is placed 77 EndData string // address below which program data+bss is placed 78 StartBrk string // address above which program heap can be expanded with brk() 79 ArgStart string // address above which program command line is placed 80 ArgEnd string // address below which program command line is placed 81 EnvStart string // address above which program environment is placed 82 EnvEnd string // address below which program environment is placed 83 ExitCode string // the thread's exit_code in the form reported by the waitpid system call (end of stat) 84 Ctty string // extra member (don't parsed from stat) 85 Time string // extra member (don't parsed from stat) 86 } 87 88 // Parse all content of stat to a Process Struct 89 // by gived the pid (linux) 90 func (p *process) readStat(pid int) error { 91 b, err := ioutil.ReadFile(filepath.Join(proc, fmt.Sprint(pid), "stat")) 92 if err != nil { 93 return err 94 } 95 96 fields := strings.Split(string(b), " ") 97 98 // set struct fields from stat file data 99 v := reflect.ValueOf(p).Elem() 100 for i := 0; i < len(fields); i++ { 101 fieldVal := v.Field(i) 102 fieldVal.Set(reflect.ValueOf(fields[i])) 103 } 104 105 p.Time = p.getTime() 106 p.Ctty = p.getCtty() 107 cmd := p.Cmd 108 p.Cmd = cmd[1 : len(cmd)-1] 109 if flags.x && false { 110 // disable that, because after removed the max width limit 111 // we had some incredible long cmd lines whose breaks the 112 // visual table of process at running ps 113 cmdline, err := p.longCmdLine() 114 if err != nil { 115 return err 116 } 117 if cmdline != "" { 118 p.Cmd = cmdline 119 } 120 } 121 122 return nil 123 } 124 125 // Fetch data from Operating System about process 126 // on Linux read data from stat 127 func (p *Process) Parse(pid int) error { 128 return p.process.readStat(pid) 129 } 130 131 // ctty returns the ctty or "?" if none can be found. 132 // TODO: an right way to get ctty by p.TTYNr and p.TTYPgrp 133 func (p process) getCtty() string { 134 if tty, err := os.Readlink(filepath.Join(proc, p.Pid, "fd/0")); err != nil { 135 return "?" 136 } else if p.TTYPgrp != "-1" { 137 if len(tty) > 5 && tty[:5] == "/dev/" { 138 tty = tty[5:] 139 } 140 return tty 141 } 142 return "?" 143 } 144 145 // Get a named field of stat type 146 // e.g.: p.getField("Pid") => '1' 147 func (p *process) getField(field string) string { 148 v := reflect.ValueOf(p).Elem() 149 return fmt.Sprintf("%v", v.FieldByName(field)) 150 } 151 152 // Search for attributes about the process 153 func (p Process) Search(field string) string { 154 return p.process.getField(field) 155 } 156 157 // read UID of process based on or 158 func (p process) getUid() (int, error) { 159 b, err := ioutil.ReadFile(filepath.Join(proc, p.Pid, "status")) 160 161 var uid int 162 lines := strings.Split(string(b), "\n") 163 for _, line := range lines { 164 if strings.Contains(line, "Uid") { 165 fields := strings.Split(line, "\t") 166 uid, err = strconv.Atoi(fields[1]) 167 break 168 } 169 } 170 171 return uid, err 172 173 } 174 175 func (p Process) GetUid() (int, error) { 176 return p.process.getUid() 177 } 178 179 // change p.Cmd to long command line with args 180 func (p process) longCmdLine() (string, error) { 181 b, err := ioutil.ReadFile(filepath.Join(proc, p.Pid, "cmdline")) 182 183 if err != nil { 184 return "", err 185 } 186 187 return string(b), nil 188 } 189 190 // Get total time stat formated hh:mm:ss 191 func (p process) getTime() string { 192 utime, _ := strconv.Atoi(p.Utime) 193 stime, _ := strconv.Atoi(p.Stime) 194 jiffies := utime + stime 195 196 tsecs := jiffies / USER_HZ 197 secs := tsecs % 60 198 mins := (tsecs / 60) % 60 199 hrs := tsecs / 3600 200 201 return fmt.Sprintf("%02d:%02d:%02d", hrs, mins, secs) 202 } 203 204 // Create a ProcessTable containing stats on all processes. 205 func (pT *ProcessTable) LoadTable() error { 206 // Open and Readdir /proc. 207 f, err := os.Open("/proc") 208 defer f.Close() 209 if err != nil { 210 return err 211 } 212 list, err := f.Readdir(-1) 213 if err != nil { 214 return err 215 } 216 217 for _, dir := range list { 218 // Filter out files and directories which are not numbers. 219 pid, err := strconv.Atoi(dir.Name()) 220 if err != nil { 221 continue 222 } 223 224 // Parse the process's stat file. 225 p := &Process{} 226 if err := p.Parse(pid); err != nil { 227 // It is extremely common for a directory to disappear from 228 // /proc when a process terminates, so ignore those errors. 229 if os.IsNotExist(err) { 230 continue 231 } 232 return err 233 } 234 pT.table = append(pT.table, p) 235 } 236 237 return nil 238 }