1
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
66 statFile = "uptime"
67 } else if system == "docker" && role == "guest" {
68
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
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
125 cachedVirtMutex.RLock()
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"
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
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
211 if PathExists(filepath.Join(filename, "1", "environ")) {
212 contents, err := ReadFile(filepath.Join(filename, "1", "environ"))
213
214 if err == nil {
215 if strings.Contains(contents, "container=lxc") {
216 system = "lxc"
217 role = "guest"
218 }
219 }
220 }
221
222 if PathExists(filepath.Join(filename, "self", "cgroup")) {
223 contents, err := ReadLines(filepath.Join(filename, "self", "cgroup"))
224 if err == nil {
225 switch {
226 case StringsContains(contents, "lxc"):
227 system = "lxc"
228 role = "guest"
229 case StringsContains(contents, "docker"):
230 system = "docker"
231 role = "guest"
232 case StringsContains(contents, "machine-rkt"):
233 system = "rkt"
234 role = "guest"
235 case PathExists("/usr/bin/lxc-version"):
236 system = "lxc"
237 role = "host"
238 }
239 }
240 }
241
242 if PathExists(HostEtc("os-release")) {
243 p, _, err := GetOSRelease()
244 if err == nil && p == "coreos" {
245 system = "rkt"
246 role = "host"
247 }
248 }
249
250
251 cachedVirtOnce.Do(func() {
252 cachedVirtMutex.Lock()
253 defer cachedVirtMutex.Unlock()
254 cachedVirtMap = map[string]string{
255 "system": system,
256 "role": role,
257 }
258 })
259
260 return system, role, nil
261 }
262
263 func GetOSRelease() (platform string, version string, err error) {
264 contents, err := ReadLines(HostEtc("os-release"))
265 if err != nil {
266 return "", "", nil
267 }
268 for _, line := range contents {
269 field := strings.Split(line, "=")
270 if len(field) < 2 {
271 continue
272 }
273 switch field[0] {
274 case "ID":
275 platform = trimQuotes(field[1])
276 case "VERSION":
277 version = trimQuotes(field[1])
278 }
279 }
280 return platform, version, nil
281 }
282
283
284 func trimQuotes(s string) string {
285 if len(s) >= 2 {
286 if s[0] == '"' && s[len(s)-1] == '"' {
287 return s[1 : len(s)-1]
288 }
289 }
290 return s
291 }
292
View as plain text