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  }