1// +build freebsd
2
3package disk
4
5import (
6	"bytes"
7	"context"
8	"encoding/binary"
9	"strconv"
10
11	"golang.org/x/sys/unix"
12
13	"github.com/shirou/gopsutil/v3/internal/common"
14)
15
16// PartitionsWithContext returns disk partition.
17// 'all' argument is ignored, see: https://github.com/giampaolo/psutil/issues/906
18func PartitionsWithContext(ctx context.Context, all bool) ([]PartitionStat, error) {
19	var ret []PartitionStat
20
21	// get length
22	count, err := unix.Getfsstat(nil, unix.MNT_WAIT)
23	if err != nil {
24		return ret, err
25	}
26
27	fs := make([]unix.Statfs_t, count)
28	if _, err = unix.Getfsstat(fs, unix.MNT_WAIT); err != nil {
29		return ret, err
30	}
31
32	for _, stat := range fs {
33		opts := []string{"rw"}
34		if stat.Flags&unix.MNT_RDONLY != 0 {
35			opts = []string{"ro"}
36		}
37		if stat.Flags&unix.MNT_SYNCHRONOUS != 0 {
38			opts = append(opts, "sync")
39		}
40		if stat.Flags&unix.MNT_NOEXEC != 0 {
41			opts = append(opts, "noexec")
42		}
43		if stat.Flags&unix.MNT_NOSUID != 0 {
44			opts = append(opts, "nosuid")
45		}
46		if stat.Flags&unix.MNT_UNION != 0 {
47			opts = append(opts, "union")
48		}
49		if stat.Flags&unix.MNT_ASYNC != 0 {
50			opts = append(opts, "async")
51		}
52		if stat.Flags&unix.MNT_SUIDDIR != 0 {
53			opts = append(opts, "suiddir")
54		}
55		if stat.Flags&unix.MNT_SOFTDEP != 0 {
56			opts = append(opts, "softdep")
57		}
58		if stat.Flags&unix.MNT_NOSYMFOLLOW != 0 {
59			opts = append(opts, "nosymfollow")
60		}
61		if stat.Flags&unix.MNT_GJOURNAL != 0 {
62			opts = append(opts, "gjournal")
63		}
64		if stat.Flags&unix.MNT_MULTILABEL != 0 {
65			opts = append(opts, "multilabel")
66		}
67		if stat.Flags&unix.MNT_ACLS != 0 {
68			opts = append(opts, "acls")
69		}
70		if stat.Flags&unix.MNT_NOATIME != 0 {
71			opts = append(opts, "noatime")
72		}
73		if stat.Flags&unix.MNT_NOCLUSTERR != 0 {
74			opts = append(opts, "noclusterr")
75		}
76		if stat.Flags&unix.MNT_NOCLUSTERW != 0 {
77			opts = append(opts, "noclusterw")
78		}
79		if stat.Flags&unix.MNT_NFS4ACLS != 0 {
80			opts = append(opts, "nfsv4acls")
81		}
82
83		d := PartitionStat{
84			Device:     common.ByteToString(stat.Mntfromname[:]),
85			Mountpoint: common.ByteToString(stat.Mntonname[:]),
86			Fstype:     common.ByteToString(stat.Fstypename[:]),
87			Opts:       opts,
88		}
89
90		ret = append(ret, d)
91	}
92
93	return ret, nil
94}
95
96func IOCountersWithContext(ctx context.Context, names ...string) (map[string]IOCountersStat, error) {
97	// statinfo->devinfo->devstat
98	// /usr/include/devinfo.h
99	ret := make(map[string]IOCountersStat)
100
101	r, err := unix.Sysctl("kern.devstat.all")
102	if err != nil {
103		return nil, err
104	}
105	buf := []byte(r)
106	length := len(buf)
107
108	count := int(uint64(length) / uint64(sizeOfdevstat))
109
110	buf = buf[8:] // devstat.all has version in the head.
111	// parse buf to devstat
112	for i := 0; i < count; i++ {
113		b := buf[i*sizeOfdevstat : i*sizeOfdevstat+sizeOfdevstat]
114		d, err := parsedevstat(b)
115		if err != nil {
116			continue
117		}
118		un := strconv.Itoa(int(d.Unit_number))
119		name := common.IntToString(d.Device_name[:]) + un
120
121		if len(names) > 0 && !common.StringsHas(names, name) {
122			continue
123		}
124
125		ds := IOCountersStat{
126			ReadCount:  d.Operations[devstat_READ],
127			WriteCount: d.Operations[devstat_WRITE],
128			ReadBytes:  d.Bytes[devstat_READ],
129			WriteBytes: d.Bytes[devstat_WRITE],
130			ReadTime:   uint64(d.Duration[devstat_READ].Compute() * 1000),
131			WriteTime:  uint64(d.Duration[devstat_WRITE].Compute() * 1000),
132			IoTime:     uint64(d.Busy_time.Compute() * 1000),
133			Name:       name,
134		}
135		ret[name] = ds
136	}
137
138	return ret, nil
139}
140
141func (b bintime) Compute() float64 {
142	BINTIME_SCALE := 5.42101086242752217003726400434970855712890625e-20
143	return float64(b.Sec) + float64(b.Frac)*BINTIME_SCALE
144}
145
146// BT2LD(time)     ((long double)(time).sec + (time).frac * BINTIME_SCALE)
147
148func parsedevstat(buf []byte) (devstat, error) {
149	var ds devstat
150	br := bytes.NewReader(buf)
151	//	err := binary.Read(br, binary.LittleEndian, &ds)
152	err := common.Read(br, binary.LittleEndian, &ds)
153	if err != nil {
154		return ds, err
155	}
156
157	return ds, nil
158}
159
160func getFsType(stat unix.Statfs_t) string {
161	return common.ByteToString(stat.Fstypename[:])
162}
163
164func SerialNumberWithContext(ctx context.Context, name string) (string, error) {
165	return "", common.ErrNotImplementedError
166}
167
168func LabelWithContext(ctx context.Context, name string) (string, error) {
169	return "", common.ErrNotImplementedError
170}
171