package main import ( "bufio" "os" "strings" ) // OSInfo holds the detected distribution identity and init system. type OSInfo struct { ID string Version string Init string // "systemd" or "openrc" } // detectOS parses /etc/os-release and maps the distro to its init system. func detectOS() *OSInfo { f, err := os.Open("/etc/os-release") if err != nil { die("Cannot detect OS: /etc/os-release not found") } defer f.Close() vals := map[string]string{} scanner := bufio.NewScanner(f) for scanner.Scan() { line := strings.TrimSpace(scanner.Text()) if line == "" || strings.HasPrefix(line, "#") { continue } if i := strings.Index(line, "="); i >= 0 { key := strings.TrimSpace(line[:i]) vals[key] = unquoteShell(line[i+1:]) } } osInfo := &OSInfo{ ID: valueOr(vals["ID"], "unknown"), Version: valueOr(vals["VERSION_ID"], "unknown"), } switch osInfo.ID { case "ubuntu", "debian", "linuxmint": osInfo.Init = "systemd" case "alpine": osInfo.Init = "openrc" default: die("Unsupported OS: %s (supported: ubuntu, debian, linuxmint, alpine)", osInfo.ID) } // A systemd-based distro may still be running here without systemd as // PID 1 — the container case (e.g. the Linux Mint image used for the // alternative container build). systemd creates /run/systemd/system only // when it is actually active as the init system, so its absence is the // standard way to detect this. Fall back to driving awg-quick directly, // same as the Alpine/OpenRC path. if osInfo.Init == "systemd" && !systemdRunning() { osInfo.Init = "openrc" } info("Detected OS: %s %s (init: %s)", osInfo.ID, osInfo.Version, osInfo.Init) return osInfo } // systemdRunning reports whether systemd is active as PID 1. func systemdRunning() bool { _, err := os.Stat("/run/systemd/system") return err == nil } func valueOr(v, def string) string { if v == "" { return def } return v }