ALR
1/*
2* ALR - Any Linux Repository
3* Copyright (C) 2024 Евгений Храмов
4*
5* This program is free software: you can redistribute it and/or modify
6* it under the terms of the GNU General Public License as published by
7* the Free Software Foundation, either version 3 of the License, or
8* (at your option) any later version.
9*
10* This program is distributed in the hope that it will be useful,
11* but WITHOUT ANY WARRANTY; without even the implied warranty of
12* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13* GNU General Public License for more details.
14*
15* You should have received a copy of the GNU General Public License
16* along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19package distro20
21import (22"context"23"os"24"strings"25
26"plemya-x.ru/alr/internal/shutils/handlers"27"mvdan.cc/sh/v3/expand"28"mvdan.cc/sh/v3/interp"29"mvdan.cc/sh/v3/syntax"30)
31
32// OSRelease contains information from an os-release file
33type OSRelease struct {34Name string35PrettyName string36ID string37Like []string38VersionID string39ANSIColor string40HomeURL string41DocumentationURL string42SupportURL string43BugReportURL string44Logo string45}
46
47var parsed *OSRelease48
49// OSReleaseName returns a struct parsed from the system's os-release
50// file. It checks /etc/os-release as well as /usr/lib/os-release.
51// The first time it's called, it'll parse the os-release file.
52// Subsequent calls will return the same value.
53func ParseOSRelease(ctx context.Context) (*OSRelease, error) {54if parsed != nil {55return parsed, nil56}57
58fl, err := os.Open("/usr/lib/os-release")59if err != nil {60fl, err = os.Open("/etc/os-release")61if err != nil {62return nil, err63}64}65
66file, err := syntax.NewParser().Parse(fl, "/usr/lib/os-release")67if err != nil {68return nil, err69}70
71fl.Close()72
73// Create new shell interpreter with nop open, exec, readdir, and stat handlers74// as well as no environment variables in order to prevent vulnerabilities75// caused by changing the os-release file.76runner, err := interp.New(77interp.OpenHandler(handlers.NopOpen),78interp.ExecHandler(handlers.NopExec),79interp.ReadDirHandler(handlers.NopReadDir),80interp.StatHandler(handlers.NopStat),81interp.Env(expand.ListEnviron()),82)83if err != nil {84return nil, err85}86
87err = runner.Run(ctx, file)88if err != nil {89return nil, err90}91
92out := &OSRelease{93Name: runner.Vars["NAME"].Str,94PrettyName: runner.Vars["PRETTY_NAME"].Str,95ID: runner.Vars["ID"].Str,96VersionID: runner.Vars["VERSION_ID"].Str,97ANSIColor: runner.Vars["ANSI_COLOR"].Str,98HomeURL: runner.Vars["HOME_URL"].Str,99DocumentationURL: runner.Vars["DOCUMENTATION_URL"].Str,100SupportURL: runner.Vars["SUPPORT_URL"].Str,101BugReportURL: runner.Vars["BUG_REPORT_URL"].Str,102Logo: runner.Vars["LOGO"].Str,103}104
105distroUpdated := false106if distID, ok := os.LookupEnv("ALR_DISTRO"); ok {107out.ID = distID108}109
110if distLike, ok := os.LookupEnv("ALR_DISTRO_LIKE"); ok {111out.Like = strings.Split(distLike, " ")112} else if runner.Vars["ID_LIKE"].IsSet() && !distroUpdated {113out.Like = strings.Split(runner.Vars["ID_LIKE"].Str, " ")114}115
116parsed = out117return out, nil118}
119