gopkg.in/hugelgupf/u-root.v9@v9.0.0-20180831063832-3f6f1057f09b/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 93 // We prefer to use os.ErrNotExist in this case. 94 // It is more universal. 95 if err != nil && err.Error() == "no such process" { 96 err = os.ErrNotExist 97 } 98 99 if err != nil { 100 return err 101 } 102 103 fields := strings.Split(string(b), " ") 104 105 // set struct fields from stat file data 106 v := reflect.ValueOf(p).Elem() 107 for i := 0; i < len(fields); i++ { 108 fieldVal := v.Field(i) 109 fieldVal.Set(reflect.ValueOf(fields[i])) 110 } 111 112 p.Time = p.getTime() 113 p.Ctty = p.getCtty() 114 cmd := p.Cmd 115 p.Cmd = cmd[1 : len(cmd)-1] 116 if flags.x && false { 117 // disable that, because after removed the max width limit 118 // we had some incredible long cmd lines whose breaks the 119 // visual table of process at running ps 120 cmdline, err := p.longCmdLine() 121 if err != nil { 122 return err 123 } 124 if cmdline != "" { 125 p.Cmd = cmdline 126 } 127 } 128 129 return nil 130 } 131 132 // Fetch data from Operating System about process 133 // on Linux read data from stat 134 func (p *Process) Parse(pid int) error { 135 return p.process.readStat(pid) 136 } 137 138 // ctty returns the ctty or "?" if none can be found. 139 // TODO: an right way to get ctty by p.TTYNr and p.TTYPgrp 140 func (p process) getCtty() string { 141 if tty, err := os.Readlink(filepath.Join(proc, p.Pid, "fd/0")); err != nil { 142 return "?" 143 } else if p.TTYPgrp != "-1" { 144 if len(tty) > 5 && tty[:5] == "/dev/" { 145 tty = tty[5:] 146 } 147 return tty 148 } 149 return "?" 150 } 151 152 // Get a named field of stat type 153 // e.g.: p.getField("Pid") => '1' 154 func (p *process) getField(field string) string { 155 v := reflect.ValueOf(p).Elem() 156 return fmt.Sprintf("%v", v.FieldByName(field)) 157 } 158 159 // Search for attributes about the process 160 func (p Process) Search(field string) string { 161 return p.process.getField(field) 162 } 163 164 // read UID of process based on or 165 func (p process) getUid() (int, error) { 166 b, err := ioutil.ReadFile(filepath.Join(proc, p.Pid, "status")) 167 if err != nil && err.Error() == "no such process" { 168 err = os.ErrNotExist 169 } 170 171 var uid int 172 lines := strings.Split(string(b), "\n") 173 for _, line := range lines { 174 if strings.Contains(line, "Uid") { 175 fields := strings.Split(line, "\t") 176 uid, err = strconv.Atoi(fields[1]) 177 break 178 } 179 } 180 181 return uid, err 182 183 } 184 185 func (p Process) GetUid() (int, error) { 186 return p.process.getUid() 187 } 188 189 // change p.Cmd to long command line with args 190 func (p process) longCmdLine() (string, error) { 191 b, err := ioutil.ReadFile(filepath.Join(proc, p.Pid, "cmdline")) 192 193 if err != nil { 194 return "", err 195 } 196 197 return string(b), nil 198 } 199 200 // Get total time stat formated hh:mm:ss 201 func (p process) getTime() string { 202 utime, _ := strconv.Atoi(p.Utime) 203 stime, _ := strconv.Atoi(p.Stime) 204 jiffies := utime + stime 205 206 tsecs := jiffies / USER_HZ 207 secs := tsecs % 60 208 mins := (tsecs / 60) % 60 209 hrs := tsecs / 3600 210 211 return fmt.Sprintf("%02d:%02d:%02d", hrs, mins, secs) 212 } 213 214 // Create a ProcessTable containing stats on all processes. 215 func (pT *ProcessTable) LoadTable() error { 216 // Open and Readdir /proc. 217 f, err := os.Open("/proc") 218 defer f.Close() 219 if err != nil { 220 return err 221 } 222 list, err := f.Readdir(-1) 223 if err != nil { 224 return err 225 } 226 if len(list) == 0 { 227 return fmt.Errorf("No processes in /proc.") 228 } 229 230 for _, dir := range list { 231 // Filter out files and directories which are not numbers. 232 pid, err := strconv.Atoi(dir.Name()) 233 if err != nil { 234 continue 235 } 236 237 // Parse the process's stat file. 238 p := &Process{} 239 if err := p.Parse(pid); err != nil { 240 // It is extremely common for a directory to disappear from 241 // /proc when a process terminates, so ignore those errors. 242 if os.IsNotExist(err) { 243 continue 244 } 245 return err 246 } 247 pT.table = append(pT.table, p) 248 } 249 250 return nil 251 }