gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

os_release_unix.go (4733B)


      1 // Copyright The OpenTelemetry Authors
      2 //
      3 // Licensed under the Apache License, Version 2.0 (the "License");
      4 // you may not use this file except in compliance with the License.
      5 // You may obtain a copy of the License at
      6 //
      7 //     http://www.apache.org/licenses/LICENSE-2.0
      8 //
      9 // Unless required by applicable law or agreed to in writing, software
     10 // distributed under the License is distributed on an "AS IS" BASIS,
     11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 // See the License for the specific language governing permissions and
     13 // limitations under the License.
     14 
     15 //go:build aix || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos
     16 // +build aix dragonfly freebsd linux netbsd openbsd solaris zos
     17 
     18 package resource // import "go.opentelemetry.io/otel/sdk/resource"
     19 
     20 import (
     21 	"bufio"
     22 	"fmt"
     23 	"io"
     24 	"os"
     25 	"strings"
     26 )
     27 
     28 // osRelease builds a string describing the operating system release based on the
     29 // properties of the os-release file. If no os-release file is found, or if the
     30 // required properties to build the release description string are missing, an empty
     31 // string is returned instead. For more information about os-release files, see:
     32 // https://www.freedesktop.org/software/systemd/man/os-release.html
     33 func osRelease() string {
     34 	file, err := getOSReleaseFile()
     35 	if err != nil {
     36 		return ""
     37 	}
     38 
     39 	defer file.Close()
     40 
     41 	values := parseOSReleaseFile(file)
     42 
     43 	return buildOSRelease(values)
     44 }
     45 
     46 // getOSReleaseFile returns a *os.File pointing to one of the well-known os-release
     47 // files, according to their order of preference. If no file can be opened, it
     48 // returns an error.
     49 func getOSReleaseFile() (*os.File, error) {
     50 	return getFirstAvailableFile([]string{"/etc/os-release", "/usr/lib/os-release"})
     51 }
     52 
     53 // parseOSReleaseFile process the file pointed by `file` as an os-release file and
     54 // returns a map with the key-values contained in it. Empty lines or lines starting
     55 // with a '#' character are ignored, as well as lines with the missing key=value
     56 // separator. Values are unquoted and unescaped.
     57 func parseOSReleaseFile(file io.Reader) map[string]string {
     58 	values := make(map[string]string)
     59 	scanner := bufio.NewScanner(file)
     60 
     61 	for scanner.Scan() {
     62 		line := scanner.Text()
     63 
     64 		if skip(line) {
     65 			continue
     66 		}
     67 
     68 		key, value, ok := parse(line)
     69 		if ok {
     70 			values[key] = value
     71 		}
     72 	}
     73 
     74 	return values
     75 }
     76 
     77 // skip returns true if the line is blank or starts with a '#' character, and
     78 // therefore should be skipped from processing.
     79 func skip(line string) bool {
     80 	line = strings.TrimSpace(line)
     81 
     82 	return len(line) == 0 || strings.HasPrefix(line, "#")
     83 }
     84 
     85 // parse attempts to split the provided line on the first '=' character, and then
     86 // sanitize each side of the split before returning them as a key-value pair.
     87 func parse(line string) (string, string, bool) {
     88 	k, v, found := strings.Cut(line, "=")
     89 
     90 	if !found || len(k) == 0 {
     91 		return "", "", false
     92 	}
     93 
     94 	key := strings.TrimSpace(k)
     95 	value := unescape(unquote(strings.TrimSpace(v)))
     96 
     97 	return key, value, true
     98 }
     99 
    100 // unquote checks whether the string `s` is quoted with double or single quotes
    101 // and, if so, returns a version of the string without them. Otherwise it returns
    102 // the provided string unchanged.
    103 func unquote(s string) string {
    104 	if len(s) < 2 {
    105 		return s
    106 	}
    107 
    108 	if (s[0] == '"' || s[0] == '\'') && s[0] == s[len(s)-1] {
    109 		return s[1 : len(s)-1]
    110 	}
    111 
    112 	return s
    113 }
    114 
    115 // unescape removes the `\` prefix from some characters that are expected
    116 // to have it added in front of them for escaping purposes.
    117 func unescape(s string) string {
    118 	return strings.NewReplacer(
    119 		`\$`, `$`,
    120 		`\"`, `"`,
    121 		`\'`, `'`,
    122 		`\\`, `\`,
    123 		"\\`", "`",
    124 	).Replace(s)
    125 }
    126 
    127 // buildOSRelease builds a string describing the OS release based on the properties
    128 // available on the provided map. It favors a combination of the `NAME` and `VERSION`
    129 // properties as first option (falling back to `VERSION_ID` if `VERSION` isn't
    130 // found), and using `PRETTY_NAME` alone if some of the previous are not present. If
    131 // none of these properties are found, it returns an empty string.
    132 //
    133 // The rationale behind not using `PRETTY_NAME` as first choice was that, for some
    134 // Linux distributions, it doesn't include the same detail that can be found on the
    135 // individual `NAME` and `VERSION` properties, and combining `PRETTY_NAME` with
    136 // other properties can produce "pretty" redundant strings in some cases.
    137 func buildOSRelease(values map[string]string) string {
    138 	var osRelease string
    139 
    140 	name := values["NAME"]
    141 	version := values["VERSION"]
    142 
    143 	if version == "" {
    144 		version = values["VERSION_ID"]
    145 	}
    146 
    147 	if name != "" && version != "" {
    148 		osRelease = fmt.Sprintf("%s %s", name, version)
    149 	} else {
    150 		osRelease = values["PRETTY_NAME"]
    151 	}
    152 
    153 	return osRelease
    154 }