2 Commits

Author SHA1 Message Date
11b0744e5c fixing merge conflict 2021-11-16 03:03:25 -05:00
3e51ac58db update some stuff for paths, add env mappings 2021-11-16 02:49:17 -05:00
3 changed files with 355 additions and 91 deletions

264
funcs.go Normal file
View File

@@ -0,0 +1,264 @@
package sysutils
import (
`bytes`
`errors`
`fmt`
`io/ioutil`
`os`
`runtime`
`strconv`
`strings`
`r00t2.io/sysutils/paths`
)
/*
EnvMapper contains the environment variables as grouped by their basic type.
If a variable's type cannot be determined, it's placed in Strings.
If a variable's type is a list, it will be an []interface{} as each item may be a different variable type.
It essentially is the same as EnvMap except with the types split out for convenience.
*/
type EnvMapper struct {
Booleans map[string]bool `json:"bools"`
Numbers map[string]int `json:"nums"`
Strings map[string]string `json:"strings"`
Lists map[string][]interface{} `json:"lists"`
}
// GetEnvMapper returns a pointer to a populated EnvMapper.
func GetEnvMapper() (e *EnvMapper, err error) {
var em map[string]interface{}
var env EnvMapper
env = EnvMapper{
Booleans: nil,
Numbers: nil,
Strings: nil,
Lists: nil,
}
for k, v := range em {
switch t := v.(type) {
case bool:
if env.Booleans == nil {
env.Booleans = make(map[string]bool, 0)
}
env.Booleans[k] = t
continue
case int:
if env.Numbers == nil {
env.Numbers = make(map[string]int, 0)
}
env.Numbers[k] = t
continue
case []interface{}:
if env.Lists == nil {
env.Lists = make(map[string][]interface{}, 0)
}
env.Lists[k] = t
case string: // the "default" since everything could probably be typeswitched to a string otherwise.
if env.Strings == nil {
env.Strings = make(map[string]string, 0)
}
env.Strings[k] = t
}
}
*e = env
return
}
/*
EnvMap returns a map of environment variables.
The variable is an interface due to it attempting to use a variable's value to its "true" native type.
If a type cannot be determined, it will be treated a string.
*/
func EnvMap() (envs map[string]interface{}, err error) {
var ems map[string]string
if ems, err = EnvMapString(); err != nil {
return
}
for k, v := range ems {
// Is int?
if i, ok := getNum(v); ok {
envs[k] = i
continue
}
// Is bool?
if b, ok := getBool(v); ok {
envs[k] = b
continue
}
// Is array?
if a, ok := getArr(v); ok {
envs[k] = a
continue
}
// It's a string (probably).
envs[k] = v
}
return
}
// EnvMapString is like EnvMap, but all values are treated as strings.
func EnvMapString() (envs map[string]string, err error) {
var envArray []string
envs = make(map[string]string, 0)
envArray = os.Environ()
for _, e := range envArray {
var k, v string
var kv []string
kv = strings.SplitN(e, "=", 2)
k = kv[0]
if len(kv) == 2 {
v = kv[1]
} else {
v = ""
}
envs[k] = v
}
return
}
// UTILITY FUNCS
// getBool attempts to convert a string value to a boolean.
func getBool(s string) (b bool, ok bool) {
switch s2 := strings.ToLower(strings.TrimSpace(s)); s2 {
case "true", "yes", "y":
b = true
ok = true
case "false", "no", "n":
b = false
ok = true
}
return
}
// getNum attempts to convert a string value to an int.
func getNum(s string) (n int, ok bool) {
var err error
if n, err = strconv.Atoi(s); err == nil {
ok = true
}
return
}
// getArr attempts to convert a string value to an array of interface{}.
func getArr(s string) (a []interface{}, ok bool) {
var arrS []string
if arrS, ok = getArrStr(s); !ok {
return
}
a = make([]interface{}, len(arrS))
for idx, i := range arrS {
if b, ok := getBool(i); ok {
a[idx] = b
} else if n, ok := getNum(i); ok {
a[idx] = n
} else if l, ok := getArr(i); ok {
a[idx] = l
} else {
a[idx] = i
}
}
return
}
// getArrStr attempts to convert a string value to an array of strings.
func getArrStr(s string) (a []string, ok bool) {
var sep string = ":"
if runtime.GOOS == "windows" {
sep = ";"
}
a = strings.Split(s, sep)
l := len(s)
if l <= 1 {
return
}
ok = true
return
}
/*
GetEnvPid will only work on *NIX-like systems with procfs.
It gets the environment variables of a given process' PID.
*/
func GetEnvPid(pid uint32) (env map[string]string, err error) {
var envBytes []byte
var envArr [][]byte
var procPath string
var exists bool
env = make(map[string]string, 0)
procPath = fmt.Sprintf("/proc/%v/environ", pid)
if exists, err = paths.RealPathExists(&procPath); err != nil {
return
}
if !exists {
err = errors.New(fmt.Sprintf("information for pid %v does not exist", pid))
return
}
if envBytes, err = ioutil.ReadFile(procPath); err != nil {
return
}
envArr = bytes.Split(envBytes, []byte{0x0})
for _, b := range envArr {
// s := strings.TrimSpace(string(b))
s := string(b)
e := strings.SplitN(s, "=", 2)
for _, i := range e {
if len(i) != 2 {
env[string(i[0])] = ""
continue
}
env[string(i[0])] = string(i[1])
}
}
return
}

View File

