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  }