| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291 |
- // +build linux
- package common
- import (
- "context"
- "fmt"
- "os"
- "os/exec"
- "path/filepath"
- "strconv"
- "strings"
- "sync"
- "time"
- )
- func DoSysctrl(mib string) ([]string, error) {
- sysctl, err := exec.LookPath("sysctl")
- if err != nil {
- return []string{}, err
- }
- cmd := exec.Command(sysctl, "-n", mib)
- cmd.Env = getSysctrlEnv(os.Environ())
- out, err := cmd.Output()
- if err != nil {
- return []string{}, err
- }
- v := strings.Replace(string(out), "{ ", "", 1)
- v = strings.Replace(v, " }", "", 1)
- values := strings.Fields(v)
- return values, nil
- }
- func NumProcs() (uint64, error) {
- f, err := os.Open(HostProc())
- if err != nil {
- return 0, err
- }
- defer f.Close()
- list, err := f.Readdirnames(-1)
- if err != nil {
- return 0, err
- }
- var cnt uint64
- for _, v := range list {
- if _, err = strconv.ParseUint(v, 10, 64); err == nil {
- cnt++
- }
- }
- return cnt, nil
- }
- func BootTimeWithContext(ctx context.Context) (uint64, error) {
- system, role, err := Virtualization()
- if err != nil {
- return 0, err
- }
- statFile := "stat"
- if system == "lxc" && role == "guest" {
- // if lxc, /proc/uptime is used.
- statFile = "uptime"
- } else if system == "docker" && role == "guest" {
- // also docker, guest
- statFile = "uptime"
- }
- filename := HostProc(statFile)
- lines, err := ReadLines(filename)
- if err != nil {
- return 0, err
- }
- if statFile == "uptime" {
- if len(lines) != 1 {
- return 0, fmt.Errorf("wrong uptime format")
- }
- f := strings.Fields(lines[0])
- b, err := strconv.ParseFloat(f[0], 64)
- if err != nil {
- return 0, err
- }
- t := uint64(time.Now().Unix()) - uint64(b)
- return t, nil
- }
- if statFile == "stat" {
- for _, line := range lines {
- if strings.HasPrefix(line, "btime") {
- f := strings.Fields(line)
- if len(f) != 2 {
- return 0, fmt.Errorf("wrong btime format")
- }
- b, err := strconv.ParseInt(f[1], 10, 64)
- if err != nil {
- return 0, err
- }
- t := uint64(b)
- return t, nil
- }
- }
- }
- return 0, fmt.Errorf("could not find btime")
- }
- func Virtualization() (string, string, error) {
- return VirtualizationWithContext(context.Background())
- }
- // required variables for concurrency safe virtualization caching.
- var (
- cachedVirtMap map[string]string
- cachedVirtMutex sync.RWMutex
- cachedVirtOnce sync.Once
- )
- func VirtualizationWithContext(ctx context.Context) (string, string, error) {
- var system, role string
- // if cached already, return from cache
- cachedVirtMutex.RLock() // unlock won't be deferred so concurrent reads don't wait for long
- if cachedVirtMap != nil {
- cachedSystem, cachedRole := cachedVirtMap["system"], cachedVirtMap["role"]
- cachedVirtMutex.RUnlock()
- return cachedSystem, cachedRole, nil
- }
- cachedVirtMutex.RUnlock()
- filename := HostProc("xen")
- if PathExists(filename) {
- system = "xen"
- role = "guest" // assume guest
- if PathExists(filepath.Join(filename, "capabilities")) {
- contents, err := ReadLines(filepath.Join(filename, "capabilities"))
- if err == nil && StringsContains(contents, "control_d") {
- role = "host"
- }
- }
- }
- filename = HostProc("modules")
- if PathExists(filename) {
- contents, err := ReadLines(filename)
- if err == nil {
- switch {
- case StringsContains(contents, "kvm"):
- system = "kvm"
- role = "host"
- case StringsContains(contents, "vboxdrv"):
- system = "vbox"
- role = "host"
- case StringsContains(contents, "vboxguest"):
- system = "vbox"
- role = "guest"
- case StringsContains(contents, "vmware"):
- system = "vmware"
- role = "guest"
- }
- }
- }
- filename = HostProc("cpuinfo")
- if PathExists(filename) {
- contents, err := ReadLines(filename)
- if err == nil {
- if StringsContains(contents, "QEMU Virtual CPU") ||
- StringsContains(contents, "Common KVM processor") ||
- StringsContains(contents, "Common 32-bit KVM processor") {
- system = "kvm"
- role = "guest"
- }
- }
- }
- filename = HostProc("bus/pci/devices")
- if PathExists(filename) {
- contents, err := ReadLines(filename)
- if err == nil {
- if StringsContains(contents, "virtio-pci") {
- role = "guest"
- }
- }
- }
- filename = HostProc()
- if PathExists(filepath.Join(filename, "bc", "0")) {
- system = "openvz"
- role = "host"
- } else if PathExists(filepath.Join(filename, "vz")) {
- system = "openvz"
- role = "guest"
- }
- // not use dmidecode because it requires root
- if PathExists(filepath.Join(filename, "self", "status")) {
- contents, err := ReadLines(filepath.Join(filename, "self", "status"))
- if err == nil {
- if StringsContains(contents, "s_context:") ||
- StringsContains(contents, "VxID:") {
- system = "linux-vserver"
- }
- // TODO: guest or host
- }
- }
- if PathExists(filepath.Join(filename, "1", "environ")) {
- contents, err := ReadFile(filepath.Join(filename, "1", "environ"))
- if err == nil {
- if strings.Contains(contents, "container=lxc") {
- system = "lxc"
- role = "guest"
- }
- }
- }
- if PathExists(filepath.Join(filename, "self", "cgroup")) {
- contents, err := ReadLines(filepath.Join(filename, "self", "cgroup"))
- if err == nil {
- switch {
- case StringsContains(contents, "lxc"):
- system = "lxc"
- role = "guest"
- case StringsContains(contents, "docker"):
- system = "docker"
- role = "guest"
- case StringsContains(contents, "machine-rkt"):
- system = "rkt"
- role = "guest"
- case PathExists("/usr/bin/lxc-version"):
- system = "lxc"
- role = "host"
- }
- }
- }
- if PathExists(HostEtc("os-release")) {
- p, _, err := GetOSRelease()
- if err == nil && p == "coreos" {
- system = "rkt" // Is it true?
- role = "host"
- }
- }
- // before returning for the first time, cache the system and role
- cachedVirtOnce.Do(func() {
- cachedVirtMutex.Lock()
- defer cachedVirtMutex.Unlock()
- cachedVirtMap = map[string]string{
- "system": system,
- "role": role,
- }
- })
- return system, role, nil
- }
- func GetOSRelease() (platform string, version string, err error) {
- contents, err := ReadLines(HostEtc("os-release"))
- if err != nil {
- return "", "", nil // return empty
- }
- for _, line := range contents {
- field := strings.Split(line, "=")
- if len(field) < 2 {
- continue
- }
- switch field[0] {
- case "ID": // use ID for lowercase
- platform = trimQuotes(field[1])
- case "VERSION":
- version = trimQuotes(field[1])
- }
- }
- return platform, version, nil
- }
- // trimQuotes removes quotes in the source string.
- func trimQuotes(s string) string {
- if len(s) >= 2 {
- if s[0] == '"' && s[len(s)-1] == '"' {
- return s[1 : len(s)-1]
- }
- }
- return s
- }
|