github.com/isyscore/isc-gobase@v1.5.3-0.20231218061332-cbc7451899e9/system/common/common.go (about)

     1  package common
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"context"
     7  	"errors"
     8  	"fmt"
     9  	"net/url"
    10  	"os"
    11  	"os/exec"
    12  	"path"
    13  	"path/filepath"
    14  	"reflect"
    15  	"runtime"
    16  	"strconv"
    17  	"strings"
    18  	"time"
    19  )
    20  
    21  var (
    22  	Timeout    = 3 * time.Second
    23  	ErrTimeout = errors.New("command timed out")
    24  )
    25  
    26  type Invoker interface {
    27  	Command(string, ...string) ([]byte, error)
    28  	CommandWithContext(context.Context, string, ...string) ([]byte, error)
    29  }
    30  
    31  type Invoke struct{}
    32  
    33  func (i Invoke) Command(name string, arg ...string) ([]byte, error) {
    34  	ctx, cancel := context.WithTimeout(context.Background(), Timeout)
    35  	defer cancel()
    36  	return i.CommandWithContext(ctx, name, arg...)
    37  }
    38  
    39  func (i Invoke) CommandWithContext(ctx context.Context, name string, arg ...string) ([]byte, error) {
    40  	cmd := exec.CommandContext(ctx, name, arg...)
    41  
    42  	var buf bytes.Buffer
    43  	cmd.Stdout = &buf
    44  	cmd.Stderr = &buf
    45  
    46  	if err := cmd.Start(); err != nil {
    47  		return buf.Bytes(), err
    48  	}
    49  
    50  	if err := cmd.Wait(); err != nil {
    51  		return buf.Bytes(), err
    52  	}
    53  
    54  	return buf.Bytes(), nil
    55  }
    56  
    57  type FakeInvoke struct {
    58  	Suffix string // Suffix species expected file name suffix such as "fail"
    59  	Error  error  // If Error specfied, return the error.
    60  }
    61  
    62  // Command in FakeInvoke returns from expected file if exists.
    63  func (i FakeInvoke) Command(name string, arg ...string) ([]byte, error) {
    64  	if i.Error != nil {
    65  		return []byte{}, i.Error
    66  	}
    67  
    68  	arch := runtime.GOOS
    69  
    70  	commandName := filepath.Base(name)
    71  
    72  	fname := strings.Join(append([]string{commandName}, arg...), "")
    73  	fname = url.QueryEscape(fname)
    74  	fpath := path.Join("testdata", arch, fname)
    75  	if i.Suffix != "" {
    76  		fpath += "_" + i.Suffix
    77  	}
    78  	if PathExists(fpath) {
    79  		return os.ReadFile(fpath)
    80  	}
    81  	return []byte{}, fmt.Errorf("could not find testdata: %s", fpath)
    82  }
    83  
    84  func (i FakeInvoke) CommandWithContext(ctx context.Context, name string, arg ...string) ([]byte, error) {
    85  	return i.Command(name, arg...)
    86  }
    87  
    88  var ErrNotImplementedError = errors.New("not implemented yet")
    89  
    90  // ReadFile reads contents from a file.
    91  func ReadFile(filename string) (string, error) {
    92  	content, err := os.ReadFile(filename)
    93  
    94  	if err != nil {
    95  		return "", err
    96  	}
    97  
    98  	return string(content), nil
    99  }
   100  
   101  // ReadLines reads contents from a file and splits them by new lines.
   102  // A convenience wrapper to ReadLinesOffsetN(filename, 0, -1).
   103  func ReadLines(filename string) ([]string, error) {
   104  	return ReadLinesOffsetN(filename, 0, -1)
   105  }
   106  
   107  // ReadLinesOffsetN reads contents from file and splits them by new line.
   108  // The offset tells at which line number to start.
   109  // The count determines the number of lines to read (starting from offset):
   110  //   n >= 0: at most n lines
   111  //   n < 0: whole file
   112  func ReadLinesOffsetN(filename string, offset uint, n int) ([]string, error) {
   113  	f, err := os.Open(filename)
   114  	if err != nil {
   115  		return []string{""}, err
   116  	}
   117  	defer func(f *os.File) {
   118  		_ = f.Close()
   119  	}(f)
   120  
   121  	var ret []string
   122  
   123  	r := bufio.NewReader(f)
   124  	for i := 0; i < n+int(offset) || n < 0; i++ {
   125  		line, err := r.ReadString('\n')
   126  		if err != nil {
   127  			break
   128  		}
   129  		if i < int(offset) {
   130  			continue
   131  		}
   132  		ret = append(ret, strings.Trim(line, "\n"))
   133  	}
   134  
   135  	return ret, nil
   136  }
   137  
   138  func IntToString(orig []int8) string {
   139  	ret := make([]byte, len(orig))
   140  	size := -1
   141  	for i, o := range orig {
   142  		if o == 0 {
   143  			size = i
   144  			break
   145  		}
   146  		ret[i] = byte(o)
   147  	}
   148  	if size == -1 {
   149  		size = len(orig)
   150  	}
   151  
   152  	return string(ret[0:size])
   153  }
   154  
   155  func UintToString(orig []uint8) string {
   156  	ret := make([]byte, len(orig))
   157  	size := -1
   158  	for i, o := range orig {
   159  		if o == 0 {
   160  			size = i
   161  			break
   162  		}
   163  		ret[i] = o
   164  	}
   165  	if size == -1 {
   166  		size = len(orig)
   167  	}
   168  
   169  	return string(ret[0:size])
   170  }
   171  
   172  func ByteToString(orig []byte) string {
   173  	n := -1
   174  	l := -1
   175  	for i, b := range orig {
   176  		// skip left side null
   177  		if l == -1 && b == 0 {
   178  			continue
   179  		}
   180  		if l == -1 {
   181  			l = i
   182  		}
   183  
   184  		if b == 0 {
   185  			break
   186  		}
   187  		n = i + 1
   188  	}
   189  	if n == -1 {
   190  		return string(orig)
   191  	}
   192  	return string(orig[l:n])
   193  }
   194  
   195  // ReadInts reads contents from single line file and returns them as []int32.
   196  func ReadInts(filename string) ([]int64, error) {
   197  	f, err := os.Open(filename)
   198  	if err != nil {
   199  		return []int64{}, err
   200  	}
   201  	defer func(f *os.File) {
   202  		_ = f.Close()
   203  	}(f)
   204  
   205  	var ret []int64
   206  
   207  	r := bufio.NewReader(f)
   208  
   209  	// The int files that this is concerned with should only be one liners.
   210  	line, err := r.ReadString('\n')
   211  	if err != nil {
   212  		return []int64{}, err
   213  	}
   214  
   215  	i, err := strconv.ParseInt(strings.Trim(line, "\n"), 10, 32)
   216  	if err != nil {
   217  		return []int64{}, err
   218  	}
   219  	ret = append(ret, i)
   220  
   221  	return ret, nil
   222  }
   223  
   224  // HexToUint32 parses Hex to uint32 without error.
   225  func HexToUint32(hex string) uint32 {
   226  	vv, _ := strconv.ParseUint(hex, 16, 32)
   227  	return uint32(vv)
   228  }
   229  
   230  // mustParseInt32 parses to int32 without error.
   231  func mustParseInt32(val string) int32 {
   232  	vv, _ := strconv.ParseInt(val, 10, 32)
   233  	return int32(vv)
   234  }
   235  
   236  // mustParseUint64 parses to uint64 without error.
   237  func mustParseUint64(val string) uint64 {
   238  	vv, _ := strconv.ParseInt(val, 10, 64)
   239  	return uint64(vv)
   240  }
   241  
   242  // mustParseFloat64 parses to Float64 without error.
   243  func mustParseFloat64(val string) float64 {
   244  	vv, _ := strconv.ParseFloat(val, 64)
   245  	return vv
   246  }
   247  
   248  // StringsHas checks the target string slice contains src or not.
   249  func StringsHas(target []string, src string) bool {
   250  	for _, t := range target {
   251  		if strings.TrimSpace(t) == src {
   252  			return true
   253  		}
   254  	}
   255  	return false
   256  }
   257  
   258  // StringsContains checks the src in any string of the target string slice.
   259  func StringsContains(target []string, src string) bool {
   260  	for _, t := range target {
   261  		if strings.Contains(t, src) {
   262  			return true
   263  		}
   264  	}
   265  	return false
   266  }
   267  
   268  // IntContains checks the src in any int of the target int slice.
   269  func IntContains(target []int, src int) bool {
   270  	for _, t := range target {
   271  		if src == t {
   272  			return true
   273  		}
   274  	}
   275  	return false
   276  }
   277  
   278  // get struct attributes.
   279  // This method is used only for debugging platform dependent code.
   280  func attributes(m interface{}) map[string]reflect.Type {
   281  	typ := reflect.TypeOf(m)
   282  	if typ.Kind() == reflect.Ptr {
   283  		typ = typ.Elem()
   284  	}
   285  
   286  	attrs := make(map[string]reflect.Type)
   287  	if typ.Kind() != reflect.Struct {
   288  		return nil
   289  	}
   290  
   291  	for i := 0; i < typ.NumField(); i++ {
   292  		p := typ.Field(i)
   293  		if !p.Anonymous {
   294  			attrs[p.Name] = p.Type
   295  		}
   296  	}
   297  
   298  	return attrs
   299  }
   300  
   301  func PathExists(filename string) bool {
   302  	if _, err := os.Stat(filename); err == nil {
   303  		return true
   304  	}
   305  	return false
   306  }
   307  
   308  // GetEnv retrieves the environment variable key. If it does not exist it returns the default.
   309  func GetEnv(key string, dfault string, combineWith ...string) string {
   310  	value := os.Getenv(key)
   311  	if value == "" {
   312  		value = dfault
   313  	}
   314  
   315  	switch len(combineWith) {
   316  	case 0:
   317  		return value
   318  	case 1:
   319  		return filepath.Join(value, combineWith[0])
   320  	default:
   321  		all := make([]string, len(combineWith)+1)
   322  		all[0] = value
   323  		copy(all[1:], combineWith)
   324  		return filepath.Join(all...)
   325  	}
   326  }
   327  
   328  func HostProc(combineWith ...string) string {
   329  	return GetEnv("HOST_PROC", "/proc", combineWith...)
   330  }
   331  
   332  func HostSys(combineWith ...string) string {
   333  	return GetEnv("HOST_SYS", "/sys", combineWith...)
   334  }
   335  
   336  func HostEtc(combineWith ...string) string {
   337  	return GetEnv("HOST_ETC", "/etc", combineWith...)
   338  }
   339  
   340  func HostVar(combineWith ...string) string {
   341  	return GetEnv("HOST_VAR", "/var", combineWith...)
   342  }
   343  
   344  func HostRun(combineWith ...string) string {
   345  	return GetEnv("HOST_RUN", "/run", combineWith...)
   346  }
   347  
   348  func HostDev(combineWith ...string) string {
   349  	return GetEnv("HOST_DEV", "/dev", combineWith...)
   350  }
   351  
   352  // MockEnv set environment variable and return revert function.
   353  // MockEnv should be used testing only.
   354  func MockEnv(key string, value string) func() {
   355  	original := os.Getenv(key)
   356  	_ = os.Setenv(key, value)
   357  	return func() {
   358  		_ = os.Setenv(key, original)
   359  	}
   360  }
   361  
   362  // getSysctrlEnv sets LC_ALL=C in a list of env vars for use when running
   363  // sysctl commands (see DoSysctrl).
   364  func getSysctrlEnv(env []string) []string {
   365  	foundLC := false
   366  	for i, line := range env {
   367  		if strings.HasPrefix(line, "LC_ALL") {
   368  			env[i] = "LC_ALL=C"
   369  			foundLC = true
   370  		}
   371  	}
   372  	if !foundLC {
   373  		env = append(env, "LC_ALL=C")
   374  	}
   375  	return env
   376  }