diff options
author | Valery Piashchynski <[email protected]> | 2021-12-26 00:50:30 +0300 |
---|---|---|
committer | Valery Piashchynski <[email protected]> | 2021-12-26 00:50:30 +0300 |
commit | b27b2a1c9030f38e729e6e2d411379047c28402e (patch) | |
tree | 129d9d1fd1d2803712fa4b0f05d5cfbf466d10e3 /utils | |
parent | 9cbb6be27ca0bd56eaa6db9a875830a8ce6110e8 (diff) |
implement common logger
Signed-off-by: Valery Piashchynski <[email protected]>
Diffstat (limited to 'utils')
-rw-r--r-- | utils/convert.go | 25 | ||||
-rwxr-xr-x | utils/isolate.go | 60 | ||||
-rwxr-xr-x | utils/isolate_win.go | 18 | ||||
-rwxr-xr-x | utils/network.go | 108 | ||||
-rwxr-xr-x | utils/network_windows.go | 64 | ||||
-rw-r--r-- | utils/race_checker.go | 35 | ||||
-rw-r--r-- | utils/race_checker_unsafe.go | 5 | ||||
-rw-r--r-- | utils/to_ptr.go | 467 |
8 files changed, 777 insertions, 5 deletions
diff --git a/utils/convert.go b/utils/convert.go index 8728ad1f..d96acfbb 100644 --- a/utils/convert.go +++ b/utils/convert.go @@ -5,6 +5,24 @@ import ( "unsafe" ) +// AsBytes returns a slice that refers to the data backing the string s. +func AsBytes(s string) []byte { + // get the pointer to the data of the string + p := unsafe.Pointer((*reflect.StringHeader)(unsafe.Pointer(&s)).Data) + + var b []byte + hdr := (*reflect.SliceHeader)(unsafe.Pointer(&b)) + hdr.Data = uintptr(p) + // we need to set the cap and len for the string to byte convert + // because string is shorter than []bytes + hdr.Cap = len(s) + hdr.Len = len(s) + + // checker to check mutable access to the data + SetChecker(b) + return b +} + // AsString returns a string that refers to the data backing the slice s. func AsString(b []byte) string { p := unsafe.Pointer((*reflect.SliceHeader)(unsafe.Pointer(&b)).Data) @@ -14,10 +32,7 @@ func AsString(b []byte) string { hdr.Data = uintptr(p) hdr.Len = len(b) + // checker to check mutable access to the data + SetChecker(b) return s } - -// Uint64 returns a pointer value for the uint64 value passed in. -func Uint64(v uint64) *uint64 { - return &v -} diff --git a/utils/isolate.go b/utils/isolate.go new file mode 100755 index 00000000..202f538c --- /dev/null +++ b/utils/isolate.go @@ -0,0 +1,60 @@ +//go:build !windows +// +build !windows + +package utils + +import ( + "fmt" + "os" + "os/exec" + "os/user" + "strconv" + "syscall" + + "github.com/spiral/errors" +) + +// IsolateProcess change gpid for the process to avoid bypassing signals to php processes. +func IsolateProcess(cmd *exec.Cmd) { + cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true, Pgid: 0} +} + +// ExecuteFromUser may work only if run RR under root user +func ExecuteFromUser(cmd *exec.Cmd, u string) error { + const op = errors.Op("execute_from_user") + usr, err := user.Lookup(u) + if err != nil { + return errors.E(op, err) + } + + usrI32, err := strconv.ParseInt(usr.Uid, 10, 32) + if err != nil { + return errors.E(op, err) + } + + grI32, err := strconv.ParseInt(usr.Gid, 10, 32) + if err != nil { + return errors.E(op, err) + } + + // For more information: + // https://www.man7.org/linux/man-pages/man7/user_namespaces.7.html + // https://www.man7.org/linux/man-pages/man7/namespaces.7.html + if _, err := os.Stat("/proc/self/ns/user"); err != nil { + if os.IsNotExist(err) { + return fmt.Errorf("kernel doesn't support user namespaces") + } + if os.IsPermission(err) { + return fmt.Errorf("unable to test user namespaces due to permissions") + } + + return errors.E(op, errors.Errorf("failed to stat /proc/self/ns/user: %v", err)) + } + + cmd.SysProcAttr.Credential = &syscall.Credential{ + Uid: uint32(usrI32), + Gid: uint32(grI32), + } + + return nil +} diff --git a/utils/isolate_win.go b/utils/isolate_win.go new file mode 100755 index 00000000..6b6d22e0 --- /dev/null +++ b/utils/isolate_win.go @@ -0,0 +1,18 @@ +//go:build windows +// +build windows + +package utils + +import ( + "os/exec" + "syscall" +) + +// IsolateProcess change gpid for the process to avoid bypassing signals to php processes. +func IsolateProcess(cmd *exec.Cmd) { + cmd.SysProcAttr = &syscall.SysProcAttr{CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP} +} + +func ExecuteFromUser(cmd *exec.Cmd, u string) error { + return nil +} diff --git a/utils/network.go b/utils/network.go new file mode 100755 index 00000000..d9269269 --- /dev/null +++ b/utils/network.go @@ -0,0 +1,108 @@ +//go:build linux || darwin || freebsd +// +build linux darwin freebsd + +package utils + +import ( + "fmt" + "net" + "os" + "strings" + "syscall" + + "github.com/spiral/tcplisten" +) + +const ( + IPV4 string = "tcp4" + IPV6 string = "tcp6" +) + +// CreateListener +// - SO_REUSEPORT. This option allows linear scaling server performance +// on multi-CPU servers. +// See https://www.nginx.com/blog/socket-sharding-nginx-release-1-9-1/ for details. +// +// - TCP_DEFER_ACCEPT. This option expects the server reads from the accepted +// connection before writing to them. +// +// - TCP_FASTOPEN. See https://lwn.net/Articles/508865/ for details. +// CreateListener crates socket listener based on DSN definition. +func CreateListener(address string) (net.Listener, error) { + dsn := strings.Split(address, "://") + + switch len(dsn) { + case 1: + // assume, that there is no prefix here [127.0.0.1:8000] + return createTCPListener(dsn[0]) + case 2: + // we got two part here, first part is the transport, second - address + // [tcp://127.0.0.1:8000] OR [unix:///path/to/unix.socket] OR [error://path] + // where error is wrong transport name + switch dsn[0] { + case "unix": + // check of file exist. If exist, unlink + if fileExists(dsn[1]) { + err := syscall.Unlink(dsn[1]) + if err != nil { + return nil, fmt.Errorf("error during the unlink syscall: error %w", err) + } + } + return net.Listen(dsn[0], dsn[1]) + case "tcp": + return createTCPListener(dsn[1]) + // not an tcp or unix + default: + return nil, fmt.Errorf("invalid Protocol ([tcp://]:6001, unix://file.sock), address: %s", address) + } + // wrong number of split parts + default: + return nil, fmt.Errorf("wrong number of parsed protocol parts, address: %s", address) + } +} + +func createTCPListener(addr string) (net.Listener, error) { + cfg := tcplisten.Config{ + ReusePort: true, + DeferAccept: false, + FastOpen: true, + } + + /* + Options we may have here: + 1. [::1]:8080 //ipv6 + 2. [0:0:..]:8080 //ipv6 + 3. 127.0.0.1:8080 //ipv4 + 4. :8080 //ipv4 + 5. [::]:8080 //ipv6 + */ + host, _, err := net.SplitHostPort(addr) + if err != nil { + return nil, err + } + + // consider this is IPv4 + if host == "" { + return cfg.NewListener(IPV4, addr) + } + + return cfg.NewListener(netw(net.ParseIP(host)), addr) +} + +// check if we are listening on the ipv6 or ipv4 address +func netw(addr net.IP) string { + if addr.To4() == nil { + return IPV6 + } + return IPV4 +} + +// fileExists checks if a file exists and is not a directory before we +// try using it to prevent further errors. +func fileExists(filename string) bool { + info, err := os.Stat(filename) + if os.IsNotExist(err) { + return false + } + return !info.IsDir() +} diff --git a/utils/network_windows.go b/utils/network_windows.go new file mode 100755 index 00000000..88e0fdb6 --- /dev/null +++ b/utils/network_windows.go @@ -0,0 +1,64 @@ +//go:build windows +// +build windows + +package utils + +import ( + "fmt" + "net" + "os" + "strings" + "syscall" +) + +// CreateListener crates socket listener based on DSN definition. +func CreateListener(address string) (net.Listener, error) { + dsn := strings.Split(address, "://") + + switch len(dsn) { + case 1: + // assume, that there is no prefix here [127.0.0.1:8000] + return createTCPListener(dsn[0]) + case 2: + // we got two part here, first part is the transport, second - address + // [tcp://127.0.0.1:8000] OR [unix:///path/to/unix.socket] OR [error://path] + // where error is wrong transport name + switch dsn[0] { + case "unix": + // check of file exist. If exist, unlink + if fileExists(dsn[1]) { + err := syscall.Unlink(dsn[1]) + if err != nil { + return nil, fmt.Errorf("error during the unlink syscall: error %v", err) + } + } + return net.Listen(dsn[0], dsn[1]) + case "tcp": + return createTCPListener(dsn[1]) + // not an tcp or unix + default: + return nil, fmt.Errorf("invalid Protocol ([tcp://]:6001, unix://file.sock), address: %s", address) + } + // wrong number of split parts + default: + return nil, fmt.Errorf("wrong number of parsed protocol parts, address: %s", address) + } +} + +func createTCPListener(addr string) (net.Listener, error) { + listener, err := net.Listen("tcp", addr) + if err != nil { + return nil, err + } + return listener, nil +} + +// fileExists checks if a file exists and is not a directory before we +// try using it to prevent further errors. +func fileExists(filename string) bool { + info, err := os.Stat(filename) + if os.IsNotExist(err) { + return false + } + return !info.IsDir() +} diff --git a/utils/race_checker.go b/utils/race_checker.go new file mode 100644 index 00000000..cd5ed556 --- /dev/null +++ b/utils/race_checker.go @@ -0,0 +1,35 @@ +//go:build race + +package utils + +import ( + "crypto/sha512" + "fmt" + "runtime" +) + +func SetChecker(b []byte) { + if len(b) == 0 { + return + } + c := checkIfConst(b) + go c.isStillConst() + runtime.SetFinalizer(c, (*constSlice).isStillConst) +} + +type constSlice struct { + b []byte + checksum [64]byte +} + +func checkIfConst(b []byte) *constSlice { + c := &constSlice{b: b} + c.checksum = sha512.Sum512(c.b) + return c +} + +func (c *constSlice) isStillConst() { + if sha512.Sum512(c.b) != c.checksum { + panic(fmt.Sprintf("mutable access detected 0x%012x", &c.b[0])) + } +} diff --git a/utils/race_checker_unsafe.go b/utils/race_checker_unsafe.go new file mode 100644 index 00000000..d2b622a5 --- /dev/null +++ b/utils/race_checker_unsafe.go @@ -0,0 +1,5 @@ +//go:build !race + +package utils + +func SetChecker(_ []byte) {} diff --git a/utils/to_ptr.go b/utils/to_ptr.go new file mode 100644 index 00000000..7c93ef46 --- /dev/null +++ b/utils/to_ptr.go @@ -0,0 +1,467 @@ +package utils + +import "time" + +// Bool returns a pointer value for the bool value passed in. +func Bool(v bool) *bool { + return &v +} + +// BoolSlice returns a slice of bool pointers from the values +// passed in. +func BoolSlice(vs []bool) []*bool { + ps := make([]*bool, len(vs)) + for i, v := range vs { + vv := v + ps[i] = &vv + } + + return ps +} + +// BoolMap returns a map of bool pointers from the values +// passed in. +func BoolMap(vs map[string]bool) map[string]*bool { + ps := make(map[string]*bool, len(vs)) + for k, v := range vs { + vv := v + ps[k] = &vv + } + + return ps +} + +// Byte returns a pointer value for the byte value passed in. +func Byte(v byte) *byte { + return &v +} + +// ByteSlice returns a slice of byte pointers from the values +// passed in. +func ByteSlice(vs []byte) []*byte { + ps := make([]*byte, len(vs)) + for i, v := range vs { + vv := v + ps[i] = &vv + } + + return ps +} + +// ByteMap returns a map of byte pointers from the values +// passed in. +func ByteMap(vs map[string]byte) map[string]*byte { + ps := make(map[string]*byte, len(vs)) + for k, v := range vs { + vv := v + ps[k] = &vv + } + + return ps +} + +// String returns a pointer value for the string value passed in. +func String(v string) *string { + return &v +} + +// StringSlice returns a slice of string pointers from the values +// passed in. +func StringSlice(vs []string) []*string { + ps := make([]*string, len(vs)) + for i, v := range vs { + vv := v + ps[i] = &vv + } + + return ps +} + +// StringMap returns a map of string pointers from the values +// passed in. +func StringMap(vs map[string]string) map[string]*string { + ps := make(map[string]*string, len(vs)) + for k, v := range vs { + vv := v + ps[k] = &vv + } + + return ps +} + +// Int returns a pointer value for the int value passed in. +func Int(v int) *int { + return &v +} + +// IntSlice returns a slice of int pointers from the values +// passed in. +func IntSlice(vs []int) []*int { + ps := make([]*int, len(vs)) + for i, v := range vs { + vv := v + ps[i] = &vv + } + + return ps +} + +// IntMap returns a map of int pointers from the values +// passed in. +func IntMap(vs map[string]int) map[string]*int { + ps := make(map[string]*int, len(vs)) + for k, v := range vs { + vv := v + ps[k] = &vv + } + + return ps +} + +// Int8 returns a pointer value for the int8 value passed in. +func Int8(v int8) *int8 { + return &v +} + +// Int8Slice returns a slice of int8 pointers from the values +// passed in. +func Int8Slice(vs []int8) []*int8 { + ps := make([]*int8, len(vs)) + for i, v := range vs { + vv := v + ps[i] = &vv + } + + return ps +} + +// Int8Map returns a map of int8 pointers from the values +// passed in. +func Int8Map(vs map[string]int8) map[string]*int8 { + ps := make(map[string]*int8, len(vs)) + for k, v := range vs { + vv := v + ps[k] = &vv + } + + return ps +} + +// Int16 returns a pointer value for the int16 value passed in. +func Int16(v int16) *int16 { + return &v +} + +// Int16Slice returns a slice of int16 pointers from the values +// passed in. +func Int16Slice(vs []int16) []*int16 { + ps := make([]*int16, len(vs)) + for i, v := range vs { + vv := v + ps[i] = &vv + } + + return ps +} + +// Int16Map returns a map of int16 pointers from the values +// passed in. +func Int16Map(vs map[string]int16) map[string]*int16 { + ps := make(map[string]*int16, len(vs)) + for k, v := range vs { + vv := v + ps[k] = &vv + } + + return ps +} + +// Int32 returns a pointer value for the int32 value passed in. +func Int32(v int32) *int32 { + return &v +} + +// Int32Slice returns a slice of int32 pointers from the values +// passed in. +func Int32Slice(vs []int32) []*int32 { + ps := make([]*int32, len(vs)) + for i, v := range vs { + vv := v + ps[i] = &vv + } + + return ps +} + +// Int32Map returns a map of int32 pointers from the values +// passed in. +func Int32Map(vs map[string]int32) map[string]*int32 { + ps := make(map[string]*int32, len(vs)) + for k, v := range vs { + vv := v + ps[k] = &vv + } + + return ps +} + +// Int64 returns a pointer value for the int64 value passed in. +func Int64(v int64) *int64 { + return &v +} + +// Int64Slice returns a slice of int64 pointers from the values +// passed in. +func Int64Slice(vs []int64) []*int64 { + ps := make([]*int64, len(vs)) + for i, v := range vs { + vv := v + ps[i] = &vv + } + + return ps +} + +// Int64Map returns a map of int64 pointers from the values +// passed in. +func Int64Map(vs map[string]int64) map[string]*int64 { + ps := make(map[string]*int64, len(vs)) + for k, v := range vs { + vv := v + ps[k] = &vv + } + + return ps +} + +// Uint returns a pointer value for the uint value passed in. +func Uint(v uint) *uint { + return &v +} + +// UintSlice returns a slice of uint pointers from the values +// passed in. +func UintSlice(vs []uint) []*uint { + ps := make([]*uint, len(vs)) + for i, v := range vs { + vv := v + ps[i] = &vv + } + + return ps +} + +// UintMap returns a map of uint pointers from the values +// passed in. +func UintMap(vs map[string]uint) map[string]*uint { + ps := make(map[string]*uint, len(vs)) + for k, v := range vs { + vv := v + ps[k] = &vv + } + + return ps +} + +// Uint8 returns a pointer value for the uint8 value passed in. +func Uint8(v uint8) *uint8 { + return &v +} + +// Uint8Slice returns a slice of uint8 pointers from the values +// passed in. +func Uint8Slice(vs []uint8) []*uint8 { + ps := make([]*uint8, len(vs)) + for i, v := range vs { + vv := v + ps[i] = &vv + } + + return ps +} + +// Uint8Map returns a map of uint8 pointers from the values +// passed in. +func Uint8Map(vs map[string]uint8) map[string]*uint8 { + ps := make(map[string]*uint8, len(vs)) + for k, v := range vs { + vv := v + ps[k] = &vv + } + + return ps +} + +// Uint16 returns a pointer value for the uint16 value passed in. +func Uint16(v uint16) *uint16 { + return &v +} + +// Uint16Slice returns a slice of uint16 pointers from the values +// passed in. +func Uint16Slice(vs []uint16) []*uint16 { + ps := make([]*uint16, len(vs)) + for i, v := range vs { + vv := v + ps[i] = &vv + } + + return ps +} + +// Uint16Map returns a map of uint16 pointers from the values +// passed in. +func Uint16Map(vs map[string]uint16) map[string]*uint16 { + ps := make(map[string]*uint16, len(vs)) + for k, v := range vs { + vv := v + ps[k] = &vv + } + + return ps +} + +// Uint32 returns a pointer value for the uint32 value passed in. +func Uint32(v uint32) *uint32 { + return &v +} + +// Uint32Slice returns a slice of uint32 pointers from the values +// passed in. +func Uint32Slice(vs []uint32) []*uint32 { + ps := make([]*uint32, len(vs)) + for i, v := range vs { + vv := v + ps[i] = &vv + } + + return ps +} + +// Uint32Map returns a map of uint32 pointers from the values +// passed in. +func Uint32Map(vs map[string]uint32) map[string]*uint32 { + ps := make(map[string]*uint32, len(vs)) + for k, v := range vs { + vv := v + ps[k] = &vv + } + + return ps +} + +// Uint64 returns a pointer value for the uint64 value passed in. +func Uint64(v uint64) *uint64 { + return &v +} + +// Uint64Slice returns a slice of uint64 pointers from the values +// passed in. +func Uint64Slice(vs []uint64) []*uint64 { + ps := make([]*uint64, len(vs)) + for i, v := range vs { + vv := v + ps[i] = &vv + } + + return ps +} + +// Uint64Map returns a map of uint64 pointers from the values +// passed in. +func Uint64Map(vs map[string]uint64) map[string]*uint64 { + ps := make(map[string]*uint64, len(vs)) + for k, v := range vs { + vv := v + ps[k] = &vv + } + + return ps +} + +// Float32 returns a pointer value for the float32 value passed in. +func Float32(v float32) *float32 { + return &v +} + +// Float32Slice returns a slice of float32 pointers from the values +// passed in. +func Float32Slice(vs []float32) []*float32 { + ps := make([]*float32, len(vs)) + for i, v := range vs { + vv := v + ps[i] = &vv + } + + return ps +} + +// Float32Map returns a map of float32 pointers from the values +// passed in. +func Float32Map(vs map[string]float32) map[string]*float32 { + ps := make(map[string]*float32, len(vs)) + for k, v := range vs { + vv := v + ps[k] = &vv + } + + return ps +} + +// Float64 returns a pointer value for the float64 value passed in. +func Float64(v float64) *float64 { + return &v +} + +// Float64Slice returns a slice of float64 pointers from the values +// passed in. +func Float64Slice(vs []float64) []*float64 { + ps := make([]*float64, len(vs)) + for i, v := range vs { + vv := v + ps[i] = &vv + } + + return ps +} + +// Float64Map returns a map of float64 pointers from the values +// passed in. +func Float64Map(vs map[string]float64) map[string]*float64 { + ps := make(map[string]*float64, len(vs)) + for k, v := range vs { + vv := v + ps[k] = &vv + } + + return ps +} + +// Time returns a pointer value for the time.Time value passed in. +func Time(v time.Time) *time.Time { + return &v +} + +// TimeSlice returns a slice of time.Time pointers from the values +// passed in. +func TimeSlice(vs []time.Time) []*time.Time { + ps := make([]*time.Time, len(vs)) + for i, v := range vs { + vv := v + ps[i] = &vv + } + + return ps +} + +// TimeMap returns a map of time.Time pointers from the values +// passed in. +func TimeMap(vs map[string]time.Time) map[string]*time.Time { + ps := make(map[string]*time.Time, len(vs)) + for k, v := range vs { + vv := v + ps[k] = &vv + } + + return ps +} |