github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/cmds/core/ps/ps.go (about) 1 // Copyright 2013-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 // 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 "fmt" 25 flag "github.com/spf13/pflag" 26 "io" 27 "log" 28 "os" 29 "sort" 30 ) 31 32 var ( 33 flags struct { 34 all bool 35 nSidTty bool 36 x bool 37 aux bool 38 } 39 cmd = "ps [-Aaex] [aux]" 40 eUID = os.Geteuid() 41 ) 42 43 func init() { 44 defUsage := flag.Usage 45 flag.Usage = func() { 46 os.Args[0] = cmd 47 defUsage() 48 } 49 flag.BoolVarP(&flags.all, "All", "A", false, "Select all processes. Identical to -e.") 50 flag.BoolVarP(&flags.all, "every", "e", false, "Select all processes. Identical to -A.") 51 flag.BoolVarP(&flags.x, "bsd", "x", false, "BSD-Like style, with STAT Column and long CommandLine") 52 flag.BoolVarP(&flags.nSidTty, "nSIDTTY", "a", false, "Print all process except whose are session leaders or unlinked with terminal") 53 } 54 55 // ProcessTable holds all the information needed for ps 56 type ProcessTable struct { 57 table []*Process 58 mProc *Process 59 headers []string // each column to print 60 fields []string // which fields of process to print, on order 61 fstring []string // formated strings 62 } 63 64 // NewProcessTable creates an empty process table 65 func NewProcessTable() *ProcessTable { 66 return &ProcessTable{} 67 } 68 69 // Len returns the number of processes in the ProcessTable. 70 func (pT ProcessTable) Len() int { 71 return len(pT.table) 72 } 73 74 // to use on sort.Sort 75 func (pT ProcessTable) Less(i, j int) bool { 76 return pT.table[i].Pidno < pT.table[j].Pidno 77 } 78 79 // to use on sort.Sort 80 func (pT ProcessTable) Swap(i, j int) { 81 pT.table[i], pT.table[j] = pT.table[j], pT.table[i] 82 } 83 84 // Return the biggest value in a slice of ints. 85 func max(slice []int) int { 86 max := slice[0] 87 for _, value := range slice { 88 if value > max { 89 max = value 90 } 91 } 92 return max 93 } 94 95 // MaxLength returns the longest string of a field of ProcessTable 96 func (pT ProcessTable) MaxLength(field string) int { 97 slice := make([]int, 0) 98 for _, p := range pT.table { 99 slice = append(slice, len(p.Search(field))) 100 } 101 102 return max(slice) 103 } 104 105 // PrintHeader prints the header for ps, with correct spacing. 106 func (pT ProcessTable) PrintHeader(w io.Writer) { 107 var row string 108 for index, field := range pT.headers { 109 formated := pT.fstring[index] 110 row += fmt.Sprintf(formated, field) 111 } 112 113 fmt.Fprintf(w, "%v\n", row) 114 } 115 116 // PrintProcess prints information about one process. 117 func (pT ProcessTable) PrintProcess(index int, w io.Writer) { 118 var row string 119 p := pT.table[index] 120 for index, f := range pT.fields { 121 field := p.Search(f) 122 formated := pT.fstring[index] 123 row += fmt.Sprintf(formated, field) 124 125 } 126 127 fmt.Fprintf(w, "%v\n", row) 128 } 129 130 // PrepareString figures out how to lay out a process table print 131 func (pT *ProcessTable) PrepareString() { 132 var ( 133 fstring []string 134 formated string 135 PID = pT.MaxLength("Pid") 136 TTY = pT.MaxLength("Ctty") 137 STAT = 4 | pT.MaxLength("State") // min : 4 138 TIME = pT.MaxLength("Time") 139 CMD = pT.MaxLength("Cmd") 140 ) 141 for _, f := range pT.headers { 142 switch f { 143 case "PID": 144 formated = fmt.Sprintf("%%%dv ", PID) 145 case "TTY": 146 formated = fmt.Sprintf("%%-%dv ", TTY) 147 case "STAT": 148 formated = fmt.Sprintf("%%-%dv ", STAT) 149 case "TIME": 150 formated = fmt.Sprintf("%%%dv ", TIME) 151 case "CMD": 152 formated = fmt.Sprintf("%%-%dv ", CMD) 153 } 154 fstring = append(fstring, formated) 155 } 156 157 pT.fstring = fstring 158 } 159 160 // For now, just read /proc/pid/stat and dump its brains. 161 func ps(pT *ProcessTable, w io.Writer) error { 162 if len(pT.table) == 0 { 163 return nil 164 } 165 // sorting ProcessTable by PID 166 sort.Sort(pT) 167 168 switch { 169 case flags.aux: 170 pT.headers = []string{"PID", "PGRP", "SID", "TTY", "STAT", "TIME", "COMMAND"} 171 pT.fields = []string{"Pid", "Pgrp", "Sid", "Ctty", "State", "Time", "Cmd"} 172 case flags.x: 173 pT.headers = []string{"PID", "TTY", "STAT", "TIME", "COMMAND"} 174 pT.fields = []string{"Pid", "Ctty", "State", "Time", "Cmd"} 175 default: 176 pT.headers = []string{"PID", "TTY", "TIME", "CMD"} 177 pT.fields = []string{"Pid", "Ctty", "Time", "Cmd"} 178 } 179 180 pT.PrepareString() 181 pT.PrintHeader(w) 182 for index, p := range pT.table { 183 switch { 184 case flags.nSidTty: 185 // no session leaders and no unlinked terminals 186 if p.Sid == p.Pid || p.Ctty == "?" { 187 continue 188 } 189 190 case flags.x: 191 // print only process with same eUID of caller 192 if eUID != p.uid { 193 continue 194 } 195 196 case flags.all: 197 // pass, print all 198 199 default: 200 // default for no flags only same session 201 // and same uid process 202 if p.Sid != pT.mProc.Sid || eUID != p.uid { 203 continue 204 } 205 } 206 207 pT.PrintProcess(index, w) 208 } 209 210 return nil 211 212 } 213 214 func usage() { 215 log.Printf("Uage: ps [flags] [aux]") 216 flag.Usage() 217 os.Exit(1) 218 } 219 220 func main() { 221 flag.Parse() 222 // The original ps was designed before many flag conventions existed. 223 // It had switchwes not needing a -. Try to emulate that. 224 // It's pretty awful, however :-) 225 for _, a := range flag.Args() { 226 switch a { 227 case "aux": 228 flags.all, flags.aux = true, true 229 default: 230 usage() 231 } 232 } 233 pT := NewProcessTable() 234 if err := pT.LoadTable(); err != nil { 235 log.Fatal(err) 236 } 237 238 err := ps(pT, os.Stderr) 239 if err != nil { 240 log.Fatal(err) 241 } 242 }