1package provision
2
3import (
4	"bufio"
5	"bytes"
6	"fmt"
7	"reflect"
8	"strings"
9
10	"github.com/docker/machine/libmachine/log"
11)
12
13// The /etc/os-release file contains operating system identification data
14// See http://www.freedesktop.org/software/systemd/man/os-release.html for more details
15
16// OsRelease reflects values in /etc/os-release
17// Values in this struct must always be string
18// or the reflection will not work properly.
19type OsRelease struct {
20	AnsiColor    string `osr:"ANSI_COLOR"`
21	Name         string `osr:"NAME"`
22	Version      string `osr:"VERSION"`
23	Variant      string `osr:"VARIANT"`
24	VariantID    string `osr:"VARIANT_ID"`
25	ID           string `osr:"ID"`
26	IDLike       string `osr:"ID_LIKE"`
27	PrettyName   string `osr:"PRETTY_NAME"`
28	VersionID    string `osr:"VERSION_ID"`
29	HomeURL      string `osr:"HOME_URL"`
30	SupportURL   string `osr:"SUPPORT_URL"`
31	BugReportURL string `osr:"BUG_REPORT_URL"`
32}
33
34func stripQuotes(val string) string {
35	if len(val) > 0 && val[0] == '"' {
36		return val[1 : len(val)-1]
37	}
38	return val
39}
40
41func (osr *OsRelease) setIfPossible(key, val string) error {
42	v := reflect.ValueOf(osr).Elem()
43	for i := 0; i < v.NumField(); i++ {
44		fieldValue := v.Field(i)
45		fieldType := v.Type().Field(i)
46		originalName := fieldType.Tag.Get("osr")
47		if key == originalName && fieldValue.Kind() == reflect.String {
48			fieldValue.SetString(val)
49			return nil
50		}
51	}
52	return fmt.Errorf("Couldn't set key %s, no corresponding struct field found", key)
53}
54
55func parseLine(osrLine string) (string, string, error) {
56	if osrLine == "" {
57		return "", "", nil
58	}
59
60	vals := strings.Split(osrLine, "=")
61	if len(vals) != 2 {
62		return "", "", fmt.Errorf("Expected %s to split by '=' char into two strings, instead got %d strings", osrLine, len(vals))
63	}
64	key := vals[0]
65	val := stripQuotes(vals[1])
66	return key, val, nil
67}
68
69func (osr *OsRelease) ParseOsRelease(osReleaseContents []byte) error {
70	r := bytes.NewReader(osReleaseContents)
71	scanner := bufio.NewScanner(r)
72	for scanner.Scan() {
73		key, val, err := parseLine(scanner.Text())
74		if err != nil {
75			log.Warnf("Warning: got an invalid line error parsing /etc/os-release: %s", err)
76			continue
77		}
78		if err := osr.setIfPossible(key, val); err != nil {
79			log.Debug(err)
80		}
81	}
82	return nil
83}
84
85func NewOsRelease(contents []byte) (*OsRelease, error) {
86	osr := &OsRelease{}
87	if err := osr.ParseOsRelease(contents); err != nil {
88		return nil, err
89	}
90	return osr, nil
91}
92