@@ -63,7 +63,7 @@ func main() {
"\tService: %v\n" +
"\tDesc: %v\n" +
"\tReserved?: %v\n",
p.Number, p.Proto.Name, p.ServiceName, p.Description, p.Reserved)
p.Number, p.Protocol.Name, p.ServiceName, p.Description, p.Reserved)
}
----

View File

@@ -19,156 +19,156 @@
package paths
import (
"bytes"
"errors"
"fmt"
"io/ioutil"
`fmt`
"os"
"os/user"
"path/filepath"
`runtime`
// "strconv"
"strings"
// "syscall"
)
var err error
/*
ExpandHome will take a tilde(~)-prefixed path and resolve it to the actual path in-place.
Note that it only works for current user; the syntax ~someotheruser/foo/bar is currently unsupported.
*/
func ExpandHome(path *string) (err error) {
var usr *user.User
// Props to this guy.
// https://stackoverflow.com/a/43578461/733214
if len(*path) == 0 {
return errors.New("empty path")
err = errors.New("empty path")
return
} else if (*path)[0] != '~' {
return
}
// E(ffective)UID (e.g. chown'd user for SUID)
/*
uid := strconv.Itoa(syscall.Geteuid())
usr, err := user.LookupId(euid)
*/
// R(real)UID (invoking user)
usr, err := user.Current()
if err != nil {
return
// (Real)UID (invoking user)
if usr, err = user.Current(); err != nil {
return err
}
*path = filepath.Join(usr.HomeDir, (*path)[1:])
return
}
func GetPathEnv() (s []string, err error) {
s = make([]string, 0)
for _, p := range strings.Split(os.Getenv("PATH"), ":") {
// GetPathEnv returns a slice of the PATH variable's items.
func GetPathEnv() (paths []string, err error) {
var pathVar string = "PATH"
var sep string = ":"
paths = make([]string, 0)
if runtime.GOOS == "windows" {
pathVar = "Path"
sep = ";"
}
for _, p := range strings.Split(os.Getenv(pathVar), sep) {
if err = RealPath(&p); err != nil {
return
}
s = append(s, p)
paths = append(paths, p)
}
return
}
func GetEnvPid(pid uint32) (env map[string]string, err error) {
// MakeDirIfNotExist will create a directory at a given path if it doesn't exist.
func MakeDirIfNotExist(path string) (err error) {
var envBytes []byte
var envArr [][]byte
var procPath string
var stat os.FileInfo
var exists bool
var locPath string = path
env = make(map[string]string)
procPath = fmt.Sprintf("/proc/%v/environ", pid)
if exists, err = RealPathExists(&procPath); err != nil {
return
}
if !exists {
err = errors.New(fmt.Sprintf("information for pid %v does not exist", pid))
}
if envBytes, err = ioutil.ReadFile(procPath); err != nil {
return
}
envArr = bytes.Split(envBytes, []byte{0x0})
for _, b := range envArr {
// s := strings.TrimSpace(string(b))
s := string(b)
e := strings.SplitN(s, "=", 2)
for _, i := range e {
if len(i) != 2 {
continue
}
env[string(i[0])] = string(i[1])
}
}
return
}
func MakeDirIfNotExist(path *string) error {
exists, stat, err := RealPathExistsStat(path)
if err != nil {
if exists, stat, err = RealPathExistsStat(&locPath); err != nil {
if !exists {
// This, at least as of golang 1.15, uses the user's umask.
// It does not actually create a dir with 0777.
// It's up to the caller to do an os.Chmod() on the path after, if desired.
os.MkdirAll(*path, 0777)
return nil
if err = os.MkdirAll(locPath, 0777); err != nil {
return
}
err = nil
return
} else {
return err
return
}
}
// So it exists, but it probably isn't a dir.
if !stat.Mode().IsDir() {
return errors.New(fmt.Sprintf("path %v exists but is not a directory", *path))
err = errors.New(fmt.Sprintf("path %v exists but is not a directory", locPath))
return
}
// This should probably never happen. Probably.
return errors.New("undefined behaviour")
err = errors.New("undefined")
return
}
func RealPath(path *string) error {
err := ExpandHome(path)
if err != nil {
return err
}
*path, err = filepath.Abs(*path)
if err != nil {
return err
}
return nil
// RealPath will transform a given path into the very best guess for an absolute path in-place.
func RealPath(path *string) (err error) {
if err = ExpandHome(path); err != nil {
return
}
if *path, err = filepath.Abs(*path); err != nil {
return
}
return
}
/*
RealPathExists is like RealPath, but will also return a boolean as to whether the path
actually exists or not.
It's hacky, but the "exists" bool along with err is a sort of proto-state-machine.
If err != nil and bool is true, the error occurred during path absolution.
If err != nil and bool is false, the path does not exist.
*/
func RealPathExists(path *string) (exists bool, err error) {
if err = RealPath(path); err != nil {
exists = true
return
}
if _, err = os.Stat(*path); err != nil {
if os.IsNotExist(err) {
exists = false
err = nil
} else {
return
}
} else {
exists = true
}
return
}
// RealPathExistsStat is like RealPathExists except it will also return the os.FileInfo for the path (assuming it exists).
func RealPathExistsStat(path *string) (exists bool, stat os.FileInfo, err error) {
// See the comments for RealPathExists for details on this.
if err = RealPath(path); err != nil {
return
}
if stat, err = os.Stat(*path); err != nil {
if os.IsNotExist(err) {
exists = false
err = nil
} else {
return
}
} else {
exists = true
return
}
if stat, err = os.Stat(*path); err != nil {
return
}
exists = true
return
}