gitlab.com/apertussolutions/u-root@v7.0.0+incompatible/cmds/core/ps/ps_linux.go (about) 1 // Copyright 2016-2018 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 "log" 11 "os" 12 "path/filepath" 13 "reflect" 14 "strconv" 15 "strings" 16 ) 17 18 const ( 19 defaultGlob = "/proc" 20 userHZ = 100 21 ) 22 23 var ( 24 psglob string 25 // by convention, the first element of the path is "/proc" 26 // This allows us to point to any place as our "/proc" 27 procdir = "/proc" 28 ) 29 30 // Process contains both kernel-dependent and kernel-independent information. 31 type Process struct { 32 process 33 status string 34 cmdline string 35 stat string 36 Pidno int // process id # 37 uid int 38 } 39 40 // table content of stat file defined by: 41 // https://www.kernel.org/doc/Documentation/filesystems/proc.txt (2009) 42 // Section (ctrl + f) : Table 1-4: Contents of the stat files (as of 2.6.30-rc7) 43 type process struct { 44 Pid string // process id name 45 Cmd string // filename of the executable 46 State string // state (R is running, S is sleeping, D is sleeping in an uninterruptible wait, Z is zombie, T is traced or stopped) 47 Ppid string // process id of the parent process 48 Pgrp string // pgrp of the process 49 Sid string // session id 50 TTYNr string // tty the process uses 51 TTYPgrp string // pgrp of the tty 52 Flags string // task flags 53 MinFlt string // number of minor faults 54 CminFlt string // number of minor faults with child's 55 MajFlt string // number of major faults 56 CmajFlt string // number of major faults with child's 57 Utime string // user mode jiffies 58 Stime string // kernel mode jiffies 59 Cutime string // user mode jiffies with child's 60 Cstime string // kernel mode jiffies with child's 61 Priority string // priority level 62 Nice string // nice level 63 NumThreads string // number of threads 64 ItRealValue string // (obsolete, always 0) 65 StartTime string // time the process started after system boot 66 Vsize string // virtual memory size 67 Rss string // resident set memory size 68 Rsslim string // current limit in bytes on the rss 69 StartCode string // address above which program text can run 70 EndCode string // address below which program text can run 71 StartStack string // address of the start of the main process stack 72 Esp string // current value of ESP 73 Eip string // current value of EIP 74 Pending string // bitmap of pending signals 75 Blocked string // bitmap of blocked signals 76 Sigign string // bitmap of ignored signals 77 Sigcatch string // bitmap of caught signals 78 Wchan string // place holder, used to be the wchan address, use /proc/PID/wchan 79 Zero1 string // ignored 80 Zero2 string // ignored 81 ExitSignal string // signal to send to parent thread on exit 82 TaskCPU string // which CPU the task is scheduled on 83 RtPriority string // realtime priority 84 Policy string // scheduling policy (man sched_setscheduler) 85 BlkioTicks string // time spent waiting for block IO 86 Gtime string // guest time of the task in jiffies 87 Cgtime string // guest time of the task children in jiffies 88 StartData string // address above which program data+bss is placed 89 EndData string // address below which program data+bss is placed 90 StartBrk string // address above which program heap can be expanded with brk() 91 ArgStart string // address above which program command line is placed 92 ArgEnd string // address below which program command line is placed 93 EnvStart string // address above which program environment is placed 94 EnvEnd string // address below which program environment is placed 95 ExitCode string // the thread's exit_code in the form reported by the waitpid system call (end of stat) 96 Ctty string // extra member (don't parsed from stat) 97 Time string // extra member (don't parsed from stat) 98 } 99 100 // Parse all content of stat to a Process Struct 101 // by gived the pid (linux) 102 func (p *Process) readStat(s string) error { 103 fields := strings.Split(s, " ") 104 // set struct fields from stat file data 105 v := reflect.ValueOf(&p.process).Elem() 106 for i := 0; i < len(fields); i++ { 107 fieldVal := v.Field(i) 108 fieldVal.Set(reflect.ValueOf(fields[i])) 109 } 110 111 p.Time = p.getTime() 112 p.Ctty = p.getCtty() 113 p.Cmd = strings.TrimSuffix(strings.TrimPrefix(p.Cmd, "("), ")") 114 if flags.x && p.cmdline != "" { 115 p.Cmd = p.cmdline 116 } 117 118 return nil 119 } 120 121 // Parse data from various strings in the Process struct 122 func (p *Process) Parse() error { 123 err := p.readStat(p.stat) 124 if err != nil { 125 return err 126 } 127 if p.uid, err = p.GetUID(); err != nil { 128 return err 129 } 130 return nil 131 } 132 133 // ctty returns the ctty or "?" if none can be found. 134 // TODO: an right way to get ctty by p.TTYNr and p.TTYPgrp 135 func (p process) getCtty() string { 136 if tty, err := os.Readlink(filepath.Join(procdir, p.Pid, "fd/0")); err != nil { 137 return "?" 138 } else if p.TTYPgrp != "-1" { 139 if len(tty) > 5 && tty[:5] == "/dev/" { 140 tty = tty[5:] 141 } 142 return tty 143 } 144 return "?" 145 } 146 147 // Get a named field of stat type 148 // e.g.: p.getField("Pid") => '1' 149 func (p *process) getField(field string) string { 150 v := reflect.ValueOf(p).Elem() 151 return fmt.Sprintf("%v", v.FieldByName(field)) 152 } 153 154 // Search for attributes about the process 155 func (p *Process) Search(field string) string { 156 return p.process.getField(field) 157 } 158 159 // GetUID gets the UID of the process from the status string 160 func (p Process) GetUID() (int, error) { 161 lines := strings.Split(p.status, "\n") 162 for _, line := range lines { 163 if strings.Contains(line, "Uid") { 164 fields := strings.Split(line, "\t") 165 return strconv.Atoi(fields[1]) 166 } 167 } 168 169 return -1, fmt.Errorf("no Uid string in %s", p.status) 170 171 } 172 173 // Get total time stat formated hh:mm:ss 174 func (p process) getTime() string { 175 utime, _ := strconv.Atoi(p.Utime) 176 stime, _ := strconv.Atoi(p.Stime) 177 jiffies := utime + stime 178 179 tsecs := jiffies / userHZ 180 secs := tsecs % 60 181 mins := (tsecs / 60) % 60 182 hrs := tsecs / 3600 183 184 return fmt.Sprintf("%02d:%02d:%02d", hrs, mins, secs) 185 } 186 187 func getAllGlobNames() []string { 188 psglob = os.Getenv("UROOT_PSPATH") 189 if psglob == "" { 190 // The reason we glob with stat, even though 191 // we strip it off later, is it is a cheap way 192 // to ensure we're getting a process directory 193 // and not some other weird thing in /proc. 194 psglob = defaultGlob 195 } 196 l := filepath.SplitList(psglob) 197 if len(l) > 0 { 198 procdir = l[0] 199 } 200 return l 201 } 202 203 // Create a set of stat file names from an array of globs 204 func getAllStatNames(globs []string) ([]string, error) { 205 var list []string 206 for _, g := range globs { 207 l, err := filepath.Glob(filepath.Join(g, "[0-9]*/stat")) 208 if err != nil { 209 log.Printf("Glob err on %s: %v", g, err) 210 continue 211 } 212 list = append(list, l...) 213 } 214 if len(list) == 0 { 215 return nil, fmt.Errorf("no files found in %q; check if proc is mounted", psglob) 216 } 217 return list, nil 218 } 219 220 func file(s string) (string, error) { 221 b, err := ioutil.ReadFile(s) 222 return string(b), err 223 } 224 225 func (pT *ProcessTable) doTable(statFileNames []string) error { 226 var err error 227 for _, stat := range statFileNames { 228 p := &Process{} 229 230 //log.Printf("Check %s", stat) 231 // ps is a snapshot in time of /proc. Hence we want to grab 232 // all the files we need in as close to an instant in time as 233 // we can. 234 // Read the files. It may have vanished or we may not have 235 // access; we do not consider those to be errors. 236 // if *any* of the files are not there, just skip this pid. 237 p.stat, err = file(stat) 238 if err != nil { 239 continue 240 } 241 d := filepath.Dir(stat) 242 pid := filepath.Base(d) 243 pidno, err := strconv.Atoi(pid) 244 if err != nil { 245 return fmt.Errorf("last element of %v is not a number", pid) 246 } 247 p.status, err = file(filepath.Join(d, "status")) 248 if err != nil { 249 continue 250 } 251 if flags.x { 252 p.cmdline, err = file(filepath.Join(d, "cmdline")) 253 if err != nil { 254 continue 255 } 256 } 257 // if filepath.Base is *not* proc, then use it, else 258 // it's just the directory containing the pid. 259 proot := filepath.Dir(d) 260 //log.Printf("procdir %v d %v proot %v", procdir, d, proot) 261 if proot != procdir { 262 pid = filepath.Join(filepath.Base(proot), pid) 263 } 264 p.Pidno = pidno 265 if err := p.Parse(); err != nil { 266 return err 267 } 268 p.Pid = pid 269 //log.Printf("stat is %v p is %v", stat,p) 270 if p.Pidno == os.Getpid() { 271 pT.mProc = p 272 } 273 pT.table = append(pT.table, p) 274 } 275 // if mProc is nil, something is really wrong. 276 if pT.mProc == nil && len(pT.table) > 0 { 277 pT.mProc = pT.table[0] 278 } 279 return nil 280 } 281 282 // LoadTable creates a ProcessTable containing stats on all processes. 283 // We use UROOT_PSPATH if set, else the default glob 284 // of /proc/[0-9]*/stat. 285 // We want to allow ps to run against the standard /proc but also 286 // proc mounted over a network in, e.g., /netproc/host/pid/... 287 // (i.e. we mount node:/proc on /netproc/node) 288 // The question then becomes what to store for the pid. 289 // For /proc, it's easy: strip the first directory component. 290 // For additional directories, e.g. /netproc/host/[0-9]*/stat, 291 // we can follow the same rule: strip the first component. 292 // We will do that for now and see if it works; if not we'll 293 // need more complex processing for UROOT_PSPATH. 294 func (pT *ProcessTable) LoadTable() error { 295 g := getAllGlobNames() 296 n, err := getAllStatNames(g) 297 if err != nil { 298 return err 299 } 300 return pT.doTable(n) 301 }