github.com/isyscore/isc-gobase@v1.5.3-0.20231218061332-cbc7451899e9/system/process/process_linux.go (about)

     1  //go:build linux
     2  
     3  package process
     4  
     5  import (
     6  	"bufio"
     7  	"bytes"
     8  	"context"
     9  	"encoding/json"
    10  	"fmt"
    11  	"github.com/isyscore/isc-gobase/system/common"
    12  	"github.com/isyscore/isc-gobase/system/cpu"
    13  	"github.com/isyscore/isc-gobase/system/net"
    14  	"github.com/tklauser/go-sysconf"
    15  	"golang.org/x/sys/unix"
    16  	"io/ioutil"
    17  	"math"
    18  	"os"
    19  	"path/filepath"
    20  	"strconv"
    21  	"strings"
    22  )
    23  
    24  var PageSize = uint64(os.Getpagesize())
    25  
    26  const PrioProcess = 0 // linux/resource.h
    27  
    28  var ClockTicks = 100 // default value
    29  
    30  func init() {
    31  	clkTck, err := sysconf.Sysconf(sysconf.SC_CLK_TCK)
    32  	// ignore errors
    33  	if err == nil {
    34  		ClockTicks = int(clkTck)
    35  	}
    36  }
    37  
    38  // MemoryInfoExStat is different between OSes
    39  type MemoryInfoExStat struct {
    40  	RSS    uint64 `json:"rss"`    // bytes
    41  	VMS    uint64 `json:"vms"`    // bytes
    42  	Shared uint64 `json:"shared"` // bytes
    43  	Text   uint64 `json:"text"`   // bytes
    44  	Lib    uint64 `json:"lib"`    // bytes
    45  	Data   uint64 `json:"data"`   // bytes
    46  	Dirty  uint64 `json:"dirty"`  // bytes
    47  }
    48  
    49  func (m MemoryInfoExStat) String() string {
    50  	s, _ := json.Marshal(m)
    51  	return string(s)
    52  }
    53  
    54  type MemoryMapsStat struct {
    55  	Path         string `json:"path"`
    56  	Rss          uint64 `json:"rss"`
    57  	Size         uint64 `json:"size"`
    58  	Pss          uint64 `json:"pss"`
    59  	SharedClean  uint64 `json:"sharedClean"`
    60  	SharedDirty  uint64 `json:"sharedDirty"`
    61  	PrivateClean uint64 `json:"privateClean"`
    62  	PrivateDirty uint64 `json:"privateDirty"`
    63  	Referenced   uint64 `json:"referenced"`
    64  	Anonymous    uint64 `json:"anonymous"`
    65  	Swap         uint64 `json:"swap"`
    66  }
    67  
    68  // String returns JSON value of the process.
    69  func (m MemoryMapsStat) String() string {
    70  	s, _ := json.Marshal(m)
    71  	return string(s)
    72  }
    73  
    74  func (p *Process) PpidWithContext(ctx context.Context) (int32, error) {
    75  	_, ppid, _, _, _, _, _, err := p.fillFromStatWithContext(ctx)
    76  	if err != nil {
    77  		return -1, err
    78  	}
    79  	return ppid, nil
    80  }
    81  
    82  func (p *Process) NameWithContext(ctx context.Context) (string, error) {
    83  	if p.name == "" {
    84  		if err := p.fillNameWithContext(ctx); err != nil {
    85  			return "", err
    86  		}
    87  	}
    88  	return p.name, nil
    89  }
    90  
    91  func (p *Process) TgidWithContext(ctx context.Context) (int32, error) {
    92  	if p.tgid == 0 {
    93  		if err := p.fillFromStatusWithContext(ctx); err != nil {
    94  			return 0, err
    95  		}
    96  	}
    97  	return p.tgid, nil
    98  }
    99  
   100  func (p *Process) ExeWithContext(ctx context.Context) (string, error) {
   101  	return p.fillFromExeWithContext(ctx)
   102  }
   103  
   104  func (p *Process) CmdlineWithContext(ctx context.Context) (string, error) {
   105  	return p.fillFromCmdlineWithContext(ctx)
   106  }
   107  
   108  func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error) {
   109  	return p.fillSliceFromCmdlineWithContext(ctx)
   110  }
   111  
   112  func (p *Process) createTimeWithContext(ctx context.Context) (int64, error) {
   113  	_, _, _, createTime, _, _, _, err := p.fillFromStatWithContext(ctx)
   114  	if err != nil {
   115  		return 0, err
   116  	}
   117  	return createTime, nil
   118  }
   119  
   120  func (p *Process) CwdWithContext(ctx context.Context) (string, error) {
   121  	return p.fillFromCwdWithContext(ctx)
   122  }
   123  
   124  func (p *Process) ParentWithContext(ctx context.Context) (*Process, error) {
   125  	err := p.fillFromStatusWithContext(ctx)
   126  	if err != nil {
   127  		return nil, err
   128  	}
   129  	if p.parent == 0 {
   130  		return nil, fmt.Errorf("wrong number of parents")
   131  	}
   132  	return NewProcessWithContext(ctx, p.parent)
   133  }
   134  
   135  func (p *Process) StatusWithContext(ctx context.Context) (string, error) {
   136  	err := p.fillFromStatusWithContext(ctx)
   137  	if err != nil {
   138  		return "", err
   139  	}
   140  	return p.status, nil
   141  }
   142  
   143  func (p *Process) ForegroundWithContext(ctx context.Context) (bool, error) {
   144  	pid := p.Pid
   145  	statPath := common.HostProc(strconv.Itoa(int(pid)), "stat")
   146  	contents, err := ioutil.ReadFile(statPath)
   147  	if err != nil {
   148  		return false, err
   149  	}
   150  	fields := strings.Fields(string(contents))
   151  	if len(fields) < 8 {
   152  		return false, fmt.Errorf("insufficient data in %s", statPath)
   153  	}
   154  	pgid := fields[4]
   155  	tpgid := fields[7]
   156  	return pgid == tpgid, nil
   157  }
   158  
   159  func (p *Process) UidsWithContext(ctx context.Context) ([]int32, error) {
   160  	err := p.fillFromStatusWithContext(ctx)
   161  	if err != nil {
   162  		return []int32{}, err
   163  	}
   164  	return p.uids, nil
   165  }
   166  
   167  func (p *Process) GidsWithContext(ctx context.Context) ([]int32, error) {
   168  	err := p.fillFromStatusWithContext(ctx)
   169  	if err != nil {
   170  		return []int32{}, err
   171  	}
   172  	return p.gids, nil
   173  }
   174  
   175  func (p *Process) GroupsWithContext(ctx context.Context) ([]int32, error) {
   176  	err := p.fillFromStatusWithContext(ctx)
   177  	if err != nil {
   178  		return []int32{}, err
   179  	}
   180  	return p.groups, nil
   181  }
   182  
   183  func (p *Process) TerminalWithContext(ctx context.Context) (string, error) {
   184  	t, _, _, _, _, _, _, err := p.fillFromStatWithContext(ctx)
   185  	if err != nil {
   186  		return "", err
   187  	}
   188  	termmap, err := getTerminalMap()
   189  	if err != nil {
   190  		return "", err
   191  	}
   192  	terminal := termmap[t]
   193  	return terminal, nil
   194  }
   195  
   196  func (p *Process) NiceWithContext(ctx context.Context) (int32, error) {
   197  	_, _, _, _, _, nice, _, err := p.fillFromStatWithContext(ctx)
   198  	if err != nil {
   199  		return 0, err
   200  	}
   201  	return nice, nil
   202  }
   203  
   204  func (p *Process) IOniceWithContext(ctx context.Context) (int32, error) {
   205  	return 0, common.ErrNotImplementedError
   206  }
   207  
   208  func (p *Process) RlimitWithContext(ctx context.Context) ([]RlimitStat, error) {
   209  	return p.RlimitUsageWithContext(ctx, false)
   210  }
   211  
   212  func (p *Process) RlimitUsageWithContext(ctx context.Context, gatherUsed bool) ([]RlimitStat, error) {
   213  	rlimits, err := p.fillFromLimitsWithContext(ctx)
   214  	if !gatherUsed || err != nil {
   215  		return rlimits, err
   216  	}
   217  
   218  	_, _, _, _, rtprio, nice, _, err := p.fillFromStatWithContext(ctx)
   219  	if err != nil {
   220  		return nil, err
   221  	}
   222  	if err := p.fillFromStatusWithContext(ctx); err != nil {
   223  		return nil, err
   224  	}
   225  
   226  	for i := range rlimits {
   227  		rs := &rlimits[i]
   228  		switch rs.Resource {
   229  		case RLIMIT_CPU:
   230  			times, err := p.TimesWithContext(ctx)
   231  			if err != nil {
   232  				return nil, err
   233  			}
   234  			rs.Used = uint64(times.User + times.System)
   235  		case RLIMIT_DATA:
   236  			rs.Used = uint64(p.memInfo.Data)
   237  		case RLIMIT_STACK:
   238  			rs.Used = uint64(p.memInfo.Stack)
   239  		case RLIMIT_RSS:
   240  			rs.Used = uint64(p.memInfo.RSS)
   241  		case RLIMIT_NOFILE:
   242  			n, err := p.NumFDsWithContext(ctx)
   243  			if err != nil {
   244  				return nil, err
   245  			}
   246  			rs.Used = uint64(n)
   247  		case RLIMIT_MEMLOCK:
   248  			rs.Used = uint64(p.memInfo.Locked)
   249  		case RLIMIT_AS:
   250  			rs.Used = uint64(p.memInfo.VMS)
   251  		case RLIMIT_LOCKS:
   252  			//TODO we can get the used value from /proc/$pid/locks. But linux doesn't enforce it, so not a high priority.
   253  		case RLIMIT_SIGPENDING:
   254  			rs.Used = p.sigInfo.PendingProcess
   255  		case RLIMIT_NICE:
   256  			// The rlimit for nice is a little unusual, in that 0 means the niceness cannot be decreased beyond the current value, but it can be increased.
   257  			// So effectively: if rs.Soft == 0 { rs.Soft = rs.Used }
   258  			rs.Used = uint64(nice)
   259  		case RLIMIT_RTPRIO:
   260  			rs.Used = uint64(rtprio)
   261  		}
   262  	}
   263  
   264  	return rlimits, err
   265  }
   266  
   267  func (p *Process) IOCountersWithContext(ctx context.Context) (*IOCountersStat, error) {
   268  	return p.fillFromIOWithContext(ctx)
   269  }
   270  
   271  func (p *Process) NumCtxSwitchesWithContext(ctx context.Context) (*NumCtxSwitchesStat, error) {
   272  	err := p.fillFromStatusWithContext(ctx)
   273  	if err != nil {
   274  		return nil, err
   275  	}
   276  	return p.numCtxSwitches, nil
   277  }
   278  
   279  func (p *Process) NumFDsWithContext(ctx context.Context) (int32, error) {
   280  	_, fnames, err := p.fillFromfdListWithContext(ctx)
   281  	return int32(len(fnames)), err
   282  }
   283  
   284  func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) {
   285  	err := p.fillFromStatusWithContext(ctx)
   286  	if err != nil {
   287  		return 0, err
   288  	}
   289  	return p.numThreads, nil
   290  }
   291  
   292  func (p *Process) ThreadsWithContext(ctx context.Context) (map[int32]*cpu.TimesStat, error) {
   293  	ret := make(map[int32]*cpu.TimesStat)
   294  	taskPath := common.HostProc(strconv.Itoa(int(p.Pid)), "task")
   295  
   296  	tids, err := readPidsFromDir(taskPath)
   297  	if err != nil {
   298  		return nil, err
   299  	}
   300  
   301  	for _, tid := range tids {
   302  		_, _, cpuTimes, _, _, _, _, err := p.fillFromTIDStatWithContext(ctx, tid)
   303  		if err != nil {
   304  			return nil, err
   305  		}
   306  		ret[tid] = cpuTimes
   307  	}
   308  
   309  	return ret, nil
   310  }
   311  
   312  func (p *Process) TimesWithContext(ctx context.Context) (*cpu.TimesStat, error) {
   313  	_, _, cpuTimes, _, _, _, _, err := p.fillFromStatWithContext(ctx)
   314  	if err != nil {
   315  		return nil, err
   316  	}
   317  	return cpuTimes, nil
   318  }
   319  
   320  func (p *Process) CPUAffinityWithContext(ctx context.Context) ([]int32, error) {
   321  	return nil, common.ErrNotImplementedError
   322  }
   323  
   324  func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, error) {
   325  	meminfo, _, err := p.fillFromStatmWithContext(ctx)
   326  	if err != nil {
   327  		return nil, err
   328  	}
   329  	return meminfo, nil
   330  }
   331  
   332  func (p *Process) MemoryInfoExWithContext(ctx context.Context) (*MemoryInfoExStat, error) {
   333  	_, memInfoEx, err := p.fillFromStatmWithContext(ctx)
   334  	if err != nil {
   335  		return nil, err
   336  	}
   337  	return memInfoEx, nil
   338  }
   339  
   340  func (p *Process) PageFaultsWithContext(ctx context.Context) (*PageFaultsStat, error) {
   341  	_, _, _, _, _, _, pageFaults, err := p.fillFromStatWithContext(ctx)
   342  	if err != nil {
   343  		return nil, err
   344  	}
   345  	return pageFaults, nil
   346  
   347  }
   348  
   349  func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) {
   350  	pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid)
   351  	if err != nil {
   352  		if len(pids) == 0 {
   353  			return nil, ErrorNoChildren
   354  		}
   355  		return nil, err
   356  	}
   357  	ret := make([]*Process, 0, len(pids))
   358  	for _, pid := range pids {
   359  		np, err := NewProcessWithContext(ctx, pid)
   360  		if err != nil {
   361  			return nil, err
   362  		}
   363  		ret = append(ret, np)
   364  	}
   365  	return ret, nil
   366  }
   367  
   368  func (p *Process) OpenFilesWithContext(ctx context.Context) ([]OpenFilesStat, error) {
   369  	_, ofs, err := p.fillFromfdWithContext(ctx)
   370  	if err != nil {
   371  		return nil, err
   372  	}
   373  	ret := make([]OpenFilesStat, len(ofs))
   374  	for i, o := range ofs {
   375  		ret[i] = *o
   376  	}
   377  
   378  	return ret, nil
   379  }
   380  
   381  func (p *Process) ConnectionsWithContext(ctx context.Context) ([]net.ConnectionStat, error) {
   382  	return net.ConnectionsPidWithContext(ctx, "all", p.Pid)
   383  }
   384  
   385  func (p *Process) ConnectionsMaxWithContext(ctx context.Context, max int) ([]net.ConnectionStat, error) {
   386  	return net.ConnectionsPidMaxWithContext(ctx, "all", p.Pid, max)
   387  }
   388  
   389  func (p *Process) NetIOCountersWithContext(ctx context.Context, pernic bool) ([]net.IOCountersStat, error) {
   390  	filename := common.HostProc(strconv.Itoa(int(p.Pid)), "net/dev")
   391  	return net.IOCountersByFileWithContext(ctx, pernic, filename)
   392  }
   393  
   394  func (p *Process) MemoryMapsWithContext(ctx context.Context, grouped bool) (*[]MemoryMapsStat, error) {
   395  	pid := p.Pid
   396  	var ret []MemoryMapsStat
   397  	if grouped {
   398  		ret = make([]MemoryMapsStat, 1)
   399  	}
   400  	smapsPath := common.HostProc(strconv.Itoa(int(pid)), "smaps")
   401  	contents, err := ioutil.ReadFile(smapsPath)
   402  	if err != nil {
   403  		return nil, err
   404  	}
   405  	lines := strings.Split(string(contents), "\n")
   406  
   407  	// function of parsing a block
   408  	getBlock := func(firstLine []string, block []string) (MemoryMapsStat, error) {
   409  		m := MemoryMapsStat{}
   410  		m.Path = firstLine[len(firstLine)-1]
   411  
   412  		for _, line := range block {
   413  			if strings.Contains(line, "VmFlags") {
   414  				continue
   415  			}
   416  			field := strings.Split(line, ":")
   417  			if len(field) < 2 {
   418  				continue
   419  			}
   420  			v := strings.Trim(field[1], "kB") // remove last "kB"
   421  			v = strings.TrimSpace(v)
   422  			t, err := strconv.ParseUint(v, 10, 64)
   423  			if err != nil {
   424  				return m, err
   425  			}
   426  
   427  			switch field[0] {
   428  			case "Size":
   429  				m.Size = t
   430  			case "Rss":
   431  				m.Rss = t
   432  			case "Pss":
   433  				m.Pss = t
   434  			case "Shared_Clean":
   435  				m.SharedClean = t
   436  			case "Shared_Dirty":
   437  				m.SharedDirty = t
   438  			case "Private_Clean":
   439  				m.PrivateClean = t
   440  			case "Private_Dirty":
   441  				m.PrivateDirty = t
   442  			case "Referenced":
   443  				m.Referenced = t
   444  			case "Anonymous":
   445  				m.Anonymous = t
   446  			case "Swap":
   447  				m.Swap = t
   448  			}
   449  		}
   450  		return m, nil
   451  	}
   452  
   453  	var firstLine []string
   454  	blocks := make([]string, 0, 16)
   455  	for i, line := range lines {
   456  		fields := strings.Fields(line)
   457  
   458  		if (len(fields) > 0 && !strings.HasSuffix(fields[0], ":")) || i == len(lines)-1 {
   459  			// new block section
   460  			if len(firstLine) > 0 && len(blocks) > 0 {
   461  				g, err := getBlock(firstLine, blocks)
   462  				if err != nil {
   463  					return &ret, err
   464  				}
   465  				if grouped {
   466  					ret[0].Size += g.Size
   467  					ret[0].Rss += g.Rss
   468  					ret[0].Pss += g.Pss
   469  					ret[0].SharedClean += g.SharedClean
   470  					ret[0].SharedDirty += g.SharedDirty
   471  					ret[0].PrivateClean += g.PrivateClean
   472  					ret[0].PrivateDirty += g.PrivateDirty
   473  					ret[0].Referenced += g.Referenced
   474  					ret[0].Anonymous += g.Anonymous
   475  					ret[0].Swap += g.Swap
   476  				} else {
   477  					ret = append(ret, g)
   478  				}
   479  			}
   480  			// starts new block
   481  			blocks = make([]string, 0, 16)
   482  			firstLine = fields
   483  		} else {
   484  			blocks = append(blocks, line)
   485  		}
   486  	}
   487  
   488  	return &ret, nil
   489  }
   490  
   491  func (p *Process) EnvironWithContext(ctx context.Context) ([]string, error) {
   492  	environPath := common.HostProc(strconv.Itoa(int(p.Pid)), "environ")
   493  
   494  	environContent, err := ioutil.ReadFile(environPath)
   495  	if err != nil {
   496  		return nil, err
   497  	}
   498  
   499  	return strings.Split(string(environContent), "\000"), nil
   500  }
   501  
   502  /**
   503  ** Internal functions
   504  **/
   505  
   506  func limitToInt(val string) (int32, error) {
   507  	if val == "unlimited" {
   508  		return math.MaxInt32, nil
   509  	} else {
   510  		res, err := strconv.ParseInt(val, 10, 32)
   511  		if err != nil {
   512  			return 0, err
   513  		}
   514  		return int32(res), nil
   515  	}
   516  }
   517  
   518  // Get name from /proc/(pid)/comm or /proc/(pid)/status
   519  func (p *Process) fillNameWithContext(ctx context.Context) error {
   520  	err := p.fillFromCommWithContext(ctx)
   521  	if err == nil && p.name != "" && len(p.name) < 15 {
   522  		return nil
   523  	}
   524  	return p.fillFromStatusWithContext(ctx)
   525  }
   526  
   527  // Get name from /proc/(pid)/comm
   528  func (p *Process) fillFromCommWithContext(ctx context.Context) error {
   529  	pid := p.Pid
   530  	statPath := common.HostProc(strconv.Itoa(int(pid)), "comm")
   531  	contents, err := ioutil.ReadFile(statPath)
   532  	if err != nil {
   533  		return err
   534  	}
   535  
   536  	p.name = strings.TrimSuffix(string(contents), "\n")
   537  	return nil
   538  }
   539  
   540  // Get num_fds from /proc/(pid)/limits
   541  func (p *Process) fillFromLimitsWithContext(ctx context.Context) ([]RlimitStat, error) {
   542  	pid := p.Pid
   543  	limitsFile := common.HostProc(strconv.Itoa(int(pid)), "limits")
   544  	d, err := os.Open(limitsFile)
   545  	if err != nil {
   546  		return nil, err
   547  	}
   548  	defer d.Close()
   549  
   550  	var limitStats []RlimitStat
   551  
   552  	limitsScanner := bufio.NewScanner(d)
   553  	for limitsScanner.Scan() {
   554  		var statItem RlimitStat
   555  
   556  		str := strings.Fields(limitsScanner.Text())
   557  
   558  		// Remove the header line
   559  		if strings.Contains(str[len(str)-1], "Units") {
   560  			continue
   561  		}
   562  
   563  		// Assert that last item is a Hard limit
   564  		statItem.Hard, err = limitToInt(str[len(str)-1])
   565  		if err != nil {
   566  			// On error remove the last item an try once again since it can be unit or header line
   567  			str = str[:len(str)-1]
   568  			statItem.Hard, err = limitToInt(str[len(str)-1])
   569  			if err != nil {
   570  				return nil, err
   571  			}
   572  		}
   573  		// Remove last item from string
   574  		str = str[:len(str)-1]
   575  
   576  		//Now last item is a Soft limit
   577  		statItem.Soft, err = limitToInt(str[len(str)-1])
   578  		if err != nil {
   579  			return nil, err
   580  		}
   581  		// Remove last item from string
   582  		str = str[:len(str)-1]
   583  
   584  		//The rest is a stats name
   585  		resourceName := strings.Join(str, " ")
   586  		switch resourceName {
   587  		case "Max cpu time":
   588  			statItem.Resource = RLIMIT_CPU
   589  		case "Max file size":
   590  			statItem.Resource = RLIMIT_FSIZE
   591  		case "Max data size":
   592  			statItem.Resource = RLIMIT_DATA
   593  		case "Max stack size":
   594  			statItem.Resource = RLIMIT_STACK
   595  		case "Max core file size":
   596  			statItem.Resource = RLIMIT_CORE
   597  		case "Max resident set":
   598  			statItem.Resource = RLIMIT_RSS
   599  		case "Max processes":
   600  			statItem.Resource = RLIMIT_NPROC
   601  		case "Max open files":
   602  			statItem.Resource = RLIMIT_NOFILE
   603  		case "Max locked memory":
   604  			statItem.Resource = RLIMIT_MEMLOCK
   605  		case "Max address space":
   606  			statItem.Resource = RLIMIT_AS
   607  		case "Max file locks":
   608  			statItem.Resource = RLIMIT_LOCKS
   609  		case "Max pending signals":
   610  			statItem.Resource = RLIMIT_SIGPENDING
   611  		case "Max msgqueue size":
   612  			statItem.Resource = RLIMIT_MSGQUEUE
   613  		case "Max nice priority":
   614  			statItem.Resource = RLIMIT_NICE
   615  		case "Max realtime priority":
   616  			statItem.Resource = RLIMIT_RTPRIO
   617  		case "Max realtime timeout":
   618  			statItem.Resource = RLIMIT_RTTIME
   619  		default:
   620  			continue
   621  		}
   622  
   623  		limitStats = append(limitStats, statItem)
   624  	}
   625  
   626  	if err := limitsScanner.Err(); err != nil {
   627  		return nil, err
   628  	}
   629  
   630  	return limitStats, nil
   631  }
   632  
   633  // Get list of /proc/(pid)/fd files
   634  func (p *Process) fillFromfdListWithContext(ctx context.Context) (string, []string, error) {
   635  	pid := p.Pid
   636  	statPath := common.HostProc(strconv.Itoa(int(pid)), "fd")
   637  	d, err := os.Open(statPath)
   638  	if err != nil {
   639  		return statPath, []string{}, err
   640  	}
   641  	defer d.Close()
   642  	fnames, err := d.Readdirnames(-1)
   643  	return statPath, fnames, err
   644  }
   645  
   646  // Get num_fds from /proc/(pid)/fd
   647  func (p *Process) fillFromfdWithContext(ctx context.Context) (int32, []*OpenFilesStat, error) {
   648  	statPath, fnames, err := p.fillFromfdListWithContext(ctx)
   649  	if err != nil {
   650  		return 0, nil, err
   651  	}
   652  	numFDs := int32(len(fnames))
   653  
   654  	var openfiles []*OpenFilesStat
   655  	for _, fd := range fnames {
   656  		fpath := filepath.Join(statPath, fd)
   657  		filepath, err := os.Readlink(fpath)
   658  		if err != nil {
   659  			continue
   660  		}
   661  		t, err := strconv.ParseUint(fd, 10, 64)
   662  		if err != nil {
   663  			return numFDs, openfiles, err
   664  		}
   665  		o := &OpenFilesStat{
   666  			Path: filepath,
   667  			Fd:   t,
   668  		}
   669  		openfiles = append(openfiles, o)
   670  	}
   671  
   672  	return numFDs, openfiles, nil
   673  }
   674  
   675  // Get cwd from /proc/(pid)/cwd
   676  func (p *Process) fillFromCwdWithContext(ctx context.Context) (string, error) {
   677  	pid := p.Pid
   678  	cwdPath := common.HostProc(strconv.Itoa(int(pid)), "cwd")
   679  	cwd, err := os.Readlink(cwdPath)
   680  	if err != nil {
   681  		return "", err
   682  	}
   683  	return string(cwd), nil
   684  }
   685  
   686  // Get exe from /proc/(pid)/exe
   687  func (p *Process) fillFromExeWithContext(ctx context.Context) (string, error) {
   688  	pid := p.Pid
   689  	exePath := common.HostProc(strconv.Itoa(int(pid)), "exe")
   690  	exe, err := os.Readlink(exePath)
   691  	if err != nil {
   692  		return "", err
   693  	}
   694  	return string(exe), nil
   695  }
   696  
   697  // Get cmdline from /proc/(pid)/cmdline
   698  func (p *Process) fillFromCmdlineWithContext(ctx context.Context) (string, error) {
   699  	pid := p.Pid
   700  	cmdPath := common.HostProc(strconv.Itoa(int(pid)), "cmdline")
   701  	cmdline, err := ioutil.ReadFile(cmdPath)
   702  	if err != nil {
   703  		return "", err
   704  	}
   705  	ret := strings.FieldsFunc(string(cmdline), func(r rune) bool {
   706  		return r == '\u0000'
   707  	})
   708  
   709  	return strings.Join(ret, " "), nil
   710  }
   711  
   712  func (p *Process) fillSliceFromCmdlineWithContext(ctx context.Context) ([]string, error) {
   713  	pid := p.Pid
   714  	cmdPath := common.HostProc(strconv.Itoa(int(pid)), "cmdline")
   715  	cmdline, err := ioutil.ReadFile(cmdPath)
   716  	if err != nil {
   717  		return nil, err
   718  	}
   719  	if len(cmdline) == 0 {
   720  		return nil, nil
   721  	}
   722  	if cmdline[len(cmdline)-1] == 0 {
   723  		cmdline = cmdline[:len(cmdline)-1]
   724  	}
   725  	parts := bytes.Split(cmdline, []byte{0})
   726  	var strParts []string
   727  	for _, p := range parts {
   728  		strParts = append(strParts, string(p))
   729  	}
   730  
   731  	return strParts, nil
   732  }
   733  
   734  // Get IO status from /proc/(pid)/io
   735  func (p *Process) fillFromIOWithContext(ctx context.Context) (*IOCountersStat, error) {
   736  	pid := p.Pid
   737  	ioPath := common.HostProc(strconv.Itoa(int(pid)), "io")
   738  	ioline, err := ioutil.ReadFile(ioPath)
   739  	if err != nil {
   740  		return nil, err
   741  	}
   742  	lines := strings.Split(string(ioline), "\n")
   743  	ret := &IOCountersStat{}
   744  
   745  	for _, line := range lines {
   746  		field := strings.Fields(line)
   747  		if len(field) < 2 {
   748  			continue
   749  		}
   750  		t, err := strconv.ParseUint(field[1], 10, 64)
   751  		if err != nil {
   752  			return nil, err
   753  		}
   754  		param := field[0]
   755  		if strings.HasSuffix(param, ":") {
   756  			param = param[:len(param)-1]
   757  		}
   758  		switch param {
   759  		case "syscr":
   760  			ret.ReadCount = t
   761  		case "syscw":
   762  			ret.WriteCount = t
   763  		case "read_bytes":
   764  			ret.ReadBytes = t
   765  		case "write_bytes":
   766  			ret.WriteBytes = t
   767  		}
   768  	}
   769  
   770  	return ret, nil
   771  }
   772  
   773  // Get memory info from /proc/(pid)/statm
   774  func (p *Process) fillFromStatmWithContext(ctx context.Context) (*MemoryInfoStat, *MemoryInfoExStat, error) {
   775  	pid := p.Pid
   776  	memPath := common.HostProc(strconv.Itoa(int(pid)), "statm")
   777  	contents, err := ioutil.ReadFile(memPath)
   778  	if err != nil {
   779  		return nil, nil, err
   780  	}
   781  	fields := strings.Split(string(contents), " ")
   782  
   783  	vms, err := strconv.ParseUint(fields[0], 10, 64)
   784  	if err != nil {
   785  		return nil, nil, err
   786  	}
   787  	rss, err := strconv.ParseUint(fields[1], 10, 64)
   788  	if err != nil {
   789  		return nil, nil, err
   790  	}
   791  	memInfo := &MemoryInfoStat{
   792  		RSS: rss * PageSize,
   793  		VMS: vms * PageSize,
   794  	}
   795  
   796  	shared, err := strconv.ParseUint(fields[2], 10, 64)
   797  	if err != nil {
   798  		return nil, nil, err
   799  	}
   800  	text, err := strconv.ParseUint(fields[3], 10, 64)
   801  	if err != nil {
   802  		return nil, nil, err
   803  	}
   804  	lib, err := strconv.ParseUint(fields[4], 10, 64)
   805  	if err != nil {
   806  		return nil, nil, err
   807  	}
   808  	dirty, err := strconv.ParseUint(fields[5], 10, 64)
   809  	if err != nil {
   810  		return nil, nil, err
   811  	}
   812  
   813  	memInfoEx := &MemoryInfoExStat{
   814  		RSS:    rss * PageSize,
   815  		VMS:    vms * PageSize,
   816  		Shared: shared * PageSize,
   817  		Text:   text * PageSize,
   818  		Lib:    lib * PageSize,
   819  		Dirty:  dirty * PageSize,
   820  	}
   821  
   822  	return memInfo, memInfoEx, nil
   823  }
   824  
   825  // Get various status from /proc/(pid)/status
   826  func (p *Process) fillFromStatusWithContext(ctx context.Context) error {
   827  	pid := p.Pid
   828  	statPath := common.HostProc(strconv.Itoa(int(pid)), "status")
   829  	contents, err := ioutil.ReadFile(statPath)
   830  	if err != nil {
   831  		return err
   832  	}
   833  	lines := strings.Split(string(contents), "\n")
   834  	p.numCtxSwitches = &NumCtxSwitchesStat{}
   835  	p.memInfo = &MemoryInfoStat{}
   836  	p.sigInfo = &SignalInfoStat{}
   837  	for _, line := range lines {
   838  		tabParts := strings.SplitN(line, "\t", 2)
   839  		if len(tabParts) < 2 {
   840  			continue
   841  		}
   842  		value := tabParts[1]
   843  		switch strings.TrimRight(tabParts[0], ":") {
   844  		case "Name":
   845  			p.name = strings.Trim(value, " \t")
   846  			if len(p.name) >= 15 {
   847  				cmdlineSlice, err := p.CmdlineSlice()
   848  				if err != nil {
   849  					return err
   850  				}
   851  				if len(cmdlineSlice) > 0 {
   852  					extendedName := filepath.Base(cmdlineSlice[0])
   853  					if strings.HasPrefix(extendedName, p.name) {
   854  						p.name = extendedName
   855  					} else {
   856  						p.name = cmdlineSlice[0]
   857  					}
   858  				}
   859  			}
   860  			// Ensure we have a copy and not reference into slice
   861  			p.name = string([]byte(p.name))
   862  		case "State":
   863  			p.status = value[0:1]
   864  			// Ensure we have a copy and not reference into slice
   865  			p.status = string([]byte(p.status))
   866  		case "PPid", "Ppid":
   867  			pval, err := strconv.ParseInt(value, 10, 32)
   868  			if err != nil {
   869  				return err
   870  			}
   871  			p.parent = int32(pval)
   872  		case "Tgid":
   873  			pval, err := strconv.ParseInt(value, 10, 32)
   874  			if err != nil {
   875  				return err
   876  			}
   877  			p.tgid = int32(pval)
   878  		case "Uid":
   879  			p.uids = make([]int32, 0, 4)
   880  			for _, i := range strings.Split(value, "\t") {
   881  				v, err := strconv.ParseInt(i, 10, 32)
   882  				if err != nil {
   883  					return err
   884  				}
   885  				p.uids = append(p.uids, int32(v))
   886  			}
   887  		case "Gid":
   888  			p.gids = make([]int32, 0, 4)
   889  			for _, i := range strings.Split(value, "\t") {
   890  				v, err := strconv.ParseInt(i, 10, 32)
   891  				if err != nil {
   892  					return err
   893  				}
   894  				p.gids = append(p.gids, int32(v))
   895  			}
   896  		case "Groups":
   897  			groups := strings.Fields(value)
   898  			p.groups = make([]int32, 0, len(groups))
   899  			for _, i := range groups {
   900  				v, err := strconv.ParseInt(i, 10, 32)
   901  				if err != nil {
   902  					return err
   903  				}
   904  				p.groups = append(p.groups, int32(v))
   905  			}
   906  		case "Threads":
   907  			v, err := strconv.ParseInt(value, 10, 32)
   908  			if err != nil {
   909  				return err
   910  			}
   911  			p.numThreads = int32(v)
   912  		case "voluntary_ctxt_switches":
   913  			v, err := strconv.ParseInt(value, 10, 64)
   914  			if err != nil {
   915  				return err
   916  			}
   917  			p.numCtxSwitches.Voluntary = v
   918  		case "nonvoluntary_ctxt_switches":
   919  			v, err := strconv.ParseInt(value, 10, 64)
   920  			if err != nil {
   921  				return err
   922  			}
   923  			p.numCtxSwitches.Involuntary = v
   924  		case "VmRSS":
   925  			value := strings.Trim(value, " kB") // remove last "kB"
   926  			v, err := strconv.ParseUint(value, 10, 64)
   927  			if err != nil {
   928  				return err
   929  			}
   930  			p.memInfo.RSS = v * 1024
   931  		case "VmSize":
   932  			value := strings.Trim(value, " kB") // remove last "kB"
   933  			v, err := strconv.ParseUint(value, 10, 64)
   934  			if err != nil {
   935  				return err
   936  			}
   937  			p.memInfo.VMS = v * 1024
   938  		case "VmSwap":
   939  			value := strings.Trim(value, " kB") // remove last "kB"
   940  			v, err := strconv.ParseUint(value, 10, 64)
   941  			if err != nil {
   942  				return err
   943  			}
   944  			p.memInfo.Swap = v * 1024
   945  		case "VmHWM":
   946  			value := strings.Trim(value, " kB") // remove last "kB"
   947  			v, err := strconv.ParseUint(value, 10, 64)
   948  			if err != nil {
   949  				return err
   950  			}
   951  			p.memInfo.HWM = v * 1024
   952  		case "VmData":
   953  			value := strings.Trim(value, " kB") // remove last "kB"
   954  			v, err := strconv.ParseUint(value, 10, 64)
   955  			if err != nil {
   956  				return err
   957  			}
   958  			p.memInfo.Data = v * 1024
   959  		case "VmStk":
   960  			value := strings.Trim(value, " kB") // remove last "kB"
   961  			v, err := strconv.ParseUint(value, 10, 64)
   962  			if err != nil {
   963  				return err
   964  			}
   965  			p.memInfo.Stack = v * 1024
   966  		case "VmLck":
   967  			value := strings.Trim(value, " kB") // remove last "kB"
   968  			v, err := strconv.ParseUint(value, 10, 64)
   969  			if err != nil {
   970  				return err
   971  			}
   972  			p.memInfo.Locked = v * 1024
   973  		case "SigPnd":
   974  			if len(value) > 16 {
   975  				value = value[len(value)-16:]
   976  			}
   977  			v, err := strconv.ParseUint(value, 16, 64)
   978  			if err != nil {
   979  				return err
   980  			}
   981  			p.sigInfo.PendingThread = v
   982  		case "ShdPnd":
   983  			if len(value) > 16 {
   984  				value = value[len(value)-16:]
   985  			}
   986  			v, err := strconv.ParseUint(value, 16, 64)
   987  			if err != nil {
   988  				return err
   989  			}
   990  			p.sigInfo.PendingProcess = v
   991  		case "SigBlk":
   992  			if len(value) > 16 {
   993  				value = value[len(value)-16:]
   994  			}
   995  			v, err := strconv.ParseUint(value, 16, 64)
   996  			if err != nil {
   997  				return err
   998  			}
   999  			p.sigInfo.Blocked = v
  1000  		case "SigIgn":
  1001  			if len(value) > 16 {
  1002  				value = value[len(value)-16:]
  1003  			}
  1004  			v, err := strconv.ParseUint(value, 16, 64)
  1005  			if err != nil {
  1006  				return err
  1007  			}
  1008  			p.sigInfo.Ignored = v
  1009  		case "SigCgt":
  1010  			if len(value) > 16 {
  1011  				value = value[len(value)-16:]
  1012  			}
  1013  			v, err := strconv.ParseUint(value, 16, 64)
  1014  			if err != nil {
  1015  				return err
  1016  			}
  1017  			p.sigInfo.Caught = v
  1018  		}
  1019  
  1020  	}
  1021  	return nil
  1022  }
  1023  
  1024  func (p *Process) fillFromTIDStatWithContext(ctx context.Context, tid int32) (uint64, int32, *cpu.TimesStat, int64, uint32, int32, *PageFaultsStat, error) {
  1025  	pid := p.Pid
  1026  	var statPath string
  1027  
  1028  	if tid == -1 {
  1029  		statPath = common.HostProc(strconv.Itoa(int(pid)), "stat")
  1030  	} else {
  1031  		statPath = common.HostProc(strconv.Itoa(int(pid)), "task", strconv.Itoa(int(tid)), "stat")
  1032  	}
  1033  
  1034  	contents, err := ioutil.ReadFile(statPath)
  1035  	if err != nil {
  1036  		return 0, 0, nil, 0, 0, 0, nil, err
  1037  	}
  1038  	// Indexing from one, as described in `man proc` about the file /proc/[pid]/stat
  1039  	fields := splitProcStat(contents)
  1040  
  1041  	terminal, err := strconv.ParseUint(fields[7], 10, 64)
  1042  	if err != nil {
  1043  		return 0, 0, nil, 0, 0, 0, nil, err
  1044  	}
  1045  
  1046  	ppid, err := strconv.ParseInt(fields[4], 10, 32)
  1047  	if err != nil {
  1048  		return 0, 0, nil, 0, 0, 0, nil, err
  1049  	}
  1050  	utime, err := strconv.ParseFloat(fields[14], 64)
  1051  	if err != nil {
  1052  		return 0, 0, nil, 0, 0, 0, nil, err
  1053  	}
  1054  
  1055  	stime, err := strconv.ParseFloat(fields[15], 64)
  1056  	if err != nil {
  1057  		return 0, 0, nil, 0, 0, 0, nil, err
  1058  	}
  1059  
  1060  	// There is no such thing as iotime in stat file.  As an approximation, we
  1061  	// will use delayacct_blkio_ticks (aggregated block I/O delays, as per Linux
  1062  	// docs).  Note: I am assuming at least Linux 2.6.18
  1063  	var iotime float64
  1064  	if len(fields) > 42 {
  1065  		iotime, err = strconv.ParseFloat(fields[42], 64)
  1066  		if err != nil {
  1067  			iotime = 0 // Ancient linux version, most likely
  1068  		}
  1069  	} else {
  1070  		iotime = 0 // e.g. SmartOS containers
  1071  	}
  1072  
  1073  	cpuTimes := &cpu.TimesStat{
  1074  		CPU:    "cpu",
  1075  		User:   utime / float64(ClockTicks),
  1076  		System: stime / float64(ClockTicks),
  1077  		Iowait: iotime / float64(ClockTicks),
  1078  	}
  1079  
  1080  	bootTime, _ := common.BootTimeWithContext(ctx)
  1081  	t, err := strconv.ParseUint(fields[22], 10, 64)
  1082  	if err != nil {
  1083  		return 0, 0, nil, 0, 0, 0, nil, err
  1084  	}
  1085  	ctime := (t / uint64(ClockTicks)) + uint64(bootTime)
  1086  	createTime := int64(ctime * 1000)
  1087  
  1088  	rtpriority, err := strconv.ParseInt(fields[18], 10, 32)
  1089  	if err != nil {
  1090  		return 0, 0, nil, 0, 0, 0, nil, err
  1091  	}
  1092  	if rtpriority < 0 {
  1093  		rtpriority = rtpriority*-1 - 1
  1094  	} else {
  1095  		rtpriority = 0
  1096  	}
  1097  
  1098  	//	p.Nice = mustParseInt32(fields[18])
  1099  	// use syscall instead of parse Stat file
  1100  	snice, _ := unix.Getpriority(PrioProcess, int(pid))
  1101  	nice := int32(snice) // FIXME: is this true?
  1102  
  1103  	minFault, err := strconv.ParseUint(fields[10], 10, 64)
  1104  	if err != nil {
  1105  		return 0, 0, nil, 0, 0, 0, nil, err
  1106  	}
  1107  	cMinFault, err := strconv.ParseUint(fields[11], 10, 64)
  1108  	if err != nil {
  1109  		return 0, 0, nil, 0, 0, 0, nil, err
  1110  	}
  1111  	majFault, err := strconv.ParseUint(fields[12], 10, 64)
  1112  	if err != nil {
  1113  		return 0, 0, nil, 0, 0, 0, nil, err
  1114  	}
  1115  	cMajFault, err := strconv.ParseUint(fields[13], 10, 64)
  1116  	if err != nil {
  1117  		return 0, 0, nil, 0, 0, 0, nil, err
  1118  	}
  1119  
  1120  	faults := &PageFaultsStat{
  1121  		MinorFaults:      minFault,
  1122  		MajorFaults:      majFault,
  1123  		ChildMinorFaults: cMinFault,
  1124  		ChildMajorFaults: cMajFault,
  1125  	}
  1126  
  1127  	return terminal, int32(ppid), cpuTimes, createTime, uint32(rtpriority), nice, faults, nil
  1128  }
  1129  
  1130  func (p *Process) fillFromStatWithContext(ctx context.Context) (uint64, int32, *cpu.TimesStat, int64, uint32, int32, *PageFaultsStat, error) {
  1131  	return p.fillFromTIDStatWithContext(ctx, -1)
  1132  }
  1133  
  1134  func pidsWithContext(ctx context.Context) ([]int32, error) {
  1135  	return readPidsFromDir(common.HostProc())
  1136  }
  1137  
  1138  func ProcessesWithContext(ctx context.Context) ([]*Process, error) {
  1139  	out := []*Process{}
  1140  
  1141  	pids, err := PidsWithContext(ctx)
  1142  	if err != nil {
  1143  		return out, err
  1144  	}
  1145  
  1146  	for _, pid := range pids {
  1147  		p, err := NewProcessWithContext(ctx, pid)
  1148  		if err != nil {
  1149  			continue
  1150  		}
  1151  		out = append(out, p)
  1152  	}
  1153  
  1154  	return out, nil
  1155  }
  1156  
  1157  func readPidsFromDir(path string) ([]int32, error) {
  1158  	var ret []int32
  1159  
  1160  	d, err := os.Open(path)
  1161  	if err != nil {
  1162  		return nil, err
  1163  	}
  1164  	defer d.Close()
  1165  
  1166  	fnames, err := d.Readdirnames(-1)
  1167  	if err != nil {
  1168  		return nil, err
  1169  	}
  1170  	for _, fname := range fnames {
  1171  		pid, err := strconv.ParseInt(fname, 10, 32)
  1172  		if err != nil {
  1173  			// if not numeric name, just skip
  1174  			continue
  1175  		}
  1176  		ret = append(ret, int32(pid))
  1177  	}
  1178  
  1179  	return ret, nil
  1180  }
  1181  
  1182  func splitProcStat(content []byte) []string {
  1183  	nameStart := bytes.IndexByte(content, '(')
  1184  	nameEnd := bytes.LastIndexByte(content, ')')
  1185  	restFields := strings.Fields(string(content[nameEnd+2:])) // +2 skip ') '
  1186  	name := content[nameStart+1 : nameEnd]
  1187  	pid := strings.TrimSpace(string(content[:nameStart]))
  1188  	fields := make([]string, 3, len(restFields)+3)
  1189  	fields[1] = string(pid)
  1190  	fields[2] = string(name)
  1191  	fields = append(fields, restFields...)
  1192  	return fields
  1193  }