1// +build freebsd
2
3package host
4
5import (
6	"bytes"
7	"context"
8	"encoding/binary"
9	"io/ioutil"
10	"math"
11	"os"
12	"strings"
13	"unsafe"
14
15	"github.com/shirou/gopsutil/v3/internal/common"
16	"github.com/shirou/gopsutil/v3/process"
17	"golang.org/x/sys/unix"
18)
19
20const (
21	UTNameSize = 16 /* see MAXLOGNAME in <sys/param.h> */
22	UTLineSize = 8
23	UTHostSize = 16
24)
25
26func HostIDWithContext(ctx context.Context) (string, error) {
27	uuid, err := unix.Sysctl("kern.hostuuid")
28	if err != nil {
29		return "", err
30	}
31	return strings.ToLower(uuid), err
32}
33
34func numProcs(ctx context.Context) (uint64, error) {
35	procs, err := process.PidsWithContext(ctx)
36	if err != nil {
37		return 0, err
38	}
39	return uint64(len(procs)), nil
40}
41
42func UsersWithContext(ctx context.Context) ([]UserStat, error) {
43	utmpfile := "/var/run/utx.active"
44	if !common.PathExists(utmpfile) {
45		utmpfile = "/var/run/utmp" // before 9.0
46		return getUsersFromUtmp(utmpfile)
47	}
48
49	var ret []UserStat
50	file, err := os.Open(utmpfile)
51	if err != nil {
52		return ret, err
53	}
54	defer file.Close()
55
56	buf, err := ioutil.ReadAll(file)
57	if err != nil {
58		return ret, err
59	}
60
61	entrySize := sizeOfUtmpx
62	count := len(buf) / entrySize
63
64	for i := 0; i < count; i++ {
65		b := buf[i*sizeOfUtmpx : (i+1)*sizeOfUtmpx]
66		var u Utmpx
67		br := bytes.NewReader(b)
68		err := binary.Read(br, binary.BigEndian, &u)
69		if err != nil || u.Type != 4 {
70			continue
71		}
72		sec := math.Floor(float64(u.Tv) / 1000000)
73		user := UserStat{
74			User:     common.IntToString(u.User[:]),
75			Terminal: common.IntToString(u.Line[:]),
76			Host:     common.IntToString(u.Host[:]),
77			Started:  int(sec),
78		}
79
80		ret = append(ret, user)
81	}
82
83	return ret, nil
84
85}
86
87func PlatformInformationWithContext(ctx context.Context) (string, string, string, error) {
88	platform, err := unix.Sysctl("kern.ostype")
89	if err != nil {
90		return "", "", "", err
91	}
92
93	version, err := unix.Sysctl("kern.osrelease")
94	if err != nil {
95		return "", "", "", err
96	}
97
98	return strings.ToLower(platform), "", strings.ToLower(version), nil
99}
100
101func VirtualizationWithContext(ctx context.Context) (string, string, error) {
102	return "", "", common.ErrNotImplementedError
103}
104
105// before 9.0
106func getUsersFromUtmp(utmpfile string) ([]UserStat, error) {
107	var ret []UserStat
108	file, err := os.Open(utmpfile)
109	if err != nil {
110		return ret, err
111	}
112	defer file.Close()
113
114	buf, err := ioutil.ReadAll(file)
115	if err != nil {
116		return ret, err
117	}
118
119	u := Utmp{}
120	entrySize := int(unsafe.Sizeof(u))
121	count := len(buf) / entrySize
122
123	for i := 0; i < count; i++ {
124		b := buf[i*entrySize : i*entrySize+entrySize]
125		var u Utmp
126		br := bytes.NewReader(b)
127		err := binary.Read(br, binary.LittleEndian, &u)
128		if err != nil || u.Time == 0 {
129			continue
130		}
131		user := UserStat{
132			User:     common.IntToString(u.Name[:]),
133			Terminal: common.IntToString(u.Line[:]),
134			Host:     common.IntToString(u.Host[:]),
135			Started:  int(u.Time),
136		}
137
138		ret = append(ret, user)
139	}
140
141	return ret, nil
142}
143
144func SensorsTemperaturesWithContext(ctx context.Context) ([]TemperatureStat, error) {
145	return []TemperatureStat{}, common.ErrNotImplementedError
146}
147
148func KernelVersionWithContext(ctx context.Context) (string, error) {
149	_, _, version, err := PlatformInformationWithContext(ctx)
150	return version, err
151}
152