From 3a47fa67069fdaf047de8f70623aa8483e69400b Mon Sep 17 00:00:00 2001 From: William P Date: Sun, 20 Jul 2025 23:14:21 -0400 Subject: [PATCH] introduce basic fact collection --- client/facts/facts.go | 38 ++ client/go.mod | 4 + client/go.sum | 4 + client/main.go | 18 + client/routes/routes.go | 2 + .../github.com/google/uuid/CHANGELOG.md | 41 ++ .../github.com/google/uuid/CONTRIBUTING.md | 26 ++ .../github.com/google/uuid/CONTRIBUTORS | 9 + client/vendor/github.com/google/uuid/LICENSE | 27 ++ .../vendor/github.com/google/uuid/README.md | 21 + client/vendor/github.com/google/uuid/dce.go | 80 ++++ client/vendor/github.com/google/uuid/doc.go | 12 + client/vendor/github.com/google/uuid/hash.go | 59 +++ .../vendor/github.com/google/uuid/marshal.go | 38 ++ client/vendor/github.com/google/uuid/node.go | 90 +++++ .../vendor/github.com/google/uuid/node_js.go | 12 + .../vendor/github.com/google/uuid/node_net.go | 33 ++ client/vendor/github.com/google/uuid/null.go | 118 ++++++ client/vendor/github.com/google/uuid/sql.go | 59 +++ client/vendor/github.com/google/uuid/time.go | 134 +++++++ client/vendor/github.com/google/uuid/util.go | 43 +++ client/vendor/github.com/google/uuid/uuid.go | 365 ++++++++++++++++++ .../vendor/github.com/google/uuid/version1.go | 44 +++ .../vendor/github.com/google/uuid/version4.go | 76 ++++ .../vendor/github.com/google/uuid/version6.go | 56 +++ .../vendor/github.com/google/uuid/version7.go | 104 +++++ .../github.com/zcalusic/sysinfo/LICENSE | 21 + .../github.com/zcalusic/sysinfo/README.md | 204 ++++++++++ .../github.com/zcalusic/sysinfo/bios.go | 18 + .../github.com/zcalusic/sysinfo/board.go | 22 ++ .../github.com/zcalusic/sysinfo/chassis.go | 26 ++ .../vendor/github.com/zcalusic/sysinfo/cpu.go | 85 ++++ .../github.com/zcalusic/sysinfo/cpuid/LICENSE | 27 ++ .../zcalusic/sysinfo/cpuid/cpuid.go | 9 + .../zcalusic/sysinfo/cpuid/cpuid_386.s | 16 + .../zcalusic/sysinfo/cpuid/cpuid_amd64.s | 16 + .../zcalusic/sysinfo/cpuid/cpuid_default.s | 10 + .../github.com/zcalusic/sysinfo/hypervisor.go | 65 ++++ .../zcalusic/sysinfo/kernel_darwin.go | 14 + .../zcalusic/sysinfo/kernel_linux.go | 33 ++ .../github.com/zcalusic/sysinfo/memory.go | 121 ++++++ .../github.com/zcalusic/sysinfo/meta.go | 18 + .../github.com/zcalusic/sysinfo/network.go | 146 +++++++ .../github.com/zcalusic/sysinfo/node.go | 99 +++++ .../vendor/github.com/zcalusic/sysinfo/os.go | 101 +++++ .../github.com/zcalusic/sysinfo/product.go | 42 ++ .../github.com/zcalusic/sysinfo/storage.go | 104 +++++ .../github.com/zcalusic/sysinfo/sysinfo.go | 49 +++ .../github.com/zcalusic/sysinfo/util.go | 26 ++ .../github.com/zcalusic/sysinfo/version.go | 8 + client/vendor/modules.txt | 7 + flake.nix | 2 +- go.mod | 3 - http/handler.go | 10 - http/helloworld.go | 9 - http/routes.go | 10 - http/server.go | 40 -- main.go | 34 -- 58 files changed, 2801 insertions(+), 107 deletions(-) create mode 100644 client/facts/facts.go create mode 100644 client/go.sum create mode 100644 client/vendor/github.com/google/uuid/CHANGELOG.md create mode 100644 client/vendor/github.com/google/uuid/CONTRIBUTING.md create mode 100644 client/vendor/github.com/google/uuid/CONTRIBUTORS create mode 100644 client/vendor/github.com/google/uuid/LICENSE create mode 100644 client/vendor/github.com/google/uuid/README.md create mode 100644 client/vendor/github.com/google/uuid/dce.go create mode 100644 client/vendor/github.com/google/uuid/doc.go create mode 100644 client/vendor/github.com/google/uuid/hash.go create mode 100644 client/vendor/github.com/google/uuid/marshal.go create mode 100644 client/vendor/github.com/google/uuid/node.go create mode 100644 client/vendor/github.com/google/uuid/node_js.go create mode 100644 client/vendor/github.com/google/uuid/node_net.go create mode 100644 client/vendor/github.com/google/uuid/null.go create mode 100644 client/vendor/github.com/google/uuid/sql.go create mode 100644 client/vendor/github.com/google/uuid/time.go create mode 100644 client/vendor/github.com/google/uuid/util.go create mode 100644 client/vendor/github.com/google/uuid/uuid.go create mode 100644 client/vendor/github.com/google/uuid/version1.go create mode 100644 client/vendor/github.com/google/uuid/version4.go create mode 100644 client/vendor/github.com/google/uuid/version6.go create mode 100644 client/vendor/github.com/google/uuid/version7.go create mode 100644 client/vendor/github.com/zcalusic/sysinfo/LICENSE create mode 100644 client/vendor/github.com/zcalusic/sysinfo/README.md create mode 100644 client/vendor/github.com/zcalusic/sysinfo/bios.go create mode 100644 client/vendor/github.com/zcalusic/sysinfo/board.go create mode 100644 client/vendor/github.com/zcalusic/sysinfo/chassis.go create mode 100644 client/vendor/github.com/zcalusic/sysinfo/cpu.go create mode 100644 client/vendor/github.com/zcalusic/sysinfo/cpuid/LICENSE create mode 100644 client/vendor/github.com/zcalusic/sysinfo/cpuid/cpuid.go create mode 100644 client/vendor/github.com/zcalusic/sysinfo/cpuid/cpuid_386.s create mode 100644 client/vendor/github.com/zcalusic/sysinfo/cpuid/cpuid_amd64.s create mode 100644 client/vendor/github.com/zcalusic/sysinfo/cpuid/cpuid_default.s create mode 100644 client/vendor/github.com/zcalusic/sysinfo/hypervisor.go create mode 100644 client/vendor/github.com/zcalusic/sysinfo/kernel_darwin.go create mode 100644 client/vendor/github.com/zcalusic/sysinfo/kernel_linux.go create mode 100644 client/vendor/github.com/zcalusic/sysinfo/memory.go create mode 100644 client/vendor/github.com/zcalusic/sysinfo/meta.go create mode 100644 client/vendor/github.com/zcalusic/sysinfo/network.go create mode 100644 client/vendor/github.com/zcalusic/sysinfo/node.go create mode 100644 client/vendor/github.com/zcalusic/sysinfo/os.go create mode 100644 client/vendor/github.com/zcalusic/sysinfo/product.go create mode 100644 client/vendor/github.com/zcalusic/sysinfo/storage.go create mode 100644 client/vendor/github.com/zcalusic/sysinfo/sysinfo.go create mode 100644 client/vendor/github.com/zcalusic/sysinfo/util.go create mode 100644 client/vendor/github.com/zcalusic/sysinfo/version.go create mode 100644 client/vendor/modules.txt delete mode 100644 go.mod delete mode 100644 http/handler.go delete mode 100644 http/helloworld.go delete mode 100644 http/routes.go delete mode 100644 http/server.go delete mode 100644 main.go diff --git a/client/facts/facts.go b/client/facts/facts.go new file mode 100644 index 0000000..a36fd3a --- /dev/null +++ b/client/facts/facts.go @@ -0,0 +1,38 @@ +package facts + +import ( + "net/http" + + "git.dubyatp.xyz/orphanage/client/httputil" + "github.com/zcalusic/sysinfo" +) + +type FactsResponse struct { + APIVersion string `json:"apiVersion"` + CPUInfo sysinfo.CPU `json:"cpu"` + BoardInfo sysinfo.Board `json:"motherboard"` + DeviceInfo sysinfo.Product `json:"device"` + ChassisInfo sysinfo.Chassis `json:"chassis"` + MemoryInfo sysinfo.Memory `json:"memory"` + NetworkInfo []sysinfo.NetworkDevice `json:"network"` + StorageInfo []sysinfo.StorageDevice `json:"storage"` +} + +func GetFacts(next http.Handler) http.Handler { + var si sysinfo.SysInfo + si.GetSysInfo() + + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + resp := FactsResponse{ + APIVersion: "v1", + CPUInfo: si.CPU, + BoardInfo: si.Board, + DeviceInfo: si.Product, + ChassisInfo: si.Chassis, + MemoryInfo: si.Memory, + NetworkInfo: si.Network, + StorageInfo: si.Storage, + } + httputil.WriteJSON(w, http.StatusOK, resp) + }) +} diff --git a/client/go.mod b/client/go.mod index 60d9961..12d5133 100644 --- a/client/go.mod +++ b/client/go.mod @@ -1,3 +1,7 @@ module git.dubyatp.xyz/orphanage/client go 1.24.4 + +require github.com/zcalusic/sysinfo v1.1.3 + +require github.com/google/uuid v1.6.0 // indirect diff --git a/client/go.sum b/client/go.sum new file mode 100644 index 0000000..5a727a4 --- /dev/null +++ b/client/go.sum @@ -0,0 +1,4 @@ +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/zcalusic/sysinfo v1.1.3 h1:u/AVENkuoikKuIZ4sUEJ6iibpmQP6YpGD8SSMCrqAF0= +github.com/zcalusic/sysinfo v1.1.3/go.mod h1:NX+qYnWGtJVPV0yWldff9uppNKU4h40hJIRPf/pGLv4= diff --git a/client/main.go b/client/main.go index a1ac3dd..98a1ad6 100644 --- a/client/main.go +++ b/client/main.go @@ -4,13 +4,31 @@ import ( "context" "fmt" "io" + "log" "os" "os/signal" + "os/user" "git.dubyatp.xyz/orphanage/client/server" ) +func check_superuser() bool { + current, err := user.Current() + if err != nil { + log.Fatal(err) + } + if current.Uid != "0" { + return false + } else { + return true + } +} + func run(ctx context.Context, w io.Writer, args []string) error { + if !check_superuser() { + log.Fatal("orphan must run as superuser") + } + ctx, cancel := signal.NotifyContext(ctx, os.Interrupt) defer cancel() diff --git a/client/routes/routes.go b/client/routes/routes.go index edc7e96..9423d3d 100644 --- a/client/routes/routes.go +++ b/client/routes/routes.go @@ -3,6 +3,7 @@ package routes import ( "net/http" + "git.dubyatp.xyz/orphanage/client/facts" "git.dubyatp.xyz/orphanage/client/httputil" "git.dubyatp.xyz/orphanage/client/testfunc" ) @@ -13,4 +14,5 @@ func AddRoutes( mux.Handle("/", http.NotFoundHandler()) mux.Handle("/helloworld", httputil.HelloWorld(nil)) mux.Handle("/testjson", testfunc.HelloWorldJSON(nil)) + mux.Handle("/facts", facts.GetFacts(nil)) } diff --git a/client/vendor/github.com/google/uuid/CHANGELOG.md b/client/vendor/github.com/google/uuid/CHANGELOG.md new file mode 100644 index 0000000..7ec5ac7 --- /dev/null +++ b/client/vendor/github.com/google/uuid/CHANGELOG.md @@ -0,0 +1,41 @@ +# Changelog + +## [1.6.0](https://github.com/google/uuid/compare/v1.5.0...v1.6.0) (2024-01-16) + + +### Features + +* add Max UUID constant ([#149](https://github.com/google/uuid/issues/149)) ([c58770e](https://github.com/google/uuid/commit/c58770eb495f55fe2ced6284f93c5158a62e53e3)) + + +### Bug Fixes + +* fix typo in version 7 uuid documentation ([#153](https://github.com/google/uuid/issues/153)) ([016b199](https://github.com/google/uuid/commit/016b199544692f745ffc8867b914129ecb47ef06)) +* Monotonicity in UUIDv7 ([#150](https://github.com/google/uuid/issues/150)) ([a2b2b32](https://github.com/google/uuid/commit/a2b2b32373ff0b1a312b7fdf6d38a977099698a6)) + +## [1.5.0](https://github.com/google/uuid/compare/v1.4.0...v1.5.0) (2023-12-12) + + +### Features + +* Validate UUID without creating new UUID ([#141](https://github.com/google/uuid/issues/141)) ([9ee7366](https://github.com/google/uuid/commit/9ee7366e66c9ad96bab89139418a713dc584ae29)) + +## [1.4.0](https://github.com/google/uuid/compare/v1.3.1...v1.4.0) (2023-10-26) + + +### Features + +* UUIDs slice type with Strings() convenience method ([#133](https://github.com/google/uuid/issues/133)) ([cd5fbbd](https://github.com/google/uuid/commit/cd5fbbdd02f3e3467ac18940e07e062be1f864b4)) + +### Fixes + +* Clarify that Parse's job is to parse but not necessarily validate strings. (Documents current behavior) + +## [1.3.1](https://github.com/google/uuid/compare/v1.3.0...v1.3.1) (2023-08-18) + + +### Bug Fixes + +* Use .EqualFold() to parse urn prefixed UUIDs ([#118](https://github.com/google/uuid/issues/118)) ([574e687](https://github.com/google/uuid/commit/574e6874943741fb99d41764c705173ada5293f0)) + +## Changelog diff --git a/client/vendor/github.com/google/uuid/CONTRIBUTING.md b/client/vendor/github.com/google/uuid/CONTRIBUTING.md new file mode 100644 index 0000000..a502fdc --- /dev/null +++ b/client/vendor/github.com/google/uuid/CONTRIBUTING.md @@ -0,0 +1,26 @@ +# How to contribute + +We definitely welcome patches and contribution to this project! + +### Tips + +Commits must be formatted according to the [Conventional Commits Specification](https://www.conventionalcommits.org). + +Always try to include a test case! If it is not possible or not necessary, +please explain why in the pull request description. + +### Releasing + +Commits that would precipitate a SemVer change, as described in the Conventional +Commits Specification, will trigger [`release-please`](https://github.com/google-github-actions/release-please-action) +to create a release candidate pull request. Once submitted, `release-please` +will create a release. + +For tips on how to work with `release-please`, see its documentation. + +### Legal requirements + +In order to protect both you and ourselves, you will need to sign the +[Contributor License Agreement](https://cla.developers.google.com/clas). + +You may have already signed it for other Google projects. diff --git a/client/vendor/github.com/google/uuid/CONTRIBUTORS b/client/vendor/github.com/google/uuid/CONTRIBUTORS new file mode 100644 index 0000000..b4bb97f --- /dev/null +++ b/client/vendor/github.com/google/uuid/CONTRIBUTORS @@ -0,0 +1,9 @@ +Paul Borman +bmatsuo +shawnps +theory +jboverfelt +dsymonds +cd1 +wallclockbuilder +dansouza diff --git a/client/vendor/github.com/google/uuid/LICENSE b/client/vendor/github.com/google/uuid/LICENSE new file mode 100644 index 0000000..5dc6826 --- /dev/null +++ b/client/vendor/github.com/google/uuid/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009,2014 Google Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/client/vendor/github.com/google/uuid/README.md b/client/vendor/github.com/google/uuid/README.md new file mode 100644 index 0000000..3e9a618 --- /dev/null +++ b/client/vendor/github.com/google/uuid/README.md @@ -0,0 +1,21 @@ +# uuid +The uuid package generates and inspects UUIDs based on +[RFC 4122](https://datatracker.ietf.org/doc/html/rfc4122) +and DCE 1.1: Authentication and Security Services. + +This package is based on the github.com/pborman/uuid package (previously named +code.google.com/p/go-uuid). It differs from these earlier packages in that +a UUID is a 16 byte array rather than a byte slice. One loss due to this +change is the ability to represent an invalid UUID (vs a NIL UUID). + +###### Install +```sh +go get github.com/google/uuid +``` + +###### Documentation +[![Go Reference](https://pkg.go.dev/badge/github.com/google/uuid.svg)](https://pkg.go.dev/github.com/google/uuid) + +Full `go doc` style documentation for the package can be viewed online without +installing this package by using the GoDoc site here: +http://pkg.go.dev/github.com/google/uuid diff --git a/client/vendor/github.com/google/uuid/dce.go b/client/vendor/github.com/google/uuid/dce.go new file mode 100644 index 0000000..fa820b9 --- /dev/null +++ b/client/vendor/github.com/google/uuid/dce.go @@ -0,0 +1,80 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "encoding/binary" + "fmt" + "os" +) + +// A Domain represents a Version 2 domain +type Domain byte + +// Domain constants for DCE Security (Version 2) UUIDs. +const ( + Person = Domain(0) + Group = Domain(1) + Org = Domain(2) +) + +// NewDCESecurity returns a DCE Security (Version 2) UUID. +// +// The domain should be one of Person, Group or Org. +// On a POSIX system the id should be the users UID for the Person +// domain and the users GID for the Group. The meaning of id for +// the domain Org or on non-POSIX systems is site defined. +// +// For a given domain/id pair the same token may be returned for up to +// 7 minutes and 10 seconds. +func NewDCESecurity(domain Domain, id uint32) (UUID, error) { + uuid, err := NewUUID() + if err == nil { + uuid[6] = (uuid[6] & 0x0f) | 0x20 // Version 2 + uuid[9] = byte(domain) + binary.BigEndian.PutUint32(uuid[0:], id) + } + return uuid, err +} + +// NewDCEPerson returns a DCE Security (Version 2) UUID in the person +// domain with the id returned by os.Getuid. +// +// NewDCESecurity(Person, uint32(os.Getuid())) +func NewDCEPerson() (UUID, error) { + return NewDCESecurity(Person, uint32(os.Getuid())) +} + +// NewDCEGroup returns a DCE Security (Version 2) UUID in the group +// domain with the id returned by os.Getgid. +// +// NewDCESecurity(Group, uint32(os.Getgid())) +func NewDCEGroup() (UUID, error) { + return NewDCESecurity(Group, uint32(os.Getgid())) +} + +// Domain returns the domain for a Version 2 UUID. Domains are only defined +// for Version 2 UUIDs. +func (uuid UUID) Domain() Domain { + return Domain(uuid[9]) +} + +// ID returns the id for a Version 2 UUID. IDs are only defined for Version 2 +// UUIDs. +func (uuid UUID) ID() uint32 { + return binary.BigEndian.Uint32(uuid[0:4]) +} + +func (d Domain) String() string { + switch d { + case Person: + return "Person" + case Group: + return "Group" + case Org: + return "Org" + } + return fmt.Sprintf("Domain%d", int(d)) +} diff --git a/client/vendor/github.com/google/uuid/doc.go b/client/vendor/github.com/google/uuid/doc.go new file mode 100644 index 0000000..5b8a4b9 --- /dev/null +++ b/client/vendor/github.com/google/uuid/doc.go @@ -0,0 +1,12 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package uuid generates and inspects UUIDs. +// +// UUIDs are based on RFC 4122 and DCE 1.1: Authentication and Security +// Services. +// +// A UUID is a 16 byte (128 bit) array. UUIDs may be used as keys to +// maps or compared directly. +package uuid diff --git a/client/vendor/github.com/google/uuid/hash.go b/client/vendor/github.com/google/uuid/hash.go new file mode 100644 index 0000000..dc60082 --- /dev/null +++ b/client/vendor/github.com/google/uuid/hash.go @@ -0,0 +1,59 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "crypto/md5" + "crypto/sha1" + "hash" +) + +// Well known namespace IDs and UUIDs +var ( + NameSpaceDNS = Must(Parse("6ba7b810-9dad-11d1-80b4-00c04fd430c8")) + NameSpaceURL = Must(Parse("6ba7b811-9dad-11d1-80b4-00c04fd430c8")) + NameSpaceOID = Must(Parse("6ba7b812-9dad-11d1-80b4-00c04fd430c8")) + NameSpaceX500 = Must(Parse("6ba7b814-9dad-11d1-80b4-00c04fd430c8")) + Nil UUID // empty UUID, all zeros + + // The Max UUID is special form of UUID that is specified to have all 128 bits set to 1. + Max = UUID{ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + } +) + +// NewHash returns a new UUID derived from the hash of space concatenated with +// data generated by h. The hash should be at least 16 byte in length. The +// first 16 bytes of the hash are used to form the UUID. The version of the +// UUID will be the lower 4 bits of version. NewHash is used to implement +// NewMD5 and NewSHA1. +func NewHash(h hash.Hash, space UUID, data []byte, version int) UUID { + h.Reset() + h.Write(space[:]) //nolint:errcheck + h.Write(data) //nolint:errcheck + s := h.Sum(nil) + var uuid UUID + copy(uuid[:], s) + uuid[6] = (uuid[6] & 0x0f) | uint8((version&0xf)<<4) + uuid[8] = (uuid[8] & 0x3f) | 0x80 // RFC 4122 variant + return uuid +} + +// NewMD5 returns a new MD5 (Version 3) UUID based on the +// supplied name space and data. It is the same as calling: +// +// NewHash(md5.New(), space, data, 3) +func NewMD5(space UUID, data []byte) UUID { + return NewHash(md5.New(), space, data, 3) +} + +// NewSHA1 returns a new SHA1 (Version 5) UUID based on the +// supplied name space and data. It is the same as calling: +// +// NewHash(sha1.New(), space, data, 5) +func NewSHA1(space UUID, data []byte) UUID { + return NewHash(sha1.New(), space, data, 5) +} diff --git a/client/vendor/github.com/google/uuid/marshal.go b/client/vendor/github.com/google/uuid/marshal.go new file mode 100644 index 0000000..14bd340 --- /dev/null +++ b/client/vendor/github.com/google/uuid/marshal.go @@ -0,0 +1,38 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import "fmt" + +// MarshalText implements encoding.TextMarshaler. +func (uuid UUID) MarshalText() ([]byte, error) { + var js [36]byte + encodeHex(js[:], uuid) + return js[:], nil +} + +// UnmarshalText implements encoding.TextUnmarshaler. +func (uuid *UUID) UnmarshalText(data []byte) error { + id, err := ParseBytes(data) + if err != nil { + return err + } + *uuid = id + return nil +} + +// MarshalBinary implements encoding.BinaryMarshaler. +func (uuid UUID) MarshalBinary() ([]byte, error) { + return uuid[:], nil +} + +// UnmarshalBinary implements encoding.BinaryUnmarshaler. +func (uuid *UUID) UnmarshalBinary(data []byte) error { + if len(data) != 16 { + return fmt.Errorf("invalid UUID (got %d bytes)", len(data)) + } + copy(uuid[:], data) + return nil +} diff --git a/client/vendor/github.com/google/uuid/node.go b/client/vendor/github.com/google/uuid/node.go new file mode 100644 index 0000000..d651a2b --- /dev/null +++ b/client/vendor/github.com/google/uuid/node.go @@ -0,0 +1,90 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "sync" +) + +var ( + nodeMu sync.Mutex + ifname string // name of interface being used + nodeID [6]byte // hardware for version 1 UUIDs + zeroID [6]byte // nodeID with only 0's +) + +// NodeInterface returns the name of the interface from which the NodeID was +// derived. The interface "user" is returned if the NodeID was set by +// SetNodeID. +func NodeInterface() string { + defer nodeMu.Unlock() + nodeMu.Lock() + return ifname +} + +// SetNodeInterface selects the hardware address to be used for Version 1 UUIDs. +// If name is "" then the first usable interface found will be used or a random +// Node ID will be generated. If a named interface cannot be found then false +// is returned. +// +// SetNodeInterface never fails when name is "". +func SetNodeInterface(name string) bool { + defer nodeMu.Unlock() + nodeMu.Lock() + return setNodeInterface(name) +} + +func setNodeInterface(name string) bool { + iname, addr := getHardwareInterface(name) // null implementation for js + if iname != "" && addr != nil { + ifname = iname + copy(nodeID[:], addr) + return true + } + + // We found no interfaces with a valid hardware address. If name + // does not specify a specific interface generate a random Node ID + // (section 4.1.6) + if name == "" { + ifname = "random" + randomBits(nodeID[:]) + return true + } + return false +} + +// NodeID returns a slice of a copy of the current Node ID, setting the Node ID +// if not already set. +func NodeID() []byte { + defer nodeMu.Unlock() + nodeMu.Lock() + if nodeID == zeroID { + setNodeInterface("") + } + nid := nodeID + return nid[:] +} + +// SetNodeID sets the Node ID to be used for Version 1 UUIDs. The first 6 bytes +// of id are used. If id is less than 6 bytes then false is returned and the +// Node ID is not set. +func SetNodeID(id []byte) bool { + if len(id) < 6 { + return false + } + defer nodeMu.Unlock() + nodeMu.Lock() + copy(nodeID[:], id) + ifname = "user" + return true +} + +// NodeID returns the 6 byte node id encoded in uuid. It returns nil if uuid is +// not valid. The NodeID is only well defined for version 1 and 2 UUIDs. +func (uuid UUID) NodeID() []byte { + var node [6]byte + copy(node[:], uuid[10:]) + return node[:] +} diff --git a/client/vendor/github.com/google/uuid/node_js.go b/client/vendor/github.com/google/uuid/node_js.go new file mode 100644 index 0000000..b2a0bc8 --- /dev/null +++ b/client/vendor/github.com/google/uuid/node_js.go @@ -0,0 +1,12 @@ +// Copyright 2017 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build js + +package uuid + +// getHardwareInterface returns nil values for the JS version of the code. +// This removes the "net" dependency, because it is not used in the browser. +// Using the "net" library inflates the size of the transpiled JS code by 673k bytes. +func getHardwareInterface(name string) (string, []byte) { return "", nil } diff --git a/client/vendor/github.com/google/uuid/node_net.go b/client/vendor/github.com/google/uuid/node_net.go new file mode 100644 index 0000000..0cbbcdd --- /dev/null +++ b/client/vendor/github.com/google/uuid/node_net.go @@ -0,0 +1,33 @@ +// Copyright 2017 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !js + +package uuid + +import "net" + +var interfaces []net.Interface // cached list of interfaces + +// getHardwareInterface returns the name and hardware address of interface name. +// If name is "" then the name and hardware address of one of the system's +// interfaces is returned. If no interfaces are found (name does not exist or +// there are no interfaces) then "", nil is returned. +// +// Only addresses of at least 6 bytes are returned. +func getHardwareInterface(name string) (string, []byte) { + if interfaces == nil { + var err error + interfaces, err = net.Interfaces() + if err != nil { + return "", nil + } + } + for _, ifs := range interfaces { + if len(ifs.HardwareAddr) >= 6 && (name == "" || name == ifs.Name) { + return ifs.Name, ifs.HardwareAddr + } + } + return "", nil +} diff --git a/client/vendor/github.com/google/uuid/null.go b/client/vendor/github.com/google/uuid/null.go new file mode 100644 index 0000000..d7fcbf2 --- /dev/null +++ b/client/vendor/github.com/google/uuid/null.go @@ -0,0 +1,118 @@ +// Copyright 2021 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "bytes" + "database/sql/driver" + "encoding/json" + "fmt" +) + +var jsonNull = []byte("null") + +// NullUUID represents a UUID that may be null. +// NullUUID implements the SQL driver.Scanner interface so +// it can be used as a scan destination: +// +// var u uuid.NullUUID +// err := db.QueryRow("SELECT name FROM foo WHERE id=?", id).Scan(&u) +// ... +// if u.Valid { +// // use u.UUID +// } else { +// // NULL value +// } +// +type NullUUID struct { + UUID UUID + Valid bool // Valid is true if UUID is not NULL +} + +// Scan implements the SQL driver.Scanner interface. +func (nu *NullUUID) Scan(value interface{}) error { + if value == nil { + nu.UUID, nu.Valid = Nil, false + return nil + } + + err := nu.UUID.Scan(value) + if err != nil { + nu.Valid = false + return err + } + + nu.Valid = true + return nil +} + +// Value implements the driver Valuer interface. +func (nu NullUUID) Value() (driver.Value, error) { + if !nu.Valid { + return nil, nil + } + // Delegate to UUID Value function + return nu.UUID.Value() +} + +// MarshalBinary implements encoding.BinaryMarshaler. +func (nu NullUUID) MarshalBinary() ([]byte, error) { + if nu.Valid { + return nu.UUID[:], nil + } + + return []byte(nil), nil +} + +// UnmarshalBinary implements encoding.BinaryUnmarshaler. +func (nu *NullUUID) UnmarshalBinary(data []byte) error { + if len(data) != 16 { + return fmt.Errorf("invalid UUID (got %d bytes)", len(data)) + } + copy(nu.UUID[:], data) + nu.Valid = true + return nil +} + +// MarshalText implements encoding.TextMarshaler. +func (nu NullUUID) MarshalText() ([]byte, error) { + if nu.Valid { + return nu.UUID.MarshalText() + } + + return jsonNull, nil +} + +// UnmarshalText implements encoding.TextUnmarshaler. +func (nu *NullUUID) UnmarshalText(data []byte) error { + id, err := ParseBytes(data) + if err != nil { + nu.Valid = false + return err + } + nu.UUID = id + nu.Valid = true + return nil +} + +// MarshalJSON implements json.Marshaler. +func (nu NullUUID) MarshalJSON() ([]byte, error) { + if nu.Valid { + return json.Marshal(nu.UUID) + } + + return jsonNull, nil +} + +// UnmarshalJSON implements json.Unmarshaler. +func (nu *NullUUID) UnmarshalJSON(data []byte) error { + if bytes.Equal(data, jsonNull) { + *nu = NullUUID{} + return nil // valid null UUID + } + err := json.Unmarshal(data, &nu.UUID) + nu.Valid = err == nil + return err +} diff --git a/client/vendor/github.com/google/uuid/sql.go b/client/vendor/github.com/google/uuid/sql.go new file mode 100644 index 0000000..2e02ec0 --- /dev/null +++ b/client/vendor/github.com/google/uuid/sql.go @@ -0,0 +1,59 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "database/sql/driver" + "fmt" +) + +// Scan implements sql.Scanner so UUIDs can be read from databases transparently. +// Currently, database types that map to string and []byte are supported. Please +// consult database-specific driver documentation for matching types. +func (uuid *UUID) Scan(src interface{}) error { + switch src := src.(type) { + case nil: + return nil + + case string: + // if an empty UUID comes from a table, we return a null UUID + if src == "" { + return nil + } + + // see Parse for required string format + u, err := Parse(src) + if err != nil { + return fmt.Errorf("Scan: %v", err) + } + + *uuid = u + + case []byte: + // if an empty UUID comes from a table, we return a null UUID + if len(src) == 0 { + return nil + } + + // assumes a simple slice of bytes if 16 bytes + // otherwise attempts to parse + if len(src) != 16 { + return uuid.Scan(string(src)) + } + copy((*uuid)[:], src) + + default: + return fmt.Errorf("Scan: unable to scan type %T into UUID", src) + } + + return nil +} + +// Value implements sql.Valuer so that UUIDs can be written to databases +// transparently. Currently, UUIDs map to strings. Please consult +// database-specific driver documentation for matching types. +func (uuid UUID) Value() (driver.Value, error) { + return uuid.String(), nil +} diff --git a/client/vendor/github.com/google/uuid/time.go b/client/vendor/github.com/google/uuid/time.go new file mode 100644 index 0000000..c351129 --- /dev/null +++ b/client/vendor/github.com/google/uuid/time.go @@ -0,0 +1,134 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "encoding/binary" + "sync" + "time" +) + +// A Time represents a time as the number of 100's of nanoseconds since 15 Oct +// 1582. +type Time int64 + +const ( + lillian = 2299160 // Julian day of 15 Oct 1582 + unix = 2440587 // Julian day of 1 Jan 1970 + epoch = unix - lillian // Days between epochs + g1582 = epoch * 86400 // seconds between epochs + g1582ns100 = g1582 * 10000000 // 100s of a nanoseconds between epochs +) + +var ( + timeMu sync.Mutex + lasttime uint64 // last time we returned + clockSeq uint16 // clock sequence for this run + + timeNow = time.Now // for testing +) + +// UnixTime converts t the number of seconds and nanoseconds using the Unix +// epoch of 1 Jan 1970. +func (t Time) UnixTime() (sec, nsec int64) { + sec = int64(t - g1582ns100) + nsec = (sec % 10000000) * 100 + sec /= 10000000 + return sec, nsec +} + +// GetTime returns the current Time (100s of nanoseconds since 15 Oct 1582) and +// clock sequence as well as adjusting the clock sequence as needed. An error +// is returned if the current time cannot be determined. +func GetTime() (Time, uint16, error) { + defer timeMu.Unlock() + timeMu.Lock() + return getTime() +} + +func getTime() (Time, uint16, error) { + t := timeNow() + + // If we don't have a clock sequence already, set one. + if clockSeq == 0 { + setClockSequence(-1) + } + now := uint64(t.UnixNano()/100) + g1582ns100 + + // If time has gone backwards with this clock sequence then we + // increment the clock sequence + if now <= lasttime { + clockSeq = ((clockSeq + 1) & 0x3fff) | 0x8000 + } + lasttime = now + return Time(now), clockSeq, nil +} + +// ClockSequence returns the current clock sequence, generating one if not +// already set. The clock sequence is only used for Version 1 UUIDs. +// +// The uuid package does not use global static storage for the clock sequence or +// the last time a UUID was generated. Unless SetClockSequence is used, a new +// random clock sequence is generated the first time a clock sequence is +// requested by ClockSequence, GetTime, or NewUUID. (section 4.2.1.1) +func ClockSequence() int { + defer timeMu.Unlock() + timeMu.Lock() + return clockSequence() +} + +func clockSequence() int { + if clockSeq == 0 { + setClockSequence(-1) + } + return int(clockSeq & 0x3fff) +} + +// SetClockSequence sets the clock sequence to the lower 14 bits of seq. Setting to +// -1 causes a new sequence to be generated. +func SetClockSequence(seq int) { + defer timeMu.Unlock() + timeMu.Lock() + setClockSequence(seq) +} + +func setClockSequence(seq int) { + if seq == -1 { + var b [2]byte + randomBits(b[:]) // clock sequence + seq = int(b[0])<<8 | int(b[1]) + } + oldSeq := clockSeq + clockSeq = uint16(seq&0x3fff) | 0x8000 // Set our variant + if oldSeq != clockSeq { + lasttime = 0 + } +} + +// Time returns the time in 100s of nanoseconds since 15 Oct 1582 encoded in +// uuid. The time is only defined for version 1, 2, 6 and 7 UUIDs. +func (uuid UUID) Time() Time { + var t Time + switch uuid.Version() { + case 6: + time := binary.BigEndian.Uint64(uuid[:8]) // Ignore uuid[6] version b0110 + t = Time(time) + case 7: + time := binary.BigEndian.Uint64(uuid[:8]) + t = Time((time>>16)*10000 + g1582ns100) + default: // forward compatible + time := int64(binary.BigEndian.Uint32(uuid[0:4])) + time |= int64(binary.BigEndian.Uint16(uuid[4:6])) << 32 + time |= int64(binary.BigEndian.Uint16(uuid[6:8])&0xfff) << 48 + t = Time(time) + } + return t +} + +// ClockSequence returns the clock sequence encoded in uuid. +// The clock sequence is only well defined for version 1 and 2 UUIDs. +func (uuid UUID) ClockSequence() int { + return int(binary.BigEndian.Uint16(uuid[8:10])) & 0x3fff +} diff --git a/client/vendor/github.com/google/uuid/util.go b/client/vendor/github.com/google/uuid/util.go new file mode 100644 index 0000000..5ea6c73 --- /dev/null +++ b/client/vendor/github.com/google/uuid/util.go @@ -0,0 +1,43 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "io" +) + +// randomBits completely fills slice b with random data. +func randomBits(b []byte) { + if _, err := io.ReadFull(rander, b); err != nil { + panic(err.Error()) // rand should never fail + } +} + +// xvalues returns the value of a byte as a hexadecimal digit or 255. +var xvalues = [256]byte{ + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255, + 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, +} + +// xtob converts hex characters x1 and x2 into a byte. +func xtob(x1, x2 byte) (byte, bool) { + b1 := xvalues[x1] + b2 := xvalues[x2] + return (b1 << 4) | b2, b1 != 255 && b2 != 255 +} diff --git a/client/vendor/github.com/google/uuid/uuid.go b/client/vendor/github.com/google/uuid/uuid.go new file mode 100644 index 0000000..5232b48 --- /dev/null +++ b/client/vendor/github.com/google/uuid/uuid.go @@ -0,0 +1,365 @@ +// Copyright 2018 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "bytes" + "crypto/rand" + "encoding/hex" + "errors" + "fmt" + "io" + "strings" + "sync" +) + +// A UUID is a 128 bit (16 byte) Universal Unique IDentifier as defined in RFC +// 4122. +type UUID [16]byte + +// A Version represents a UUID's version. +type Version byte + +// A Variant represents a UUID's variant. +type Variant byte + +// Constants returned by Variant. +const ( + Invalid = Variant(iota) // Invalid UUID + RFC4122 // The variant specified in RFC4122 + Reserved // Reserved, NCS backward compatibility. + Microsoft // Reserved, Microsoft Corporation backward compatibility. + Future // Reserved for future definition. +) + +const randPoolSize = 16 * 16 + +var ( + rander = rand.Reader // random function + poolEnabled = false + poolMu sync.Mutex + poolPos = randPoolSize // protected with poolMu + pool [randPoolSize]byte // protected with poolMu +) + +type invalidLengthError struct{ len int } + +func (err invalidLengthError) Error() string { + return fmt.Sprintf("invalid UUID length: %d", err.len) +} + +// IsInvalidLengthError is matcher function for custom error invalidLengthError +func IsInvalidLengthError(err error) bool { + _, ok := err.(invalidLengthError) + return ok +} + +// Parse decodes s into a UUID or returns an error if it cannot be parsed. Both +// the standard UUID forms defined in RFC 4122 +// (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and +// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx) are decoded. In addition, +// Parse accepts non-standard strings such as the raw hex encoding +// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx and 38 byte "Microsoft style" encodings, +// e.g. {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}. Only the middle 36 bytes are +// examined in the latter case. Parse should not be used to validate strings as +// it parses non-standard encodings as indicated above. +func Parse(s string) (UUID, error) { + var uuid UUID + switch len(s) { + // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + case 36: + + // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + case 36 + 9: + if !strings.EqualFold(s[:9], "urn:uuid:") { + return uuid, fmt.Errorf("invalid urn prefix: %q", s[:9]) + } + s = s[9:] + + // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} + case 36 + 2: + s = s[1:] + + // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + case 32: + var ok bool + for i := range uuid { + uuid[i], ok = xtob(s[i*2], s[i*2+1]) + if !ok { + return uuid, errors.New("invalid UUID format") + } + } + return uuid, nil + default: + return uuid, invalidLengthError{len(s)} + } + // s is now at least 36 bytes long + // it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' { + return uuid, errors.New("invalid UUID format") + } + for i, x := range [16]int{ + 0, 2, 4, 6, + 9, 11, + 14, 16, + 19, 21, + 24, 26, 28, 30, 32, 34, + } { + v, ok := xtob(s[x], s[x+1]) + if !ok { + return uuid, errors.New("invalid UUID format") + } + uuid[i] = v + } + return uuid, nil +} + +// ParseBytes is like Parse, except it parses a byte slice instead of a string. +func ParseBytes(b []byte) (UUID, error) { + var uuid UUID + switch len(b) { + case 36: // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + case 36 + 9: // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + if !bytes.EqualFold(b[:9], []byte("urn:uuid:")) { + return uuid, fmt.Errorf("invalid urn prefix: %q", b[:9]) + } + b = b[9:] + case 36 + 2: // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} + b = b[1:] + case 32: // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + var ok bool + for i := 0; i < 32; i += 2 { + uuid[i/2], ok = xtob(b[i], b[i+1]) + if !ok { + return uuid, errors.New("invalid UUID format") + } + } + return uuid, nil + default: + return uuid, invalidLengthError{len(b)} + } + // s is now at least 36 bytes long + // it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + if b[8] != '-' || b[13] != '-' || b[18] != '-' || b[23] != '-' { + return uuid, errors.New("invalid UUID format") + } + for i, x := range [16]int{ + 0, 2, 4, 6, + 9, 11, + 14, 16, + 19, 21, + 24, 26, 28, 30, 32, 34, + } { + v, ok := xtob(b[x], b[x+1]) + if !ok { + return uuid, errors.New("invalid UUID format") + } + uuid[i] = v + } + return uuid, nil +} + +// MustParse is like Parse but panics if the string cannot be parsed. +// It simplifies safe initialization of global variables holding compiled UUIDs. +func MustParse(s string) UUID { + uuid, err := Parse(s) + if err != nil { + panic(`uuid: Parse(` + s + `): ` + err.Error()) + } + return uuid +} + +// FromBytes creates a new UUID from a byte slice. Returns an error if the slice +// does not have a length of 16. The bytes are copied from the slice. +func FromBytes(b []byte) (uuid UUID, err error) { + err = uuid.UnmarshalBinary(b) + return uuid, err +} + +// Must returns uuid if err is nil and panics otherwise. +func Must(uuid UUID, err error) UUID { + if err != nil { + panic(err) + } + return uuid +} + +// Validate returns an error if s is not a properly formatted UUID in one of the following formats: +// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx +// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx +// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +// {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} +// It returns an error if the format is invalid, otherwise nil. +func Validate(s string) error { + switch len(s) { + // Standard UUID format + case 36: + + // UUID with "urn:uuid:" prefix + case 36 + 9: + if !strings.EqualFold(s[:9], "urn:uuid:") { + return fmt.Errorf("invalid urn prefix: %q", s[:9]) + } + s = s[9:] + + // UUID enclosed in braces + case 36 + 2: + if s[0] != '{' || s[len(s)-1] != '}' { + return fmt.Errorf("invalid bracketed UUID format") + } + s = s[1 : len(s)-1] + + // UUID without hyphens + case 32: + for i := 0; i < len(s); i += 2 { + _, ok := xtob(s[i], s[i+1]) + if !ok { + return errors.New("invalid UUID format") + } + } + + default: + return invalidLengthError{len(s)} + } + + // Check for standard UUID format + if len(s) == 36 { + if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' { + return errors.New("invalid UUID format") + } + for _, x := range []int{0, 2, 4, 6, 9, 11, 14, 16, 19, 21, 24, 26, 28, 30, 32, 34} { + if _, ok := xtob(s[x], s[x+1]); !ok { + return errors.New("invalid UUID format") + } + } + } + + return nil +} + +// String returns the string form of uuid, xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx +// , or "" if uuid is invalid. +func (uuid UUID) String() string { + var buf [36]byte + encodeHex(buf[:], uuid) + return string(buf[:]) +} + +// URN returns the RFC 2141 URN form of uuid, +// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, or "" if uuid is invalid. +func (uuid UUID) URN() string { + var buf [36 + 9]byte + copy(buf[:], "urn:uuid:") + encodeHex(buf[9:], uuid) + return string(buf[:]) +} + +func encodeHex(dst []byte, uuid UUID) { + hex.Encode(dst, uuid[:4]) + dst[8] = '-' + hex.Encode(dst[9:13], uuid[4:6]) + dst[13] = '-' + hex.Encode(dst[14:18], uuid[6:8]) + dst[18] = '-' + hex.Encode(dst[19:23], uuid[8:10]) + dst[23] = '-' + hex.Encode(dst[24:], uuid[10:]) +} + +// Variant returns the variant encoded in uuid. +func (uuid UUID) Variant() Variant { + switch { + case (uuid[8] & 0xc0) == 0x80: + return RFC4122 + case (uuid[8] & 0xe0) == 0xc0: + return Microsoft + case (uuid[8] & 0xe0) == 0xe0: + return Future + default: + return Reserved + } +} + +// Version returns the version of uuid. +func (uuid UUID) Version() Version { + return Version(uuid[6] >> 4) +} + +func (v Version) String() string { + if v > 15 { + return fmt.Sprintf("BAD_VERSION_%d", v) + } + return fmt.Sprintf("VERSION_%d", v) +} + +func (v Variant) String() string { + switch v { + case RFC4122: + return "RFC4122" + case Reserved: + return "Reserved" + case Microsoft: + return "Microsoft" + case Future: + return "Future" + case Invalid: + return "Invalid" + } + return fmt.Sprintf("BadVariant%d", int(v)) +} + +// SetRand sets the random number generator to r, which implements io.Reader. +// If r.Read returns an error when the package requests random data then +// a panic will be issued. +// +// Calling SetRand with nil sets the random number generator to the default +// generator. +func SetRand(r io.Reader) { + if r == nil { + rander = rand.Reader + return + } + rander = r +} + +// EnableRandPool enables internal randomness pool used for Random +// (Version 4) UUID generation. The pool contains random bytes read from +// the random number generator on demand in batches. Enabling the pool +// may improve the UUID generation throughput significantly. +// +// Since the pool is stored on the Go heap, this feature may be a bad fit +// for security sensitive applications. +// +// Both EnableRandPool and DisableRandPool are not thread-safe and should +// only be called when there is no possibility that New or any other +// UUID Version 4 generation function will be called concurrently. +func EnableRandPool() { + poolEnabled = true +} + +// DisableRandPool disables the randomness pool if it was previously +// enabled with EnableRandPool. +// +// Both EnableRandPool and DisableRandPool are not thread-safe and should +// only be called when there is no possibility that New or any other +// UUID Version 4 generation function will be called concurrently. +func DisableRandPool() { + poolEnabled = false + defer poolMu.Unlock() + poolMu.Lock() + poolPos = randPoolSize +} + +// UUIDs is a slice of UUID types. +type UUIDs []UUID + +// Strings returns a string slice containing the string form of each UUID in uuids. +func (uuids UUIDs) Strings() []string { + var uuidStrs = make([]string, len(uuids)) + for i, uuid := range uuids { + uuidStrs[i] = uuid.String() + } + return uuidStrs +} diff --git a/client/vendor/github.com/google/uuid/version1.go b/client/vendor/github.com/google/uuid/version1.go new file mode 100644 index 0000000..4631096 --- /dev/null +++ b/client/vendor/github.com/google/uuid/version1.go @@ -0,0 +1,44 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "encoding/binary" +) + +// NewUUID returns a Version 1 UUID based on the current NodeID and clock +// sequence, and the current time. If the NodeID has not been set by SetNodeID +// or SetNodeInterface then it will be set automatically. If the NodeID cannot +// be set NewUUID returns nil. If clock sequence has not been set by +// SetClockSequence then it will be set automatically. If GetTime fails to +// return the current NewUUID returns nil and an error. +// +// In most cases, New should be used. +func NewUUID() (UUID, error) { + var uuid UUID + now, seq, err := GetTime() + if err != nil { + return uuid, err + } + + timeLow := uint32(now & 0xffffffff) + timeMid := uint16((now >> 32) & 0xffff) + timeHi := uint16((now >> 48) & 0x0fff) + timeHi |= 0x1000 // Version 1 + + binary.BigEndian.PutUint32(uuid[0:], timeLow) + binary.BigEndian.PutUint16(uuid[4:], timeMid) + binary.BigEndian.PutUint16(uuid[6:], timeHi) + binary.BigEndian.PutUint16(uuid[8:], seq) + + nodeMu.Lock() + if nodeID == zeroID { + setNodeInterface("") + } + copy(uuid[10:], nodeID[:]) + nodeMu.Unlock() + + return uuid, nil +} diff --git a/client/vendor/github.com/google/uuid/version4.go b/client/vendor/github.com/google/uuid/version4.go new file mode 100644 index 0000000..7697802 --- /dev/null +++ b/client/vendor/github.com/google/uuid/version4.go @@ -0,0 +1,76 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import "io" + +// New creates a new random UUID or panics. New is equivalent to +// the expression +// +// uuid.Must(uuid.NewRandom()) +func New() UUID { + return Must(NewRandom()) +} + +// NewString creates a new random UUID and returns it as a string or panics. +// NewString is equivalent to the expression +// +// uuid.New().String() +func NewString() string { + return Must(NewRandom()).String() +} + +// NewRandom returns a Random (Version 4) UUID. +// +// The strength of the UUIDs is based on the strength of the crypto/rand +// package. +// +// Uses the randomness pool if it was enabled with EnableRandPool. +// +// A note about uniqueness derived from the UUID Wikipedia entry: +// +// Randomly generated UUIDs have 122 random bits. One's annual risk of being +// hit by a meteorite is estimated to be one chance in 17 billion, that +// means the probability is about 0.00000000006 (6 × 10−11), +// equivalent to the odds of creating a few tens of trillions of UUIDs in a +// year and having one duplicate. +func NewRandom() (UUID, error) { + if !poolEnabled { + return NewRandomFromReader(rander) + } + return newRandomFromPool() +} + +// NewRandomFromReader returns a UUID based on bytes read from a given io.Reader. +func NewRandomFromReader(r io.Reader) (UUID, error) { + var uuid UUID + _, err := io.ReadFull(r, uuid[:]) + if err != nil { + return Nil, err + } + uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4 + uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10 + return uuid, nil +} + +func newRandomFromPool() (UUID, error) { + var uuid UUID + poolMu.Lock() + if poolPos == randPoolSize { + _, err := io.ReadFull(rander, pool[:]) + if err != nil { + poolMu.Unlock() + return Nil, err + } + poolPos = 0 + } + copy(uuid[:], pool[poolPos:(poolPos+16)]) + poolPos += 16 + poolMu.Unlock() + + uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4 + uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10 + return uuid, nil +} diff --git a/client/vendor/github.com/google/uuid/version6.go b/client/vendor/github.com/google/uuid/version6.go new file mode 100644 index 0000000..339a959 --- /dev/null +++ b/client/vendor/github.com/google/uuid/version6.go @@ -0,0 +1,56 @@ +// Copyright 2023 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import "encoding/binary" + +// UUID version 6 is a field-compatible version of UUIDv1, reordered for improved DB locality. +// It is expected that UUIDv6 will primarily be used in contexts where there are existing v1 UUIDs. +// Systems that do not involve legacy UUIDv1 SHOULD consider using UUIDv7 instead. +// +// see https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format-03#uuidv6 +// +// NewV6 returns a Version 6 UUID based on the current NodeID and clock +// sequence, and the current time. If the NodeID has not been set by SetNodeID +// or SetNodeInterface then it will be set automatically. If the NodeID cannot +// be set NewV6 set NodeID is random bits automatically . If clock sequence has not been set by +// SetClockSequence then it will be set automatically. If GetTime fails to +// return the current NewV6 returns Nil and an error. +func NewV6() (UUID, error) { + var uuid UUID + now, seq, err := GetTime() + if err != nil { + return uuid, err + } + + /* + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | time_high | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | time_mid | time_low_and_version | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |clk_seq_hi_res | clk_seq_low | node (0-1) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | node (2-5) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + + binary.BigEndian.PutUint64(uuid[0:], uint64(now)) + binary.BigEndian.PutUint16(uuid[8:], seq) + + uuid[6] = 0x60 | (uuid[6] & 0x0F) + uuid[8] = 0x80 | (uuid[8] & 0x3F) + + nodeMu.Lock() + if nodeID == zeroID { + setNodeInterface("") + } + copy(uuid[10:], nodeID[:]) + nodeMu.Unlock() + + return uuid, nil +} diff --git a/client/vendor/github.com/google/uuid/version7.go b/client/vendor/github.com/google/uuid/version7.go new file mode 100644 index 0000000..3167b64 --- /dev/null +++ b/client/vendor/github.com/google/uuid/version7.go @@ -0,0 +1,104 @@ +// Copyright 2023 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "io" +) + +// UUID version 7 features a time-ordered value field derived from the widely +// implemented and well known Unix Epoch timestamp source, +// the number of milliseconds seconds since midnight 1 Jan 1970 UTC, leap seconds excluded. +// As well as improved entropy characteristics over versions 1 or 6. +// +// see https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format-03#name-uuid-version-7 +// +// Implementations SHOULD utilize UUID version 7 over UUID version 1 and 6 if possible. +// +// NewV7 returns a Version 7 UUID based on the current time(Unix Epoch). +// Uses the randomness pool if it was enabled with EnableRandPool. +// On error, NewV7 returns Nil and an error +func NewV7() (UUID, error) { + uuid, err := NewRandom() + if err != nil { + return uuid, err + } + makeV7(uuid[:]) + return uuid, nil +} + +// NewV7FromReader returns a Version 7 UUID based on the current time(Unix Epoch). +// it use NewRandomFromReader fill random bits. +// On error, NewV7FromReader returns Nil and an error. +func NewV7FromReader(r io.Reader) (UUID, error) { + uuid, err := NewRandomFromReader(r) + if err != nil { + return uuid, err + } + + makeV7(uuid[:]) + return uuid, nil +} + +// makeV7 fill 48 bits time (uuid[0] - uuid[5]), set version b0111 (uuid[6]) +// uuid[8] already has the right version number (Variant is 10) +// see function NewV7 and NewV7FromReader +func makeV7(uuid []byte) { + /* + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | unix_ts_ms | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | unix_ts_ms | ver | rand_a (12 bit seq) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |var| rand_b | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | rand_b | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + _ = uuid[15] // bounds check + + t, s := getV7Time() + + uuid[0] = byte(t >> 40) + uuid[1] = byte(t >> 32) + uuid[2] = byte(t >> 24) + uuid[3] = byte(t >> 16) + uuid[4] = byte(t >> 8) + uuid[5] = byte(t) + + uuid[6] = 0x70 | (0x0F & byte(s>>8)) + uuid[7] = byte(s) +} + +// lastV7time is the last time we returned stored as: +// +// 52 bits of time in milliseconds since epoch +// 12 bits of (fractional nanoseconds) >> 8 +var lastV7time int64 + +const nanoPerMilli = 1000000 + +// getV7Time returns the time in milliseconds and nanoseconds / 256. +// The returned (milli << 12 + seq) is guarenteed to be greater than +// (milli << 12 + seq) returned by any previous call to getV7Time. +func getV7Time() (milli, seq int64) { + timeMu.Lock() + defer timeMu.Unlock() + + nano := timeNow().UnixNano() + milli = nano / nanoPerMilli + // Sequence number is between 0 and 3906 (nanoPerMilli>>8) + seq = (nano - milli*nanoPerMilli) >> 8 + now := milli<<12 + seq + if now <= lastV7time { + now = lastV7time + 1 + milli = now >> 12 + seq = now & 0xfff + } + lastV7time = now + return milli, seq +} diff --git a/client/vendor/github.com/zcalusic/sysinfo/LICENSE b/client/vendor/github.com/zcalusic/sysinfo/LICENSE new file mode 100644 index 0000000..1cd08b4 --- /dev/null +++ b/client/vendor/github.com/zcalusic/sysinfo/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright © 2016 Zlatko Čalušić + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/client/vendor/github.com/zcalusic/sysinfo/README.md b/client/vendor/github.com/zcalusic/sysinfo/README.md new file mode 100644 index 0000000..7d72066 --- /dev/null +++ b/client/vendor/github.com/zcalusic/sysinfo/README.md @@ -0,0 +1,204 @@ +# Sysinfo + +[![Go Report Card](https://goreportcard.com/badge/github.com/zcalusic/sysinfo)](https://goreportcard.com/report/github.com/zcalusic/sysinfo) +[![GoDoc](https://godoc.org/github.com/zcalusic/sysinfo?status.svg)](https://godoc.org/github.com/zcalusic/sysinfo) +[![License](https://img.shields.io/badge/license-MIT-a31f34.svg?maxAge=2592000)](https://github.com/zcalusic/sysinfo/blob/master/LICENSE) +[![Powered by](https://img.shields.io/badge/powered_by-Go-5272b4.svg?maxAge=2592000)](https://go.dev/) +[![Platform](https://img.shields.io/badge/platform-Linux-009bde.svg?maxAge=2592000)](https://www.linuxfoundation.org/) + +Package sysinfo is a Go library providing Linux OS / kernel / hardware system information. It's completely standalone, +has no dependencies on the host system and doesn't execute external programs. It collects only "inventory type" +information, things that don't change often. + +## Code Example + +```go +package main + +import ( + "encoding/json" + "fmt" + "log" + "os/user" + + "github.com/zcalusic/sysinfo" +) + +func main() { + current, err := user.Current() + if err != nil { + log.Fatal(err) + } + + if current.Uid != "0" { + log.Fatal("requires superuser privilege") + } + + var si sysinfo.SysInfo + + si.GetSysInfo() + + data, err := json.MarshalIndent(&si, "", " ") + if err != nil { + log.Fatal(err) + } + + fmt.Println(string(data)) +} +``` + +## Motivation + +I couldn't find any self-contained library that would provide set of data/features I needed. So another sysinfo was +born. + +The purpose of the library is to collect only inventory info. No metrics like CPU usage or load average will be added. +The rule of thumb is, if it's changing during the day, every day, it doesn't belong in the library. + +The library should work well on any modern/supported Linux distribution. There are no plans to add support for older +unsupported Linux distributions/kernels, to keep the code clean and robust and reduce the maintenance burden. + +## Requirements + +Sysinfo requires: + +- Linux kernel 4.2 or later +- access to /sys & /proc Linux virtual file systems +- access to various files in /etc, /var, /run FS hierarchy +- superuser privileges (to access SMBIOS/DMI table and detect RAM size and properties) + +Sysinfo doesn't require ANY other external utility on the target system, which is its primary strength, IMHO. + +It depends on Linux internals heavily, so there are no plans to support other operating systems. + +## Installation + +Just use go get. + +``` +go get github.com/zcalusic/sysinfo +``` + +There's also a very simple utility demonstrating sysinfo library capabilities. Start it (as superuser) to get pretty +formatted JSON output of all the info that sysinfo library provides. Due to its simplicity, the source code of the +utility also doubles down as an example of how to use the library. + +``` +go get github.com/zcalusic/sysinfo/cmd/sysinfo +``` + +-- + +Build demo utility in Docker container: https://github.com/mattscilipoti/compile_sysinfo + +## Sample output + +```json +{ + "sysinfo": { + "version": "0.9.1", + "timestamp": "2016-09-24T13:30:28.369498856+02:00" + }, + "node": { + "hostname": "web12", + "machineid": "04aa55927ebd390829367c1757b98cac", + "timezone": "Europe/Zagreb" + }, + "os": { + "name": "CentOS Linux 7 (Core)", + "vendor": "centos", + "version": "7", + "release": "7.2.1511", + "architecture": "amd64" + }, + "kernel": { + "release": "3.10.0-327.13.1.el7.x86_64", + "version": "#1 SMP Thu Mar 31 16:04:38 UTC 2016", + "architecture": "x86_64" + }, + "product": { + "name": "RH2288H V3", + "vendor": "Huawei", + "version": "V100R003", + "serial": "2103711GEL10F3430658" + }, + "board": { + "name": "BC11HGSA0", + "vendor": "Huawei", + "version": "V100R003", + "serial": "033HXVCNG3107624" + }, + "chassis": { + "type": 17, + "vendor": "Huawei" + }, + "bios": { + "vendor": "Insyde Corp.", + "version": "3.16", + "date": "03/16/2016" + }, + "cpu": { + "vendor": "GenuineIntel", + "model": "Intel(R) Xeon(R) CPU E5-2630 v3 @ 2.40GHz", + "speed": 2400, + "cache": 20480, + "cpus": 1, + "cores": 8, + "threads": 16 + }, + "memory": { + "type": "DRAM", + "speed": 2133, + "size": 65536 + }, + "storage": [ + { + "name": "sda", + "driver": "sd", + "vendor": "ATA", + "model": "ST9500620NS", + "serial": "9XF2HZ9K", + "size": 500 + } + ], + "network": [ + { + "name": "enp3s0f1", + "driver": "ixgbe", + "macaddress": "84:ad:5a:e3:79:71", + "port": "fibre", + "speed": 10000 + } + ] +} +``` + +## Contributors + +Contributors are welcome, just open a new issue / pull request. + +## License + +``` +The MIT License (MIT) + +Copyright © 2016 Zlatko Čalušić + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +``` diff --git a/client/vendor/github.com/zcalusic/sysinfo/bios.go b/client/vendor/github.com/zcalusic/sysinfo/bios.go new file mode 100644 index 0000000..06333fc --- /dev/null +++ b/client/vendor/github.com/zcalusic/sysinfo/bios.go @@ -0,0 +1,18 @@ +// Copyright © 2016 Zlatko Čalušić +// +// Use of this source code is governed by an MIT-style license that can be found in the LICENSE file. + +package sysinfo + +// BIOS information. +type BIOS struct { + Vendor string `json:"vendor,omitempty"` + Version string `json:"version,omitempty"` + Date string `json:"date,omitempty"` +} + +func (si *SysInfo) getBIOSInfo() { + si.BIOS.Vendor = slurpFile("/sys/class/dmi/id/bios_vendor") + si.BIOS.Version = slurpFile("/sys/class/dmi/id/bios_version") + si.BIOS.Date = slurpFile("/sys/class/dmi/id/bios_date") +} diff --git a/client/vendor/github.com/zcalusic/sysinfo/board.go b/client/vendor/github.com/zcalusic/sysinfo/board.go new file mode 100644 index 0000000..dc90289 --- /dev/null +++ b/client/vendor/github.com/zcalusic/sysinfo/board.go @@ -0,0 +1,22 @@ +// Copyright © 2016 Zlatko Čalušić +// +// Use of this source code is governed by an MIT-style license that can be found in the LICENSE file. + +package sysinfo + +// Board information. +type Board struct { + Name string `json:"name,omitempty"` + Vendor string `json:"vendor,omitempty"` + Version string `json:"version,omitempty"` + Serial string `json:"serial,omitempty"` + AssetTag string `json:"assettag,omitempty"` +} + +func (si *SysInfo) getBoardInfo() { + si.Board.Name = slurpFile("/sys/class/dmi/id/board_name") + si.Board.Vendor = slurpFile("/sys/class/dmi/id/board_vendor") + si.Board.Version = slurpFile("/sys/class/dmi/id/board_version") + si.Board.Serial = slurpFile("/sys/class/dmi/id/board_serial") + si.Board.AssetTag = slurpFile("/sys/class/dmi/id/board_asset_tag") +} diff --git a/client/vendor/github.com/zcalusic/sysinfo/chassis.go b/client/vendor/github.com/zcalusic/sysinfo/chassis.go new file mode 100644 index 0000000..3948dad --- /dev/null +++ b/client/vendor/github.com/zcalusic/sysinfo/chassis.go @@ -0,0 +1,26 @@ +// Copyright © 2016 Zlatko Čalušić +// +// Use of this source code is governed by an MIT-style license that can be found in the LICENSE file. + +package sysinfo + +import "strconv" + +// Chassis information. +type Chassis struct { + Type uint `json:"type,omitempty"` + Vendor string `json:"vendor,omitempty"` + Version string `json:"version,omitempty"` + Serial string `json:"serial,omitempty"` + AssetTag string `json:"assettag,omitempty"` +} + +func (si *SysInfo) getChassisInfo() { + if chtype, err := strconv.ParseUint(slurpFile("/sys/class/dmi/id/chassis_type"), 10, 64); err == nil { + si.Chassis.Type = uint(chtype) + } + si.Chassis.Vendor = slurpFile("/sys/class/dmi/id/chassis_vendor") + si.Chassis.Version = slurpFile("/sys/class/dmi/id/chassis_version") + si.Chassis.Serial = slurpFile("/sys/class/dmi/id/chassis_serial") + si.Chassis.AssetTag = slurpFile("/sys/class/dmi/id/chassis_asset_tag") +} diff --git a/client/vendor/github.com/zcalusic/sysinfo/cpu.go b/client/vendor/github.com/zcalusic/sysinfo/cpu.go new file mode 100644 index 0000000..11fe17b --- /dev/null +++ b/client/vendor/github.com/zcalusic/sysinfo/cpu.go @@ -0,0 +1,85 @@ +// Copyright © 2016 Zlatko Čalušić +// +// Use of this source code is governed by an MIT-style license that can be found in the LICENSE file. + +package sysinfo + +import ( + "bufio" + "fmt" + "os" + "regexp" + "runtime" + "strconv" + "strings" +) + +// CPU information. +type CPU struct { + Vendor string `json:"vendor,omitempty"` + Model string `json:"model,omitempty"` + Speed uint `json:"speed,omitempty"` // CPU clock rate in MHz + Cache uint `json:"cache,omitempty"` // CPU cache size in KB + Cpus uint `json:"cpus,omitempty"` // number of physical CPUs + Cores uint `json:"cores,omitempty"` // number of physical CPU cores + Threads uint `json:"threads,omitempty"` // number of logical (HT) CPU cores +} + +var ( + reTwoColumns = regexp.MustCompile("\t+: ") + reExtraSpace = regexp.MustCompile(" +") + reCacheSize = regexp.MustCompile(`^(\d+) KB$`) +) + +func (si *SysInfo) getCPUInfo() { + si.CPU.Threads = uint(runtime.NumCPU()) + + f, err := os.Open("/proc/cpuinfo") + if err != nil { + return + } + defer f.Close() + + cpu := make(map[string]bool) + core := make(map[string]bool) + + var cpuID string + + s := bufio.NewScanner(f) + for s.Scan() { + if sl := reTwoColumns.Split(s.Text(), 2); sl != nil { + switch sl[0] { + case "physical id": + cpuID = sl[1] + cpu[cpuID] = true + case "core id": + coreID := fmt.Sprintf("%s/%s", cpuID, sl[1]) + core[coreID] = true + case "vendor_id": + if si.CPU.Vendor == "" { + si.CPU.Vendor = sl[1] + } + case "model name": + if si.CPU.Model == "" { + // CPU model, as reported by /proc/cpuinfo, can be a bit ugly. Clean up... + model := reExtraSpace.ReplaceAllLiteralString(sl[1], " ") + si.CPU.Model = strings.Replace(model, "- ", "-", 1) + } + case "cache size": + if si.CPU.Cache == 0 { + if m := reCacheSize.FindStringSubmatch(sl[1]); m != nil { + if cache, err := strconv.ParseUint(m[1], 10, 64); err == nil { + si.CPU.Cache = uint(cache) + } + } + } + } + } + } + if s.Err() != nil { + return + } + + si.CPU.Cpus = uint(len(cpu)) + si.CPU.Cores = uint(len(core)) +} diff --git a/client/vendor/github.com/zcalusic/sysinfo/cpuid/LICENSE b/client/vendor/github.com/zcalusic/sysinfo/cpuid/LICENSE new file mode 100644 index 0000000..6a66aea --- /dev/null +++ b/client/vendor/github.com/zcalusic/sysinfo/cpuid/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/client/vendor/github.com/zcalusic/sysinfo/cpuid/cpuid.go b/client/vendor/github.com/zcalusic/sysinfo/cpuid/cpuid.go new file mode 100644 index 0000000..aec5d40 --- /dev/null +++ b/client/vendor/github.com/zcalusic/sysinfo/cpuid/cpuid.go @@ -0,0 +1,9 @@ +// Copyright © 2018 Zlatko Čalušić +// +// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. + +// Package cpuid gives Go programs access to CPUID opcode. +package cpuid + +// CPUID returns processor identification and feature information. +func CPUID(info *[4]uint32, ax uint32) diff --git a/client/vendor/github.com/zcalusic/sysinfo/cpuid/cpuid_386.s b/client/vendor/github.com/zcalusic/sysinfo/cpuid/cpuid_386.s new file mode 100644 index 0000000..96cac04 --- /dev/null +++ b/client/vendor/github.com/zcalusic/sysinfo/cpuid/cpuid_386.s @@ -0,0 +1,16 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !gccgo + +TEXT ·CPUID(SB),$0-8 + MOVL ax+4(FP), AX + CPUID + MOVL info+0(FP), DI + MOVL AX, 0(DI) + MOVL BX, 4(DI) + MOVL CX, 8(DI) + MOVL DX, 12(DI) + RET + diff --git a/client/vendor/github.com/zcalusic/sysinfo/cpuid/cpuid_amd64.s b/client/vendor/github.com/zcalusic/sysinfo/cpuid/cpuid_amd64.s new file mode 100644 index 0000000..3a6feba --- /dev/null +++ b/client/vendor/github.com/zcalusic/sysinfo/cpuid/cpuid_amd64.s @@ -0,0 +1,16 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !gccgo + +TEXT ·CPUID(SB),$0-12 + MOVL ax+8(FP), AX + CPUID + MOVQ info+0(FP), DI + MOVL AX, 0(DI) + MOVL BX, 4(DI) + MOVL CX, 8(DI) + MOVL DX, 12(DI) + RET + diff --git a/client/vendor/github.com/zcalusic/sysinfo/cpuid/cpuid_default.s b/client/vendor/github.com/zcalusic/sysinfo/cpuid/cpuid_default.s new file mode 100644 index 0000000..8a892cc --- /dev/null +++ b/client/vendor/github.com/zcalusic/sysinfo/cpuid/cpuid_default.s @@ -0,0 +1,10 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !386,!amd64,!gccgo + +#include "textflag.h" + +TEXT ·CPUID(SB),NOSPLIT,$0-0 + RET diff --git a/client/vendor/github.com/zcalusic/sysinfo/hypervisor.go b/client/vendor/github.com/zcalusic/sysinfo/hypervisor.go new file mode 100644 index 0000000..e75d641 --- /dev/null +++ b/client/vendor/github.com/zcalusic/sysinfo/hypervisor.go @@ -0,0 +1,65 @@ +// Copyright © 2016 Zlatko Čalušić +// +// Use of this source code is governed by an MIT-style license that can be found in the LICENSE file. + +package sysinfo + +import ( + "strings" + "unsafe" + + "github.com/zcalusic/sysinfo/cpuid" +) + +// https://en.wikipedia.org/wiki/CPUID#EAX.3D0:_Get_vendor_ID +var hvmap = map[string]string{ + "bhyve bhyve ": "bhyve", + "KVMKVMKVM": "kvm", + "Microsoft Hv": "hyperv", + " lrpepyh vr": "parallels", + "VMwareVMware": "vmware", + "XenVMMXenVMM": "xenhvm", +} + +func isHypervisorActive() bool { + var info [4]uint32 + cpuid.CPUID(&info, 0x1) + return info[2]&(1<<31) != 0 +} + +func getHypervisorCpuid(ax uint32) string { + var info [4]uint32 + cpuid.CPUID(&info, ax) + return hvmap[strings.TrimRight(string((*[12]byte)(unsafe.Pointer(&info[1]))[:]), "\000")] +} + +func (si *SysInfo) getHypervisor() { + if !isHypervisorActive() { + if hypervisorType := slurpFile("/sys/hypervisor/type"); hypervisorType != "" { + if hypervisorType == "xen" { + si.Node.Hypervisor = "xenpv" + } + } + return + } + + // KVM has been caught to move its real signature to this leaf, and put something completely different in the + // standard location. So this leaf must be checked first. + if hv := getHypervisorCpuid(0x40000100); hv != "" { + si.Node.Hypervisor = hv + return + } + + if hv := getHypervisorCpuid(0x40000000); hv != "" { + si.Node.Hypervisor = hv + return + } + + // getBIOSInfo() must have run first, to detect BIOS vendor + if si.BIOS.Vendor == "Bochs" { + si.Node.Hypervisor = "bochs" + return + } + + si.Node.Hypervisor = "unknown" +} diff --git a/client/vendor/github.com/zcalusic/sysinfo/kernel_darwin.go b/client/vendor/github.com/zcalusic/sysinfo/kernel_darwin.go new file mode 100644 index 0000000..73e0c98 --- /dev/null +++ b/client/vendor/github.com/zcalusic/sysinfo/kernel_darwin.go @@ -0,0 +1,14 @@ +//go:build darwin +// +build darwin + +package sysinfo + +// Kernel information. +type Kernel struct { + Release string `json:"release,omitempty"` + Version string `json:"version,omitempty"` + Architecture string `json:"architecture,omitempty"` +} + +func (si *SysInfo) getKernelInfo() { +} diff --git a/client/vendor/github.com/zcalusic/sysinfo/kernel_linux.go b/client/vendor/github.com/zcalusic/sysinfo/kernel_linux.go new file mode 100644 index 0000000..4b8b889 --- /dev/null +++ b/client/vendor/github.com/zcalusic/sysinfo/kernel_linux.go @@ -0,0 +1,33 @@ +// Copyright © 2016 Zlatko Čalušić +// +// Use of this source code is governed by an MIT-style license that can be found in the LICENSE file. + +//go:build linux +// +build linux + +package sysinfo + +import ( + "strings" + "syscall" + "unsafe" +) + +// Kernel information. +type Kernel struct { + Release string `json:"release,omitempty"` + Version string `json:"version,omitempty"` + Architecture string `json:"architecture,omitempty"` +} + +func (si *SysInfo) getKernelInfo() { + si.Kernel.Release = slurpFile("/proc/sys/kernel/osrelease") + si.Kernel.Version = slurpFile("/proc/sys/kernel/version") + + var uname syscall.Utsname + if err := syscall.Uname(&uname); err != nil { + return + } + + si.Kernel.Architecture = strings.TrimRight(string((*[65]byte)(unsafe.Pointer(&uname.Machine))[:]), "\000") +} diff --git a/client/vendor/github.com/zcalusic/sysinfo/memory.go b/client/vendor/github.com/zcalusic/sysinfo/memory.go new file mode 100644 index 0000000..f052925 --- /dev/null +++ b/client/vendor/github.com/zcalusic/sysinfo/memory.go @@ -0,0 +1,121 @@ +// Copyright © 2016 Zlatko Čalušić +// +// Use of this source code is governed by an MIT-style license that can be found in the LICENSE file. + +package sysinfo + +import ( + "bytes" + "encoding/binary" + "os" + "strconv" +) + +// Memory information. +type Memory struct { + Type string `json:"type,omitempty"` + Speed uint `json:"speed,omitempty"` // RAM data rate in MT/s + Size uint `json:"size,omitempty"` // RAM size in MB +} + +func word(data []byte, index int) uint16 { + return binary.LittleEndian.Uint16(data[index : index+2]) +} + +func dword(data []byte, index int) uint32 { + return binary.LittleEndian.Uint32(data[index : index+4]) +} + +func qword(data []byte, index int) uint64 { + return binary.LittleEndian.Uint64(data[index : index+8]) +} + +func (si *SysInfo) getMemoryInfo() { + dmi, err := os.ReadFile("/sys/firmware/dmi/tables/DMI") + if err != nil { + // Xen hypervisor + if targetKB := slurpFile("/sys/devices/system/xen_memory/xen_memory0/target_kb"); targetKB != "" { + si.Memory.Type = "DRAM" + size, _ := strconv.ParseUint(targetKB, 10, 64) + si.Memory.Size = uint(size) / 1024 + } + return + } + + si.Memory.Size = 0 + var memSizeAlt uint +loop: + for p := 0; p < len(dmi)-1; { + recType := dmi[p] + recLen := dmi[p+1] + + switch recType { + case 4: + if si.CPU.Speed == 0 { + si.CPU.Speed = uint(word(dmi, p+0x16)) + } + case 17: + size := uint(word(dmi, p+0x0c)) + if size == 0 || size == 0xffff || size&0x8000 == 0x8000 { + break + } + if size == 0x7fff { + if recLen >= 0x20 { + size = uint(dword(dmi, p+0x1c)) + } else { + break + } + } + + si.Memory.Size += size + + if si.Memory.Type == "" { + // SMBIOS Reference Specification Version 3.0.0, page 92 + memTypes := [...]string{ + "Other", "Unknown", "DRAM", "EDRAM", "VRAM", "SRAM", "RAM", "ROM", "FLASH", + "EEPROM", "FEPROM", "EPROM", "CDRAM", "3DRAM", "SDRAM", "SGRAM", "RDRAM", + "DDR", "DDR2", "DDR2 FB-DIMM", "Reserved", "Reserved", "Reserved", "DDR3", + "FBD2", "DDR4", "LPDDR", "LPDDR2", "LPDDR3", "LPDDR4", + } + + if index := int(dmi[p+0x12]); index >= 1 && index <= len(memTypes) { + si.Memory.Type = memTypes[index-1] + } + } + + if si.Memory.Speed == 0 && recLen >= 0x17 { + if speed := uint(word(dmi, p+0x15)); speed != 0 { + si.Memory.Speed = speed + } + } + case 19: + start := uint(dword(dmi, p+0x04)) + end := uint(dword(dmi, p+0x08)) + if start == 0xffffffff && end == 0xffffffff { + if recLen >= 0x1f { + start64 := qword(dmi, p+0x0f) + end64 := qword(dmi, p+0x17) + memSizeAlt += uint((end64 - start64 + 1) / 1048576) + } + } else { + memSizeAlt += (end - start + 1) / 1024 + } + case 127: + break loop + } + + for p += int(recLen); p < len(dmi)-1; { + if bytes.Equal(dmi[p:p+2], []byte{0, 0}) { + p += 2 + break + } + p++ + } + } + + // Sometimes DMI type 17 has no information, so we fall back to DMI type 19, to at least get the RAM size. + if si.Memory.Size == 0 && memSizeAlt > 0 { + si.Memory.Type = "DRAM" + si.Memory.Size = memSizeAlt + } +} diff --git a/client/vendor/github.com/zcalusic/sysinfo/meta.go b/client/vendor/github.com/zcalusic/sysinfo/meta.go new file mode 100644 index 0000000..2051fa9 --- /dev/null +++ b/client/vendor/github.com/zcalusic/sysinfo/meta.go @@ -0,0 +1,18 @@ +// Copyright © 2016 Zlatko Čalušić +// +// Use of this source code is governed by an MIT-style license that can be found in the LICENSE file. + +package sysinfo + +import "time" + +// Meta information. +type Meta struct { + Version string `json:"version"` + Timestamp time.Time `json:"timestamp"` +} + +func (si *SysInfo) getMetaInfo() { + si.Meta.Version = Version + si.Meta.Timestamp = time.Now() +} diff --git a/client/vendor/github.com/zcalusic/sysinfo/network.go b/client/vendor/github.com/zcalusic/sysinfo/network.go new file mode 100644 index 0000000..fa24dc6 --- /dev/null +++ b/client/vendor/github.com/zcalusic/sysinfo/network.go @@ -0,0 +1,146 @@ +// Copyright © 2016 Zlatko Čalušić +// +// Use of this source code is governed by an MIT-style license that can be found in the LICENSE file. + +package sysinfo + +import ( + "os" + "path" + "strings" + "syscall" + "unsafe" +) + +// NetworkDevice information. +type NetworkDevice struct { + Name string `json:"name,omitempty"` + Driver string `json:"driver,omitempty"` + MACAddress string `json:"macaddress,omitempty"` + Port string `json:"port,omitempty"` + Speed uint `json:"speed,omitempty"` // device max supported speed in Mbps +} + +func getPortType(supp uint32) (port string) { + for i, p := range [...]string{"tp", "aui", "mii", "fibre", "bnc"} { + if supp&(1< 0 { + port += p + "/" + } + } + + port = strings.TrimRight(port, "/") + return +} + +func getMaxSpeed(supp uint32) (speed uint) { + // Fancy, right? + switch { + case supp&0x78000000 > 0: + speed = 56000 + case supp&0x07800000 > 0: + speed = 40000 + case supp&0x00600000 > 0: + speed = 20000 + case supp&0x001c1000 > 0: + speed = 10000 + case supp&0x00008000 > 0: + speed = 2500 + case supp&0x00020030 > 0: + speed = 1000 + case supp&0x0000000c > 0: + speed = 100 + case supp&0x00000003 > 0: + speed = 10 + } + + return +} + +func getSupported(name string) uint32 { + fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM, syscall.IPPROTO_IP) + if err != nil { + return 0 + } + defer syscall.Close(fd) + + // struct ethtool_cmd from /usr/include/linux/ethtool.h + var ethtool struct { + Cmd uint32 + Supported uint32 + Advertising uint32 + Speed uint16 + Duplex uint8 + Port uint8 + PhyAddress uint8 + Transceiver uint8 + Autoneg uint8 + MdioSupport uint8 + Maxtxpkt uint32 + Maxrxpkt uint32 + SpeedHi uint16 + EthTpMdix uint8 + Reserved2 uint8 + LpAdvertising uint32 + Reserved [2]uint32 + } + + // ETHTOOL_GSET from /usr/include/linux/ethtool.h + const GSET = 0x1 + + ethtool.Cmd = GSET + + // struct ifreq from /usr/include/linux/if.h + var ifr struct { + Name [16]byte + Data uintptr + } + + copy(ifr.Name[:], name+"\000") + ifr.Data = uintptr(unsafe.Pointer(ðtool)) + + // SIOCETHTOOL from /usr/include/linux/sockios.h + const SIOCETHTOOL = 0x8946 + + _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(SIOCETHTOOL), uintptr(unsafe.Pointer(&ifr))) + if errno == 0 { + return ethtool.Supported + } + + return 0 +} + +func (si *SysInfo) getNetworkInfo() { + sysClassNet := "/sys/class/net" + devices, err := os.ReadDir(sysClassNet) + if err != nil { + return + } + + si.Network = make([]NetworkDevice, 0) + for _, link := range devices { + fullpath := path.Join(sysClassNet, link.Name()) + dev, err := os.Readlink(fullpath) + if err != nil { + continue + } + + if strings.HasPrefix(dev, "../../devices/virtual/") { + continue + } + + supp := getSupported(link.Name()) + + device := NetworkDevice{ + Name: link.Name(), + MACAddress: slurpFile(path.Join(fullpath, "address")), + Port: getPortType(supp), + Speed: getMaxSpeed(supp), + } + + if driver, err := os.Readlink(path.Join(fullpath, "device", "driver")); err == nil { + device.Driver = path.Base(driver) + } + + si.Network = append(si.Network, device) + } +} diff --git a/client/vendor/github.com/zcalusic/sysinfo/node.go b/client/vendor/github.com/zcalusic/sysinfo/node.go new file mode 100644 index 0000000..216ee3a --- /dev/null +++ b/client/vendor/github.com/zcalusic/sysinfo/node.go @@ -0,0 +1,99 @@ +// Copyright © 2016 Zlatko Čalušić +// +// Use of this source code is governed by an MIT-style license that can be found in the LICENSE file. + +package sysinfo + +import ( + "bufio" + "os" + "strings" +) + +// Node information. +type Node struct { + Hostname string `json:"hostname,omitempty"` + MachineID string `json:"machineid,omitempty"` + Hypervisor string `json:"hypervisor,omitempty"` + Timezone string `json:"timezone,omitempty"` +} + +func (si *SysInfo) getHostname() { + si.Node.Hostname = slurpFile("/proc/sys/kernel/hostname") +} + +func (si *SysInfo) getSetMachineID() { + const pathSystemdMachineID = "/etc/machine-id" + const pathDbusMachineID = "/var/lib/dbus/machine-id" + + systemdMachineID := slurpFile(pathSystemdMachineID) + dbusMachineID := slurpFile(pathDbusMachineID) + + if systemdMachineID != "" && dbusMachineID != "" { + // All OK, just return the machine id. + if systemdMachineID == dbusMachineID { + si.Node.MachineID = systemdMachineID + return + } + + // They both exist, but they don't match! Copy systemd machine id to DBUS machine id. + spewFile(pathDbusMachineID, systemdMachineID, 0444) + si.Node.MachineID = systemdMachineID + return + } + + // Copy DBUS machine id to non-existent systemd machine id. + if systemdMachineID == "" && dbusMachineID != "" { + spewFile(pathSystemdMachineID, dbusMachineID, 0444) + si.Node.MachineID = dbusMachineID + return + } + + // Copy systemd machine id to non-existent DBUS machine id. + if systemdMachineID != "" && dbusMachineID == "" { + spewFile(pathDbusMachineID, systemdMachineID, 0444) + si.Node.MachineID = systemdMachineID + return + } +} + +func (si *SysInfo) getTimezone() { + const zoneInfoPrefix = "/usr/share/zoneinfo/" + + if fi, err := os.Lstat("/etc/localtime"); err == nil { + if fi.Mode()&os.ModeSymlink == os.ModeSymlink { + if tzfile, err := os.Readlink("/etc/localtime"); err == nil { + tzfile = strings.TrimPrefix(tzfile, "..") + if strings.HasPrefix(tzfile, zoneInfoPrefix) { + si.Node.Timezone = strings.TrimPrefix(tzfile, zoneInfoPrefix) + return + } + } + } + } + + if timezone := slurpFile("/etc/timezone"); timezone != "" { + si.Node.Timezone = timezone + return + } + + if f, err := os.Open("/etc/sysconfig/clock"); err == nil { + defer f.Close() + s := bufio.NewScanner(f) + for s.Scan() { + if sl := strings.Split(s.Text(), "="); len(sl) == 2 { + if sl[0] == "ZONE" { + si.Node.Timezone = strings.Trim(sl[1], `"`) + return + } + } + } + } +} + +func (si *SysInfo) getNodeInfo() { + si.getHostname() + si.getSetMachineID() + si.getHypervisor() + si.getTimezone() +} diff --git a/client/vendor/github.com/zcalusic/sysinfo/os.go b/client/vendor/github.com/zcalusic/sysinfo/os.go new file mode 100644 index 0000000..9d24a43 --- /dev/null +++ b/client/vendor/github.com/zcalusic/sysinfo/os.go @@ -0,0 +1,101 @@ +// Copyright © 2016 Zlatko Čalušić +// +// Use of this source code is governed by an MIT-style license that can be found in the LICENSE file. + +package sysinfo + +import ( + "bufio" + "os" + "regexp" + "strings" +) + +// OS information. +type OS struct { + Name string `json:"name,omitempty"` + Vendor string `json:"vendor,omitempty"` + Version string `json:"version,omitempty"` + Release string `json:"release,omitempty"` + Architecture string `json:"architecture,omitempty"` +} + +var ( + rePrettyName = regexp.MustCompile(`^PRETTY_NAME=(.*)$`) + reID = regexp.MustCompile(`^ID=(.*)$`) + reVersionID = regexp.MustCompile(`^VERSION_ID=(.*)$`) + reUbuntu = regexp.MustCompile(`[\( ]([\d\.]+)`) + reAlma = regexp.MustCompile(`^AlmaLinux release ([\d\.]+)`) + reCentOS = regexp.MustCompile(`^CentOS( Linux)? release ([\d\.]+)`) + reRocky = regexp.MustCompile(`^Rocky Linux release ([\d\.]+)`) + reRedHat = regexp.MustCompile(`[\( ]([\d\.]+)`) +) + +func (si *SysInfo) getOSInfo() { + // This seems to be the best and most portable way to detect OS architecture (NOT kernel!) + if _, err := os.Stat("/lib64/ld-linux-x86-64.so.2"); err == nil { + si.OS.Architecture = "amd64" + } else if _, err := os.Stat("/lib/ld-linux.so.2"); err == nil { + si.OS.Architecture = "i386" + } + + f, err := os.Open("/etc/os-release") + if err != nil { + return + } + defer f.Close() + + s := bufio.NewScanner(f) + for s.Scan() { + if m := rePrettyName.FindStringSubmatch(s.Text()); m != nil { + si.OS.Name = strings.Trim(m[1], `"`) + } else if m := reID.FindStringSubmatch(s.Text()); m != nil { + si.OS.Vendor = strings.Trim(m[1], `"`) + } else if m := reVersionID.FindStringSubmatch(s.Text()); m != nil { + si.OS.Version = strings.Trim(m[1], `"`) + } + } + + switch si.OS.Vendor { + case "debian": + si.OS.Release = slurpFile("/etc/debian_version") + case "ubuntu": + if m := reUbuntu.FindStringSubmatch(si.OS.Name); m != nil { + si.OS.Release = m[1] + } + case "almalinux": + if release := slurpFile("/etc/almalinux-release"); release != "" { + if m := reAlma.FindStringSubmatch(release); m != nil { + si.OS.Release = m[1] + } + } + + si.OS.Version = strings.Split(si.OS.Release, ".")[0] + case "centos": + if release := slurpFile("/etc/centos-release"); release != "" { + if m := reCentOS.FindStringSubmatch(release); m != nil { + si.OS.Release = m[2] + } + } + case "rocky": + if release := slurpFile("/etc/rocky-release"); release != "" { + if m := reRocky.FindStringSubmatch(release); m != nil { + si.OS.Release = m[1] + } + } + + si.OS.Version = strings.Split(si.OS.Release, ".")[0] + + case "rhel": + if release := slurpFile("/etc/redhat-release"); release != "" { + if m := reRedHat.FindStringSubmatch(release); m != nil { + si.OS.Release = m[1] + } + } + if si.OS.Release == "" { + if m := reRedHat.FindStringSubmatch(si.OS.Name); m != nil { + si.OS.Release = m[1] + } + } + } +} diff --git a/client/vendor/github.com/zcalusic/sysinfo/product.go b/client/vendor/github.com/zcalusic/sysinfo/product.go new file mode 100644 index 0000000..23d716e --- /dev/null +++ b/client/vendor/github.com/zcalusic/sysinfo/product.go @@ -0,0 +1,42 @@ +// Copyright © 2016 Zlatko Čalušić +// +// Use of this source code is governed by an MIT-style license that can be found in the LICENSE file. + +package sysinfo + +import "github.com/google/uuid" + +// Product information. +type Product struct { + Name string `json:"name,omitempty"` + Vendor string `json:"vendor,omitempty"` + Version string `json:"version,omitempty"` + Serial string `json:"serial,omitempty"` + UUID uuid.UUID `json:"uuid,omitempty"` + SKU string `json:"sku,omitempty"` +} + +func (si *SysInfo) getProductInfo() { + si.Product.Name = slurpFile("/sys/class/dmi/id/product_name") + si.Product.Vendor = slurpFile("/sys/class/dmi/id/sys_vendor") + si.Product.Version = slurpFile("/sys/class/dmi/id/product_version") + si.Product.Serial = slurpFile("/sys/class/dmi/id/product_serial") + si.Product.SKU = slurpFile("/sys/class/dmi/id/product_sku") + + uid, err := uuid.Parse(slurpFile("/sys/class/dmi/id/product_uuid")) + if err == nil { + si.Product.UUID = uid + } + + // try a fallback to device-tree (ex: dmi is not available on ARM devices) + // full details: https://www.devicetree.org/specifications/ + + // on linux root path is /proc/device-tree (see: https://github.com/torvalds/linux/blob/v5.9/Documentation/ABI/testing/sysfs-firmware-ofw) + if si.Product.Name == "" { + si.Product.Name = slurpFile("/proc/device-tree/model") + } + + if si.Product.Serial == "" { + si.Product.Serial = slurpFile("/proc/device-tree/serial-number") + } +} diff --git a/client/vendor/github.com/zcalusic/sysinfo/storage.go b/client/vendor/github.com/zcalusic/sysinfo/storage.go new file mode 100644 index 0000000..eaee7a7 --- /dev/null +++ b/client/vendor/github.com/zcalusic/sysinfo/storage.go @@ -0,0 +1,104 @@ +// Copyright © 2016 Zlatko Čalušić +// +// Use of this source code is governed by an MIT-style license that can be found in the LICENSE file. + +package sysinfo + +import ( + "bufio" + "os" + "path" + "strconv" + "strings" +) + +// StorageDevice information. +type StorageDevice struct { + Name string `json:"name,omitempty"` + Driver string `json:"driver,omitempty"` + Vendor string `json:"vendor,omitempty"` + Model string `json:"model,omitempty"` + Serial string `json:"serial,omitempty"` + Size uint `json:"size,omitempty"` // device size in GB +} + +func getSerial(name, fullpath string) (serial string) { + var f *os.File + var err error + + // Modern location/format of the udev database. + if dev := slurpFile(path.Join(fullpath, "dev")); dev != "" { + if f, err = os.Open(path.Join("/run/udev/data", "b"+dev)); err == nil { + goto scan + } + } + + // Legacy location/format of the udev database. + if f, err = os.Open(path.Join("/dev/.udev/db", "block:"+name)); err == nil { + goto scan + } + + // No serial :( + return + +scan: + defer f.Close() + + s := bufio.NewScanner(f) + for s.Scan() { + if sl := strings.Split(s.Text(), "="); len(sl) == 2 { + if sl[0] == "E:ID_SERIAL_SHORT" { + serial = sl[1] + break + } + } + } + + return +} + +func (si *SysInfo) getStorageInfo() { + sysBlock := "/sys/block" + devices, err := os.ReadDir(sysBlock) + if err != nil { + return + } + + si.Storage = make([]StorageDevice, 0) + for _, link := range devices { + fullpath := path.Join(sysBlock, link.Name()) + dev, err := os.Readlink(fullpath) + if err != nil { + continue + } + + if strings.HasPrefix(dev, "../devices/virtual/") { + continue + } + + // We could filter all removable devices here, but some systems boot from USB flash disks, and then we + // would filter them, too. So, let's filter only floppies and CD/DVD devices, and see how it pans out. + if strings.HasPrefix(dev, "../devices/platform/floppy") || slurpFile(path.Join(fullpath, "device", "type")) == "5" { + continue + } + + device := StorageDevice{ + Name: link.Name(), + Model: slurpFile(path.Join(fullpath, "device", "model")), + Serial: getSerial(link.Name(), fullpath), + } + + if driver, err := os.Readlink(path.Join(fullpath, "device", "driver")); err == nil { + device.Driver = path.Base(driver) + } + + if vendor := slurpFile(path.Join(fullpath, "device", "vendor")); !strings.HasPrefix(vendor, "0x") { + device.Vendor = vendor + } + + size, _ := strconv.ParseUint(slurpFile(path.Join(fullpath, "size")), 10, 64) + device.Size = uint(size) / 1953125 // GiB + + si.Storage = append(si.Storage, device) + } +} diff --git a/client/vendor/github.com/zcalusic/sysinfo/sysinfo.go b/client/vendor/github.com/zcalusic/sysinfo/sysinfo.go new file mode 100644 index 0000000..30b851d --- /dev/null +++ b/client/vendor/github.com/zcalusic/sysinfo/sysinfo.go @@ -0,0 +1,49 @@ +// Copyright © 2016 Zlatko Čalušić +// +// Use of this source code is governed by an MIT-style license that can be found in the LICENSE file. + +// Package sysinfo is a Go library providing Linux OS / kernel / hardware system information. +package sysinfo + +// SysInfo struct encapsulates all other information structs. +type SysInfo struct { + Meta Meta `json:"sysinfo"` + Node Node `json:"node"` + OS OS `json:"os"` + Kernel Kernel `json:"kernel"` + Product Product `json:"product"` + Board Board `json:"board"` + Chassis Chassis `json:"chassis"` + BIOS BIOS `json:"bios"` + CPU CPU `json:"cpu"` + Memory Memory `json:"memory"` + Storage []StorageDevice `json:"storage,omitempty"` + Network []NetworkDevice `json:"network,omitempty"` +} + +// GetSysInfo gathers all available system information. +func (si *SysInfo) GetSysInfo() { + // Meta info + si.getMetaInfo() + + // DMI info + si.getProductInfo() + si.getBoardInfo() + si.getChassisInfo() + si.getBIOSInfo() + + // SMBIOS info + si.getMemoryInfo() + + // Node info + si.getNodeInfo() // depends on BIOS info + + // Hardware info + si.getCPUInfo() // depends on Node info + si.getStorageInfo() + si.getNetworkInfo() + + // Software info + si.getOSInfo() + si.getKernelInfo() +} diff --git a/client/vendor/github.com/zcalusic/sysinfo/util.go b/client/vendor/github.com/zcalusic/sysinfo/util.go new file mode 100644 index 0000000..4fce330 --- /dev/null +++ b/client/vendor/github.com/zcalusic/sysinfo/util.go @@ -0,0 +1,26 @@ +// Copyright © 2016 Zlatko Čalušić +// +// Use of this source code is governed by an MIT-style license that can be found in the LICENSE file. + +package sysinfo + +import ( + "os" + "strings" +) + +// Read one-liner text files, strip newline. +func slurpFile(path string) string { + data, err := os.ReadFile(path) + if err != nil { + return "" + } + + // Trim spaces & \u0000 \uffff + return strings.Trim(string(data), " \r\n\t\u0000\uffff") +} + +// Write one-liner text files, add newline, ignore errors (best effort). +func spewFile(path string, data string, perm os.FileMode) { + _ = os.WriteFile(path, []byte(data+"\n"), perm) +} diff --git a/client/vendor/github.com/zcalusic/sysinfo/version.go b/client/vendor/github.com/zcalusic/sysinfo/version.go new file mode 100644 index 0000000..a730840 --- /dev/null +++ b/client/vendor/github.com/zcalusic/sysinfo/version.go @@ -0,0 +1,8 @@ +// Copyright © 2016 Zlatko Čalušić +// +// Use of this source code is governed by an MIT-style license that can be found in the LICENSE file. + +package sysinfo + +// Version of the sysinfo library. +const Version = "1.1.3" diff --git a/client/vendor/modules.txt b/client/vendor/modules.txt new file mode 100644 index 0000000..ef4a4c7 --- /dev/null +++ b/client/vendor/modules.txt @@ -0,0 +1,7 @@ +# github.com/google/uuid v1.6.0 +## explicit +github.com/google/uuid +# github.com/zcalusic/sysinfo v1.1.3 +## explicit; go 1.22 +github.com/zcalusic/sysinfo +github.com/zcalusic/sysinfo/cpuid diff --git a/flake.nix b/flake.nix index 7e40a88..9183cf1 100644 --- a/flake.nix +++ b/flake.nix @@ -22,7 +22,7 @@ default = pkgs.buildGoModule { pname = "orphanage-client"; inherit version; - src = ./.; + src = ./client; vendorHash = null; }; }); diff --git a/go.mod b/go.mod deleted file mode 100644 index 42bcf97..0000000 --- a/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module git.dubyatp.xyz/orphanage - -go 1.24.4 diff --git a/http/handler.go b/http/handler.go deleted file mode 100644 index fc08239..0000000 --- a/http/handler.go +++ /dev/null @@ -1,10 +0,0 @@ -package http - -import "net/http" - -func NewServer() http.Handler { - mux := http.NewServeMux() - addRoutes(mux) - var handler http.Handler = mux - return handler -} diff --git a/http/helloworld.go b/http/helloworld.go deleted file mode 100644 index f497ac2..0000000 --- a/http/helloworld.go +++ /dev/null @@ -1,9 +0,0 @@ -package http - -import "net/http" - -func HelloWorld(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte("hello world")) - }) -} diff --git a/http/routes.go b/http/routes.go deleted file mode 100644 index f53888f..0000000 --- a/http/routes.go +++ /dev/null @@ -1,10 +0,0 @@ -package http - -import "net/http" - -func addRoutes( - mux *http.ServeMux, -) { - mux.Handle("/", http.NotFoundHandler()) - mux.Handle("/helloworld", HelloWorld(nil)) -} diff --git a/http/server.go b/http/server.go deleted file mode 100644 index 158f8c8..0000000 --- a/http/server.go +++ /dev/null @@ -1,40 +0,0 @@ -package http - -import ( - "context" - "fmt" - "log" - "net" - "net/http" - "os" - "sync" - "time" -) - -func StartServer(ctx context.Context, config struct{ Host, Port string }) error { - srv := NewServer() - httpServer := &http.Server{ - Addr: net.JoinHostPort(config.Host, config.Port), - Handler: srv, - } - go func() { - log.Printf("orphan listening on %s\n", httpServer.Addr) - if err := httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed { - fmt.Fprintf(os.Stderr, "error listening and serving: %s\n", err) - } - }() - var wg sync.WaitGroup - wg.Add(1) - go func() { - defer wg.Done() - <-ctx.Done() - shutdownCtx := context.Background() - shutdownCtx, cancel := context.WithTimeout(shutdownCtx, 10*time.Second) - defer cancel() - if err := httpServer.Shutdown(shutdownCtx); err != nil { - fmt.Fprintf(os.Stderr, "error shutting down http server: %s\n", err) - } - }() - wg.Wait() - return nil -} diff --git a/main.go b/main.go deleted file mode 100644 index 82b22dd..0000000 --- a/main.go +++ /dev/null @@ -1,34 +0,0 @@ -package main - -import ( - "context" - "fmt" - "io" - "os" - "os/signal" - - "git.dubyatp.xyz/orphanage/http" -) - -func run(ctx context.Context, w io.Writer, args []string) error { - ctx, cancel := signal.NotifyContext(ctx, os.Interrupt) - defer cancel() - - config := struct { - Host string - Port string - }{ - Host: "0.0.0.0", - Port: "8080", - } - - return http.StartServer(ctx, config) -} - -func main() { - ctx := context.Background() - if err := run(ctx, os.Stdout, os.Args); err != nil { - fmt.Fprintf(os.Stderr, "%s\n", err) - os.Exit(1) - } -}