summaryrefslogtreecommitdiff
path: root/utils
diff options
context:
space:
mode:
authorValery Piashchynski <[email protected]>2021-12-26 00:50:30 +0300
committerValery Piashchynski <[email protected]>2021-12-26 00:50:30 +0300
commitb27b2a1c9030f38e729e6e2d411379047c28402e (patch)
tree129d9d1fd1d2803712fa4b0f05d5cfbf466d10e3 /utils
parent9cbb6be27ca0bd56eaa6db9a875830a8ce6110e8 (diff)
implement common logger
Signed-off-by: Valery Piashchynski <[email protected]>
Diffstat (limited to 'utils')
-rw-r--r--utils/convert.go25
-rwxr-xr-xutils/isolate.go60
-rwxr-xr-xutils/isolate_win.go18
-rwxr-xr-xutils/network.go108
-rwxr-xr-xutils/network_windows.go64
-rw-r--r--utils/race_checker.go35
-rw-r--r--utils/race_checker_unsafe.go5
-rw-r--r--utils/to_ptr.go467
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
+}