summaryrefslogtreecommitdiff
path: root/sd_id128/get.go
blob: 5da7a3256c45b03f49851fa4bb9f52f4b4ae9599 (plain)
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
// Copyright 2016 Luke Shumaker
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package sd_id128

import (
	"crypto/rand"
	"io/ioutil"
	"os"
	"strings"
)

// GetRandomUUID generates a random UUID (122 random bits).
//
// For convenience when a valid UUID is needed, rather than returning
// a totally random 128-bit number, this sets the UUID type
// ("variant") to RFC 4122 format, and the sub-type ("version") to
// "4", which is (pseudo) random generation.  This leaves 122 random
// bits.
//
// The pseudorandom number generator used is cryptographically secure.
func GetRandomUUID() (ID128, error) {
	var id ID128
	_, err := rand.Read(id[:])
	if err != nil {
		return ID128{}, err
	}

	// Set the type ("variant") to RFC 4122 format
	id[8] = (id[8] & 0x3F) | 0x80
	// Set the sub-type ("version") to 4 (random generation).
	id[6] = (id[6] & 0x0F) | 0x40

	return id, nil
}

var cachedMachineID ID128

// GetMachineID returns a unique identifier for this operating system
// installation.
//
// This is read as a plain hexadecimal number (with an optional
// trailing newline) from "/etc/machine-id".
//
// Note that unlike every other "Get*ID" function, the ID returned by
// GetMachineID is not guaranteed to be a valid UUID, though values
// generated by systemd v30 and later are valid UUIDs.
//
// See machine-id(5) for more information.
//
// machine-id(5): https://www.freedesktop.org/software/systemd/man/machine-id.html
func GetMachineID() (ID128, error) {
	if cachedMachineID == (ID128{}) {
		str, err := ioutil.ReadFile("/etc/machine-id")
		if err != nil {
			return ID128{}, err
		}
		id, err := ParsePlain(strings.TrimSuffix(string(str), "\n"))
		if err != nil {
			return ID128{}, err
		}
		if id == (ID128{}) {
			return ID128{}, ErrInvalid
		}
		cachedMachineID = id
	}
	return cachedMachineID, nil
}

var cachedBootID ID128

// GetBootUUID returns a UUID unique to this boot; it is generated by
// the kernel once very early in the boot process.
//
// This is read as a UUID (with trailing newline) from
// "/proc/sys/kernel/random/boot_id".
//
// It may be relied on that the Linux kernel sets boot_id to a UUID
// with the UUID type ("variant") set to RFC 4122 format, and the
// sub-type ("version") set appropriately.
//
// See random(4) for more information.
//
// random(4): http://man7.org/linux/man-pages/man4/random.4.html
func GetBootUUID() (ID128, error) {
	if cachedBootID == (ID128{}) {
		str, err := ioutil.ReadFile("/proc/sys/kernel/random/boot_id")
		if err != nil {
			return ID128{}, err
		}
		id, err := ParseUUID(strings.TrimSuffix(string(str), "\n"))
		if err != nil {
			return ID128{}, err
		}
		cachedBootID = id
	}
	return cachedBootID, nil
}

var cachedInvocationID ID128

// GetInvocationUUID returns a UUID unique to this invocation of the
// currently executed service.
//
// This is read from the "INVOCATION_ID" environment variable, which
// is expected to be set by the service manager.
//
// It may be relied on that the service manager sets this to a UUID
// with the UUID type ("variant") set to RFC 4122 format, and the
// sub-type ("version") set appropriately.
//
// The error returned is ErrNone if the service manager did not set
// INVOCATION_ID, or ErrInvalid if the value of INVOCATION_ID could
// not be parsed.
//
// See sd_id128_get_invocation(3) for more information.
//
// sd_id128_get_invocation(3): https://www.freedesktop.org/software/systemd/man/sd_id128_get_invocation.html
//
// BUG(lukeshu): GetInvocationUUID uses a mechanism specified by "what
// systemd currently does", rather than a stable specification.
func GetInvocationUUID() (ID128, error) {
	// BUG(lukeshu): GetInvocationUUID might logically belong in the
	// sd_daemon package, but we defer to the C-language
	// libsystemd's placement of sd_id128_get_invocation() in
	// sd-id128.h.
	if cachedInvocationID == (ID128{}) {
		// BUG(lukeshu): GetInvocationUUID should distrust the
		// environment in certain situations, similarly to GNU
		// libc secure_getenv.
		str, have := os.LookupEnv("INVOCATION_ID")
		if !have {
			return ID128{}, ErrNone
		}
		t, err := Parse(str)
		if err != nil {
			return ID128{}, err
		}
		cachedInvocationID = t
	}
	return cachedInvocationID, nil
}