github.com/ratrocket/u-root@v0.0.0-20180201221235-1cf9f48ee2cf/cmds/ps/ps.go (about) 1 // Copyright 2013-2017 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 // Print process information. 6 // 7 // Synopsis: 8 // ps [-Aaex] [aux] 9 // 10 // Description: 11 // ps reads the /proc filesystem and prints nice things about what it 12 // finds. /proc in linux has grown by a process of Evilution, so it's 13 // messy. 14 // 15 // Options: 16 // -A: select all processes. Identical to -e. 17 // -e: select all processes. Identical to -A. 18 // -x: BSD-Like style, with STAT Column and long CommandLine 19 // -a: print all process except whose are session leaders or unlinked with terminal 20 // aux: see every process on the system using BSD syntax 21 package main 22 23 import ( 24 "flag" 25 "fmt" 26 "log" 27 "os" 28 "sort" 29 "strconv" 30 "strings" 31 ) 32 33 var ( 34 flags struct { 35 all bool 36 nSidTty bool 37 x bool 38 aux bool 39 } 40 cmd = "ps [-Aaex] [aux]" 41 eUID = os.Geteuid() 42 mainPID = os.Getpid() 43 ) 44 45 func init() { 46 defUsage := flag.Usage 47 flag.Usage = func() { 48 os.Args[0] = cmd 49 defUsage() 50 } 51 flag.BoolVar(&flags.all, "A", false, "Select all processes. Identical to -e.") 52 flag.BoolVar(&flags.all, "e", false, "Select all processes. Identical to -A.") 53 flag.BoolVar(&flags.x, "x", false, "BSD-Like style, with STAT Column and long CommandLine") 54 flag.BoolVar(&flags.nSidTty, "a", false, "Print all process except whose are session leaders or unlinked with terminal") 55 56 if len(os.Args) > 1 { 57 if isPermutation(os.Args[1], "aux") { 58 flags.aux = true 59 flags.all = true 60 } 61 } 62 } 63 64 // main process table of ps 65 // used to make more flexible 66 type ProcessTable struct { 67 table []*Process 68 headers []string // each column to print 69 fields []string // which fields of process to print, on order 70 fstring []string // formated strings 71 maxwidth int // DEPRECATED: reason -> remove terminal stuff 72 } 73 74 // to use on sort.Sort 75 func (pT ProcessTable) Len() int { 76 return len(pT.table) 77 } 78 79 // to use on sort.Sort 80 func (pT ProcessTable) Less(i, j int) bool { 81 a, _ := strconv.Atoi(pT.table[i].Pid) 82 b, _ := strconv.Atoi(pT.table[j].Pid) 83 return a < b 84 } 85 86 // to use on sort.Sort 87 func (pT ProcessTable) Swap(i, j int) { 88 pT.table[i], pT.table[j] = pT.table[j], pT.table[i] 89 } 90 91 // Gived a pid, search for a process 92 // Returns nil if not found 93 func (pT ProcessTable) GetProcess(pid int) (found *Process) { 94 for _, p := range pT.table { 95 if p.Pid == strconv.Itoa(pid) { 96 found = p 97 break 98 } 99 } 100 return 101 } 102 103 // Return the biggest value in a slice of ints. 104 func max(slice []int) int { 105 max := slice[0] 106 for _, value := range slice { 107 if value > max { 108 max = value 109 } 110 } 111 return max 112 } 113 114 // fetch the most long string of a field of ProcessTable 115 // example: biggest len of string Pid of processes 116 func (pT ProcessTable) MaxLenght(field string) int { 117 slice := make([]int, 0) 118 for _, p := range pT.table { 119 slice = append(slice, len(p.Search(field))) 120 } 121 122 return max(slice) 123 } 124 125 // Defined the each header 126 // Print them pT.headers 127 func (pT ProcessTable) PrintHeader() { 128 var row string 129 for index, field := range pT.headers { 130 formated := pT.fstring[index] 131 row += fmt.Sprintf(formated, field) 132 } 133 134 fmt.Printf("%v\n", row) 135 } 136 137 // Print an single processing for defined fields 138 // by ith-position on table slice of ProcessTable 139 func (pT ProcessTable) PrintProcess(index int) { 140 var row string 141 p := pT.table[index] 142 for index, f := range pT.fields { 143 field := p.Search(f) 144 formated := pT.fstring[index] 145 row += fmt.Sprintf(formated, field) 146 147 } 148 149 fmt.Printf("%v\n", row) 150 } 151 152 func (pT *ProcessTable) PrepareString() { 153 var ( 154 fstring []string 155 formated string 156 PID = pT.MaxLenght("Pid") 157 TTY = pT.MaxLenght("Ctty") 158 STAT = 4 | pT.MaxLenght("State") // min : 4 159 TIME = pT.MaxLenght("Time") 160 CMD = pT.MaxLenght("Cmd") 161 ) 162 for _, f := range pT.headers { 163 switch f { 164 case "PID": 165 formated = fmt.Sprintf("%%%dv ", PID) 166 case "TTY": 167 formated = fmt.Sprintf("%%-%dv ", TTY) 168 case "STAT": 169 formated = fmt.Sprintf("%%-%dv ", STAT) 170 case "TIME": 171 formated = fmt.Sprintf("%%%dv ", TIME) 172 case "CMD": 173 formated = fmt.Sprintf("%%-%dv ", CMD) 174 } 175 fstring = append(fstring, formated) 176 } 177 178 pT.fstring = fstring 179 } 180 181 func isPermutation(check string, ref string) bool { 182 if len(check) != len(ref) { 183 return false 184 } 185 checkArray := strings.Split(check, "") 186 refArray := strings.Split(ref, "") 187 188 sort.Strings(checkArray) 189 sort.Strings(refArray) 190 191 for i := range check { 192 if checkArray[i] != refArray[i] { 193 return false 194 } 195 } 196 return true 197 } 198 199 // For now, just read /proc/pid/stat and dump its brains. 200 // TODO: a nice clean way to turn /proc/pid/stat into a struct. (trying now) 201 // There has to be a way. 202 func ps(pT ProcessTable) error { 203 // sorting ProcessTable by PID 204 sort.Sort(pT) 205 206 switch { 207 case flags.aux: 208 pT.headers = []string{"PID", "PGRP", "SID", "TTY", "STAT", "TIME", "COMMAND"} 209 pT.fields = []string{"Pid", "Pgrp", "Sid", "Ctty", "State", "Time", "Cmd"} 210 case flags.x: 211 pT.headers = []string{"PID", "TTY", "STAT", "TIME", "COMMAND"} 212 pT.fields = []string{"Pid", "Ctty", "State", "Time", "Cmd"} 213 default: 214 pT.headers = []string{"PID", "TTY", "TIME", "CMD"} 215 pT.fields = []string{"Pid", "Ctty", "Time", "Cmd"} 216 } 217 218 mProc := pT.GetProcess(mainPID) 219 220 pT.PrepareString() 221 pT.PrintHeader() 222 for index, p := range pT.table { 223 uid, err := p.GetUid() 224 if err != nil { 225 // It is extremely common for a directory to disappear from 226 // /proc when a process terminates, so ignore those errors. 227 if os.IsNotExist(err) { 228 continue 229 } 230 return err 231 } 232 233 switch { 234 case flags.nSidTty: 235 // no session leaders and no unlinked terminals 236 if p.Sid == p.Pid || p.Ctty == "?" { 237 continue 238 } 239 240 case flags.x: 241 // print only process with same eUID of caller 242 if eUID != uid { 243 continue 244 } 245 246 case flags.all: 247 // pass, print all 248 249 default: 250 // default for no flags only same session 251 // and same uid process 252 if p.Sid != mProc.Sid || eUID != uid { 253 continue 254 } 255 } 256 257 pT.PrintProcess(index) 258 } 259 260 return nil 261 262 } 263 264 func main() { 265 flag.Parse() 266 pT := ProcessTable{} 267 if err := pT.LoadTable(); err != nil { 268 log.Fatal(err) 269 } 270 271 err := ps(pT) 272 if err != nil { 273 log.Fatal(err) 274 } 275 }