github.com/ratrocket/u-root@v0.0.0-20180201221235-1cf9f48ee2cf/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  	if err != nil {
    93  		return err
    94  	}
    95  
    96  	fields := strings.Split(string(b), " ")
    97  
    98  	// set struct fields from stat file data
    99  	v := reflect.ValueOf(p).Elem()
   100  	for i := 0; i < len(fields); i++ {
   101  		fieldVal := v.Field(i)
   102  		fieldVal.Set(reflect.ValueOf(fields[i]))
   103  	}
   104  
   105  	p.Time = p.getTime()
   106  	p.Ctty = p.getCtty()
   107  	cmd := p.Cmd
   108  	p.Cmd = cmd[1 : len(cmd)-1]
   109  	if flags.x && false {
   110  		// disable that, because after removed the max width limit
   111  		// we had some incredible long cmd lines whose breaks the
   112  		// visual table of process at running ps
   113  		cmdline, err := p.longCmdLine()
   114  		if err != nil {
   115  			return err
   116  		}
   117  		if cmdline != "" {
   118  			p.Cmd = cmdline
   119  		}
   120  	}
   121  
   122  	return nil
   123  }
   124  
   125  // Fetch data from Operating System about process
   126  // on Linux read data from stat
   127  func (p *Process) Parse(pid int) error {
   128  	return p.process.readStat(pid)
   129  }
   130  
   131  // ctty returns the ctty or "?" if none can be found.
   132  // TODO: an right way to get ctty by p.TTYNr and p.TTYPgrp
   133  func (p process) getCtty() string {
   134  	if tty, err := os.Readlink(filepath.Join(proc, p.Pid, "fd/0")); err != nil {
   135  		return "?"
   136  	} else if p.TTYPgrp != "-1" {
   137  		if len(tty) > 5 && tty[:5] == "/dev/" {
   138  			tty = tty[5:]
   139  		}
   140  		return tty
   141  	}
   142  	return "?"
   143  }
   144  
   145  // Get a named field of stat type
   146  // e.g.: p.getField("Pid") => '1'
   147  func (p *process) getField(field string) string {
   148  	v := reflect.ValueOf(p).Elem()
   149  	return fmt.Sprintf("%v", v.FieldByName(field))
   150  }
   151  
   152  // Search for attributes about the process
   153  func (p Process) Search(field string) string {
   154  	return p.process.getField(field)
   155  }
   156  
   157  // read UID of process based on or
   158  func (p process) getUid() (int, error) {
   159  	b, err := ioutil.ReadFile(filepath.Join(proc, p.Pid, "status"))
   160  
   161  	var uid int
   162  	lines := strings.Split(string(b), "\n")
   163  	for _, line := range lines {
   164  		if strings.Contains(line, "Uid") {
   165  			fields := strings.Split(line, "\t")
   166  			uid, err = strconv.Atoi(fields[1])
   167  			break
   168  		}
   169  	}
   170  
   171  	return uid, err
   172  
   173  }
   174  
   175  func (p Process) GetUid() (int, error) {
   176  	return p.process.getUid()
   177  }
   178  
   179  // change p.Cmd to long command line with args
   180  func (p process) longCmdLine() (string, error) {
   181  	b, err := ioutil.ReadFile(filepath.Join(proc, p.Pid, "cmdline"))
   182  
   183  	if err != nil {
   184  		return "", err
   185  	}
   186  
   187  	return string(b), nil
   188  }
   189  
   190  // Get total time stat formated hh:mm:ss
   191  func (p process) getTime() string {
   192  	utime, _ := strconv.Atoi(p.Utime)
   193  	stime, _ := strconv.Atoi(p.Stime)
   194  	jiffies := utime + stime
   195  
   196  	tsecs := jiffies / USER_HZ
   197  	secs := tsecs % 60
   198  	mins := (tsecs / 60) % 60
   199  	hrs := tsecs / 3600
   200  
   201  	return fmt.Sprintf("%02d:%02d:%02d", hrs, mins, secs)
   202  }
   203  
   204  // Create a ProcessTable containing stats on all processes.
   205  func (pT *ProcessTable) LoadTable() error {
   206  	// Open and Readdir /proc.
   207  	f, err := os.Open("/proc")
   208  	defer f.Close()
   209  	if err != nil {
   210  		return err
   211  	}
   212  	list, err := f.Readdir(-1)
   213  	if err != nil {
   214  		return err
   215  	}
   216  
   217  	for _, dir := range list {
   218  		// Filter out files and directories which are not numbers.
   219  		pid, err := strconv.Atoi(dir.Name())
   220  		if err != nil {
   221  			continue
   222  		}
   223  
   224  		// Parse the process's stat file.
   225  		p := &Process{}
   226  		if err := p.Parse(pid); err != nil {
   227  			// It is extremely common for a directory to disappear from
   228  			// /proc when a process terminates, so ignore those errors.
   229  			if os.IsNotExist(err) {
   230  				continue
   231  			}
   232  			return err
   233  		}
   234  		pT.table = append(pT.table, p)
   235  	}
   236  
   237  	return nil
   238  }