1// +build darwin 2 3package host 4 5import ( 6 "bytes" 7 "context" 8 "encoding/binary" 9 "io/ioutil" 10 "os" 11 "os/exec" 12 "runtime" 13 "strings" 14 "sync/atomic" 15 "time" 16 "unsafe" 17 18 "github.com/shirou/gopsutil/internal/common" 19 "github.com/shirou/gopsutil/process" 20 "golang.org/x/sys/unix" 21) 22 23// from utmpx.h 24const USER_PROCESS = 7 25 26func Info() (*InfoStat, error) { 27 return InfoWithContext(context.Background()) 28} 29 30func InfoWithContext(ctx context.Context) (*InfoStat, error) { 31 ret := &InfoStat{ 32 OS: runtime.GOOS, 33 PlatformFamily: "darwin", 34 } 35 36 hostname, err := os.Hostname() 37 if err == nil { 38 ret.Hostname = hostname 39 } 40 41 kernelVersion, err := KernelVersionWithContext(ctx) 42 if err == nil { 43 ret.KernelVersion = kernelVersion 44 } 45 46 kernelArch, err := kernelArch() 47 if err == nil { 48 ret.KernelArch = kernelArch 49 } 50 51 platform, family, pver, err := PlatformInformation() 52 if err == nil { 53 ret.Platform = platform 54 ret.PlatformFamily = family 55 ret.PlatformVersion = pver 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 uuid, err := unix.Sysctl("kern.uuid") 76 if err == nil && uuid != "" { 77 ret.HostID = strings.ToLower(uuid) 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 // https://github.com/AaronO/dashd/blob/222e32ef9f7a1f9bea4a8da2c3627c4cb992f860/probe/probe_darwin.go 92 t := atomic.LoadUint64(&cachedBootTime) 93 if t != 0 { 94 return t, nil 95 } 96 value, err := unix.Sysctl("kern.boottime") 97 if err != nil { 98 return 0, err 99 } 100 bytes := []byte(value[:]) 101 var boottime uint64 102 boottime = uint64(bytes[0]) + uint64(bytes[1])*256 + uint64(bytes[2])*256*256 + uint64(bytes[3])*256*256*256 103 104 atomic.StoreUint64(&cachedBootTime, boottime) 105 106 return boottime, nil 107} 108 109func uptime(boot uint64) uint64 { 110 return uint64(time.Now().Unix()) - boot 111} 112 113func Uptime() (uint64, error) { 114 return UptimeWithContext(context.Background()) 115} 116 117func UptimeWithContext(ctx context.Context) (uint64, error) { 118 boot, err := BootTimeWithContext(ctx) 119 if err != nil { 120 return 0, err 121 } 122 return uptime(boot), nil 123} 124 125func Users() ([]UserStat, error) { 126 return UsersWithContext(context.Background()) 127} 128 129func UsersWithContext(ctx context.Context) ([]UserStat, error) { 130 utmpfile := "/var/run/utmpx" 131 var ret []UserStat 132 133 file, err := os.Open(utmpfile) 134 if err != nil { 135 return ret, err 136 } 137 defer file.Close() 138 139 buf, err := ioutil.ReadAll(file) 140 if err != nil { 141 return ret, err 142 } 143 144 u := Utmpx{} 145 entrySize := int(unsafe.Sizeof(u)) 146 count := len(buf) / entrySize 147 148 for i := 0; i < count; i++ { 149 b := buf[i*entrySize : i*entrySize+entrySize] 150 151 var u Utmpx 152 br := bytes.NewReader(b) 153 err := binary.Read(br, binary.LittleEndian, &u) 154 if err != nil { 155 continue 156 } 157 if u.Type != USER_PROCESS { 158 continue 159 } 160 user := UserStat{ 161 User: common.IntToString(u.User[:]), 162 Terminal: common.IntToString(u.Line[:]), 163 Host: common.IntToString(u.Host[:]), 164 Started: int(u.Tv.Sec), 165 } 166 ret = append(ret, user) 167 } 168 169 return ret, nil 170 171} 172 173func PlatformInformation() (string, string, string, error) { 174 return PlatformInformationWithContext(context.Background()) 175} 176 177func PlatformInformationWithContext(ctx context.Context) (string, string, string, error) { 178 platform := "" 179 family := "" 180 pver := "" 181 182 sw_vers, err := exec.LookPath("sw_vers") 183 if err != nil { 184 return "", "", "", err 185 } 186 187 p, err := unix.Sysctl("kern.ostype") 188 if err == nil { 189 platform = strings.ToLower(p) 190 } 191 192 out, err := invoke.CommandWithContext(ctx, sw_vers, "-productVersion") 193 if err == nil { 194 pver = strings.ToLower(strings.TrimSpace(string(out))) 195 } 196 197 // check if the macos server version file exists 198 _, err = os.Stat("/System/Library/CoreServices/ServerVersion.plist") 199 200 // server file doesn't exist 201 if os.IsNotExist(err) { 202 family = "Standalone Workstation" 203 } else { 204 family = "Server" 205 } 206 207 return platform, family, pver, nil 208} 209 210func Virtualization() (string, string, error) { 211 return VirtualizationWithContext(context.Background()) 212} 213 214func VirtualizationWithContext(ctx context.Context) (string, string, error) { 215 return "", "", common.ErrNotImplementedError 216} 217 218func KernelVersion() (string, error) { 219 return KernelVersionWithContext(context.Background()) 220} 221 222func KernelVersionWithContext(ctx context.Context) (string, error) { 223 version, err := unix.Sysctl("kern.osrelease") 224 return strings.ToLower(version), err 225} 226