Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
241a46c9b4
|
No files matched your search
@@ -12,6 +12,17 @@
|
||||
- constants/vars for errors
|
||||
|
||||
- func and struct to return segregated system-level env vars vs. user env vars (mostly usable on windows) (see envs/.TODO.go.UNFINISHED)
|
||||
-- https://www3.ntu.edu.sg/home/ehchua/programming/howto/Environment_Variables.html
|
||||
-- windows:
|
||||
--- https://docs.microsoft.com/en-us/windows/deployment/usmt/usmt-recognized-environment-variables
|
||||
--- https://pureinfotech.com/list-environment-variables-windows-10/
|
||||
--- https://gist.github.com/RebeccaWhit3/5dad8627b8227142e1bea432db3f8824
|
||||
--- https://ss64.com/nt/syntax-variables.html
|
||||
-- linux/XDG:
|
||||
--- https://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html
|
||||
--- https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
|
||||
-- macOS:
|
||||
--- https://ss64.com/osx/syntax-env_vars.html
|
||||
|
||||
- validator for windows usernames, domains, etc. (for *NIX, https://unix.stackexchange.com/a/435120/284004)
|
||||
-- https://docs.microsoft.com/en-us/troubleshoot/windows-server/identity/naming-conventions-for-computer-domain-site-ou
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
macOS:
|
||||
HOME
|
||||
LOGNAME
|
||||
OLDPWD
|
||||
PATH
|
||||
PWD
|
||||
SHELL
|
||||
SHLVL
|
||||
SSH_CLIENT
|
||||
SSH_CONNECTION
|
||||
SSH_TTY
|
||||
TERM
|
||||
TMPDIR
|
||||
USER
|
||||
_
|
||||
e.g.
|
||||
HOME=/Users/bts
|
||||
LOGNAME=bts
|
||||
OLDPWD=/Users/bts
|
||||
PATH=/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin
|
||||
PWD=/Users/bts
|
||||
SHELL=/bin/zsh
|
||||
SHLVL=1
|
||||
SSH_CLIENT=192.168.2.99 39690 22
|
||||
SSH_CONNECTION=192.168.2.99 39690 192.168.2.148 22
|
||||
SSH_TTY=/dev/ttys000
|
||||
TERM=xterm-256color
|
||||
TMPDIR=/var/folders/qv/bm6dlyd94x5fs6qpkfpj0jsr0000gq/T/
|
||||
USER=bts
|
||||
_=/usr/bin/env
|
||||
@@ -1,7 +0,0 @@
|
||||
/*
|
||||
Package osenvs contains information (and, in some cases, defaults and associated methods) for
|
||||
[environment variables] ("env vars") as provided by/as part of the OS.
|
||||
|
||||
[environment variables]: https://www3.ntu.edu.sg/home/ehchua/programming/howto/Environment_Variables.html
|
||||
*/
|
||||
package osenvs
|
||||
@@ -1,56 +0,0 @@
|
||||
package osenvs
|
||||
|
||||
type (
|
||||
/*
|
||||
AppleEnv are default environment variables found in all macOS instances.
|
||||
|
||||
See also the [ss64 article].
|
||||
|
||||
macOS also has [POSIXEnv] env vars.
|
||||
|
||||
[ss64 article]: https://ss64.com/osx/syntax-env_vars.html
|
||||
*/
|
||||
AppleEnv string
|
||||
/*
|
||||
MSEnv are default environment variables found in Windows instances.
|
||||
|
||||
These include:
|
||||
|
||||
* [USMT environment variables]
|
||||
* ONLY the [Environment Variables] for Poweshell
|
||||
** Thus no Powershell ["Automatic variables"] or ["Preference variables"]
|
||||
* standard system environment variables and user environment variables
|
||||
|
||||
See also the [ss64 article] and the [compiled Windows 10 list] and [Windows 10/11 list].
|
||||
|
||||
[USMT environment variables]: https://docs.microsoft.com/en-us/windows/deployment/usmt/usmt-recognized-environment-variables
|
||||
[Environment Variables]: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_environment_variables?view=powershell-7.5
|
||||
["Automatic variables"]: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_automatic_variables
|
||||
["Preference variables"]: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_preference_variables
|
||||
[ss64 article]: https://ss64.com/nt/syntax-variables.html
|
||||
[compiled Windows 10 list]: https://gist.github.com/RebeccaWhit3/5dad8627b8227142e1bea432db3f8824
|
||||
[Windows 10/11 list]: https://pureinfotech.com/list-environment-variables-windows-10/
|
||||
*/
|
||||
MSEnv string
|
||||
/*
|
||||
POSIXEnv are environment variables defined by/in [POSIX.1-2024] § [8. Environment Variables].
|
||||
|
||||
[POSIX.1-2024]: https://pubs.opengroup.org/onlinepubs/9799919799/
|
||||
[8. Environment Variables]: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/V1_chap08.html
|
||||
*/
|
||||
POSIXEnv string
|
||||
/*
|
||||
XDGEnv are environment variables defined by the [FreeDesktop] [spec] for the
|
||||
[XDG Base Directory Specification] § [3 Environment Variables].
|
||||
|
||||
Generally these are only found/expected on Linux, and only a subset of them at that.
|
||||
|
||||
Linux also has [POSIXEnv] env vars.
|
||||
|
||||
[FreeDesktop]: https://www.freedesktop.org/wiki/
|
||||
[spec]: https://specifications.freedesktop.org/
|
||||
[XDG Base Directory Specification]: https://specifications.freedesktop.org/basedir/latest/#variables
|
||||
[3 Environment Variables]: https://specifications.freedesktop.org/basedir/latest/#variables
|
||||
*/
|
||||
XDGEnv string
|
||||
)
|
||||
@@ -1,5 +1,22 @@
|
||||
package envs
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/shirou/gopsutil/v4/process"
|
||||
"r00t2.io/sysutils/internal"
|
||||
)
|
||||
|
||||
var (
|
||||
StructTagInterpolate string = "envsub"
|
||||
)
|
||||
|
||||
var (
|
||||
defEnv *StaticEnv = &StaticEnv{
|
||||
dynamic: true,
|
||||
self: true,
|
||||
// don't need a process.NewProcess since the only extra thing it does is check if the PID exists.
|
||||
proc: &process.Process{Pid: int32(os.Getpid())},
|
||||
envVars: internal.EnvListToMap(os.Environ()),
|
||||
}
|
||||
)
|
||||
+86
-312
@@ -1,17 +1,88 @@
|
||||
package envs
|
||||
|
||||
import (
|
||||
"math"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"r00t2.io/goutils/multierr"
|
||||
"r00t2.io/goutils/structutils"
|
||||
"github.com/shirou/gopsutil/v4/process"
|
||||
"r00t2.io/sysutils/errs"
|
||||
"r00t2.io/sysutils/internal"
|
||||
)
|
||||
|
||||
/*
|
||||
Current returns a *copy* of the StaticEnv for this current process' environment.
|
||||
|
||||
It is set to dynamically refresh with strictRefresh mode set to false.
|
||||
(see [NewEnvFromPid] docs for what these do/mean.)
|
||||
Assuming permissions haven't wildly gone silly during runtime, it shouldn't ever
|
||||
have issues with dynamic refreshing.
|
||||
It will never panic regardless.
|
||||
*/
|
||||
func Current() (s *StaticEnv) {
|
||||
|
||||
s = &StaticEnv{
|
||||
dynamic: defEnv.dynamic,
|
||||
envVars: defEnv.GetEnvMap(),
|
||||
}
|
||||
for k, v := range defEnv.envVars {
|
||||
s.envVars[k] = v
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
NewEnvFromMap returns a [StaticEnv] from a fixed list of environment variables.
|
||||
This is primarily useful for mocking and other tests.
|
||||
*/
|
||||
func NewEnvFromMap(envMap map[string]string) (s *StaticEnv, err error) {
|
||||
|
||||
if envMap == nil {
|
||||
err = errs.ErrNilPtr
|
||||
}
|
||||
|
||||
s = &StaticEnv{
|
||||
envVars: envMap,
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
NewEnvFromPid returns a [StaticEnv] from a given PID.
|
||||
|
||||
If dynamicRefresh is true, the env vars will be refreshed from the process
|
||||
on every method call.
|
||||
Note that this will obviously cause errors/panics if the process it binds to disappears
|
||||
during runtime,
|
||||
or
|
||||
*/
|
||||
func NewEnvFromPid(pid uint, dynamicRefresh, strictRefresh bool) (s *StaticEnv, err error) {
|
||||
|
||||
if pid > math.MaxInt32 {
|
||||
err = errs.ErrHighPid
|
||||
return
|
||||
}
|
||||
|
||||
s = &StaticEnv{
|
||||
dynamic: dynamicRefresh,
|
||||
}
|
||||
|
||||
if s.proc, err = process.NewProcess(int32(pid)); err != nil {
|
||||
return
|
||||
}
|
||||
if err = s.Refresh(); err != nil {
|
||||
return
|
||||
}
|
||||
// Test the ability to attach to procs.
|
||||
err = s.platChecks()
|
||||
s.strict = strictRefresh
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
DefEnv operates like Python's .get() method on dicts (maps);
|
||||
if the environment variable specified by key does not exist/is not specified,
|
||||
@@ -98,6 +169,18 @@ func GetEnvMap() (envVars map[string]string) {
|
||||
return
|
||||
}
|
||||
|
||||
// GetEnvMust wraps GetEnvErr but will panic on error.
|
||||
func GetEnvMust(key string) (value string) {
|
||||
|
||||
var err error
|
||||
|
||||
if value, err = GetEnvErr(key); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
GetEnvMapNative returns a map of all environment variables, but attempts to "nativize" them.
|
||||
All values are interfaces. It is up to the caller to typeswitch them to proper types.
|
||||
@@ -331,312 +414,3 @@ func InterpolateString(s *string) (err error) {
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// interpolateMap is used by [Interpolate] for maps. v should be a [reflect.Value] of a map.
|
||||
func interpolateMap(v reflect.Value) (err error) {
|
||||
|
||||
var kVal reflect.Value
|
||||
var vVal reflect.Value
|
||||
var newMap reflect.Value
|
||||
var wg sync.WaitGroup
|
||||
var numJobs int
|
||||
var errChan chan error
|
||||
var doneChan chan bool = make(chan bool, 1)
|
||||
var mErr *multierr.MultiError = multierr.NewMultiError(nil)
|
||||
var t reflect.Type = v.Type()
|
||||
var kind reflect.Kind = t.Kind()
|
||||
|
||||
if kind != reflect.Map {
|
||||
err = errs.ErrBadType
|
||||
return
|
||||
}
|
||||
|
||||
if v.IsNil() || v.IsZero() || !v.IsValid() {
|
||||
return
|
||||
}
|
||||
|
||||
numJobs = v.Len()
|
||||
errChan = make(chan error, numJobs)
|
||||
wg.Add(numJobs)
|
||||
|
||||
newMap = reflect.MakeMap(v.Type())
|
||||
|
||||
for _, kVal = range v.MapKeys() {
|
||||
vVal = v.MapIndex(kVal)
|
||||
go func(key, val reflect.Value) {
|
||||
var mapErr error
|
||||
var newKey reflect.Value
|
||||
var newVal reflect.Value
|
||||
|
||||
newKey = reflect.New(key.Type()).Elem()
|
||||
newVal = reflect.New(val.Type()).Elem()
|
||||
|
||||
newKey.Set(key.Convert(newKey.Type()))
|
||||
newVal.Set(val.Convert(newVal.Type()))
|
||||
|
||||
defer wg.Done()
|
||||
|
||||
// key
|
||||
if key.Kind() == reflect.String {
|
||||
if mapErr = interpolateStringReflect(newKey); mapErr != nil {
|
||||
errChan <- mapErr
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if mapErr = interpolateValue(newKey); mapErr != nil {
|
||||
errChan <- mapErr
|
||||
return
|
||||
}
|
||||
}
|
||||
// value
|
||||
if val.Kind() == reflect.String {
|
||||
if mapErr = interpolateStringReflect(newVal); mapErr != nil {
|
||||
errChan <- mapErr
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if mapErr = interpolateValue(newVal); mapErr != nil {
|
||||
errChan <- mapErr
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
newMap.SetMapIndex(newKey.Convert(key.Type()), newVal.Convert(key.Type()))
|
||||
}(kVal, vVal)
|
||||
}
|
||||
|
||||
go func() {
|
||||
wg.Wait()
|
||||
close(errChan)
|
||||
doneChan <- true
|
||||
}()
|
||||
|
||||
<-doneChan
|
||||
|
||||
for i := 0; i < numJobs; i++ {
|
||||
if err = <-errChan; err != nil {
|
||||
mErr.AddError(err)
|
||||
err = nil
|
||||
}
|
||||
}
|
||||
|
||||
if !mErr.IsEmpty() {
|
||||
err = mErr
|
||||
return
|
||||
}
|
||||
|
||||
v.Set(newMap.Convert(v.Type()))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// interpolateSlice is used by [Interpolate] for slices and arrays. v should be a [reflect.Value] of a slice/array.
|
||||
func interpolateSlice(v reflect.Value) (err error) {
|
||||
|
||||
var wg sync.WaitGroup
|
||||
var errChan chan error
|
||||
var numJobs int
|
||||
var doneChan chan bool = make(chan bool, 1)
|
||||
var mErr *multierr.MultiError = multierr.NewMultiError(nil)
|
||||
var t reflect.Type = v.Type()
|
||||
var kind reflect.Kind = t.Kind()
|
||||
|
||||
switch kind {
|
||||
case reflect.Slice:
|
||||
if v.IsNil() || v.IsZero() || !v.IsValid() {
|
||||
return
|
||||
}
|
||||
case reflect.Array:
|
||||
if v.IsZero() || !v.IsValid() {
|
||||
return
|
||||
}
|
||||
default:
|
||||
err = errs.ErrBadType
|
||||
return
|
||||
}
|
||||
|
||||
numJobs = v.Len()
|
||||
errChan = make(chan error, numJobs)
|
||||
wg.Add(numJobs)
|
||||
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
go func(idx int) {
|
||||
var sErr error
|
||||
|
||||
defer wg.Done()
|
||||
|
||||
if v.Index(idx).Kind() == reflect.String {
|
||||
if sErr = interpolateStringReflect(v.Index(idx)); sErr != nil {
|
||||
errChan <- sErr
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if sErr = interpolateValue(v.Index(idx)); sErr != nil {
|
||||
errChan <- sErr
|
||||
return
|
||||
}
|
||||
}
|
||||
}(i)
|
||||
}
|
||||
|
||||
go func() {
|
||||
wg.Wait()
|
||||
close(errChan)
|
||||
doneChan <- true
|
||||
}()
|
||||
|
||||
<-doneChan
|
||||
|
||||
for i := 0; i < numJobs; i++ {
|
||||
if err = <-errChan; err != nil {
|
||||
mErr.AddError(err)
|
||||
err = nil
|
||||
}
|
||||
}
|
||||
|
||||
if !mErr.IsEmpty() {
|
||||
err = mErr
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// interpolateStringReflect is used for structs/nested strings using reflection.
|
||||
func interpolateStringReflect(v reflect.Value) (err error) {
|
||||
|
||||
var strVal string
|
||||
|
||||
if v.Kind() != reflect.String {
|
||||
err = errs.ErrBadType
|
||||
return
|
||||
}
|
||||
|
||||
if strVal, err = interpolateString(v.String()); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
v.Set(reflect.ValueOf(strVal).Convert(v.Type()))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// interpolateStruct is used by [Interpolate] for structs. v should be a [reflect.Value] of a struct.
|
||||
func interpolateStruct(v reflect.Value) (err error) {
|
||||
|
||||
var field reflect.StructField
|
||||
var fieldVal reflect.Value
|
||||
var wg sync.WaitGroup
|
||||
var errChan chan error
|
||||
var numJobs int
|
||||
var doneChan chan bool = make(chan bool, 1)
|
||||
var mErr *multierr.MultiError = multierr.NewMultiError(nil)
|
||||
var t reflect.Type = v.Type()
|
||||
var kind reflect.Kind = t.Kind()
|
||||
|
||||
if kind != reflect.Struct {
|
||||
err = errs.ErrBadType
|
||||
return
|
||||
}
|
||||
|
||||
numJobs = v.NumField()
|
||||
wg.Add(numJobs)
|
||||
errChan = make(chan error, numJobs)
|
||||
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
field = t.Field(i)
|
||||
fieldVal = v.Field(i)
|
||||
|
||||
go func(f reflect.StructField, fv reflect.Value) {
|
||||
var fErr error
|
||||
|
||||
defer wg.Done()
|
||||
|
||||
if fErr = interpolateStructField(f, fv); fErr != nil {
|
||||
errChan <- fErr
|
||||
return
|
||||
}
|
||||
}(field, fieldVal)
|
||||
}
|
||||
|
||||
go func() {
|
||||
wg.Wait()
|
||||
close(errChan)
|
||||
doneChan <- true
|
||||
}()
|
||||
|
||||
<-doneChan
|
||||
|
||||
for i := 0; i < numJobs; i++ {
|
||||
if err = <-errChan; err != nil {
|
||||
mErr.AddError(err)
|
||||
err = nil
|
||||
}
|
||||
}
|
||||
|
||||
if !mErr.IsEmpty() {
|
||||
err = mErr
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// interpolateStructField interpolates a struct field.
|
||||
func interpolateStructField(field reflect.StructField, v reflect.Value) (err error) {
|
||||
|
||||
var parsedTagOpts map[string]bool
|
||||
|
||||
if !v.CanSet() {
|
||||
return
|
||||
}
|
||||
|
||||
// Skip if explicitly instructed to do so.
|
||||
parsedTagOpts = structutils.TagToBoolMap(field, StructTagInterpolate, structutils.TagMapTrim)
|
||||
if parsedTagOpts["-"] {
|
||||
return
|
||||
}
|
||||
|
||||
if v.Kind() == reflect.Ptr {
|
||||
err = interpolateStructField(field, v.Elem())
|
||||
} else {
|
||||
err = interpolateValue(v)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// interpolateValue is a dispatcher for a reflect value.
|
||||
func interpolateValue(v reflect.Value) (err error) {
|
||||
|
||||
var kind reflect.Kind = v.Kind()
|
||||
|
||||
switch kind {
|
||||
case reflect.Ptr:
|
||||
if v.IsNil() || v.IsZero() || !v.IsValid() {
|
||||
return
|
||||
}
|
||||
v = v.Elem()
|
||||
if err = interpolateValue(v); err != nil {
|
||||
return
|
||||
}
|
||||
case reflect.String:
|
||||
if err = interpolateStringReflect(v); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
case reflect.Slice, reflect.Array:
|
||||
if err = interpolateSlice(v); err != nil {
|
||||
}
|
||||
case reflect.Map:
|
||||
if err = interpolateMap(v); err != nil {
|
||||
return
|
||||
}
|
||||
case reflect.Struct:
|
||||
if err = interpolateStruct(v); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,697 @@
|
||||
package envs
|
||||
|
||||
import (
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"r00t2.io/goutils/multierr"
|
||||
"r00t2.io/goutils/structutils"
|
||||
"r00t2.io/sysutils/errs"
|
||||
"r00t2.io/sysutils/internal"
|
||||
)
|
||||
|
||||
/*
|
||||
DefEnv operates like Python's .get() method on dicts (maps);
|
||||
if the environment variable specified by key does not exist/is not specified,
|
||||
then the value specified by fallback will be returned instead
|
||||
otherwise key's value is returned.
|
||||
*/
|
||||
func (s *StaticEnv) DefEnv(key, fallback string) (value string) {
|
||||
|
||||
var exists bool
|
||||
|
||||
if value, exists = os.LookupEnv(key); !exists {
|
||||
value = fallback
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// DefEnvBlank is like [DefEnv] but will ADDITIONALLY/ALSO apply fallback if key is *defined/exists but is an empty string*.
|
||||
func (s *StaticEnv) DefEnvBlank(key, fallback string) (value string) {
|
||||
|
||||
value = DefEnv(key, fallback)
|
||||
if value == "" {
|
||||
value = fallback
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// GetEnvErr returns the value of key if it exists. If it does not exist, err will be an [EnvErrNoVal].
|
||||
func (s *StaticEnv) GetEnvErr(key string) (value string, err error) {
|
||||
|
||||
var exists bool
|
||||
|
||||
if value, exists = os.LookupEnv(key); !exists {
|
||||
err = &EnvErrNoVal{
|
||||
VarName: key,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
GetEnvErrNoBlank behaves exactly like [GetEnvErr] with the
|
||||
additional stipulation that the value must not be empty.
|
||||
|
||||
An error for a value that is non-empty but whitespace only (e.g. VARNM="\t")
|
||||
can be returned if ignoreWhitespace == true.
|
||||
|
||||
(If it is, an [EnvErrNoVal] will also be returned.)
|
||||
*/
|
||||
func (s *StaticEnv) GetEnvErrNoBlank(key string, ignoreWhitespace bool) (value string, err error) {
|
||||
|
||||
var exists bool
|
||||
var e *EnvErrNoVal = &EnvErrNoVal{
|
||||
VarName: key,
|
||||
WasRequiredNonEmpty: true,
|
||||
IgnoreWhitespace: ignoreWhitespace,
|
||||
}
|
||||
|
||||
if value, exists = os.LookupEnv(key); !exists {
|
||||
err = e
|
||||
return
|
||||
} else {
|
||||
e.WasFound = true
|
||||
e.WasWhitespace = (strings.TrimSpace(value) == "") && (value != "")
|
||||
if ignoreWhitespace && e.WasWhitespace {
|
||||
err = e
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// GetEnvMap returns a map of all environment variables. All values are strings.
|
||||
func (s *StaticEnv) GetEnvMap() (envVars map[string]string) {
|
||||
|
||||
var envList []string = os.Environ()
|
||||
|
||||
envVars = internal.EnvListToMap(envList)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
GetEnvMapNative returns a map of all environment variables, but attempts to "nativize" them.
|
||||
All values are interfaces. It is up to the caller to typeswitch them to proper types.
|
||||
|
||||
Note that the PATH/Path environment variable (for *Nix and Windows, respectively) will be
|
||||
a []string (as per [GetPathEnv]). No other env vars, even if they contain [os.PathListSeparator],
|
||||
will be transformed to a slice or the like.
|
||||
If an error occurs during parsing the path env var, it will be rendered as a string.
|
||||
|
||||
All number types will attempt to be their 64-bit version (i.e. int64, uint64, float64, etc.).
|
||||
|
||||
If a type cannot be determined for a value, its string form will be used
|
||||
(as it would be found in [GetEnvMap]).
|
||||
*/
|
||||
func (s *StaticEnv) GetEnvMapNative() (envMap map[string]interface{}) {
|
||||
|
||||
var stringMap map[string]string = GetEnvMap()
|
||||
|
||||
envMap = internal.NativizeEnvMap(stringMap)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
GetFirst gets the first instance if populated/set occurrence of varNames.
|
||||
|
||||
For example, if you have three potential env vars, FOO, FOOBAR, FOOBARBAZ,
|
||||
and want to follow the logic flow of:
|
||||
|
||||
1.) Check if FOO is set. If not,
|
||||
2.) Check if FOOBAR is set. If not,
|
||||
3.) Check if FOOBARBAZ is set.
|
||||
|
||||
Then this would be specified as:
|
||||
|
||||
GetFirst([]string{"FOO", "FOOBAR", "FOOBARBAZ"})
|
||||
|
||||
If val is "" and ok is true, this means that one of the specified variable names IS
|
||||
set but is set to an empty value. If ok is false, none of the specified variables
|
||||
are set.
|
||||
|
||||
It is a thin wrapper around [GetFirstWithRef].
|
||||
*/
|
||||
func (s *StaticEnv) GetFirst(varNames []string) (val string, ok bool) {
|
||||
|
||||
val, ok, _ = GetFirstWithRef(varNames)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
GetFirstWithRef behaves exactly like [GetFirst], but with an additional returned value, idx,
|
||||
which specifies the index in varNames in which a set variable was found. e.g. if:
|
||||
|
||||
GetFirstWithRef([]string{"FOO", "FOOBAR", "FOOBAZ"})
|
||||
|
||||
is called and FOO is not set but FOOBAR is, idx will be 1.
|
||||
|
||||
If ok is false, idx will always be -1 and should be ignored.
|
||||
*/
|
||||
func (s *StaticEnv) GetFirstWithRef(varNames []string) (val string, ok bool, idx int) {
|
||||
|
||||
idx = -1
|
||||
|
||||
for i, vn := range varNames {
|
||||
if HasEnv(vn) {
|
||||
ok = true
|
||||
idx = i
|
||||
val = os.Getenv(vn)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// GetPathEnv returns a slice of the PATH variable's items.
|
||||
func (s *StaticEnv) GetPathEnv() (pathList []string, err error) {
|
||||
|
||||
if pathList, err = internal.GetPathEnv(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
GetPidEnvMap will only work on *NIX-like systems with procfs.
|
||||
It gets the environment variables of a given process' PID.
|
||||
*/
|
||||
func (s *StaticEnv) GetPidEnvMap(pid uint32) (envMap map[string]string, err error) {
|
||||
|
||||
if envMap, err = internal.GetPidEnvMap(pid); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
GetPidEnvMapNative returns a map of all environment variables (like [GetEnvMapNative]), but attempts to "nativize" them.
|
||||
All values are interfaces. It is up to the caller to typeswitch them to proper types.
|
||||
|
||||
See the documentation for [GetEnvMapNative] for details.
|
||||
*/
|
||||
func (s *StaticEnv) GetPidEnvMapNative(pid uint32) (envMap map[string]interface{}, err error) {
|
||||
|
||||
var stringMap map[string]string
|
||||
|
||||
if stringMap, err = internal.GetPidEnvMap(pid); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
envMap = internal.NativizeEnvMap(stringMap)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
HasEnv is much like [os.LookupEnv], but only returns a boolean indicating
|
||||
if the environment variable key exists or not.
|
||||
|
||||
This is useful anywhere you may need to set a boolean in a func call
|
||||
depending on the *presence* of an env var or not.
|
||||
*/
|
||||
func (s *StaticEnv) HasEnv(key string) (envIsSet bool) {
|
||||
|
||||
_, envIsSet = os.LookupEnv(key)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Interpolate takes one of:
|
||||
|
||||
- a string (pointer only)
|
||||
- a struct (pointer only)
|
||||
- a map (applied to both keys *and* values)
|
||||
- a slice
|
||||
|
||||
and performs variable substitution on strings from environment variables.
|
||||
|
||||
It supports both UNIX/Linux/POSIX syntax formats (e.g. $VARNAME, ${VARNAME}) and,
|
||||
if on Windows, it *additionally* supports the EXPAND_SZ format (e.g. %VARNAME%).
|
||||
|
||||
For structs, the tag name used can be changed by setting the [StructTagInterpolate]
|
||||
variable in this submodule; the default is `envsub`.
|
||||
If the tag value is "-", the field will be skipped.
|
||||
For map fields within structs etc., the default is to apply interpolation to both keys and values.
|
||||
All other tag value(s) are ignored.
|
||||
|
||||
For maps and slices, Interpolate will recurse into values (e.g. [][]string will work as expected).
|
||||
|
||||
If s is nil, no interpolation will be performed. No error will be returned.
|
||||
If s is not a valid/supported type, no interpolation will be performed. No error will be returned.
|
||||
*/
|
||||
func (s *StaticEnv) Interpolate(inStr any) (err error) {
|
||||
|
||||
var ptrVal reflect.Value
|
||||
var ptrType reflect.Type
|
||||
var ptrKind reflect.Kind
|
||||
var sVal reflect.Value = reflect.ValueOf(inStr)
|
||||
var sType reflect.Type = sVal.Type()
|
||||
var kind reflect.Kind = sType.Kind()
|
||||
|
||||
switch kind {
|
||||
case reflect.Ptr:
|
||||
if sVal.IsNil() || sVal.IsZero() || !sVal.IsValid() {
|
||||
return
|
||||
}
|
||||
ptrVal = sVal.Elem()
|
||||
ptrType = ptrVal.Type()
|
||||
ptrKind = ptrType.Kind()
|
||||
if ptrKind == reflect.String {
|
||||
err = s.interpolateStringReflect(ptrVal)
|
||||
} else {
|
||||
// Otherwise, it should be a struct ptr.
|
||||
if ptrKind != reflect.Struct {
|
||||
return
|
||||
}
|
||||
err = s.interpolateStruct(ptrVal)
|
||||
}
|
||||
case reflect.Map:
|
||||
if sVal.IsNil() || sVal.IsZero() || !sVal.IsValid() {
|
||||
return
|
||||
}
|
||||
err = s.interpolateMap(sVal)
|
||||
case reflect.Slice:
|
||||
if sVal.IsNil() || sVal.IsZero() || !sVal.IsValid() {
|
||||
return
|
||||
}
|
||||
err = s.interpolateSlice(sVal)
|
||||
/*
|
||||
case reflect.Struct:
|
||||
if sVal.IsZero() || !sVal.IsValid() {
|
||||
return
|
||||
}
|
||||
err = interpolateStruct(sVal)
|
||||
|
||||
*/
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
InterpolateString takes (a pointer to) a struct or string and performs variable substitution on it
|
||||
from environment variables.
|
||||
|
||||
It supports both UNIX/Linux/POSIX syntax formats (e.g. $VARNAME, ${VARNAME}) and,
|
||||
if on Windows, it *additionally* supports the EXPAND_SZ format (e.g. %VARNAME%).
|
||||
|
||||
If s is nil, nothing will be done and err will be [errs.ErrNilPtr].
|
||||
|
||||
This is a standalone function that is much more performant than [Interpolate]
|
||||
at the cost of rigidity.
|
||||
*/
|
||||
func (s *StaticEnv) InterpolateString(inStr *string) (err error) {
|
||||
|
||||
var newStr string
|
||||
|
||||
if inStr == nil {
|
||||
err = errs.ErrNilPtr
|
||||
return
|
||||
}
|
||||
|
||||
if newStr, err = interpolateString(*inStr); err != nil {
|
||||
return
|
||||
}
|
||||
*inStr = newStr
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Refresh refreshes the current environment.
|
||||
|
||||
This is called automatically on all other methods if
|
||||
the [StaticEnv] was allocated with `dynamic` set to true.
|
||||
*/
|
||||
func (s *StaticEnv) Refresh() (err error) {
|
||||
|
||||
var ev []string
|
||||
|
||||
if !s.self && s.proc == nil {
|
||||
// NO-OP; static mapping
|
||||
return
|
||||
}
|
||||
|
||||
if s.self {
|
||||
ev = os.Environ()
|
||||
} else if s.proc != nil {
|
||||
if ev, err = s.proc.Environ(); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
s.envVars = internal.EnvListToMap(ev)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Set sets variable key to value.
|
||||
|
||||
This only works under the following conditions:
|
||||
|
||||
- [SetEnv] was created from [NewEnvFromMap]
|
||||
- [SetEnv] is from [Current]
|
||||
- [SetEnv] is from [NewEnvFromPid] *and*
|
||||
this process has PTRACE permissions/capabilities on/for
|
||||
the target PID's process.
|
||||
*/
|
||||
func (s *StaticEnv) Set(key string, value string) {
|
||||
|
||||
var err error
|
||||
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
s.envVars[key] = value
|
||||
if s.proc != nil {
|
||||
if err = s.setProcVal(key, value); err != nil && s.strict {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// interpolateMap is used by [Interpolate] for maps. v should be a [reflect.Value] of a map.
|
||||
func (s *StaticEnv) interpolateMap(v reflect.Value) (err error) {
|
||||
|
||||
var kVal reflect.Value
|
||||
var vVal reflect.Value
|
||||
var newMap reflect.Value
|
||||
var wg sync.WaitGroup
|
||||
var numJobs int
|
||||
var errChan chan error
|
||||
var doneChan chan bool = make(chan bool, 1)
|
||||
var mErr *multierr.MultiError = multierr.NewMultiError(nil)
|
||||
var t reflect.Type = v.Type()
|
||||
var kind reflect.Kind = t.Kind()
|
||||
|
||||
if kind != reflect.Map {
|
||||
err = errs.ErrBadType
|
||||
return
|
||||
}
|
||||
|
||||
if v.IsNil() || v.IsZero() || !v.IsValid() {
|
||||
return
|
||||
}
|
||||
|
||||
numJobs = v.Len()
|
||||
errChan = make(chan error, numJobs)
|
||||
wg.Add(numJobs)
|
||||
|
||||
newMap = reflect.MakeMap(v.Type())
|
||||
|
||||
for _, kVal = range v.MapKeys() {
|
||||
vVal = v.MapIndex(kVal)
|
||||
go func(key, val reflect.Value) {
|
||||
var mapErr error
|
||||
var newKey reflect.Value
|
||||
var newVal reflect.Value
|
||||
|
||||
newKey = reflect.New(key.Type()).Elem()
|
||||
newVal = reflect.New(val.Type()).Elem()
|
||||
|
||||
newKey.Set(key.Convert(newKey.Type()))
|
||||
newVal.Set(val.Convert(newVal.Type()))
|
||||
|
||||
defer wg.Done()
|
||||
|
||||
// key
|
||||
if key.Kind() == reflect.String {
|
||||
if mapErr = s.interpolateStringReflect(newKey); mapErr != nil {
|
||||
errChan <- mapErr
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if mapErr = s.interpolateValue(newKey); mapErr != nil {
|
||||
errChan <- mapErr
|
||||
return
|
||||
}
|
||||
}
|
||||
// value
|
||||
if val.Kind() == reflect.String {
|
||||
if mapErr = s.interpolateStringReflect(newVal); mapErr != nil {
|
||||
errChan <- mapErr
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if mapErr = s.interpolateValue(newVal); mapErr != nil {
|
||||
errChan <- mapErr
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
newMap.SetMapIndex(newKey.Convert(key.Type()), newVal.Convert(key.Type()))
|
||||
}(kVal, vVal)
|
||||
}
|
||||
|
||||
go func() {
|
||||
wg.Wait()
|
||||
close(errChan)
|
||||
doneChan <- true
|
||||
}()
|
||||
|
||||
<-doneChan
|
||||
|
||||
for i := 0; i < numJobs; i++ {
|
||||
if err = <-errChan; err != nil {
|
||||
mErr.AddError(err)
|
||||
err = nil
|
||||
}
|
||||
}
|
||||
|
||||
if !mErr.IsEmpty() {
|
||||
err = mErr
|
||||
return
|
||||
}
|
||||
|
||||
v.Set(newMap.Convert(v.Type()))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// interpolateSlice is used by [Interpolate] for slices and arrays. v should be a [reflect.Value] of a slice/array.
|
||||
func (s *StaticEnv) interpolateSlice(v reflect.Value) (err error) {
|
||||
|
||||
var wg sync.WaitGroup
|
||||
var errChan chan error
|
||||
var numJobs int
|
||||
var doneChan chan bool = make(chan bool, 1)
|
||||
var mErr *multierr.MultiError = multierr.NewMultiError(nil)
|
||||
var t reflect.Type = v.Type()
|
||||
var kind reflect.Kind = t.Kind()
|
||||
|
||||
switch kind {
|
||||
case reflect.Slice:
|
||||
if v.IsNil() || v.IsZero() || !v.IsValid() {
|
||||
return
|
||||
}
|
||||
case reflect.Array:
|
||||
if v.IsZero() || !v.IsValid() {
|
||||
return
|
||||
}
|
||||
default:
|
||||
err = errs.ErrBadType
|
||||
return
|
||||
}
|
||||
|
||||
numJobs = v.Len()
|
||||
errChan = make(chan error, numJobs)
|
||||
wg.Add(numJobs)
|
||||
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
go func(idx int) {
|
||||
var sErr error
|
||||
|
||||
defer wg.Done()
|
||||
|
||||
if v.Index(idx).Kind() == reflect.String {
|
||||
if sErr = s.interpolateStringReflect(v.Index(idx)); sErr != nil {
|
||||
errChan <- sErr
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if sErr = s.interpolateValue(v.Index(idx)); sErr != nil {
|
||||
errChan <- sErr
|
||||
return
|
||||
}
|
||||
}
|
||||
}(i)
|
||||
}
|
||||
|
||||
go func() {
|
||||
wg.Wait()
|
||||
close(errChan)
|
||||
doneChan <- true
|
||||
}()
|
||||
|
||||
<-doneChan
|
||||
|
||||
for i := 0; i < numJobs; i++ {
|
||||
if err = <-errChan; err != nil {
|
||||
mErr.AddError(err)
|
||||
err = nil
|
||||
}
|
||||
}
|
||||
|
||||
if !mErr.IsEmpty() {
|
||||
err = mErr
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// interpolateStringReflect is used for structs/nested strings using reflection.
|
||||
func (s *StaticEnv) interpolateStringReflect(v reflect.Value) (err error) {
|
||||
|
||||
var strVal string
|
||||
|
||||
if v.Kind() != reflect.String {
|
||||
err = errs.ErrBadType
|
||||
return
|
||||
}
|
||||
|
||||
if strVal, err = interpolateString(v.String()); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
v.Set(reflect.ValueOf(strVal).Convert(v.Type()))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// interpolateStruct is used by [Interpolate] for structs. v should be a [reflect.Value] of a struct.
|
||||
func (s *StaticEnv) interpolateStruct(v reflect.Value) (err error) {
|
||||
|
||||
var field reflect.StructField
|
||||
var fieldVal reflect.Value
|
||||
var wg sync.WaitGroup
|
||||
var errChan chan error
|
||||
var numJobs int
|
||||
var doneChan chan bool = make(chan bool, 1)
|
||||
var mErr *multierr.MultiError = multierr.NewMultiError(nil)
|
||||
var t reflect.Type = v.Type()
|
||||
var kind reflect.Kind = t.Kind()
|
||||
|
||||
if kind != reflect.Struct {
|
||||
err = errs.ErrBadType
|
||||
return
|
||||
}
|
||||
|
||||
numJobs = v.NumField()
|
||||
wg.Add(numJobs)
|
||||
errChan = make(chan error, numJobs)
|
||||
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
field = t.Field(i)
|
||||
fieldVal = v.Field(i)
|
||||
|
||||
go func(f reflect.StructField, fv reflect.Value) {
|
||||
var fErr error
|
||||
|
||||
defer wg.Done()
|
||||
|
||||
if fErr = s.interpolateStructField(f, fv); fErr != nil {
|
||||
errChan <- fErr
|
||||
return
|
||||
}
|
||||
}(field, fieldVal)
|
||||
}
|
||||
|
||||
go func() {
|
||||
wg.Wait()
|
||||
close(errChan)
|
||||
doneChan <- true
|
||||
}()
|
||||
|
||||
<-doneChan
|
||||
|
||||
for i := 0; i < numJobs; i++ {
|
||||
if err = <-errChan; err != nil {
|
||||
mErr.AddError(err)
|
||||
err = nil
|
||||
}
|
||||
}
|
||||
|
||||
if !mErr.IsEmpty() {
|
||||
err = mErr
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// interpolateStructField interpolates a struct field.
|
||||
func (s *StaticEnv) interpolateStructField(field reflect.StructField, v reflect.Value) (err error) {
|
||||
|
||||
var parsedTagOpts map[string]bool
|
||||
|
||||
if !v.CanSet() {
|
||||
return
|
||||
}
|
||||
|
||||
// Skip if explicitly instructed to do so.
|
||||
parsedTagOpts = structutils.TagToBoolMap(field, StructTagInterpolate, structutils.TagMapTrim)
|
||||
if parsedTagOpts["-"] {
|
||||
return
|
||||
}
|
||||
|
||||
if v.Kind() == reflect.Ptr {
|
||||
err = s.interpolateStructField(field, v.Elem())
|
||||
} else {
|
||||
err = s.interpolateValue(v)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// interpolateValue is a dispatcher for a reflect value.
|
||||
func (s *StaticEnv) interpolateValue(v reflect.Value) (err error) {
|
||||
|
||||
var kind reflect.Kind = v.Kind()
|
||||
|
||||
switch kind {
|
||||
case reflect.Ptr:
|
||||
if v.IsNil() || v.IsZero() || !v.IsValid() {
|
||||
return
|
||||
}
|
||||
v = v.Elem()
|
||||
if err = s.interpolateValue(v); err != nil {
|
||||
return
|
||||
}
|
||||
case reflect.String:
|
||||
if err = s.interpolateStringReflect(v); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
case reflect.Slice, reflect.Array:
|
||||
if err = s.interpolateSlice(v); err != nil {
|
||||
}
|
||||
case reflect.Map:
|
||||
if err = s.interpolateMap(v); err != nil {
|
||||
return
|
||||
}
|
||||
case reflect.Struct:
|
||||
if err = s.interpolateStruct(v); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
//go:build !(windows || plan9 || wasip1 || js || ios)
|
||||
|
||||
package envs
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func (s *StaticEnv) platChecks() (err error) {
|
||||
|
||||
if s.proc != nil && !s.self {
|
||||
// Check for ptrace caps/perms
|
||||
if err = unix.PtraceAttach(int(s.proc.Pid)); err != nil {
|
||||
return
|
||||
}
|
||||
if err = unix.PtraceDetach(int(s.proc.Pid)); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *StaticEnv) setProcVal(key, value string) (err error) {
|
||||
|
||||
if s.self {
|
||||
if err = unix.Setenv(key, value); err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if err = unix.PtraceAttach(int(s.proc.Pid)); err != nil {
|
||||
return
|
||||
}
|
||||
if err = unix.PtraceDetach(int(s.proc.Pid)); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *StaticEnv) unsetProcVal(key string) (err error) {
|
||||
|
||||
err = unix.Unsetenv(key)
|
||||
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
//go:build windows
|
||||
|
||||
package envs
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
func (s *StaticEnv) platChecks() (err error) {
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *StaticEnv) setProcVal(key, value string) (err error) {
|
||||
|
||||
err = windows.Setenv(key, value)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *StaticEnv) unsetProcVal(key string) (err error) {
|
||||
|
||||
err = windows.Unsetenv(key)
|
||||
|
||||
return
|
||||
}
|
||||
@@ -1,5 +1,11 @@
|
||||
package envs
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/shirou/gopsutil/v4/process"
|
||||
)
|
||||
|
||||
type (
|
||||
/*
|
||||
EnvErrNoVal is an error containing the variable that does not exist
|
||||
@@ -17,4 +23,19 @@ type (
|
||||
// WasWhitespace is true if the value was whitespace-only.
|
||||
WasWhitespace bool `json:"was_ws" toml:"WasWhitespace" yaml:"Was Whitespace Only" xml:"wasWhitespace,attr"`
|
||||
}
|
||||
|
||||
/*
|
||||
StaticEnv is an environment variable mapping that duplicates the normal functions of the standalone functions
|
||||
but can be used for other processes, mock tests, etc.
|
||||
|
||||
(The standalone functions actually perform the same functions on the default StaticEnv as returned from [Current].)
|
||||
*/
|
||||
StaticEnv struct {
|
||||
dynamic bool
|
||||
strict bool
|
||||
envVars map[string]string
|
||||
self bool
|
||||
proc *process.Process
|
||||
lock sync.RWMutex
|
||||
}
|
||||
)
|
||||
@@ -1,17 +1,17 @@
|
||||
module r00t2.io/sysutils
|
||||
|
||||
go 1.25
|
||||
go 1.24.5
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/djherbis/times v1.6.0
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
||||
github.com/shirou/gopsutil/v3 v3.24.5
|
||||
github.com/shirou/gopsutil/v4 v4.25.11
|
||||
golang.org/x/sync v0.19.0
|
||||
golang.org/x/sys v0.39.0
|
||||
github.com/shirou/gopsutil/v4 v4.25.10
|
||||
golang.org/x/sync v0.17.0
|
||||
golang.org/x/sys v0.37.0
|
||||
honnef.co/go/augeas v0.0.0-20161110001225-ca62e35ed6b8
|
||||
r00t2.io/goutils v1.14.0
|
||||
r00t2.io/goutils v1.10.3
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -20,7 +20,7 @@ require (
|
||||
github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
||||
github.com/shoenig/go-m1cpu v0.1.7 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.16 // indirect
|
||||
github.com/tklauser/numcpus v0.11.0 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.15 // indirect
|
||||
github.com/tklauser/numcpus v0.10.0 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
)
|
||||
@@ -19,31 +19,31 @@ github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
|
||||
github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk=
|
||||
github.com/shirou/gopsutil/v4 v4.25.11 h1:X53gB7muL9Gnwwo2evPSE+SfOrltMoR6V3xJAXZILTY=
|
||||
github.com/shirou/gopsutil/v4 v4.25.11/go.mod h1:EivAfP5x2EhLp2ovdpKSozecVXn1TmuG7SMzs/Wh4PU=
|
||||
github.com/shirou/gopsutil/v4 v4.25.10 h1:at8lk/5T1OgtuCp+AwrDofFRjnvosn0nkN2OLQ6g8tA=
|
||||
github.com/shirou/gopsutil/v4 v4.25.10/go.mod h1:+kSwyC8DRUD9XXEHCAFjK+0nuArFJM0lva+StQAcskM=
|
||||
github.com/shoenig/go-m1cpu v0.1.7 h1:C76Yd0ObKR82W4vhfjZiCp0HxcSZ8Nqd84v+HZ0qyI0=
|
||||
github.com/shoenig/go-m1cpu v0.1.7/go.mod h1:KkDOw6m3ZJQAPHbrzkZki4hnx+pDRR1Lo+ldA56wD5w=
|
||||
github.com/shoenig/test v1.7.0 h1:eWcHtTXa6QLnBvm0jgEabMRN/uJ4DMV3M8xUGgRkZmk=
|
||||
github.com/shoenig/test v1.7.0/go.mod h1:UxJ6u/x2v/TNs/LoLxBNJRV9DiwBBKYxXSyczsBHFoI=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA=
|
||||
github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI=
|
||||
github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw=
|
||||
github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ=
|
||||
github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4=
|
||||
github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4=
|
||||
github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso=
|
||||
github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ=
|
||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
||||
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
||||
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
|
||||
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/augeas v0.0.0-20161110001225-ca62e35ed6b8 h1:FW42yWB1sGClqswyHIB68wo0+oPrav1IuQ+Tdy8Qp8E=
|
||||
honnef.co/go/augeas v0.0.0-20161110001225-ca62e35ed6b8/go.mod h1:44w9OfBSQ9l3o59rc2w3AnABtE44bmtNnRMNC7z+oKE=
|
||||
r00t2.io/goutils v1.14.0 h1:mvEiJLrTy/hx7ZX2TzCm/y0be2TcTu822m++qsSQGLc=
|
||||
r00t2.io/goutils v1.14.0/go.mod h1:68jkIl/QYxEEVmVz8k1a7QI9vAA4faQUIcjSHSbtgHw=
|
||||
r00t2.io/goutils v1.10.3 h1:GmEtsM/nrrVWooYJllXDRsgInobEinv2dn5ccU4zGAA=
|
||||
r00t2.io/goutils v1.10.3/go.mod h1:76AxpXUeL10uFklxRB11kQsrtj2AKiNm8AwG1bNoBCA=
|
||||
@@ -0,0 +1 @@
|
||||
package mem
|
||||
@@ -0,0 +1,81 @@
|
||||
package mem
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func getStdRIP(r *unix.PtraceRegs) (rip uint64) {
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func getRIP(r *unix.PtraceRegs) uint64 {
|
||||
// amd64
|
||||
if binary.Size(unix.PtraceRegs{}) == 216 {
|
||||
return r.Rip
|
||||
}
|
||||
// arm64
|
||||
type regsArm64 struct{ Regs [18]uint64 }
|
||||
return (*regsArm64)(unsafe.Pointer(r)).Regs[16] // PC
|
||||
}
|
||||
|
||||
func setRIP(r *unix.PtraceRegs, v uint64) *unix.PtraceRegs {
|
||||
if binary.Size(unix.PtraceRegs{}) == 216 {
|
||||
r.Rip = v
|
||||
} else {
|
||||
type regsArm64 struct{ Regs [18]uint64 }
|
||||
(*regsArm64)(unsafe.Pointer(r)).Regs[16] = v
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func getRSP(r *unix.PtraceRegs) uint64 {
|
||||
if binary.Size(unix.PtraceRegs{}) == 216 {
|
||||
return r.Rsp
|
||||
}
|
||||
type regsArm64 struct{ Regs [18]uint64 }
|
||||
return (*regsArm64)(unsafe.Pointer(r)).Regs[17] // SP
|
||||
}
|
||||
|
||||
func setRSP(r *unix.PtraceRegs, v uint64) *unix.PtraceRegs {
|
||||
if binary.Size(unix.PtraceRegs{}) == 216 {
|
||||
r.Rsp = v
|
||||
} else {
|
||||
type regsArm64 struct{ Regs [18]uint64 }
|
||||
(*regsArm64)(unsafe.Pointer(r)).Regs[17] = v
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func setArg0(r *unix.PtraceRegs, v uint64) *unix.PtraceRegs {
|
||||
if binary.Size(unix.PtraceRegs{}) == 216 {
|
||||
r.Rdi = v
|
||||
} else {
|
||||
type regsArm64 struct{ Regs [18]uint64 }
|
||||
(*regsArm64)(unsafe.Pointer(r)).Regs[0] = v // X0
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func setArg1(r *unix.PtraceRegs, v uint64) *unix.PtraceRegs {
|
||||
if binary.Size(unix.PtraceRegs{}) == 216 {
|
||||
r.Rsi = v
|
||||
} else {
|
||||
type regsArm64 struct{ Regs [18]uint64 }
|
||||
(*regsArm64)(unsafe.Pointer(r)).Regs[1] = v // X1
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func setArg2(r *unix.PtraceRegs, v uint64) *unix.PtraceRegs {
|
||||
if binary.Size(unix.PtraceRegs{}) == 216 {
|
||||
r.Rdx = v
|
||||
} else {
|
||||
type regsArm64 struct{ Regs [18]uint64 }
|
||||
(*regsArm64)(unsafe.Pointer(r)).Regs[2] = v // X2
|
||||
}
|
||||
return r
|
||||
}
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Reference in new issue
Block a user