summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Shumaker <lukeshu@sbcglobal.net>2016-12-27 21:59:17 -0700
committerLuke Shumaker <lukeshu@sbcglobal.net>2016-12-27 21:59:17 -0700
commit963644c19058829f74bd7d19a484c0786d6777cd (patch)
treec827fc6b5245aec2fc78aaf29eb0552f1e8bce41
parent13cdadaf934e6079f7a835cd680cf0ffe89d6ca6 (diff)
Documentation improvements.
Also, I renamed a couple of the sd_id128 functions for better clarity. But since I haven't pushed since originally implementing them, I'm not labeling it as a breaking change.
-rw-r--r--readme.go7
-rw-r--r--sd_daemon/booted.go4
-rw-r--r--sd_daemon/doc.go8
-rw-r--r--sd_id128/get.go152
-rw-r--r--sd_id128/sd_id128.go222
-rw-r--r--sd_id128/types.go184
6 files changed, 351 insertions, 226 deletions
diff --git a/readme.go b/readme.go
index 88719f0..aac1aa7 100644
--- a/readme.go
+++ b/readme.go
@@ -12,6 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// Package libsystemd provides libraries for working with systemd.
-// Right now it only has a mostly-complete sd_daemon.
+// Package libsystemd implements the client-side of generic system
+// APIs.
+//
+// Several of these APIs originated with systemd, but they are all
+// very simple, and may be implemented with a variety of systems.
package libsystemd
diff --git a/sd_daemon/booted.go b/sd_daemon/booted.go
index b833b45..18fd4fb 100644
--- a/sd_daemon/booted.go
+++ b/sd_daemon/booted.go
@@ -23,6 +23,10 @@ import "os"
// Please do not use this function. All of the other functionality in
// this package uses interfaces that are not systemd-specific.
func SdBooted() bool {
+ // BUG(lukeshu): SdBooted is systemd-specific, and isn't of
+ // particular value to daemons. It doesn't really belong in a
+ // library of generic daemon utilities. But, we defer to its
+ // inclusion in the C-language libsystemd.
fi, err := os.Lstat("/run/systemd/system")
return err != nil && fi.IsDir()
}
diff --git a/sd_daemon/doc.go b/sd_daemon/doc.go
index 90616f6..927ac7f 100644
--- a/sd_daemon/doc.go
+++ b/sd_daemon/doc.go
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// Package sd_daemon provides functions for writing "new-style"
+// Package sd_daemon implements utilities for writing "new-style"
// daemons.
//
// The daemon(7) manual page has historically documented the very long
@@ -24,7 +24,11 @@
// systemd, they are all very simple mechanisms which can easily be
// implemented with a variety of service managers.
//
-// [daemon(7)]: https://www.freedesktop.org/software/systemd/man/daemon.html
+// daemon(7): https://www.freedesktop.org/software/systemd/man/daemon.html
+//
+// BUG(lukeshu): Logically, sd_id128.GetInvocationID might belong in
+// this package, but we defer to the C-language libsystemd's placement
+// of sd_id128_get_invocation() in sd-id128.h.
package sd_daemon
import "errors"
diff --git a/sd_id128/get.go b/sd_id128/get.go
new file mode 100644
index 0000000..d8cdcfa
--- /dev/null
+++ b/sd_id128/get.go
@@ -0,0 +1,152 @@
+// 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"
+ "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 := readfile("/etc/machine-id")
+ if err != nil {
+ return ID128{}, err
+ }
+ id, err := ParsePlain(strings.TrimSuffix(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 := readfile("/proc/sys/kernel/random/boot_id")
+ if err != nil {
+ return ID128{}, err
+ }
+ id, err := ParseUUID(strings.TrimSuffix(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
+}
diff --git a/sd_id128/sd_id128.go b/sd_id128/sd_id128.go
deleted file mode 100644
index 063a4d4..0000000
--- a/sd_id128/sd_id128.go
+++ /dev/null
@@ -1,222 +0,0 @@
-// 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 provides functions for dealing with 128-bit IDs.
-package sd_id128
-
-import (
- "crypto/rand"
- "errors"
- "os"
- "strings"
-)
-
-var (
- ErrInvalid = errors.New("Invalid ID")
- ErrNone = errors.New("No Invocation ID set")
-)
-
-type ID128 [16]byte
-
-// String formats the ID as a hexadecimal number.
-func (id ID128) String() string {
- var s [32]byte
- for n := range id {
- s[n*2] = hexchar(id[n] >> 4)
- s[n*2+1] = hexchar(id[n] & 0xF)
- }
- return string(s[:])
-}
-
-// UUID formats the ID as a UUID string.
-func (id ID128) UUID() string {
- var s [36]byte
- k := 0
- for n := range id {
- switch n {
- case 4, 6, 8, 10:
- s[k] = '-'
- k++
- }
- s[k] = hexchar(id[n] >> 4)
- k++
- s[k] = hexchar(id[n] & 0xF)
- k++
- }
- return string(s[:])
-}
-
-// Parse parses a 128-bit ID represented either as a hexadecimal
-// number, or as a UUID string (that is, a hexadecimal number with
-// hyphens inserted at positions 8, 12, 18, and 23).
-func Parse(s string) (ID128, error) {
- ret := ID128{}
- isGuid := false
-
- i := 0
- n := 0
- for n < 16 {
- if s[i] == '-' {
- if i == 8 {
- isGuid = true
- } else if i == 13 || i == 18 || i == 23 {
- if !isGuid {
- return ID128{}, ErrInvalid
- }
- } else {
- return ID128{}, ErrInvalid
- }
- i++
- } else {
- if i+2 >= len(s) {
- return ID128{}, ErrInvalid
- }
-
- a, err := unhexchar(s[i])
- i++
- if err != nil {
- return ID128{}, err
- }
-
- b, err := unhexchar(s[i])
- i++
- if err != nil {
- return ID128{}, err
- }
-
- ret[n] = (a << 4) | b
- n++
- }
- }
-
- if !((i == 36 && isGuid) || (i == 32 && !isGuid)) {
- return ID128{}, ErrInvalid
- }
- if i != len(s) {
- return ID128{}, ErrInvalid
- }
-
- return ret, nil
-}
-
-// ParsePlain is like Parse, but only accepts hexadecimal numbers, not
-// UUID-formatted strings.
-func ParsePlain(s string) (ID128, error) {
- if len(s) != 32 {
- return ID128{}, ErrInvalid
- }
- return Parse(s)
-}
-
-// ParseUUID is like Parse, but only accepts UUID-formatted strings,
-// not hexadecimal numbers.
-func ParseUUID(s string) (ID128, error) {
- if len(s) != 36 {
- return ID128{}, ErrInvalid
- }
- return Parse(s)
-}
-
-// GetRandomID generates a random v4 UUID (122 random bits).
-func GetRandomID() (ID128, error) {
- var id ID128
- _, err := rand.Read(id[:])
- if err != nil {
- return ID128{}, err
- }
-
- // Turn this in to a valid v4 UUID, to be nice.
-
- // Set UUID version to 4 --- truly random generation
- id[6] = (id[6] & 0x0F) | 0x40
-
- // Set the UUID variant to DCE
- id[8] = (id[8] & 0x3F) | 0x80
-
- return id, nil
-}
-
-var cachedMachineID ID128
-
-// GetMachineID returns a unique identifier for this machine.
-//
-// This is read as a plain hexadecimal number (with an optional
-// trailing newline) from "/etc/machine-id".
-func GetMachineID() (ID128, error) {
- if cachedMachineID == (ID128{}) {
- str, err := readfile("/etc/machine-id")
- if err != nil {
- return ID128{}, err
- }
- id, err := ParsePlain(strings.TrimSuffix(str, "\n"))
- if err != nil {
- return ID128{}, err
- }
- if id == (ID128{}) {
- return ID128{}, ErrInvalid
- }
- cachedMachineID = id
- }
- return cachedMachineID, nil
-}
-
-var cachedBootID ID128
-
-// GetBootID returns a unique identifier for this boot.
-//
-// This is read as a UUID (with trailing newline) from
-// "/proc/sys/kernel/random/boot_id".
-func GetBootID() (ID128, error) {
- if cachedBootID == (ID128{}) {
- str, err := readfile("/proc/sys/kernel/random/boot_id")
- if err != nil {
- return ID128{}, err
- }
- id, err := ParseUUID(strings.TrimSuffix(str, "\n"))
- if err != nil {
- return ID128{}, err
- }
- cachedBootID = id
- }
- return cachedBootID, nil
-}
-
-var cachedInvocationID ID128
-
-// GetInvocationID returns a unique identifier for 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.
-//
-// 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.
-func GetInvocationID() (ID128, error) {
- if cachedInvocationID == (ID128{}) {
- // BUG(lukeshu): GetInvocationID 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
-}
diff --git a/sd_id128/types.go b/sd_id128/types.go
new file mode 100644
index 0000000..3b5228c
--- /dev/null
+++ b/sd_id128/types.go
@@ -0,0 +1,184 @@
+// 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 reads, generates, and processes 128-bit ID values.
+//
+// These 128-bit IDs are a generalization of DCE UUIDs (of which
+// Microsoft GUIDs are one implementation), but use a simpler string
+// format. This package imposes no structure on the used IDs, unlike
+// UUIDs, but are fully compatible with those types of IDs.
+//
+// DCE UUID: https://tools.ietf.org/html/rfc4122
+//
+// Microsoft GUID: https://msdn.microsoft.com/en-us/library/windows/desktop/aa373931(v=vs.85).aspx
+//
+// The UUID string representation is a (zero-padded) 32-digit
+// hexadecimal number with a hyphen after the 8th, 12th, 16th, and
+// 20th digits (for a total length of 36 glyphs). This package
+// supports this format, but the primary ("plain") string
+// representation is simply a (zero-padded) 32-digit hexadecimal
+// number.
+//
+// For example:
+//
+// Plain: 6ba7b8109dad11d180b400c04fd430c8
+// UUID : 6ba7b810-9dad-11d1-80b4-00c04fd430c8
+//
+// Several restrictions are imposed on the structure of UUIDs. Most
+// notably, the most significant 1-3 bits of the 8th octet specify the
+// UUID type ("variant"); unless dealing with legacy UUIDs, the most
+// significant 2 bits of the octet will be "10", specifying a RFC 4122
+// type UUID. Assuming RFC 4122 type, the most significant 4 bits of
+// the 6th octet then specify the sub-type ("version"), which could
+// impose complicated requirements on the rest of the UUID.
+//
+// This package doesn't really care about all of these UUID structure
+// requirements. Nothing in this package ever inspects an ID's
+// structure. However, because it is anticipated that the IDs
+// generated by this package might be used in a context where a UUID
+// is required, many of the functions that emit an ID ensure that the
+// ID emitted is structured to be a valid UUID.
+package sd_id128
+
+import (
+ "errors"
+)
+
+var (
+ ErrInvalid = errors.New("Invalid ID")
+ ErrNone = errors.New("No Invocation ID set")
+)
+
+type ID128 [16]byte
+
+// String formats the ID as a plain zero-padded 32-digit hexadecimal
+// number.
+func (id ID128) String() string {
+ var s [32]byte
+ for n := range id {
+ s[n*2] = hexchar(id[n] >> 4)
+ s[n*2+1] = hexchar(id[n] & 0xF)
+ }
+ return string(s[:])
+}
+
+// UUID formats the ID as a UUID string.
+func (id ID128) UUID() string {
+ var s [36]byte
+ k := 0
+ for n := range id {
+ switch n {
+ case 4, 6, 8, 10:
+ s[k] = '-'
+ k++
+ }
+ s[k] = hexchar(id[n] >> 4)
+ k++
+ s[k] = hexchar(id[n] & 0xF)
+ k++
+ }
+ return string(s[:])
+}
+
+// Parse parses a 128-bit ID represented either in the plain string
+// format, or in the UUID string format.
+//
+// If you need to validate that the string is in one format or the
+// other, then you should use the ParsePlain or ParseUUID functions.
+//
+// If the input string does not match either of these formats, then
+// ErrInvalid is returned.
+func Parse(s string) (ID128, error) {
+ ret := ID128{}
+ isGuid := false
+
+ i := 0
+ n := 0
+ for n < 16 {
+ if s[i] == '-' {
+ if i == 8 {
+ isGuid = true
+ } else if i == 13 || i == 18 || i == 23 {
+ if !isGuid {
+ return ID128{}, ErrInvalid
+ }
+ } else {
+ return ID128{}, ErrInvalid
+ }
+ i++
+ } else {
+ if i+2 >= len(s) {
+ return ID128{}, ErrInvalid
+ }
+
+ a, err := unhexchar(s[i])
+ i++
+ if err != nil {
+ return ID128{}, err
+ }
+
+ b, err := unhexchar(s[i])
+ i++
+ if err != nil {
+ return ID128{}, err
+ }
+
+ ret[n] = (a << 4) | b
+ n++
+ }
+ }
+
+ if !((i == 36 && isGuid) || (i == 32 && !isGuid)) {
+ return ID128{}, ErrInvalid
+ }
+ if i != len(s) {
+ return ID128{}, ErrInvalid
+ }
+
+ return ret, nil
+}
+
+// ParsePlain parses a 128-bit ID represented in the plain string
+// format.
+//
+// If you would like more flexibility, and would like to be accept
+// either the plain string format or the UUID string format, then you
+// should use the Parse function.
+//
+// If the input string is not a 32-digit hexadecimal number, then
+// ErrInvalid is returned.
+func ParsePlain(s string) (ID128, error) {
+ if len(s) != 32 {
+ return ID128{}, ErrInvalid
+ }
+ return Parse(s)
+}
+
+// ParsePlain parses a 128-bit ID represented in the UUID string
+// format.
+//
+// If you would like more flexibility, and would like to be accept
+// either the plain string format or the UUID string format, then you
+// should use the Parse function.
+//
+// If the input string is not a 36-character UUID string, then
+// ErrInvalid is returned. This function does not validate the
+// variant or version bit fields, nor does it validate any structure
+// implied by them.
+func ParseUUID(s string) (ID128, error) {
+ if len(s) != 36 {
+ return ID128{}, ErrInvalid
+ }
+ return Parse(s)
+}