1// +build freebsd
2
3package disk
4
5import (
6	"bytes"
7	"context"
8	"encoding/binary"
9	"path"
10	"strconv"
11	"unsafe"
12
13	"golang.org/x/sys/unix"
14
15	"github.com/shirou/gopsutil/internal/common"
16)
17
18func Partitions(all bool) ([]PartitionStat, error) {
19	return PartitionsWithContext(context.Background(), all)
20}
21
22func PartitionsWithContext(ctx context.Context, all bool) ([]PartitionStat, error) {
23	var ret []PartitionStat
24
25	// get length
26	count, err := unix.Getfsstat(nil, MNT_WAIT)
27	if err != nil {
28		return ret, err
29	}
30
31	fs := make([]Statfs, count)
32	if _, err = Getfsstat(fs, MNT_WAIT); err != nil {
33		return ret, err
34	}
35
36	for _, stat := range fs {
37		opts := "rw"
38		if stat.Flags&MNT_RDONLY != 0 {
39			opts = "ro"
40		}
41		if stat.Flags&MNT_SYNCHRONOUS != 0 {
42			opts += ",sync"
43		}
44		if stat.Flags&MNT_NOEXEC != 0 {
45			opts += ",noexec"
46		}
47		if stat.Flags&MNT_NOSUID != 0 {
48			opts += ",nosuid"
49		}
50		if stat.Flags&MNT_UNION != 0 {
51			opts += ",union"
52		}
53		if stat.Flags&MNT_ASYNC != 0 {
54			opts += ",async"
55		}
56		if stat.Flags&MNT_SUIDDIR != 0 {
57			opts += ",suiddir"
58		}
59		if stat.Flags&MNT_SOFTDEP != 0 {
60			opts += ",softdep"
61		}
62		if stat.Flags&MNT_NOSYMFOLLOW != 0 {
63			opts += ",nosymfollow"
64		}
65		if stat.Flags&MNT_GJOURNAL != 0 {
66			opts += ",gjounalc"
67		}
68		if stat.Flags&MNT_MULTILABEL != 0 {
69			opts += ",multilabel"
70		}
71		if stat.Flags&MNT_ACLS != 0 {
72			opts += ",acls"
73		}
74		if stat.Flags&MNT_NOATIME != 0 {
75			opts += ",noattime"
76		}
77		if stat.Flags&MNT_NOCLUSTERR != 0 {
78			opts += ",nocluster"
79		}
80		if stat.Flags&MNT_NOCLUSTERW != 0 {
81			opts += ",noclusterw"
82		}
83		if stat.Flags&MNT_NFS4ACLS != 0 {
84			opts += ",nfs4acls"
85		}
86
87		d := PartitionStat{
88			Device:     common.IntToString(stat.Mntfromname[:]),
89			Mountpoint: common.IntToString(stat.Mntonname[:]),
90			Fstype:     common.IntToString(stat.Fstypename[:]),
91			Opts:       opts,
92		}
93		if all == false {
94			if !path.IsAbs(d.Device) || !common.PathExists(d.Device) {
95				continue
96			}
97		}
98
99		ret = append(ret, d)
100	}
101
102	return ret, nil
103}
104
105func IOCounters(names ...string) (map[string]IOCountersStat, error) {
106	return IOCountersWithContext(context.Background(), names...)
107}
108
109func IOCountersWithContext(ctx context.Context, names ...string) (map[string]IOCountersStat, error) {
110	// statinfo->devinfo->devstat
111	// /usr/include/devinfo.h
112	ret := make(map[string]IOCountersStat)
113
114	r, err := unix.Sysctl("kern.devstat.all")
115	if err != nil {
116		return nil, err
117	}
118	buf := []byte(r)
119	length := len(buf)
120
121	count := int(uint64(length) / uint64(sizeOfDevstat))
122
123	buf = buf[8:] // devstat.all has version in the head.
124	// parse buf to Devstat
125	for i := 0; i < count; i++ {
126		b := buf[i*sizeOfDevstat : i*sizeOfDevstat+sizeOfDevstat]
127		d, err := parseDevstat(b)
128		if err != nil {
129			continue
130		}
131		un := strconv.Itoa(int(d.Unit_number))
132		name := common.IntToString(d.Device_name[:]) + un
133
134		if len(names) > 0 && !common.StringsHas(names, name) {
135			continue
136		}
137
138		ds := IOCountersStat{
139			ReadCount:  d.Operations[DEVSTAT_READ],
140			WriteCount: d.Operations[DEVSTAT_WRITE],
141			ReadBytes:  d.Bytes[DEVSTAT_READ],
142			WriteBytes: d.Bytes[DEVSTAT_WRITE],
143			ReadTime:   uint64(d.Duration[DEVSTAT_READ].Compute() * 1000),
144			WriteTime:  uint64(d.Duration[DEVSTAT_WRITE].Compute() * 1000),
145			IoTime:     uint64(d.Busy_time.Compute() * 1000),
146			Name:       name,
147		}
148		ret[name] = ds
149	}
150
151	return ret, nil
152}
153
154func (b Bintime) Compute() float64 {
155	BINTIME_SCALE := 5.42101086242752217003726400434970855712890625e-20
156	return float64(b.Sec) + float64(b.Frac)*BINTIME_SCALE
157}
158
159// BT2LD(time)     ((long double)(time).sec + (time).frac * BINTIME_SCALE)
160
161// Getfsstat is borrowed from pkg/syscall/syscall_freebsd.go
162// change Statfs_t to Statfs in order to get more information
163func Getfsstat(buf []Statfs, flags int) (n int, err error) {
164	return GetfsstatWithContext(context.Background(), buf, flags)
165}
166
167func GetfsstatWithContext(ctx context.Context, buf []Statfs, flags int) (n int, err error) {
168	var _p0 unsafe.Pointer
169	var bufsize uintptr
170	if len(buf) > 0 {
171		_p0 = unsafe.Pointer(&buf[0])
172		bufsize = unsafe.Sizeof(Statfs{}) * uintptr(len(buf))
173	}
174	r0, _, e1 := unix.Syscall(unix.SYS_GETFSSTAT, uintptr(_p0), bufsize, uintptr(flags))
175	n = int(r0)
176	if e1 != 0 {
177		err = e1
178	}
179	return
180}
181
182func parseDevstat(buf []byte) (Devstat, error) {
183	var ds Devstat
184	br := bytes.NewReader(buf)
185	//	err := binary.Read(br, binary.LittleEndian, &ds)
186	err := common.Read(br, binary.LittleEndian, &ds)
187	if err != nil {
188		return ds, err
189	}
190
191	return ds, nil
192}
193
194func getFsType(stat unix.Statfs_t) string {
195	return common.IntToString(stat.Fstypename[:])
196}
197