//go:build linux
// +build linux

package common

import (
	
	
	
	
	
	
	
	
	
	
	
)

// cachedBootTime must be accessed via atomic.Load/StoreUint64
var cachedBootTime uint64

func ( string) ([]string, error) {
	 := exec.Command("sysctl", "-n", )
	.Env = getSysctrlEnv(os.Environ())
	,  := .Output()
	if  != nil {
		return []string{}, 
	}
	 := strings.Replace(string(), "{ ", "", 1)
	 = strings.Replace(string(), " }", "", 1)
	 := strings.Fields(string())

	return , nil
}

func () (uint64, error) {
	return NumProcsWithContext(context.Background())
}

func ( context.Context) (uint64, error) {
	,  := os.Open(HostProcWithContext())
	if  != nil {
		return 0, 
	}
	defer .Close()

	,  := .Readdirnames(-1)
	if  != nil {
		return 0, 
	}
	var  uint64

	for ,  := range  {
		if _,  = strconv.ParseUint(, 10, 64);  == nil {
			++
		}
	}

	return , nil
}

func ( context.Context,  bool) (uint64, error) {
	if  {
		 := atomic.LoadUint64(&cachedBootTime)
		if  != 0 {
			return , nil
		}
	}

	, ,  := VirtualizationWithContext()
	if  != nil {
		return 0, 
	}

	 := true
	if  == "lxc" &&  == "guest" {
		// if lxc, /proc/uptime is used.
		 = false
	} else if  == "docker" &&  == "guest" {
		// also docker, guest
		 = false
	}

	if  {
		,  := readBootTimeStat()
		if  != nil {
			return 0, 
		}
		if  {
			atomic.StoreUint64(&cachedBootTime, )
		}
	}

	 := HostProcWithContext(, "uptime")
	,  := ReadLines()
	if  != nil {
		return handleBootTimeFileReadErr()
	}
	if len() != 1 {
		return 0, fmt.Errorf("wrong uptime format")
	}
	 := strings.Fields([0])
	,  := strconv.ParseFloat([0], 64)
	if  != nil {
		return 0, 
	}
	 := float64(time.Now().UnixNano()) / float64(time.Second)
	 :=  - 

	if  {
		atomic.StoreUint64(&cachedBootTime, uint64())
	}

	return uint64(), nil
}

func handleBootTimeFileReadErr( error) (uint64, error) {
	if os.IsPermission() {
		var  syscall.Sysinfo_t
		 := syscall.Sysinfo(&)
		if  != nil {
			return 0, 
		}

		 := time.Now().UnixNano() / int64(time.Second)
		 :=  - int64(.Uptime)
		return uint64(), nil
	}
	return 0, 
}

func readBootTimeStat( context.Context) (uint64, error) {
	 := HostProcWithContext(, "stat")
	,  := ReadLine(, "btime")
	if  != nil {
		return handleBootTimeFileReadErr()
	}
	if strings.HasPrefix(, "btime") {
		 := strings.Fields()
		if len() != 2 {
			return 0, fmt.Errorf("wrong btime format")
		}
		,  := strconv.ParseInt([1], 10, 64)
		if  != nil {
			return 0, 
		}
		 := uint64()
		return , nil
	}
	return 0, fmt.Errorf("could not find btime")
}

