...

Source file src/github.com/Microsoft/hcsshim/internal/guest/runtime/runc/utils.go

Documentation: github.com/Microsoft/hcsshim/internal/guest/runtime/runc

     1  //go:build linux
     2  // +build linux
     3  
     4  package runc
     5  
     6  import (
     7  	"encoding/json"
     8  	"os"
     9  	"os/exec"
    10  	"path/filepath"
    11  	"strconv"
    12  	"strings"
    13  	"syscall"
    14  
    15  	"github.com/pkg/errors"
    16  	"github.com/sirupsen/logrus"
    17  
    18  	"github.com/Microsoft/hcsshim/internal/guest/runtime"
    19  )
    20  
    21  // readPidFile reads the integer pid stored in the given file.
    22  func (r *runcRuntime) readPidFile(pidFile string) (pid int, err error) {
    23  	data, err := os.ReadFile(pidFile)
    24  	if err != nil {
    25  		return -1, errors.Wrap(err, "failed reading from pid file")
    26  	}
    27  	pid, err = strconv.Atoi(string(data))
    28  	if err != nil {
    29  		return -1, errors.Wrapf(err, "failed converting pid text %q to integer form", data)
    30  	}
    31  	return pid, nil
    32  }
    33  
    34  // cleanupContainer cleans up any state left behind by the container.
    35  func (r *runcRuntime) cleanupContainer(id string) error {
    36  	containerDir := r.getContainerDir(id)
    37  	if err := os.RemoveAll(containerDir); err != nil {
    38  		return errors.Wrapf(err, "failed removing the container directory for container %s", id)
    39  	}
    40  	return nil
    41  }
    42  
    43  // cleanupProcess cleans up any state left behind by the process.
    44  func (r *runcRuntime) cleanupProcess(id string, pid int) error {
    45  	processDir := r.getProcessDir(id, pid)
    46  	if err := os.RemoveAll(processDir); err != nil {
    47  		return errors.Wrapf(err, "failed removing the process directory for process %d in container %s", pid, id)
    48  	}
    49  	return nil
    50  }
    51  
    52  // getProcessDir returns the path to the state directory of the given process.
    53  func (r *runcRuntime) getProcessDir(id string, pid int) string {
    54  	containerDir := r.getContainerDir(id)
    55  	return filepath.Join(containerDir, strconv.Itoa(pid))
    56  }
    57  
    58  // getContainerDir returns the path to the state directory of the given
    59  // container.
    60  func (*runcRuntime) getContainerDir(id string) string {
    61  	return filepath.Join(containerFilesDir, id)
    62  }
    63  
    64  // makeContainerDir creates the state directory for the given container.
    65  func (r *runcRuntime) makeContainerDir(id string) error {
    66  	dir := r.getContainerDir(id)
    67  	if err := os.MkdirAll(dir, os.ModeDir); err != nil {
    68  		return errors.Wrapf(err, "failed making container directory for container %s", id)
    69  	}
    70  	return nil
    71  }
    72  
    73  // getLogDir gets the path to the runc logs directory.
    74  func (r *runcRuntime) getLogDir(id string) string {
    75  	return filepath.Join(r.runcLogBasePath, id)
    76  }
    77  
    78  // makeLogDir creates the runc logs directory if it doesnt exist.
    79  func (r *runcRuntime) makeLogDir(id string) error {
    80  	dir := r.getLogDir(id)
    81  	if err := os.MkdirAll(dir, os.ModeDir); err != nil {
    82  		return errors.Wrapf(err, "failed making runc log directory for container %s", id)
    83  	}
    84  	return nil
    85  }
    86  
    87  // getLogPath returns the path to the log file used by the runC wrapper for a particular container
    88  func (r *runcRuntime) getLogPath(id string) string {
    89  	return filepath.Join(r.getLogDir(id), "runc.log")
    90  }
    91  
    92  // getLogPath returns the path to the log file used by the runC wrapper.
    93  //
    94  //nolint:unused
    95  func (r *runcRuntime) getGlobalLogPath() string {
    96  	// runcLogBasePath should be created by r.initialize
    97  	return filepath.Join(r.runcLogBasePath, "global-runc.log")
    98  }
    99  
   100  // processExists returns true if the given process exists in /proc, false if
   101  // not.
   102  // It should be noted that processes which have exited, but have not yet been
   103  // waited on (i.e. zombies) are still considered to exist by this function.
   104  func (*runcRuntime) processExists(pid int) bool {
   105  	_, err := os.Stat(filepath.Join("/proc", strconv.Itoa(pid)))
   106  	return !os.IsNotExist(err)
   107  }
   108  
   109  // todo (helsaawy): create `type runcCmd struct` that wraps an exec.Command, runs it and parses error from
   110  // log file or std err (similar to containerd/libcni)
   111  
   112  type standardLogEntry struct {
   113  	Level   logrus.Level `json:"level"`
   114  	Message string       `json:"msg"`
   115  	Err     error        `json:"error,omitempty"`
   116  }
   117  
   118  func (l *standardLogEntry) asError() (err error) {
   119  	err = parseRuncError(l.Message)
   120  	if l.Err != nil {
   121  		err = errors.Wrapf(err, l.Err.Error())
   122  	}
   123  	return
   124  }
   125  
   126  func parseRuncError(s string) (err error) {
   127  	// TODO (helsaawy): match with errors from
   128  	// https://github.com/opencontainers/runc/blob/master/libcontainer/error.go
   129  	if strings.HasPrefix(s, "container") && strings.HasSuffix(s, "does not exist") {
   130  		// currently: "container <container id> does not exist"
   131  		err = runtime.ErrContainerDoesNotExist
   132  	} else if strings.Contains(s, "container with id exists") ||
   133  		strings.Contains(s, "container with given ID already exists") {
   134  		err = runtime.ErrContainerAlreadyExists
   135  	} else if strings.Contains(s, "invalid id format") ||
   136  		strings.Contains(s, "invalid container ID format") {
   137  		err = runtime.ErrInvalidContainerID
   138  	} else if strings.Contains(s, "container") &&
   139  		strings.Contains(s, "that is not stopped") {
   140  		err = runtime.ErrContainerNotStopped
   141  	} else {
   142  		err = errors.New(s)
   143  	}
   144  	return err
   145  }
   146  
   147  func getRuncLogError(logPath string) error {
   148  	reader, err := os.OpenFile(logPath, syscall.O_RDONLY, 0644)
   149  	if err != nil {
   150  		return nil
   151  	}
   152  	defer reader.Close()
   153  
   154  	var lastErr error
   155  	dec := json.NewDecoder(reader)
   156  	for {
   157  		entry := &standardLogEntry{}
   158  		if err := dec.Decode(entry); err != nil {
   159  			break
   160  		}
   161  		if entry.Level <= logrus.ErrorLevel {
   162  			lastErr = entry.asError()
   163  		}
   164  	}
   165  	return lastErr
   166  }
   167  
   168  func runcCommandLog(logPath string, args ...string) *exec.Cmd {
   169  	args = append([]string{"--log", logPath, "--log-format", "json"}, args...)
   170  	return runcCommand(args...)
   171  }
   172  
   173  func runcCommand(args ...string) *exec.Cmd {
   174  	return exec.Command("runc", args...)
   175  }
   176  

View as plain text