github.com/gofiber/fiber/v2@v2.47.0/internal/gopsutil/process/process_linux.go (about)

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