git.sr.ht/~pingoo/stdx@v0.0.0-20240218134121-094174641f6e/sysinfo/node.go (about)

     1  // Copyright © 2016 Zlatko Čalušić
     2  //
     3  // Use of this source code is governed by an MIT-style license that can be found in the LICENSE file.
     4  
     5  package sysinfo
     6  
     7  import (
     8  	"bufio"
     9  	"crypto/rand"
    10  	"fmt"
    11  	"os"
    12  	"strings"
    13  	"time"
    14  )
    15  
    16  // Node information.
    17  type Node struct {
    18  	Hostname   string `json:"hostname,omitempty"`
    19  	MachineID  string `json:"machineid,omitempty"`
    20  	Hypervisor string `json:"hypervisor,omitempty"`
    21  	Timezone   string `json:"timezone,omitempty"`
    22  }
    23  
    24  func (si *SysInfo) getHostname() {
    25  	si.Node.Hostname = slurpFile("/proc/sys/kernel/hostname")
    26  }
    27  
    28  func (si *SysInfo) getSetMachineID() {
    29  	const pathSystemdMachineID = "/etc/machine-id"
    30  	const pathDbusMachineID = "/var/lib/dbus/machine-id"
    31  
    32  	systemdMachineID := slurpFile(pathSystemdMachineID)
    33  	dbusMachineID := slurpFile(pathDbusMachineID)
    34  
    35  	if systemdMachineID != "" && dbusMachineID != "" {
    36  		// All OK, just return the machine id.
    37  		if systemdMachineID == dbusMachineID {
    38  			si.Node.MachineID = systemdMachineID
    39  			return
    40  		}
    41  
    42  		// They both exist, but they don't match! Copy systemd machine id to DBUS machine id.
    43  		spewFile(pathDbusMachineID, systemdMachineID, 0444)
    44  		si.Node.MachineID = systemdMachineID
    45  		return
    46  	}
    47  
    48  	// Copy DBUS machine id to non-existent systemd machine id.
    49  	if systemdMachineID == "" && dbusMachineID != "" {
    50  		spewFile(pathSystemdMachineID, dbusMachineID, 0444)
    51  		si.Node.MachineID = dbusMachineID
    52  		return
    53  	}
    54  
    55  	// Copy systemd machine id to non-existent DBUS machine id.
    56  	if systemdMachineID != "" && dbusMachineID == "" {
    57  		spewFile(pathDbusMachineID, systemdMachineID, 0444)
    58  		si.Node.MachineID = systemdMachineID
    59  		return
    60  	}
    61  
    62  	// Generate and write fresh new machine ID to both locations, conforming to the DBUS specification:
    63  	// https://dbus.freedesktop.org/doc/dbus-specification.html#uuids
    64  
    65  	random := make([]byte, 12)
    66  	if _, err := rand.Read(random); err != nil {
    67  		return
    68  	}
    69  	newMachineID := fmt.Sprintf("%x%x", random, time.Now().Unix())
    70  
    71  	spewFile(pathSystemdMachineID, newMachineID, 0444)
    72  	spewFile(pathDbusMachineID, newMachineID, 0444)
    73  	si.Node.MachineID = newMachineID
    74  }
    75  
    76  func (si *SysInfo) getTimezone() {
    77  	const zoneInfoPrefix = "/usr/share/zoneinfo/"
    78  
    79  	if fi, err := os.Lstat("/etc/localtime"); err == nil {
    80  		if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
    81  			if tzfile, err := os.Readlink("/etc/localtime"); err == nil {
    82  				tzfile = strings.TrimPrefix(tzfile, "..")
    83  				if strings.HasPrefix(tzfile, zoneInfoPrefix) {
    84  					si.Node.Timezone = strings.TrimPrefix(tzfile, zoneInfoPrefix)
    85  					return
    86  				}
    87  			}
    88  		}
    89  	}
    90  
    91  	if timezone := slurpFile("/etc/timezone"); timezone != "" {
    92  		si.Node.Timezone = timezone
    93  		return
    94  	}
    95  
    96  	if f, err := os.Open("/etc/sysconfig/clock"); err == nil {
    97  		defer f.Close()
    98  		s := bufio.NewScanner(f)
    99  		for s.Scan() {
   100  			if sl := strings.Split(s.Text(), "="); len(sl) == 2 {
   101  				if sl[0] == "ZONE" {
   102  					si.Node.Timezone = strings.Trim(sl[1], `"`)
   103  					return
   104  				}
   105  			}
   106  		}
   107  	}
   108  }
   109  
   110  func (si *SysInfo) getNodeInfo() {
   111  	si.getHostname()
   112  	si.getSetMachineID()
   113  	si.getHypervisor()
   114  	si.getTimezone()
   115  }