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