github.com/containers/podman/v2@v2.2.2-0.20210501105131-c1e07d070c4c/utils/utils.go (about)

     1  package utils
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  	"os"
     8  	"os/exec"
     9  	"strconv"
    10  	"strings"
    11  
    12  	"github.com/containers/podman/v2/libpod/define"
    13  	"github.com/containers/storage/pkg/archive"
    14  	"github.com/pkg/errors"
    15  	"github.com/sirupsen/logrus"
    16  )
    17  
    18  // ExecCmd executes a command with args and returns its output as a string along
    19  // with an error, if any
    20  func ExecCmd(name string, args ...string) (string, error) {
    21  	cmd := exec.Command(name, args...)
    22  	var stdout bytes.Buffer
    23  	var stderr bytes.Buffer
    24  	cmd.Stdout = &stdout
    25  	cmd.Stderr = &stderr
    26  
    27  	err := cmd.Run()
    28  	if err != nil {
    29  		return "", fmt.Errorf("`%v %v` failed: %v %v (%v)", name, strings.Join(args, " "), stderr.String(), stdout.String(), err)
    30  	}
    31  
    32  	return stdout.String(), nil
    33  }
    34  
    35  // ExecCmdWithStdStreams execute a command with the specified standard streams.
    36  func ExecCmdWithStdStreams(stdin io.Reader, stdout, stderr io.Writer, env []string, name string, args ...string) error {
    37  	cmd := exec.Command(name, args...)
    38  	cmd.Stdin = stdin
    39  	cmd.Stdout = stdout
    40  	cmd.Stderr = stderr
    41  	if env != nil {
    42  		cmd.Env = env
    43  	}
    44  
    45  	err := cmd.Run()
    46  	if err != nil {
    47  		return fmt.Errorf("`%v %v` failed: %v", name, strings.Join(args, " "), err)
    48  	}
    49  
    50  	return nil
    51  }
    52  
    53  // ErrDetach is an error indicating that the user manually detached from the
    54  // container.
    55  var ErrDetach = define.ErrDetach
    56  
    57  // CopyDetachable is similar to io.Copy but support a detach key sequence to break out.
    58  func CopyDetachable(dst io.Writer, src io.Reader, keys []byte) (written int64, err error) {
    59  	buf := make([]byte, 32*1024)
    60  	for {
    61  		nr, er := src.Read(buf)
    62  		if nr > 0 {
    63  			preservBuf := []byte{}
    64  			for i, key := range keys {
    65  				preservBuf = append(preservBuf, buf[0:nr]...)
    66  				if nr != 1 || buf[0] != key {
    67  					break
    68  				}
    69  				if i == len(keys)-1 {
    70  					return 0, ErrDetach
    71  				}
    72  				nr, er = src.Read(buf)
    73  			}
    74  			var nw int
    75  			var ew error
    76  			if len(preservBuf) > 0 {
    77  				nw, ew = dst.Write(preservBuf)
    78  				nr = len(preservBuf)
    79  			} else {
    80  				nw, ew = dst.Write(buf[0:nr])
    81  			}
    82  			if nw > 0 {
    83  				written += int64(nw)
    84  			}
    85  			if ew != nil {
    86  				err = ew
    87  				break
    88  			}
    89  			if nr != nw {
    90  				err = io.ErrShortWrite
    91  				break
    92  			}
    93  		}
    94  		if er != nil {
    95  			if er != io.EOF {
    96  				err = er
    97  			}
    98  			break
    99  		}
   100  	}
   101  	return written, err
   102  }
   103  
   104  // UntarToFileSystem untars an os.file of a tarball to a destination in the filesystem
   105  func UntarToFileSystem(dest string, tarball *os.File, options *archive.TarOptions) error {
   106  	logrus.Debugf("untarring %s", tarball.Name())
   107  	return archive.Untar(tarball, dest, options)
   108  }
   109  
   110  // TarToFilesystem creates a tarball from source and writes to an os.file
   111  // provided
   112  func TarToFilesystem(source string, tarball *os.File) error {
   113  	tb, err := Tar(source)
   114  	if err != nil {
   115  		return err
   116  	}
   117  	_, err = io.Copy(tarball, tb)
   118  	if err != nil {
   119  		return err
   120  	}
   121  	logrus.Debugf("wrote tarball file %s", tarball.Name())
   122  	return nil
   123  }
   124  
   125  // Tar creates a tarball from source and returns a readcloser of it
   126  func Tar(source string) (io.ReadCloser, error) {
   127  	logrus.Debugf("creating tarball of %s", source)
   128  	return archive.Tar(source, archive.Uncompressed)
   129  }
   130  
   131  // RemoveScientificNotationFromFloat returns a float without any
   132  // scientific notation if the number has any.
   133  // golang does not handle conversion of float64s that have scientific
   134  // notation in them and otherwise stinks.  please replace this if you have
   135  // a better implementation.
   136  func RemoveScientificNotationFromFloat(x float64) (float64, error) {
   137  	bigNum := strconv.FormatFloat(x, 'g', -1, 64)
   138  	breakPoint := strings.IndexAny(bigNum, "Ee")
   139  	if breakPoint > 0 {
   140  		bigNum = bigNum[:breakPoint]
   141  	}
   142  	result, err := strconv.ParseFloat(bigNum, 64)
   143  	if err != nil {
   144  		return x, errors.Wrapf(err, "unable to remove scientific number from calculations")
   145  	}
   146  	return result, nil
   147  }