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