forked from snapcore/snapd
-
Notifications
You must be signed in to change notification settings - Fork 0
/
release.go
161 lines (136 loc) · 4.46 KB
/
release.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
// -*- Mode: Go; indent-tabs-mode: t -*-
/*
* Copyright (C) 2014-2015 Canonical Ltd
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package release
import (
"bufio"
"bytes"
"io/ioutil"
"os"
"strings"
"unicode"
"github.com/snapcore/snapd/strutil"
)
// Series holds the Ubuntu Core series for snapd to use.
var Series = "16"
// OS contains information about the system extracted from /etc/os-release.
type OS struct {
ID string `json:"id"`
IDLike []string `json:"-"`
VersionID string `json:"version-id,omitempty"`
}
// DistroLike checks if the distribution ID or ID_LIKE matches one of the given names.
func DistroLike(distros ...string) bool {
for _, distro := range distros {
if ReleaseInfo.ID == distro || strutil.ListContains(ReleaseInfo.IDLike, distro) {
return true
}
}
return false
}
var (
osReleasePath = "/etc/os-release"
fallbackOsReleasePath = "/usr/lib/os-release"
)
// readOSRelease returns the os-release information of the current system.
func readOSRelease() OS {
// TODO: separate this out into its own thing maybe (if made more general)
osRelease := OS{
// from os-release(5): If not set, defaults to "ID=linux".
ID: "linux",
}
f, err := os.Open(osReleasePath)
if err != nil {
// this fallback is as per os-release(5)
f, err = os.Open(fallbackOsReleasePath)
if err != nil {
return osRelease
}
}
scanner := bufio.NewScanner(f)
for scanner.Scan() {
ws := strings.SplitN(scanner.Text(), "=", 2)
if len(ws) < 2 {
continue
}
k := strings.TrimSpace(ws[0])
v := strings.TrimFunc(ws[1], func(r rune) bool { return r == '"' || r == '\'' || unicode.IsSpace(r) })
// XXX: should also unquote things as per os-release(5) but not needed yet in practice
switch k {
case "ID":
// ID should be “A lower-case string (no spaces or
// other characters outside of 0–9, a–z, ".", "_" and
// "-") identifying the operating system, excluding any
// version information and suitable for processing by
// scripts or usage in generated filenames.”
//
// So we mangle it a little bit to account for people
// not being too good at reading comprehension.
// Works around e.g. lp:1602317
osRelease.ID = strings.Fields(strings.ToLower(v))[0]
case "ID_LIKE":
// This is like ID, except it's a space separated list... hooray?
osRelease.IDLike = strings.Fields(strings.ToLower(v))
case "VERSION_ID":
osRelease.VersionID = v
}
}
return osRelease
}
var ioutilReadFile = ioutil.ReadFile
func isWSL() bool {
version, err := ioutilReadFile("/proc/version")
if err == nil && bytes.Contains(version, []byte("Microsoft")) {
return true
}
return false
}
// SystemctlSupportsUserUnits returns true if the systemctl utility
// supports user units.
func SystemctlSupportsUserUnits() bool {
// Ubuntu 14.04's systemctl does not support the arguments
// needed to enable user session units. Further more, it does
// not ship with a systemd user instance.
return !(ReleaseInfo.ID == "ubuntu" && ReleaseInfo.VersionID == "14.04")
}
// OnClassic states whether the process is running inside a
// classic Ubuntu system or a native Ubuntu Core image.
var OnClassic bool
// OnWSL states whether the process is running inside the Windows
// Subsystem for Linux
var OnWSL bool
// ReleaseInfo contains data loaded from /etc/os-release on startup.
var ReleaseInfo OS
func init() {
ReleaseInfo = readOSRelease()
OnClassic = (ReleaseInfo.ID != "ubuntu-core")
OnWSL = isWSL()
}
// MockOnClassic forces the process to appear inside a classic
// Ubuntu system or a native image for testing purposes.
func MockOnClassic(onClassic bool) (restore func()) {
old := OnClassic
OnClassic = onClassic
return func() { OnClassic = old }
}
// MockReleaseInfo fakes a given information to appear in ReleaseInfo,
// as if it was read /etc/os-release on startup.
func MockReleaseInfo(osRelease *OS) (restore func()) {
old := ReleaseInfo
ReleaseInfo = *osRelease
return func() { ReleaseInfo = old }
}