providers/linux/util.go (80 lines of code) (raw):
// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package linux
import (
"bufio"
"bytes"
"errors"
"fmt"
"os"
"strconv"
)
// parseKeyValue parses key/val pairs separated by the provided separator from
// each line in content and invokes the callback. White-space is trimmed from
// val. Empty lines are ignored. All non-empty lines must contain the separator
// otherwise an error is returned.
func parseKeyValue(content []byte, separator byte, callback func(key, value []byte) error) error {
var line []byte
for len(content) > 0 {
line, content, _ = bytes.Cut(content, []byte{'\n'})
if len(line) == 0 {
continue
}
key, value, ok := bytes.Cut(line, []byte{separator})
if !ok {
return fmt.Errorf("separator %q not found", separator)
}
callback(key, bytes.TrimSpace(value))
}
return nil
}
func findValue(filename, separator, key string) (string, error) {
content, err := os.ReadFile(filename)
if err != nil {
return "", err
}
var line []byte
sc := bufio.NewScanner(bytes.NewReader(content))
for sc.Scan() {
if bytes.HasPrefix(sc.Bytes(), []byte(key)) {
line = sc.Bytes()
break
}
}
if len(line) == 0 {
return "", fmt.Errorf("%v not found", key)
}
parts := bytes.SplitN(line, []byte(separator), 2)
if len(parts) != 2 {
return "", fmt.Errorf("unexpected line format for '%v'", string(line))
}
return string(bytes.TrimSpace(parts[1])), nil
}
func decodeBitMap(s string, lookupName func(int) string) ([]string, error) {
mask, err := strconv.ParseUint(s, 16, 64)
if err != nil {
return nil, err
}
var names []string
for i := 0; i < 64; i++ {
bit := mask & (1 << uint(i))
if bit > 0 {
names = append(names, lookupName(i))
}
}
return names, nil
}
// parses a meminfo field, returning either a raw numerical value, or the kB value converted to bytes
func parseBytesOrNumber(data []byte) (uint64, error) {
parts := bytes.Fields(data)
if len(parts) == 0 {
return 0, errors.New("empty value")
}
num, err := strconv.ParseUint(string(parts[0]), 10, 64)
if err != nil {
return 0, fmt.Errorf("failed to parse value: %w", err)
}
var multiplier uint64 = 1
if len(parts) >= 2 {
switch string(parts[1]) {
case "kB":
multiplier = 1024
default:
return 0, fmt.Errorf("unhandled unit %v", string(parts[1]))
}
}
return num * multiplier, nil
}