]> go.fuhry.dev Git - runtime.git/commitdiff
[utils/hostname] add CgroupInfoSelf and IsLikelyUserMachine, w/ tests
authorDan Fuhry <dan@fuhry.com>
Sat, 14 Mar 2026 23:11:19 +0000 (19:11 -0400)
committerDan Fuhry <dan@fuhry.com>
Sat, 14 Mar 2026 23:11:19 +0000 (19:11 -0400)
utils/hostname/BUILD.bazel
utils/hostname/hostname_common.go
utils/hostname/hostname_generic.go
utils/hostname/hostname_linux.go
utils/hostname/hostname_macos.go
utils/hostname/hostname_openbsd.go

index 886070ec0a2ea5862a7a929e429a55512c1fc605..36f467d843e471e83c0d6f2e69a9b9c1b6743301 100644 (file)
@@ -1,4 +1,4 @@
-load("@rules_go//go:def.bzl", "go_library")
+load("@rules_go//go:def.bzl", "go_library", "go_test")
 
 go_library(
     name = "hostname",
@@ -12,17 +12,18 @@ go_library(
     ],
     importpath = "go.fuhry.dev/runtime/utils/hostname",
     visibility = ["//visibility:public"],
-    deps = select({
+    deps = [
+        "//utils/log",
+    ] + select({
         "@rules_go//go/platform:aix": [
             "//constants",
         ],
         "@rules_go//go/platform:android": [
+            "//constants",
             "//utils/fsutil",
-            "//utils/log",
         ],
         "@rules_go//go/platform:darwin": [
             "//constants",
-            "//utils/log",
             "@net_howett_plist//:plist",
         ],
         "@rules_go//go/platform:dragonfly": [
@@ -36,22 +37,20 @@ go_library(
         ],
         "@rules_go//go/platform:ios": [
             "//constants",
-            "//utils/log",
             "@net_howett_plist//:plist",
         ],
         "@rules_go//go/platform:js": [
             "//constants",
         ],
         "@rules_go//go/platform:linux": [
+            "//constants",
             "//utils/fsutil",
-            "//utils/log",
         ],
         "@rules_go//go/platform:netbsd": [
             "//constants",
         ],
         "@rules_go//go/platform:openbsd": [
             "//constants",
-            "//utils/log",
         ],
         "@rules_go//go/platform:osx": [
             "//constants",
@@ -71,3 +70,10 @@ go_library(
         "//conditions:default": [],
     }),
 )
+
+go_test(
+    name = "hostname_test",
+    srcs = ["hostname_test.go"],
+    embed = [":hostname"],
+    deps = ["@com_github_stretchr_testify//assert"],
+)
index 86e21ddb73bec5ba24d2fa6a78dfbff3f216c59e..60a0d0ffcd9db1d5987c9a6415ee83a06794ce6f 100644 (file)
@@ -1,7 +1,24 @@
 package hostname
 
+import (
+       "errors"
+       "os"
+       "regexp"
+       "runtime"
+       "strings"
+       "sync"
+
+       "go.fuhry.dev/runtime/utils/log"
+)
+
 type ContainerType uint
 
+type CgroupInfo struct {
+       HierarchyID uint
+       ControllerList []string
+       Path string
+}
+
 const (
        ContainerUnknown ContainerType = iota
        ContainerNone
@@ -10,6 +27,13 @@ const (
        ContainerKubernetes
 )
 
+var (
+       ValidHostname   = regexp.MustCompile(`^[A-Za-z0-9-]{1,64}$`)
+       ValidDomainName = regexp.MustCompile(`^[A-Za-z0-9-]{1,64}(\.[A-Za-z0-9-]{1,64}){0,5}$`)
+
+       ErrCgroupsUnsupported = errors.New("cgroups are unsupported on "+runtime.GOOS)
+)
+
 func (c ContainerType) String() string {
        switch c {
        case ContainerNone:
@@ -23,3 +47,33 @@ func (c ContainerType) String() string {
        }
        return "unknown"
 }
+
+var (
+       isUserMachine     bool
+       isUserMachineOnce sync.Once
+)
+
+func IsLikelyUserMachine() bool {
+       isUserMachineOnce.Do(func() {
+               if cgroup, err := CgroupInfoSelf(); err == nil {
+                       log.Default().V(1).Debugf("cgroup for current process: %v", cgroup)
+               } else {
+                       log.Default().V(1).Debugf("error getting cgroup info: %v", err)
+               }
+
+               isUserMachine = Containerization() == ContainerNone &&
+                       os.Geteuid() >= 500 &&
+                       (strings.HasPrefix(os.Getenv("HOME"), "/home/") ||
+                               strings.HasPrefix(os.Getenv("HOME"), "/Users/") ||
+                               runtime.GOOS == "win32" ||
+                               runtime.GOOS == "darwin")
+
+                               if isUserMachine {
+                                       log.Default().Info("Detected human-user environment")
+                               } else {
+                                       log.Default().Info("Detected service/daemon environment")
+                               }
+       })
+
+       return isUserMachine
+}
index 8796b22300b1c624fbc68eaaeede4216e87c0185..6109d8ea70ff9838df2798393255c234c6408d17 100644 (file)
@@ -12,3 +12,7 @@ func Fqdn() string {
 func Containerization() ContainerType {
        return ContainerNone
 }
+
+func CgroupInfoSelf() (*CgroupInfo, error) {
+       return nil, ErrCgroupsUnsupported
+}
index 50d0066f23301e96a0eeef55fd4dc218f1781fc3..834a0fc3a98c806e7e725b6c0e39ec4ec191f3a6 100644 (file)
@@ -10,6 +10,7 @@ import (
        "sync"
        "syscall"
 
+       "go.fuhry.dev/runtime/constants"
        "go.fuhry.dev/runtime/utils/fsutil"
        "go.fuhry.dev/runtime/utils/log"
 )
@@ -18,10 +19,12 @@ type i8 = interface {
        int8 | uint8
 }
 
-var utsname syscall.Utsname
-var utsnameOnce sync.Once
-var spaceExp = regexp.MustCompile(`\s+`)
-var hashCommentExp = regexp.MustCompile(`#.*$`)
+var (
+       utsname        syscall.Utsname
+       utsnameOnce    sync.Once
+       spaceExp       = regexp.MustCompile(`\s+`)
+       hashCommentExp = regexp.MustCompile(`#.*$`)
+)
 
 func Hostname() string {
        return strings.Split(nodeName(), ".")[0]
@@ -46,6 +49,8 @@ func DomainName() string {
                if d := domainNameFromResolvConf(); d != "" {
                        return d
                }
+       } else if c == ContainerDocker {
+               return constants.DefaultHostDomain
        }
 
        err := fmt.Errorf(
@@ -100,11 +105,18 @@ func Fqdn() string {
        return strings.Join([]string{Hostname(), DomainName()}, ".")
 }
 
+var (
+       containerType     ContainerType
+       containerTypeOnce sync.Once
+)
+
 func Containerization() ContainerType {
-       c := containerization()
-       log.Default().Infof("Detected container type: %s", c)
+       containerTypeOnce.Do(func() {
+               containerType = containerization()
+               log.Default().Infof("Detected container type: %s", containerType)
+       })
 
-       return c
+       return containerType
 }
 
 func containerization() ContainerType {
@@ -154,3 +166,7 @@ func int8ToString[T i8](ba [65]T) string {
 
        return string(bytes)
 }
+
+func CgroupInfoSelf() (*CgroupInfo, error) {
+       return nil, ErrCgroupsUnsupported
+}
index 721e718a7b25f545bf3187695f941cf148755d38..d78da7ac9302461b077d1824e5faec637fc87b06 100644 (file)
@@ -127,3 +127,7 @@ func fqdnFromDns() (string, error) {
 
        return "", errors.New("failed to lookup any hostname for 127.0.0.1 that does not resolve to just \"localhost\"")
 }
+
+func CgroupInfoSelf() (*CgroupInfo, error) {
+       return nil, ErrCgroupsUnsupported
+}
index 75125a4e48e49214f7e28aa75a96277f8fbadd7e..8ee8ac751508fe736447ad4d156ee4802e52c7d9 100644 (file)
@@ -74,3 +74,7 @@ func fqdnFromDns() (string, error) {
 
        return "", errors.New("failed to lookup any hostname for 127.0.0.1 that does not resolve to just \"localhost\"")
 }
+
+func CgroupInfoSelf() (*CgroupInfo, error) {
+       return nil, ErrCgroupsUnsupported
+}