github.com/mier85/go-sensor@v1.30.1-0.20220920111756-9bf41b3bc7e0/util.go (about) 1 // (c) Copyright IBM Corp. 2021 2 // (c) Copyright Instana Inc. 2016 3 4 package instana 5 6 import ( 7 "bufio" 8 "bytes" 9 "encoding/binary" 10 "errors" 11 "fmt" 12 "io/ioutil" 13 "math/rand" 14 "os" 15 "strconv" 16 "strings" 17 "sync" 18 "time" 19 ) 20 21 var ( 22 seededIDGen = rand.New(rand.NewSource(time.Now().UnixNano())) 23 seededIDLock sync.Mutex 24 ) 25 26 func randomID() int64 { 27 seededIDLock.Lock() 28 defer seededIDLock.Unlock() 29 return int64(seededIDGen.Int63()) 30 } 31 32 // FormatID converts an Instana ID to a value that can be used in 33 // context propagation (such as HTTP headers). More specifically, 34 // this converts a signed 64 bit integer into an unsigned hex string. 35 // The resulting string is always padded with 0 to be 16 characters long. 36 func FormatID(id int64) string { 37 // FIXME: We're assuming LittleEndian here 38 39 // Convert uint64 to hex string equivalent and return that 40 return padHexString(strconv.FormatUint(uint64(id), 16), 64) 41 } 42 43 // FormatLongID converts a 128-bit Instana ID passed in two quad words to an 44 // unsigned hex string suitable for context propagation. 45 func FormatLongID(hi, lo int64) string { 46 return FormatID(hi) + FormatID(lo) 47 } 48 49 func padHexString(s string, bitSize int) string { 50 if len(s) >= bitSize>>2 { 51 return s 52 } 53 54 return strings.Repeat("0", bitSize>>2-len(s)) + s 55 } 56 57 // ParseID converts an header context value into an Instana ID. More 58 // specifically, this converts an unsigned 64 bit hex value into a signed 59 // 64bit integer. 60 func ParseID(header string) (int64, error) { 61 // FIXME: We're assuming LittleEndian here 62 63 // Parse unsigned 64 bit hex string into unsigned 64 bit base 10 integer 64 unsignedID, err := strconv.ParseUint(header, 16, 64) 65 if err != nil { 66 return 0, fmt.Errorf("context corrupted; could not convert value: %s", err) 67 } 68 69 // Write out _unsigned_ 64bit integer to byte buffer 70 buf := bytes.NewBuffer(nil) 71 if err := binary.Write(buf, binary.LittleEndian, unsignedID); err != nil { 72 return 0, fmt.Errorf("context corrupted; could not convert value: %s", err) 73 } 74 75 // Read bytes back into _signed_ 64 bit integer 76 var signedID int64 77 if err := binary.Read(buf, binary.LittleEndian, &signedID); err != nil { 78 return 0, fmt.Errorf("context corrupted; could not convert value: %s", err) 79 } 80 81 return signedID, nil 82 } 83 84 // ParseLongID converts an header context value into a 128-bit Instana ID. Both high and low 85 // quad words are returned as signed integers. 86 func ParseLongID(header string) (hi int64, lo int64, err error) { 87 if len(header) > 16 { 88 hi, err = ParseID(header[:len(header)-16]) 89 if err != nil { 90 return 0, 0, fmt.Errorf("failed to parse the higher 4 bytes of a 128-bit integer: %s", err) 91 } 92 93 header = header[len(header)-16:] 94 } 95 96 lo, err = ParseID(header) 97 98 return hi, lo, err 99 } 100 101 // ID2Header calls instana.FormatID() and returns its result and a nil error. 102 // This is kept here for backward compatibility with go-sensor@v1.x 103 // 104 // Deprecated: please use instana.FormatID() instead 105 func ID2Header(id int64) (string, error) { 106 return FormatID(id), nil 107 } 108 109 // Header2ID calls instana.ParseID() and returns its result. 110 // This is kept here for backward compatibility with go-sensor@v1.x 111 // 112 // Deprecated: please use instana.ParseID() instead 113 func Header2ID(header string) (int64, error) { 114 return ParseID(header) 115 } 116 117 func getProcCommandLine() (string, []string, bool) { 118 var cmdlinePath string = "/proc/" + strconv.Itoa(os.Getpid()) + "/cmdline" 119 120 cmdline, err := ioutil.ReadFile(cmdlinePath) 121 if err != nil { 122 return "", nil, false 123 } 124 125 parts := strings.FieldsFunc(string(cmdline), func(c rune) bool { 126 return c == '\u0000' 127 }) 128 129 return parts[0], parts[1:], true 130 } 131 132 func getProcessEnv() map[string]string { 133 osEnv := os.Environ() 134 135 env := make(map[string]string, len(osEnv)) 136 for _, envVar := range osEnv { 137 idx := strings.Index(envVar, "=") 138 if idx < 0 { 139 continue 140 } 141 142 env[envVar[:idx]] = envVar[idx+1:] 143 } 144 145 return env 146 } 147 148 func getDefaultGateway(routeTableFile string) (string, error) { 149 routeTable, err := os.Open(routeTableFile) 150 if err != nil { 151 return "", fmt.Errorf("failed to open %s: %s", routeTableFile, err) 152 } 153 defer routeTable.Close() 154 155 s := bufio.NewScanner(routeTable) 156 for s.Scan() { 157 entry := strings.Split(s.Text(), "\t") 158 if len(entry) < 3 { 159 continue 160 } 161 162 destination := entry[1] 163 if destination == "00000000" { 164 gatewayHex := []rune(entry[2]) 165 166 gateway, err := hexGatewayToAddr(gatewayHex) 167 if err != nil { 168 return "", err 169 } 170 171 return gateway, nil 172 } 173 } 174 175 if err := s.Err(); err != nil { 176 return "", fmt.Errorf("failed to read %s: %s", routeTableFile, err) 177 } 178 179 return "", nil 180 } 181 182 // hexGatewayToAddr converts the hex representation of the gateway address to string. 183 func hexGatewayToAddr(gateway []rune) (string, error) { 184 // gateway address is encoded in reverse order in hex 185 if len(gateway) != 8 { 186 return "", errors.New("invalid gateway length") 187 } 188 189 var octets [4]uint8 190 for i, hexOctet := range [4]string{ 191 string(gateway[6:8]), // first octet of IP Address 192 string(gateway[4:6]), // second octet 193 string(gateway[2:4]), // third octet 194 string(gateway[0:2]), // last octet 195 } { 196 octet, err := strconv.ParseUint(hexOctet, 16, 8) 197 if err != nil { 198 return "", err 199 } 200 201 octets[i] = uint8(octet) 202 } 203 204 return fmt.Sprintf("%v.%v.%v.%v", octets[0], octets[1], octets[2], octets[3]), nil 205 }