-
Notifications
You must be signed in to change notification settings - Fork 14
/
system.go
400 lines (368 loc) · 9.78 KB
/
system.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
package system
import (
"fmt"
"io"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"reflect"
"regexp"
"runtime"
"strings"
"syscall"
"time"
"unicode"
)
// SaptuneSectionDir defines saptunes saved state directory
const SaptuneSectionDir = "/run/saptune/sections"
// map to hold the current available systemd services
var services map[string]string
// stdOutOrg contains the origin stdout for resetting, if needed
var stdOutOrg = os.Stdout
// OSExit defines, which exit function should be used
var OSExit = os.Exit
// ErrorExitOut defines, which exit output function should be used
var ErrorExitOut = ErrorLog
// ErrExitOut defines the output function, which should be used in case
// of colored output
var ErrExitOut = errExitOut
// InfoOut defines, which log output function should be used
var InfoOut = InfoLog
// DmiID is the path to the dmidecode representation in the /sys filesystem
var DmiID = "/sys/class/dmi/id"
// IsUserRoot return true only if the current user is root.
func IsUserRoot() bool {
return os.Getuid() == 0
}
// GetSolutionSelector returns the architecture string
// needed to select the supported set os solutions
func GetSolutionSelector() string {
solutionSelector := runtime.GOARCH
if IsPagecacheAvailable() {
solutionSelector = solutionSelector + "_PC"
}
return solutionSelector
}
// CmdIsAvailable returns true, if the cmd is available.
func CmdIsAvailable(cmdName string) bool {
if _, err := os.Stat(cmdName); os.IsNotExist(err) {
return false
}
return true
}
// GetOsVers returns the OS version
func GetOsVers() string {
// VERSION="12", VERSION="15"
// VERSION="12-SP1", VERSION="12-SP2", VERSION="12-SP3"
var re = regexp.MustCompile(`VERSION="([\w-]+)"`)
val, err := ioutil.ReadFile("/etc/os-release")
if err != nil {
return ""
}
matches := re.FindStringSubmatch(string(val))
if len(matches) == 0 {
return ""
}
return matches[1]
}
// GetOsName returns the OS name
func GetOsName() string {
// NAME="SLES"
var re = regexp.MustCompile(`NAME="([\w\s]+)"`)
val, err := ioutil.ReadFile("/etc/os-release")
if err != nil {
return ""
}
matches := re.FindStringSubmatch(string(val))
if len(matches) == 0 {
return ""
}
return matches[1]
}
// IsSLE15 returns true, if System is running a SLE15 release
func IsSLE15() bool {
var re = regexp.MustCompile(`15-SP\d+`)
if GetOsName() == "SLES" && (GetOsVers() == "15" || re.MatchString(GetOsVers())) {
return true
}
return false
}
// IsSLE12 returns true, if System is running a SLE12 release
func IsSLE12() bool {
var re = regexp.MustCompile(`12-SP\d+`)
if GetOsName() == "SLES" && (GetOsVers() == "12" || re.MatchString(GetOsVers())) {
return true
}
return false
}
// CheckForPattern returns true, if the file is available and
// contains the expected string
func CheckForPattern(file, pattern string) bool {
if _, err := os.Stat(file); os.IsNotExist(err) {
return false
}
content, err := ioutil.ReadFile(file)
if err != nil {
return false
}
//check whether content contains substring pattern
return strings.Contains(string(content), pattern)
}
// CalledFrom returns the name and the line number of the calling source file
func CalledFrom() string {
ret := ""
_, file, no, ok := runtime.Caller(2)
if ok {
_, relfile := filepath.Split(file)
ret = fmt.Sprintf("%s:%d: ", relfile, no)
}
return ret
}
func errExitOut(writer io.Writer, template string, stuff ...interface{}) {
// stuff is: color, bold, text/template, reset bold, reset color
stuff = stuff[1:]
fmt.Fprintf(writer, "%s%sERROR: "+template+"%s%s\n", stuff...)
if len(stuff) >= 4 {
stuff = stuff[2 : len(stuff)-2]
}
ErrLog(template+"\n", stuff...)
}
// ErrorExit prints the message to stderr and exit 1.
func ErrorExit(template string, stuff ...interface{}) {
exState := 1
fieldType := ""
field := len(stuff) - 1
if field >= 0 {
fieldType = reflect.TypeOf(stuff[field]).String()
}
if fieldType == "*exec.ExitError" {
// get return code of failed command, if available
if exitError, ok := stuff[field].(*exec.ExitError); ok {
exState = exitError.Sys().(syscall.WaitStatus).ExitStatus()
}
}
if fieldType == "int" {
exState = reflect.ValueOf(stuff[field]).Interface().(int)
stuff = stuff[:len(stuff)-1]
}
if len(template) != 0 {
if len(stuff) > 0 && stuff[0] == "colorPrint" {
ErrExitOut(os.Stderr, template, stuff...)
} else {
ErrorExitOut(template+"\n", stuff...)
}
}
if isOwnLock() {
ReleaseSaptuneLock()
}
if jerr := jOut(exState); jerr != nil {
exState = 130
}
InfoOut("saptune terminated with exit code '%v'", exState)
OSExit(exState)
}
// OutIsTerm returns true, if Stdout is a terminal
func OutIsTerm(writer *os.File) bool {
fileInfo, _ := writer.Stat()
return (fileInfo.Mode() & os.ModeCharDevice) != 0
}
// InitOut initializes the various output methodes
// currently only json and screen are supported
func InitOut(logSwitch map[string]string) {
if GetFlagVal("format") == "json" {
// if writing json format, switch off
// the stdout and stderr output of the log messages
logSwitch["verbose"] = "off"
logSwitch["error"] = "off"
// switch off stdout
if os.Getenv("SAPTUNE_JDEBUG") != "on" {
os.Stdout, _ = os.Open(os.DevNull)
}
jInit()
}
}
// SwitchOffOut disables stdout and stderr
func SwitchOffOut() (*os.File, *os.File) {
oldStdout := os.Stdout
oldSdterr := os.Stderr
os.Stdout, _ = os.Open(os.DevNull)
os.Stderr, _ = os.Open(os.DevNull)
return oldStdout, oldSdterr
}
// SwitchOnOut restores stdout and stderr to the settings before SwitchOffOut
// was called
func SwitchOnOut(stdout *os.File, stderr *os.File) {
os.Stdout = stdout
os.Stderr = stderr
}
// WrapTxt implements something like 'fold' command
// A given text string will be wrapped at word borders into
// lines of a given width
func WrapTxt(text string, width int) (folded []string) {
var words []string
fallback := false
if strings.Contains(text, " ") {
words = strings.Split(text, " ")
} else {
// fallback (e.g. net.ipv4.ip_local_reserved_ports)
words = strings.Split(text, ",")
fallback = true
}
if len(words) == 0 {
return
}
foldedTxt := words[0]
spaceLeft := width - len(foldedTxt)
noSpace := false
for _, word := range words[1:] {
if word == "\n" {
foldedTxt += word
spaceLeft = width
noSpace = true
continue
}
if len(word)+1 > spaceLeft {
// fold; start next row
if fallback {
foldedTxt += ",\n" + word
} else {
foldedTxt += "\n" + word
}
if strings.HasSuffix(word, "\n") {
spaceLeft = width
noSpace = true
} else {
spaceLeft = width - len(word)
noSpace = false
}
} else {
if noSpace {
foldedTxt += word
spaceLeft -= len(word)
noSpace = false
} else if fallback {
foldedTxt += "," + word
spaceLeft -= 1 + len(word)
} else {
foldedTxt += " " + word
spaceLeft -= 1 + len(word)
}
if strings.HasSuffix(word, "\n") {
spaceLeft = width
noSpace = true
}
}
}
folded = strings.Split(foldedTxt, "\n")
return
}
// GetDmiID return the content of /sys/devices/virtual/dmi/id/<file> or
// an empty string
func GetDmiID(file string) (string, error) {
var err error
var content []byte
ret := ""
fileName := fmt.Sprintf("%s/%s", DmiID, file)
if content, err = ioutil.ReadFile(fileName); err == nil {
ret = strings.TrimSpace(string(content))
} else {
InfoLog("failed to read %s - %v", fileName, err)
}
return ret, err
}
// GetHWIdentity returns the hardwar vendor or model of the system
// needs adaption, if the files to identify the hardware will change or
// if we need to look at different files for different vendors
// but the 'open' API GetDmiID exists for workarounds at customer side
func GetHWIdentity(info string) (string, error) {
var err error
var content []byte
fix := ""
fileName := ""
ret := ""
switch info {
case "vendor":
if runtime.GOARCH == "ppc64le" {
fix = "IBM"
} else {
fileName = fmt.Sprintf("%s/board_vendor", DmiID)
}
case "model":
if runtime.GOARCH == "ppc64le" {
fileName = "/sys/firmware/devicetree/base/model"
} else {
fileName = fmt.Sprintf("%s/product_name", DmiID)
}
}
if fileName != "" {
if content, err = ioutil.ReadFile(fileName); err == nil {
ret = strings.TrimSpace(string(content))
} else {
InfoLog("failed to read %s - %v", fileName, err)
}
}
if fix != "" {
ret = fix
err = nil
}
return ret, err
}
// StripComment will strip everything right from the given comment character
// (including the comment character) and returns the resulting string
// comment characters can be '#' or ';' or something else
// or a regex like `\s#[^#]|"\s#[^#]`
func StripComment(str, commentChars string) string {
ret := str
re := regexp.MustCompile(commentChars)
if cut := re.FindStringIndex(str); cut != nil {
ret = strings.TrimRightFunc(str[:cut[0]], unicode.IsSpace)
// strip masked # (\s## -> \s#) inside the text
re = regexp.MustCompile(`\s(##)`)
ret = re.ReplaceAllString(ret, "#")
}
return ret
}
// GetVirtStatus gets the status of virtualization environment
func GetVirtStatus() string {
vtype := ""
// first check vm (-v)
virt, vm, _ := SystemdDetectVirt("-v")
if virt {
// vm detected
vtype = vm
}
// next check container (-c)
virt, container, _ := SystemdDetectVirt("-c")
if virt {
// container detected
if vtype == "" {
vtype = container
} else {
vtype = vtype + " " + container
}
}
// last check for chroot (-r)
// be in mind, that the command will not deliver any output, but only
// return 0, if it found a chroot env or 1, if not
virt, _, _ = SystemdDetectVirt("-r")
if virt {
// chroot detected
if vtype == "" {
vtype = "chroot"
} else {
vtype = vtype + " chroot"
}
}
if vtype == "" {
vtype = "none"
}
return vtype
}
// Watch prints the current time
func Watch() string {
t := time.Now()
//watch := fmt.Sprintf("%s", t.Format(time.UnixDate))
watch := t.Format("2006/01/02 15:04:05.99999999")
return watch
}