gopkg.in/hugelgupf/u-root.v9@v9.0.0-20180831063832-3f6f1057f09b/cmds/ps/ps_linux.go (about)

     1  // Copyright 2016 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  	"os"
    11  	"path/filepath"
    12  	"reflect"
    13  	"strconv"
    14  	"strings"
    15  )
    16  
    17  const (
    18  	proc    = "/proc"
    19  	USER_HZ = 100
    20  )
    21  
    22  // Portable way to implement ps cross-plataform
    23  // Like the os.File
    24  type Process struct {
    25  	process
    26  }
    27  
    28  // table content of stat file defined by:
    29  // https://www.kernel.org/doc/Documentation/filesystems/proc.txt (2009)
    30  // Section (ctrl + f) : Table 1-4: Contents of the stat files (as of 2.6.30-rc7)
    31  type process struct {
    32  	Pid         string // process id
    33  	Cmd         string // filename of the executable
    34  	State       string // state (R is running, S is sleeping, D is sleeping in an uninterruptible wait, Z is zombie, T is traced or stopped)
    35  	Ppid        string // process id of the parent process
    36  	Pgrp        string // pgrp of the process
    37  	Sid         string // session id
    38  	TTYNr       string // tty the process uses
    39  	TTYPgrp     string // pgrp of the tty
    40  	Flags       string // task flags
    41  	MinFlt      string // number of minor faults
    42  	CminFlt     string // number of minor faults with child's
    43  	MajFlt      string // number of major faults
    44  	CmajFlt     string // number of major faults with child's
    45  	Utime       string // user mode jiffies
    46  	Stime       string // kernel mode jiffies
    47  	Cutime      string // user mode jiffies with child's
    48  	Cstime      string // kernel mode jiffies with child's
    49  	Priority    string // priority level
    50  	Nice        string // nice level
    51  	NumThreads  string // number of threads
    52  	ItRealValue string // (obsolete, always 0)
    53  	StartTime   string // time the process started after system boot
    54  	Vsize       string // virtual memory size
    55  	Rss         string // resident set memory size
    56  	Rsslim      string // current limit in bytes on the rss
    57  	StartCode   string // address above which program text can run
    58  	EndCode     string // address below which program text can run
    59  	StartStack  string // address of the start of the main process stack
    60  	Esp         string // current value of ESP
    61  	Eip         string // current value of EIP
    62  	Pending     string // bitmap of pending signals
    63  	Blocked     string // bitmap of blocked signals
    64  	Sigign      string // bitmap of ignored signals
    65  	Sigcatch    string // bitmap of caught signals
    66  	Wchan       string // place holder, used to be the wchan address, use /proc/PID/wchan
    67  	Zero1       string // ignored
    68  	Zero2       string // ignored
    69  	ExitSignal  string // signal to send to parent thread on exit
    70  	TaskCpu     string // which CPU the task is scheduled on
    71  	RtPriority  string // realtime priority
    72  	Policy      string // scheduling policy (man sched_setscheduler)
    73  	BlkioTicks  string // time spent waiting for block IO
    74  	Gtime       string // guest time of the task in jiffies
    75  	Cgtime      string // guest time of the task children in jiffies
    76  	StartData   string // address above which program data+bss is placed
    77  	EndData     string // address below which program data+bss is placed
    78  	StartBrk    string // address above which program heap can be expanded with brk()
    79  	ArgStart    string // address above which program command line is placed
    80  	ArgEnd      string // address below which program command line is placed
    81  	EnvStart    string // address above which program environment is placed
    82  	EnvEnd      string // address below which program environment is placed
    83  	ExitCode    string // the thread's exit_code in the form reported by the waitpid system call (end of stat)
    84  	Ctty        string // extra member (don't parsed from stat)
    85  	Time        string // extra member (don't parsed from stat)
    86  }
    87  
    88  // Parse all content of stat to a Process Struct
    89  // by gived the pid (linux)
    90  func (p *process) readStat(pid int) error {
    91  	b, err := ioutil.ReadFile(filepath.Join(proc, fmt.Sprint(pid), "stat"))
    92  
    93  	// We prefer to use os.ErrNotExist in this case.
    94  	// It is more universal.
    95  	if err != nil && err.Error() == "no such process" {
    96  		err = os.ErrNotExist
    97  	}
    98  
    99  	if err != nil {
   100  		return err
   101  	}
   102  
   103  	fields := strings.Split(string(b), " ")
   104  
   105  	// set struct fields from stat file data
   106  	v := reflect.ValueOf(p).Elem()
   107  	for i := 0; i < len(fields); i++ {
   108  		fieldVal := v.Field(i)
   109  		fieldVal.Set(reflect.ValueOf(fields[i]))
   110  	}
   111  
   112  	p.Time = p.getTime()
   113  	p.Ctty = p.getCtty()
   114  	cmd := p.Cmd
   115  	p.Cmd = cmd[1 : len(cmd)-1]
   116  	if flags.x && false {
   117  		// disable that, because after removed the max width limit
   118  		// we had some incredible long cmd lines whose breaks the
   119  		// visual table of process at running ps
   120  		cmdline, err := p.longCmdLine()
   121  		if err != nil {
   122  			return err
   123  		}
   124  		if cmdline != "" {
   125  			p.Cmd = cmdline
   126  		}
   127  	}
   128  
   129  	return nil
   130  }
   131  
   132  // Fetch data from Operating System about process
   133  // on Linux read data from stat
   134  func (p *Process) Parse(pid int) error {
   135  	return p.process.readStat(pid)
   136  }
   137  
   138  // ctty returns the ctty or "?" if none can be found.
   139  // TODO: an right way to get ctty by p.TTYNr and p.TTYPgrp
   140  func (p process) getCtty() string {
   141  	if tty, err := os.Readlink(filepath.Join(proc, p.Pid, "fd/0")); err != nil {
   142  		return "?"
   143  	} else if p.TTYPgrp != "-1" {
   144  		if len(tty) > 5 && tty[:5] == "/dev/" {
   145  			tty = tty[5:]
   146  		}
   147  		return tty
   148  	}
   149  	return "?"
   150  }
   151  
   152  // Get a named field of stat type
   153  // e.g.: p.getField("Pid") => '1'
   154  func (p *process) getField(field string) string {
   155  	v := reflect.ValueOf(p).Elem()
   156  	return fmt.Sprintf("%v", v.FieldByName(field))
   157  }
   158  
   159  // Search for attributes about the process
   160  func (p Process) Search(field string) string {
   161  	return p.process.getField(field)
   162  }
   163  
   164  // read UID of process based on or
   165  func (p process) getUid() (int, error) {
   166  	b, err := ioutil.ReadFile(filepath.Join(proc, p.Pid, "status"))
   167  	if err != nil && err.Error() == "no such process" {
   168  		err = os.ErrNotExist
   169  	}
   170  
   171  	var uid int
   172  	lines := strings.Split(string(b), "\n")
   173  	for _, line := range lines {
   174  		if strings.Contains(line, "Uid") {
   175  			fields := strings.Split(line, "\t")
   176  			uid, err = strconv.Atoi(fields[1])
   177  			break
   178  		}
   179  	}
   180  
   181  	return uid, err
   182  
   183  }
   184  
   185  func (p Process) GetUid() (int, error) {
   186  	return p.process.getUid()
   187  }
   188  
   189  // change p.Cmd to long command line with args
   190  func (p process) longCmdLine() (string, error) {
   191  	b, err := ioutil.ReadFile(filepath.Join(proc, p.Pid, "cmdline"))
   192  
   193  	if err != nil {
   194  		return "", err
   195  	}
   196  
   197  	return string(b), nil
   198  }
   199  
   200  // Get total time stat formated hh:mm:ss
   201  func (p process) getTime() string {
   202  	utime, _ := strconv.Atoi(p.Utime)
   203  	stime, _ := strconv.Atoi(p.Stime)
   204  	jiffies := utime + stime
   205  
   206  	tsecs := jiffies / USER_HZ
   207  	secs := tsecs % 60
   208  	mins := (tsecs / 60) % 60
   209  	hrs := tsecs / 3600
   210  
   211  	return fmt.Sprintf("%02d:%02d:%02d", hrs, mins, secs)
   212  }
   213  
   214  // Create a ProcessTable containing stats on all processes.
   215  func (pT *ProcessTable) LoadTable() error {
   216  	// Open and Readdir /proc.
   217  	f, err := os.Open("/proc")
   218  	defer f.Close()
   219  	if err != nil {
   220  		return err
   221  	}
   222  	list, err := f.Readdir(-1)
   223  	if err != nil {
   224  		return err
   225  	}
   226  	if len(list) == 0 {
   227  		return fmt.Errorf("No processes in /proc.")
   228  	}
   229  
   230  	for _, dir := range list {
   231  		// Filter out files and directories which are not numbers.
   232  		pid, err := strconv.Atoi(dir.Name())
   233  		if err != nil {
   234  			continue
   235  		}
   236  
   237  		// Parse the process's stat file.
   238  		p := &Process{}
   239  		if err := p.Parse(pid); err != nil {
   240  			// It is extremely common for a directory to disappear from
   241  			// /proc when a process terminates, so ignore those errors.
   242  			if os.IsNotExist(err) {
   243  				continue
   244  			}
   245  			return err
   246  		}
   247  		pT.table = append(pT.table, p)
   248  	}
   249  
   250  	return nil
   251  }