func Chroot() (bool, error) {
	var a, b syscall.Stat_t
	if err := syscall.Lstat("/proc/1/root", &a); err != nil {
		return false, err
	}
	if err := syscall.Lstat("/", &b); err != nil {
		return false, err
	}
	return a.Ino == b.Ino && a.Dev == b.Dev, nil
}
Chroot detects if we are running in a chroot or a pivot_root.
Currently, we can not distinguish between the two.
package container
import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"strconv"
	"strings"
	"syscall"
	"github.com/syndtr/gocapability/capability"
	"golang.org/x/sys/unix"
)
const (
	RuntimeDocker = "docker"
	RuntimeRkt = "rkt"
	RuntimeNspawn = "systemd-nspawn"
	RuntimeLXC = "lxc"
	RuntimeLXCLibvirt = "lxc-libvirt"
	RuntimeOpenVZ = "openvz"
	RuntimeKubernetes = "kube"
	RuntimeGarden = "garden"
	uint32Max = 4294967295
)
var (
	ErrContainerRuntimeNotFound = errors.New("container runtime could not be found")
	runtimes = []string{RuntimeDocker, RuntimeRkt, RuntimeNspawn, RuntimeLXC, RuntimeLXCLibvirt, RuntimeOpenVZ, RuntimeKubernetes, RuntimeGarden}
)
func DetectRuntime() (string, error) {
	cgroups := readFile("/proc/self/cgroup")
	if len(cgroups) > 0 {
		for _, runtime := range runtimes {
			if strings.Contains(cgroups, runtime) {
				return runtime, nil
			}
		}
	}
	if fileExists("/proc/vz") && !fileExists("/proc/bc") {
		return RuntimeOpenVZ, nil
	}
	ctrenv := os.Getenv("container")
	if ctrenv != "" {
		for _, runtime := range runtimes {
			if ctrenv == runtime {
				return runtime, nil
			}
		}
	}
	f := readFile("/run/systemd/container")
	if len(f) > 0 {
		for _, runtime := range runtimes {
			if f == runtime {
				return runtime, nil
			}
		}
	}
	return "not-found", ErrContainerRuntimeNotFound
}
func HasNamespace(ns string) (bool, error) {
	file := fmt.Sprintf("/proc/1/ns/%s", ns)
	var info syscall.Stat_t
	if err := syscall.Lstat(file, &info); err != nil {
		return false, &os.PathError{Op: "lstat", Path: file, Err: err}
	}
	if info.Dev > 4 {
		return true, nil
	}
	return false, nil
}
func AppArmorProfile() string {
	f := readFile("/proc/self/attr/current")
	if f == "" {
		return "none"
	}
	return f
}
type UserMapping struct {
	ContainerID int64
	HostID      int64
	Range       int64
}
func UserNamespace() (bool, []UserMapping) {
	f := readFile("/proc/self/uid_map")
	if len(f) < 0 {
		return true, nil
	}
	userNs, mappings, err := readUserMappings(f)
	if err != nil {
		return false, nil
	}
	return userNs, mappings
}
func readUserMappings(f string) (iuserNS bool, mappings []UserMapping, err error) {
	parts := strings.Split(f, " ")
	parts = deleteEmpty(parts)
	if len(parts) < 3 {
		return false, nil, nil
	}
	for i := 0; i < len(parts); i += 3 {
		nsu, hu, r := parts[i], parts[i+1], parts[i+2]
		mapping := UserMapping{}
		mapping.ContainerID, err = strconv.ParseInt(nsu, 10, 0)
		if err != nil {
			return false, nil, nil
		}
		mapping.HostID, err = strconv.ParseInt(hu, 10, 0)
		if err != nil {
			return false, nil, nil
		}
		mapping.Range, err = strconv.ParseInt(r, 10, 0)
		if err != nil {
			return false, nil, nil
		}
		if mapping.ContainerID == 0 && mapping.HostID == 0 && mapping.Range == uint32Max {
			return false, nil, nil
		}
		mappings = append(mappings, mapping)
	}
	return true, mappings, nil
}
func Capabilities() (map[string][]string, error) {
	allCaps := capability.List()
	caps, err := capability.NewPid(0)
	if err != nil {
		return nil, err
	}
	allowedCaps := map[string][]string{}
	allowedCaps["EFFECTIVE | PERMITTED | INHERITABLE"] = []string{}
	allowedCaps["BOUNDING"] = []string{}
	allowedCaps["AMBIENT"] = []string{}
	for _, cap := range allCaps {
		if caps.Get(capability.CAPS, cap) {
			allowedCaps["EFFECTIVE | PERMITTED | INHERITABLE"] = append(allowedCaps["EFFECTIVE | PERMITTED | INHERITABLE"], cap.String())
		}
		if caps.Get(capability.BOUNDING, cap) {
			allowedCaps["BOUNDING"] = append(allowedCaps["BOUNDING"], cap.String())
		}
		if caps.Get(capability.AMBIENT, cap) {
			allowedCaps["AMBIENT"] = append(allowedCaps["AMBIENT"], cap.String())
		}
	}
	return allowedCaps, nil
}