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