1// +build freebsd
2
3package host
4
5import (
6	"bytes"
7	"context"
8	"encoding/binary"
9	"io/ioutil"
10	"math"
11	"os"
12	"runtime"
13	"strings"
14	"sync/atomic"
15	"syscall"
16	"time"
17	"unsafe"
18
19	"github.com/shirou/gopsutil/internal/common"
20	"github.com/shirou/gopsutil/process"
21	"golang.org/x/sys/unix"
22)
23
24const (
25	UTNameSize = 16 /* see MAXLOGNAME in <sys/param.h> */
26	UTLineSize = 8
27	UTHostSize = 16
28)
29
30func Info() (*InfoStat, error) {
31	return InfoWithContext(context.Background())
32}
33
34func InfoWithContext(ctx context.Context) (*InfoStat, error) {
35	ret := &InfoStat{
36		OS:             runtime.GOOS,
37		PlatformFamily: "freebsd",
38	}
39
40	hostname, err := os.Hostname()
41	if err == nil {
42		ret.Hostname = hostname
43	}
44
45	platform, family, version, err := PlatformInformation()
46	if err == nil {
47		ret.Platform = platform
48		ret.PlatformFamily = family
49		ret.PlatformVersion = version
50		ret.KernelVersion = version
51	}
52
53	kernelArch, err := kernelArch()
54	if err == nil {
55		ret.KernelArch = kernelArch
56	}
57
58	system, role, err := Virtualization()
59	if err == nil {
60		ret.VirtualizationSystem = system
61		ret.VirtualizationRole = role
62	}
63
64	boot, err := BootTime()
65	if err == nil {
66		ret.BootTime = boot
67		ret.Uptime = uptime(boot)
68	}
69
70	procs, err := process.Pids()
71	if err == nil {
72		ret.Procs = uint64(len(procs))
73	}
74
75	hostid, err := unix.Sysctl("kern.hostuuid")
76	if err == nil && hostid != "" {
77		ret.HostID = strings.ToLower(hostid)
78	}
79
80	return ret, nil
81}
82
83// cachedBootTime must be accessed via atomic.Load/StoreUint64
84var cachedBootTime uint64
85
86func BootTime() (uint64, error) {
87	return BootTimeWithContext(context.Background())
88}
89
90func BootTimeWithContext(ctx context.Context) (uint64, error) {
91	t := atomic.LoadUint64(&cachedBootTime)
92	if t != 0 {
93		return t, nil
94	}
95	buf, err := unix.SysctlRaw("kern.boottime")
96	if err != nil {
97		return 0, err
98	}
99
100	tv := *(*syscall.Timeval)(unsafe.Pointer((&buf[0])))
101	atomic.StoreUint64(&cachedBootTime, uint64(tv.Sec))
102
103	return t, nil
104}
105
106func uptime(boot uint64) uint64 {
107	return uint64(time.Now().Unix()) - boot
108}
109
110func Uptime() (uint64, error) {
111	return UptimeWithContext(context.Background())
112}
113
114func UptimeWithContext(ctx context.Context) (uint64, error) {
115	boot, err := BootTime()
116	if err != nil {
117		return 0, err
118	}
119	return uptime(boot), nil
120}
121
122func Users() ([]UserStat, error) {
123	return UsersWithContext(context.Background())
124}
125
126func UsersWithContext(ctx context.Context) ([]UserStat, error) {
127	utmpfile := "/var/run/utx.active"
128	if !common.PathExists(utmpfile) {
129		utmpfile = "/var/run/utmp" // before 9.0
130		return getUsersFromUtmp(utmpfile)
131	}
132
133	var ret []UserStat
134	file, err := os.Open(utmpfile)
135	if err != nil {
136		return ret, err
137	}
138	defer file.Close()
139
140	buf, err := ioutil.ReadAll(file)
141	if err != nil {
142		return ret, err
143	}
144
145	entrySize := sizeOfUtmpx
146	count := len(buf) / entrySize
147
148	for i := 0; i < count; i++ {
149		b := buf[i*sizeOfUtmpx : (i+1)*sizeOfUtmpx]
150		var u Utmpx
151		br := bytes.NewReader(b)
152		err := binary.Read(br, binary.BigEndian, &u)
153		if err != nil || u.Type != 4 {
154			continue
155		}
156		sec := math.Floor(float64(u.Tv) / 1000000)
157		user := UserStat{
158			User:     common.IntToString(u.User[:]),
159			Terminal: common.IntToString(u.Line[:]),
160			Host:     common.IntToString(u.Host[:]),
161			Started:  int(sec),
162		}
163
164		ret = append(ret, user)
165	}
166
167	return ret, nil
168
169}
170
171func PlatformInformation() (string, string, string, error) {
172	return PlatformInformationWithContext(context.Background())
173}
174
175func PlatformInformationWithContext(ctx context.Context) (string, string, string, error) {
176	platform, err := unix.Sysctl("kern.ostype")
177	if err != nil {
178		return "", "", "", err
179	}
180
181	version, err := unix.Sysctl("kern.osrelease")
182	if err != nil {
183		return "", "", "", err
184	}
185
186	return strings.ToLower(platform), "", strings.ToLower(version), nil
187}
188
189func Virtualization() (string, string, error) {
190	return VirtualizationWithContext(context.Background())
191}
192
193func VirtualizationWithContext(ctx context.Context) (string, string, error) {
194	return "", "", common.ErrNotImplementedError
195}
196
197// before 9.0
198func getUsersFromUtmp(utmpfile string) ([]UserStat, error) {
199	var ret []UserStat
200	file, err := os.Open(utmpfile)
201	if err != nil {
202		return ret, err
203	}
204	defer file.Close()
205
206	buf, err := ioutil.ReadAll(file)
207	if err != nil {
208		return ret, err
209	}
210
211	u := Utmp{}
212	entrySize := int(unsafe.Sizeof(u))
213	count := len(buf) / entrySize
214
215	for i := 0; i < count; i++ {
216		b := buf[i*entrySize : i*entrySize+entrySize]
217		var u Utmp
218		br := bytes.NewReader(b)
219		err := binary.Read(br, binary.LittleEndian, &u)
220		if err != nil || u.Time == 0 {
221			continue
222		}
223		user := UserStat{
224			User:     common.IntToString(u.Name[:]),
225			Terminal: common.IntToString(u.Line[:]),
226			Host:     common.IntToString(u.Host[:]),
227			Started:  int(u.Time),
228		}
229
230		ret = append(ret, user)
231	}
232
233	return ret, nil
234}
235
236func SensorsTemperatures() ([]TemperatureStat, error) {
237	return SensorsTemperaturesWithContext(context.Background())
238}
239
240func SensorsTemperaturesWithContext(ctx context.Context) ([]TemperatureStat, error) {
241	return []TemperatureStat{}, common.ErrNotImplementedError
242}
243
244func KernelVersion() (string, error) {
245	return KernelVersionWithContext(context.Background())
246}
247
248func KernelVersionWithContext(ctx context.Context) (string, error) {
249	_, _, version, err := PlatformInformation()
250	return version, err
251}
252