github.com/isyscore/isc-gobase@v1.5.3-0.20231218061332-cbc7451899e9/system/common/common_linux.go (about) 1 //go:build linux 2 3 package common 4 5 import ( 6 "context" 7 "fmt" 8 "os" 9 "os/exec" 10 "path/filepath" 11 "strconv" 12 "strings" 13 "sync" 14 "time" 15 ) 16 17 func DoSysctrl(mib string) ([]string, error) { 18 sysctl, err := exec.LookPath("sysctl") 19 if err != nil { 20 return []string{}, err 21 } 22 cmd := exec.Command(sysctl, "-n", mib) 23 cmd.Env = getSysctrlEnv(os.Environ()) 24 out, err := cmd.Output() 25 if err != nil { 26 return []string{}, err 27 } 28 v := strings.Replace(string(out), "{ ", "", 1) 29 v = strings.Replace(v, " }", "", 1) 30 values := strings.Fields(v) 31 32 return values, nil 33 } 34 35 func NumProcs() (uint64, error) { 36 f, err := os.Open(HostProc()) 37 if err != nil { 38 return 0, err 39 } 40 defer f.Close() 41 42 list, err := f.Readdirnames(-1) 43 if err != nil { 44 return 0, err 45 } 46 var cnt uint64 47 48 for _, v := range list { 49 if _, err = strconv.ParseUint(v, 10, 64); err == nil { 50 cnt++ 51 } 52 } 53 54 return cnt, nil 55 } 56 57 func BootTimeWithContext(ctx context.Context) (uint64, error) { 58 system, role, err := Virtualization() 59 if err != nil { 60 return 0, err 61 } 62 63 statFile := "stat" 64 if system == "lxc" && role == "guest" { 65 // if lxc, /proc/uptime is used. 66 statFile = "uptime" 67 } else if system == "docker" && role == "guest" { 68 // also docker, guest 69 statFile = "uptime" 70 } 71 72 filename := HostProc(statFile) 73 lines, err := ReadLines(filename) 74 if err != nil { 75 return 0, err 76 } 77 78 if statFile == "uptime" { 79 if len(lines) != 1 { 80 return 0, fmt.Errorf("wrong uptime format") 81 } 82 f := strings.Fields(lines[0]) 83 b, err := strconv.ParseFloat(f[0], 64) 84 if err != nil { 85 return 0, err 86 } 87 t := uint64(time.Now().Unix()) - uint64(b) 88 return t, nil 89 } 90 if statFile == "stat" { 91 for _, line := range lines { 92 if strings.HasPrefix(line, "btime") { 93 f := strings.Fields(line) 94 if len(f) != 2 { 95 return 0, fmt.Errorf("wrong btime format") 96 } 97 b, err := strconv.ParseInt(f[1], 10, 64) 98 if err != nil { 99 return 0, err 100 } 101 t := uint64(b) 102 return t, nil 103 } 104 } 105 } 106 107 return 0, fmt.Errorf("could not find btime") 108 } 109 110 func Virtualization() (string, string, error) { 111 return VirtualizationWithContext(context.Background()) 112 } 113 114 // required variables for concurrency safe virtualization caching. 115 var ( 116 cachedVirtMap map[string]string 117 cachedVirtMutex sync.RWMutex 118 cachedVirtOnce sync.Once 119 ) 120 121 func VirtualizationWithContext(ctx context.Context) (string, string, error) { 122 var system, role string 123 124 // if cached already, return from cache 125 cachedVirtMutex.RLock() // unlock won't be deferred so concurrent reads don't wait for long 126 if cachedVirtMap != nil { 127 cachedSystem, cachedRole := cachedVirtMap["system"], cachedVirtMap["role"] 128 cachedVirtMutex.RUnlock() 129 return cachedSystem, cachedRole, nil 130 } 131 cachedVirtMutex.RUnlock() 132 133 filename := HostProc("xen") 134 if PathExists(filename) { 135 system = "xen" 136 role = "guest" // assume guest 137 138 if PathExists(filepath.Join(filename, "capabilities")) { 139 contents, err := ReadLines(filepath.Join(filename, "capabilities")) 140 if err == nil && StringsContains(contents, "control_d") { 141 role = "host" 142 } 143 } 144 } 145 146 filename = HostProc("modules") 147 if PathExists(filename) { 148 contents, err := ReadLines(filename) 149 if err == nil { 150 switch { 151 case StringsContains(contents, "kvm"): 152 system = "kvm" 153 role = "host" 154 case StringsContains(contents, "vboxdrv"): 155 system = "vbox" 156 role = "host" 157 case StringsContains(contents, "vboxguest"): 158 system = "vbox" 159 role = "guest" 160 case StringsContains(contents, "vmware"): 161 system = "vmware" 162 role = "guest" 163 } 164 } 165 } 166 167 filename = HostProc("cpuinfo") 168 if PathExists(filename) { 169 contents, err := ReadLines(filename) 170 if err == nil { 171 if StringsContains(contents, "QEMU Virtual CPU") || 172 StringsContains(contents, "Common KVM processor") || 173 StringsContains(contents, "Common 32-bit KVM processor") { 174 system = "kvm" 175 role = "guest" 176 } 177 } 178 } 179 180 filename = HostProc("bus/pci/devices") 181 if PathExists(filename) { 182 contents, err := ReadLines(filename) 183 if err == nil { 184 if StringsContains(contents, "virtio-pci") { 185 role = "guest" 186 } 187 } 188 } 189 190 filename = HostProc() 191 if PathExists(filepath.Join(filename, "bc", "0")) { 192 system = "openvz" 193 role = "host" 194 } else if PathExists(filepath.Join(filename, "vz")) { 195 system = "openvz" 196 role = "guest" 197 } 198 199 // not use dmidecode because it requires root 200 if PathExists(filepath.Join(filename, "self", "status")) { 201 contents, err := ReadLines(filepath.Join(filename, "self", "status")) 202 if err == nil { 203 if StringsContains(contents, "s_context:") || 204 StringsContains(contents, "VxID:") { 205 system = "linux-vserver" 206 } 207 } 208 } 209 210 if PathExists(filepath.Join(filename, "1", "environ")) { 211 contents, err := ReadFile(filepath.Join(filename, "1", "environ")) 212 213 if err == nil { 214 if strings.Contains(contents, "container=lxc") { 215 system = "lxc" 216 role = "guest" 217 } 218 } 219 } 220 221 if PathExists(filepath.Join(filename, "self", "cgroup")) { 222 contents, err := ReadLines(filepath.Join(filename, "self", "cgroup")) 223 if err == nil { 224 switch { 225 case StringsContains(contents, "lxc"): 226 system = "lxc" 227 role = "guest" 228 case StringsContains(contents, "docker"): 229 system = "docker" 230 role = "guest" 231 case StringsContains(contents, "machine-rkt"): 232 system = "rkt" 233 role = "guest" 234 case PathExists("/usr/bin/lxc-version"): 235 system = "lxc" 236 role = "host" 237 } 238 } 239 } 240 241 if PathExists(HostEtc("os-release")) { 242 p, _, err := GetOSRelease() 243 if err == nil && p == "coreos" { 244 system = "rkt" // Is it true? 245 role = "host" 246 } 247 } 248 249 // before returning for the first time, cache the system and role 250 cachedVirtOnce.Do(func() { 251 cachedVirtMutex.Lock() 252 defer cachedVirtMutex.Unlock() 253 cachedVirtMap = map[string]string{ 254 "system": system, 255 "role": role, 256 } 257 }) 258 259 return system, role, nil 260 } 261 262 func GetOSRelease() (platform string, version string, err error) { 263 contents, err := ReadLines(HostEtc("os-release")) 264 if err != nil { 265 return "", "", nil // return empty 266 } 267 for _, line := range contents { 268 field := strings.Split(line, "=") 269 if len(field) < 2 { 270 continue 271 } 272 switch field[0] { 273 case "ID": // use ID for lowercase 274 platform = trimQuotes(field[1]) 275 case "VERSION": 276 version = trimQuotes(field[1]) 277 } 278 } 279 return platform, version, nil 280 } 281 282 // trimQuotes removes quotes in the source string. 283 func trimQuotes(s string) string { 284 if len(s) >= 2 { 285 if s[0] == '"' && s[len(s)-1] == '"' { 286 return s[1 : len(s)-1] 287 } 288 } 289 return s 290 }