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  }