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 }