// Package osdetect provides OS detection from /etc/os-release (D-032). // It's a separate package to avoid import cycles between internal/cli // and internal/doctor (both need to detect the local OS). package osdetect import ( "bufio" "os" "strings" ) // osReleasePaths are checked in order for the os-release file. The // freedesktop.org spec says /etc/os-release is the canonical path, // with /usr/lib/os-release as a fallback for minimal containers that // may not symlink the former. var osReleasePaths = []string{"/etc/os-release", "/usr/lib/os-release"} // Detect reads /etc/os-release (then /usr/lib/os-release as a // fallback) and returns the value of the ID= field. Returns "linux" // (the generic fallback per D-032) if the file is missing, the ID // field is absent, or the value is empty. Unknown ID values (e.g. // "fedora", "arch") are returned verbatim — doctor os can warn on // unknown values, but orca init must not fail. func Detect() string { for _, p := range osReleasePaths { data, err := os.ReadFile(p) if err != nil { continue } if id := ParseID(data); id != "" { return id } } return "linux" } // ParseID extracts the ID= value from os-release content. // The format is shell-compatible KEY=VALUE lines; values may be // double-quoted. Returns "" if ID is absent or empty. func ParseID(data []byte) string { scanner := bufio.NewScanner(strings.NewReader(string(data))) for scanner.Scan() { line := strings.TrimSpace(scanner.Text()) if line == "" || strings.HasPrefix(line, "#") { continue } key, value, ok := strings.Cut(line, "=") if !ok { continue } key = strings.TrimSpace(key) if key != "ID" { continue } value = strings.TrimSpace(value) // Strip surrounding double quotes (freedesktop spec allows quoted values). if len(value) >= 2 && value[0] == '"' && value[len(value)-1] == '"' { value = value[1 : len(value)-1] } return value } return "" }