github.com/kata-containers/runtime@v0.0.0-20210505125100-04f29832a923/virtcontainers/utils/utils.go (about) 1 // Copyright (c) 2017 Intel Corporation 2 // 3 // SPDX-License-Identifier: Apache-2.0 4 // 5 6 package utils 7 8 import ( 9 "crypto/rand" 10 "errors" 11 "fmt" 12 "io" 13 "os" 14 "os/exec" 15 "path/filepath" 16 ) 17 18 const cpBinaryName = "cp" 19 20 const fileMode0755 = os.FileMode(0755) 21 22 // MibToBytesShift the number to shift needed to convert MiB to Bytes 23 const MibToBytesShift = 20 24 25 // MaxSocketPathLen is the effective maximum Unix domain socket length. 26 // 27 // See unix(7). 28 const MaxSocketPathLen = 107 29 30 // VHostVSockDevicePath path to vhost-vsock device 31 var VHostVSockDevicePath = "/dev/vhost-vsock" 32 33 // FileCopy copys files from srcPath to dstPath 34 func FileCopy(srcPath, dstPath string) error { 35 if srcPath == "" { 36 return fmt.Errorf("Source path cannot be empty") 37 } 38 39 if dstPath == "" { 40 return fmt.Errorf("Destination path cannot be empty") 41 } 42 43 binPath, err := exec.LookPath(cpBinaryName) 44 if err != nil { 45 return err 46 } 47 48 cmd := exec.Command(binPath, srcPath, dstPath) 49 50 return cmd.Run() 51 } 52 53 // GenerateRandomBytes generate n random bytes 54 func GenerateRandomBytes(n int) ([]byte, error) { 55 b := make([]byte, n) 56 _, err := rand.Read(b) 57 58 if err != nil { 59 return nil, err 60 } 61 62 return b, nil 63 } 64 65 // ReverseString reverses whole string 66 func ReverseString(s string) string { 67 r := []rune(s) 68 69 length := len(r) 70 for i, j := 0, length-1; i < length/2; i, j = i+1, j-1 { 71 r[i], r[j] = r[j], r[i] 72 } 73 74 return string(r) 75 } 76 77 // CleanupFds closed bundles of open fds in batch 78 func CleanupFds(fds []*os.File, numFds int) { 79 maxFds := len(fds) 80 81 if numFds < maxFds { 82 maxFds = numFds 83 } 84 85 for i := 0; i < maxFds; i++ { 86 _ = fds[i].Close() 87 } 88 } 89 90 // WriteToFile opens a file in write only mode and writes bytes to it 91 func WriteToFile(path string, data []byte) error { 92 f, err := os.OpenFile(path, os.O_WRONLY, fileMode0755) 93 if err != nil { 94 return err 95 } 96 97 defer f.Close() 98 99 if _, err := f.Write(data); err != nil { 100 return err 101 } 102 103 return nil 104 } 105 106 //CalculateMilliCPUs converts CPU quota and period to milli-CPUs 107 func CalculateMilliCPUs(quota int64, period uint64) uint32 { 108 109 // If quota is -1, it means the CPU resource request is 110 // unconstrained. In that case, we don't currently assign 111 // additional CPUs. 112 if quota >= 0 && period != 0 { 113 return uint32((uint64(quota) * 1000) / period) 114 } 115 116 return 0 117 } 118 119 //CalculateVCpusFromMilliCpus converts from mCPU to CPU, taking the ceiling 120 // value when necessary 121 func CalculateVCpusFromMilliCpus(mCPU uint32) uint32 { 122 return (mCPU + 999) / 1000 123 } 124 125 // ConstraintsToVCPUs converts CPU quota and period to vCPUs 126 func ConstraintsToVCPUs(quota int64, period uint64) uint { 127 if quota != 0 && period != 0 { 128 // Use some math magic to round up to the nearest whole vCPU 129 // (that is, a partial part of a quota request ends up assigning 130 // a whole vCPU, for instance, a request of 1.5 'cpu quotas' 131 // will give 2 vCPUs). 132 // This also has the side effect that we will always allocate 133 // at least 1 vCPU. 134 return uint((uint64(quota) + (period - 1)) / period) 135 } 136 137 return 0 138 } 139 140 // GetVirtDriveName returns the disk name format for virtio-blk 141 // Reference: https://github.com/torvalds/linux/blob/master/drivers/block/virtio_blk.c @c0aa3e0916d7e531e69b02e426f7162dfb1c6c0 142 func GetVirtDriveName(index int) (string, error) { 143 if index < 0 { 144 return "", fmt.Errorf("Index cannot be negative for drive") 145 } 146 147 // Prefix used for virtio-block devices 148 const prefix = "vd" 149 150 //Refer to DISK_NAME_LEN: https://github.com/torvalds/linux/blob/08c521a2011ff492490aa9ed6cc574be4235ce2b/include/linux/genhd.h#L61 151 diskNameLen := 32 152 base := 26 153 154 suffLen := diskNameLen - len(prefix) 155 diskLetters := make([]byte, suffLen) 156 157 var i int 158 159 for i = 0; i < suffLen && index >= 0; i++ { 160 letter := byte('a' + (index % base)) 161 diskLetters[i] = letter 162 index = index/base - 1 163 } 164 165 if index >= 0 { 166 return "", fmt.Errorf("Index not supported") 167 } 168 169 diskName := prefix + ReverseString(string(diskLetters[:i])) 170 return diskName, nil 171 } 172 173 const maxSCSIDevices = 65535 174 175 // GetSCSIIdLun gets the SCSI id and lun, based on the index of the drive being inserted. 176 // qemu code suggests that scsi-id can take values from 0 to 255 inclusive, while lun can 177 // take values from 0 to 16383 inclusive. But lun values over 255 do not seem to follow 178 // consistent SCSI addressing. Hence we limit to 255. 179 func GetSCSIIdLun(index int) (int, int, error) { 180 if index < 0 { 181 return -1, -1, fmt.Errorf("Index cannot be negative") 182 } 183 184 if index > maxSCSIDevices { 185 return -1, -1, fmt.Errorf("Index cannot be greater than %d, maximum of %d devices are supported", maxSCSIDevices, maxSCSIDevices) 186 } 187 188 return index / 256, index % 256, nil 189 } 190 191 // GetSCSIAddress gets scsiID and lun from index, and combined them into a scsi ID 192 func GetSCSIAddress(index int) (string, error) { 193 scsiID, lun, err := GetSCSIIdLun(index) 194 if err != nil { 195 return "", err 196 } 197 198 return fmt.Sprintf("%d:%d", scsiID, lun), nil 199 } 200 201 // MakeNameID is generic function for creating a named-id for passing on the hypervisor commandline 202 func MakeNameID(namedType, id string, maxLen int) string { 203 nameID := fmt.Sprintf("%s-%s", namedType, id) 204 if len(nameID) > maxLen { 205 nameID = nameID[:maxLen] 206 } 207 208 return nameID 209 } 210 211 // BuildSocketPath concatenates the provided elements into a path and returns 212 // it. If the resulting path is longer than the maximum permitted socket path 213 // on Linux, it will return an error. 214 func BuildSocketPath(elements ...string) (string, error) { 215 result := filepath.Join(elements...) 216 217 if result == "" { 218 return "", errors.New("empty path") 219 } 220 221 l := len(result) 222 223 if l > MaxSocketPathLen { 224 return "", fmt.Errorf("path too long (got %v, max %v): %s", l, MaxSocketPathLen, result) 225 } 226 227 return result, nil 228 } 229 230 // SupportsVsocks returns true if vsocks are supported, otherwise false 231 func SupportsVsocks() bool { 232 if _, err := os.Stat(VHostVSockDevicePath); err != nil { 233 return false 234 } 235 236 return true 237 } 238 239 // StartCmd pointer to a function to start a command. 240 // Defined this way to allow mock testing. 241 var StartCmd = func(c *exec.Cmd) error { 242 return c.Start() 243 } 244 245 // AlignMem align memory provided to a block size 246 func (m MemUnit) AlignMem(blockSize MemUnit) MemUnit { 247 memSize := m 248 if m < blockSize { 249 memSize = blockSize 250 251 } 252 253 remainder := memSize % blockSize 254 255 if remainder != 0 { 256 // Align memory to memoryBlockSizeMB 257 memSize += blockSize - remainder 258 259 } 260 return memSize 261 } 262 263 type MemUnit uint64 264 265 func (m MemUnit) ToMiB() uint64 { 266 return m.ToBytes() / (1 * MiB).ToBytes() 267 } 268 269 func (m MemUnit) ToBytes() uint64 { 270 return uint64(m) 271 } 272 273 const ( 274 Byte MemUnit = 1 275 KiB = Byte << 10 276 MiB = KiB << 10 277 GiB = MiB << 10 278 ) 279 280 // Binary to use to log program output 281 const LoggerBinaryName = "systemd-cat" 282 283 type ProgramLogger struct { 284 cmd *exec.Cmd 285 } 286 287 func NewProgramLogger(loggerLabel string) ProgramLogger { 288 return ProgramLogger{cmd: exec.Command(LoggerBinaryName, "-t", loggerLabel)} 289 } 290 291 func (p *ProgramLogger) StartLogger(output io.ReadCloser) error { 292 p.cmd.Stdin = output 293 return StartCmd(p.cmd) 294 } 295 296 func (p ProgramLogger) String() string { 297 return p.cmd.Path 298 }