func () (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 ( context.Context) (string, string, error) {
	var ,  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 {
		,  := cachedVirtMap["system"], cachedVirtMap["role"]
		cachedVirtMutex.RUnlock()
		return , , nil
	}
	cachedVirtMutex.RUnlock()

	 := HostProcWithContext(, "xen")
	if PathExists() {
		 = "xen"
		 = "guest" // assume guest

		if PathExists(filepath.Join(, "capabilities")) {
			,  := ReadLines(filepath.Join(, "capabilities"))
			if  == nil {
				if StringsContains(, "control_d") {
					 = "host"
				}
			}
		}
	}

	 = HostProcWithContext(, "modules")
	if PathExists() {
		,  := ReadLines()
		if  == nil {
			if StringsContains(, "kvm") {
				 = "kvm"
				 = "host"
			} else if StringsContains(, "hv_util") {
				 = "hyperv"
				 = "guest"
			} else if StringsContains(, "vboxdrv") {
				 = "vbox"
				 = "host"
			} else if StringsContains(, "vboxguest") {
				 = "vbox"
				 = "guest"
			} else if StringsContains(, "vmware") {
				 = "vmware"
				 = "guest"
			}
		}
	}

	 = HostProcWithContext(, "cpuinfo")
	if PathExists() {
		,  := ReadLines()
		if  == nil {
			if StringsContains(, "QEMU Virtual CPU") ||
				StringsContains(, "Common KVM processor") ||
				StringsContains(, "Common 32-bit KVM processor") {
				 = "kvm"
				 = "guest"
			}
		}
	}

	 = HostProcWithContext(, "bus/pci/devices")
	if PathExists() {
		,  := ReadLines()
		if  == nil {
			if StringsContains(, "virtio-pci") {
				 = "guest"
			}
		}
	}

	 = HostProcWithContext()
	if PathExists(filepath.Join(, "bc", "0")) {
		 = "openvz"
		 = "host"
	} else if PathExists(filepath.Join(, "vz")) {
		 = "openvz"
		 = "guest"
	}

	// not use dmidecode because it requires root
	if PathExists(filepath.Join(, "self", "status")) {
		,  := ReadLines(filepath.Join(, "self", "status"))
		if  == nil {
			if StringsContains(, "s_context:") ||
				StringsContains(, "VxID:") {
				 = "linux-vserver"
			}
			// TODO: guest or host
		}
	}

	if PathExists(filepath.Join(, "1", "environ")) {
		,  := ReadFile(filepath.Join(, "1", "environ"))

		if  == nil {
			if strings.Contains(, "container=lxc") {
				 = "lxc"
				 = "guest"
			}
		}
	}

	if PathExists(filepath.Join(, "self", "cgroup")) {
		,  := ReadLines(filepath.Join(, "self", "cgroup"))
		if  == nil {
			if StringsContains(, "lxc") {
				 = "lxc"
				 = "guest"
			} else if StringsContains(, "docker") {
				 = "docker"
				 = "guest"
			} else if StringsContains(, "machine-rkt") {
				 = "rkt"
				 = "guest"
			} else if PathExists("/usr/bin/lxc-version") {
				 = "lxc"
				 = "host"
			}
		}
	}

	if PathExists(HostEtcWithContext(, "os-release")) {
		, ,  := GetOSReleaseWithContext()
		if  == nil &&  == "coreos" {
			 = "rkt" // Is it true?
			 = "host"
		}
	}

	if PathExists(HostRootWithContext(, ".dockerenv")) {
		 = "docker"
		 = "guest"
	}

	// before returning for the first time, cache the system and role
	cachedVirtOnce.Do(func() {
		cachedVirtMutex.Lock()
		defer cachedVirtMutex.Unlock()
		cachedVirtMap = map[string]string{
			"system": ,
			"role":   ,
		}
	})

	return , , nil
}

func () ( string,  string,  error) {
	return GetOSReleaseWithContext(context.Background())
}

func ( context.Context) ( string,  string,  error) {
	,  := ReadLines(HostEtcWithContext(, "os-release"))
	if  != nil {
		return "", "", nil // return empty
	}
	for ,  := range  {
		 := strings.Split(, "=")
		if len() < 2 {
			continue
		}
		switch [0] {
		case "ID": // use ID for lowercase
			 = trimQuotes([1])
		case "VERSION_ID":
			 = trimQuotes([1])
		}
	}

	// cleanup amazon ID
	if  == "amzn" {
		 = "amazon"
	}

	return , , nil
}

// Remove quotes of the source string
func trimQuotes( string) string {
	if len() >= 2 {
		if [0] == '"' && [len()-1] == '"' {
			return [1 : len()-1]
		}
	}
	return 
}