small pixel drawing of a pufferfish zoa

Make uname gathering posix compliant (dont rely on syscalls)
Jes Olson j3s@c3f.net
Sat, 01 Oct 2022 15:19:39 -0500
commit

fc9845422d3c58f437daaff29fdf9f8b98553d36

parent

2b21df5ffaaaddd13ca37b576f578fafa533d4fb

3 files changed, 62 insertions(+), 28 deletions(-)

jump to
M READMEREADME

@@ -250,6 +250,20 @@ HELPER FUNCTIONS

zoa has some little helper functions for common operations. + * zoa-clone <git repo> + usage: + zoa-clone https://git.cyberia.club/my-zoa-repo branchname + ^ ^ + git repo to clone optional git branch + + description: + zoa-clone will clone a given git repository to the local + system at ~/.local/share/zoa/repo - if the repository is + already cloned, zoa will fetch it. + is provided for a few reasons: + - no need to install git on your servers (zoa is git-capable) + - clone + exec is a very common zoa pattern + * zoa-file usage: zoa-file example.conf /etc/example.conf optional-command
M env/env.goenv/env.go

@@ -3,9 +3,10 @@

import ( "bufio" "fmt" + "log" "os" + "os/exec" "strings" - "syscall" "mvdan.cc/sh/v3/expand" )

@@ -14,10 +15,9 @@ func GenerateEnv() (expand.Environ, error) {

// syscall.Uname _should_ be supported on all *nix systems and is backed // by posix var env expand.Environ - uname := syscall.Utsname{} - err := syscall.Uname(&uname) + uname, err := getUname() if err != nil { - return env, err + log.Fatal(err) } // shell := "/bin/sh"?

@@ -31,14 +31,12 @@ //

// users can always call their special binaries with their full paths // if they are resistant to moving them for some reason. path := envString("PATH", "/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin") - uname_os := envString("OS", charsToString(uname.Sysname[:])) - uname_release := envString("RELEASE", charsToString(uname.Release[:])) - uname_arch := envString("ARCH", charsToString(uname.Machine[:])) - if err != nil { - return env, err - } + uname_os := envString("OS", uname.Sysname) + uname_release := envString("RELEASE", uname.Release) + uname_arch := envString("ARCH", uname.Arch) + uname_hostname := envString("HOSTNAME", uname.Nodename) - // standards are extremely annoying about hostnames. + // jes rant: standards are extremely annoying about hostnames. // // "Note that there is no standard that // says that the hostname set by sethostname(2)

@@ -49,9 +47,8 @@ // nodename), but this is true on Linux. The same

// holds for setdomainname(2) and the domainname field." // // in practice, there's usually not a difference between HOSTNAME - // and NODENAME, so i've chosen to only expose HOSTNAME for the - // sake of simplicity. i'm using the Golang implementation, which - // does call out to uname, annoyingly. + // and NODENAME, so i've chosen to expose the HOSTNAME variable for the + // sake of simplicity (most users expect this). // // if this becomes an issue, i'll revisit it. i doubt it though. // tldr: i'm ignoring that golang's os.Hostname() implementation

@@ -61,11 +58,6 @@ // are always identical in every case i've observed.

// // and i'm exposing only 1 because otherwise things get annoying. // shrug. - h, err := os.Hostname() - if err != nil { - return env, err - } - uname_hostname := fmt.Sprintf("HOSTNAME=%s", h) // !OS_RELEASE_* VARS ARE NOT STANDARDS-BACKED! // OS_* vars may or may not exist depending on the distro in question, so

@@ -74,6 +66,7 @@ // and are useful for identifying specific Linux distros, or their versions.

// // if you rely on these variables, I highly suggest checking for their // existence with test -z before utilizing them. there be no standards here. + os_release, err := getOSRelease() if err != nil { return env, err

@@ -91,15 +84,41 @@ func envString(key string, value string) string {

return fmt.Sprintf("%s=%s", key, value) } -func charsToString(arr []int8) string { - b := make([]byte, 0, len(arr)) - for _, v := range arr { - if v == 0x00 { - break - } - b = append(b, byte(v)) +// uname_os, uname_hostname, uname_release, uname_arch +type Uname struct { + Sysname string + Nodename string + Release string + Arch string +} + +func getUname() (Uname, error) { + var uname Uname + out, err := exec.Command("uname", "-m").Output() + if err != nil { + return uname, err } - return string(b) + uname.Arch = string(out) + + out, err = exec.Command("uname", "-n").Output() + if err != nil { + return uname, err + } + uname.Nodename = string(out) + + out, err = exec.Command("uname", "-r").Output() + if err != nil { + return uname, err + } + uname.Release = string(out) + + out, err = exec.Command("uname", "-s").Output() + if err != nil { + return uname, err + } + uname.Sysname = string(out) + + return uname, nil } // I want to keep this list as small as possible, since
M main.gomain.go

@@ -39,7 +39,7 @@ hosts := os.Args[2:]

zoaPath, err := exec.LookPath("zoa") if err != nil { - log.Fatal(err) + log.Fatal("(TODO) please install zoa to /usr/local/bin and ensure it's in your PATH") } // TODO: replace remote binary if newer

@@ -121,6 +121,7 @@ - execute zoa on the remote host

examples: zoa ~/code/my-zoa-repo + zoa . zoa https://git.sr.ht/~j3s/testy zoa deploy j3s.sh git.j3s.sh`) os.Exit(1)