// 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 }