github.com/containers/libpod@v1.9.4-0.20220419124438-4284fd425507/pkg/varlinkapi/containers.go (about)

     1  // +build varlink
     2  
     3  package varlinkapi
     4  
     5  import (
     6  	"bufio"
     7  	"encoding/json"
     8  	"fmt"
     9  	"io"
    10  	"io/ioutil"
    11  	"os"
    12  	"strings"
    13  	"sync"
    14  	"syscall"
    15  	"time"
    16  
    17  	"github.com/containers/libpod/cmd/podman/shared"
    18  	"github.com/containers/libpod/libpod"
    19  	"github.com/containers/libpod/libpod/define"
    20  	"github.com/containers/libpod/libpod/logs"
    21  	"github.com/containers/libpod/pkg/adapter/shortcuts"
    22  	"github.com/containers/libpod/pkg/cgroups"
    23  	"github.com/containers/libpod/pkg/rootless"
    24  	iopodman "github.com/containers/libpod/pkg/varlink"
    25  	"github.com/containers/libpod/pkg/varlinkapi/virtwriter"
    26  	"github.com/containers/storage/pkg/archive"
    27  	"github.com/pkg/errors"
    28  	"github.com/sirupsen/logrus"
    29  	"k8s.io/client-go/tools/remotecommand"
    30  )
    31  
    32  // ListContainers ...
    33  func (i *VarlinkAPI) ListContainers(call iopodman.VarlinkCall) error {
    34  	var (
    35  		listContainers []iopodman.Container
    36  	)
    37  
    38  	containers, err := i.Runtime.GetAllContainers()
    39  	if err != nil {
    40  		return call.ReplyErrorOccurred(err.Error())
    41  	}
    42  	opts := shared.PsOptions{
    43  		Namespace: true,
    44  		Size:      true,
    45  	}
    46  	for _, ctr := range containers {
    47  		batchInfo, err := shared.BatchContainerOp(ctr, opts)
    48  		if err != nil {
    49  			return call.ReplyErrorOccurred(err.Error())
    50  		}
    51  
    52  		listContainers = append(listContainers, makeListContainer(ctr.ID(), batchInfo))
    53  	}
    54  	return call.ReplyListContainers(listContainers)
    55  }
    56  
    57  func (i *VarlinkAPI) Ps(call iopodman.VarlinkCall, opts iopodman.PsOpts) error {
    58  	var (
    59  		containers []iopodman.PsContainer
    60  	)
    61  	maxWorkers := shared.Parallelize("ps")
    62  	psOpts := makePsOpts(opts)
    63  	filters := []string{}
    64  	if opts.Filters != nil {
    65  		filters = *opts.Filters
    66  	}
    67  	psContainerOutputs, err := shared.GetPsContainerOutput(i.Runtime, psOpts, filters, maxWorkers)
    68  	if err != nil {
    69  		return call.ReplyErrorOccurred(err.Error())
    70  	}
    71  
    72  	for _, ctr := range psContainerOutputs {
    73  		container := iopodman.PsContainer{
    74  			Id:        ctr.ID,
    75  			Image:     ctr.Image,
    76  			Command:   ctr.Command,
    77  			Created:   ctr.Created,
    78  			Ports:     ctr.Ports,
    79  			Names:     ctr.Names,
    80  			IsInfra:   ctr.IsInfra,
    81  			Status:    ctr.Status,
    82  			State:     ctr.State.String(),
    83  			PidNum:    int64(ctr.Pid),
    84  			Pod:       ctr.Pod,
    85  			CreatedAt: ctr.CreatedAt.Format(time.RFC3339Nano),
    86  			ExitedAt:  ctr.ExitedAt.Format(time.RFC3339Nano),
    87  			StartedAt: ctr.StartedAt.Format(time.RFC3339Nano),
    88  			Labels:    ctr.Labels,
    89  			NsPid:     ctr.PID,
    90  			Cgroup:    ctr.Cgroup,
    91  			Ipc:       ctr.Cgroup,
    92  			Mnt:       ctr.MNT,
    93  			Net:       ctr.NET,
    94  			PidNs:     ctr.PIDNS,
    95  			User:      ctr.User,
    96  			Uts:       ctr.UTS,
    97  			Mounts:    ctr.Mounts,
    98  		}
    99  		if ctr.Size != nil {
   100  			container.RootFsSize = ctr.Size.RootFsSize
   101  			container.RwSize = ctr.Size.RwSize
   102  		}
   103  		containers = append(containers, container)
   104  	}
   105  	return call.ReplyPs(containers)
   106  }
   107  
   108  // GetContainer ...
   109  func (i *VarlinkAPI) GetContainer(call iopodman.VarlinkCall, id string) error {
   110  	ctr, err := i.Runtime.LookupContainer(id)
   111  	if err != nil {
   112  		return call.ReplyContainerNotFound(id, err.Error())
   113  	}
   114  	opts := shared.PsOptions{
   115  		Namespace: true,
   116  		Size:      true,
   117  	}
   118  	batchInfo, err := shared.BatchContainerOp(ctr, opts)
   119  	if err != nil {
   120  		return call.ReplyErrorOccurred(err.Error())
   121  	}
   122  	return call.ReplyGetContainer(makeListContainer(ctr.ID(), batchInfo))
   123  }
   124  
   125  // GetContainersByContext returns a slice of container ids based on all, latest, or a list
   126  func (i *VarlinkAPI) GetContainersByContext(call iopodman.VarlinkCall, all, latest bool, input []string) error {
   127  	var ids []string
   128  
   129  	ctrs, err := shortcuts.GetContainersByContext(all, latest, input, i.Runtime)
   130  	if err != nil {
   131  		if errors.Cause(err) == define.ErrNoSuchCtr {
   132  			return call.ReplyContainerNotFound("", err.Error())
   133  		}
   134  		return call.ReplyErrorOccurred(err.Error())
   135  	}
   136  
   137  	for _, c := range ctrs {
   138  		ids = append(ids, c.ID())
   139  	}
   140  	return call.ReplyGetContainersByContext(ids)
   141  }
   142  
   143  // GetContainersByStatus returns a slice of containers filtered by a libpod status
   144  func (i *VarlinkAPI) GetContainersByStatus(call iopodman.VarlinkCall, statuses []string) error {
   145  	var (
   146  		filterFuncs []libpod.ContainerFilter
   147  		containers  []iopodman.Container
   148  	)
   149  	for _, status := range statuses {
   150  		lpstatus, err := define.StringToContainerStatus(status)
   151  		if err != nil {
   152  			return call.ReplyErrorOccurred(err.Error())
   153  		}
   154  		filterFuncs = append(filterFuncs, func(c *libpod.Container) bool {
   155  			state, _ := c.State()
   156  			return state == lpstatus
   157  		})
   158  	}
   159  	filteredContainers, err := i.Runtime.GetContainers(filterFuncs...)
   160  	if err != nil {
   161  		return call.ReplyErrorOccurred(err.Error())
   162  	}
   163  	opts := shared.PsOptions{Size: true, Namespace: true}
   164  	for _, ctr := range filteredContainers {
   165  		batchInfo, err := shared.BatchContainerOp(ctr, opts)
   166  		if err != nil {
   167  			return call.ReplyErrorOccurred(err.Error())
   168  		}
   169  		containers = append(containers, makeListContainer(ctr.ID(), batchInfo))
   170  	}
   171  	return call.ReplyGetContainersByStatus(containers)
   172  }
   173  
   174  // InspectContainer ...
   175  func (i *VarlinkAPI) InspectContainer(call iopodman.VarlinkCall, name string) error {
   176  	ctr, err := i.Runtime.LookupContainer(name)
   177  	if err != nil {
   178  		return call.ReplyContainerNotFound(name, err.Error())
   179  	}
   180  	data, err := ctr.Inspect(true)
   181  	if err != nil {
   182  		return call.ReplyErrorOccurred(err.Error())
   183  	}
   184  	b, err := json.Marshal(data)
   185  	if err != nil {
   186  		return call.ReplyErrorOccurred(fmt.Sprintf("unable to serialize"))
   187  	}
   188  	return call.ReplyInspectContainer(string(b))
   189  }
   190  
   191  // ListContainerProcesses ...
   192  func (i *VarlinkAPI) ListContainerProcesses(call iopodman.VarlinkCall, name string, opts []string) error {
   193  	ctr, err := i.Runtime.LookupContainer(name)
   194  	if err != nil {
   195  		return call.ReplyContainerNotFound(name, err.Error())
   196  	}
   197  	containerState, err := ctr.State()
   198  	if err != nil {
   199  		return call.ReplyErrorOccurred(err.Error())
   200  	}
   201  	if containerState != define.ContainerStateRunning {
   202  		return call.ReplyErrorOccurred(fmt.Sprintf("container %s is not running", name))
   203  	}
   204  	var psArgs []string
   205  	psOpts := []string{"user", "pid", "ppid", "pcpu", "etime", "tty", "time", "comm"}
   206  	if len(opts) > 1 {
   207  		psOpts = opts
   208  	}
   209  	psArgs = append(psArgs, psOpts...)
   210  	psOutput, err := ctr.GetContainerPidInformation(psArgs)
   211  	if err != nil {
   212  		return call.ReplyErrorOccurred(err.Error())
   213  	}
   214  
   215  	return call.ReplyListContainerProcesses(psOutput)
   216  }
   217  
   218  // GetContainerLogs ...
   219  func (i *VarlinkAPI) GetContainerLogs(call iopodman.VarlinkCall, name string) error {
   220  	var logs []string
   221  	ctr, err := i.Runtime.LookupContainer(name)
   222  	if err != nil {
   223  		return call.ReplyContainerNotFound(name, err.Error())
   224  	}
   225  	logPath := ctr.LogPath()
   226  
   227  	containerState, err := ctr.State()
   228  	if err != nil {
   229  		return call.ReplyErrorOccurred(err.Error())
   230  	}
   231  	if _, err := os.Stat(logPath); err != nil {
   232  		if containerState == define.ContainerStateConfigured {
   233  			return call.ReplyGetContainerLogs(logs)
   234  		}
   235  	}
   236  	file, err := os.Open(logPath)
   237  	if err != nil {
   238  		return errors.Wrapf(err, "unable to read container log file")
   239  	}
   240  	defer file.Close()
   241  	reader := bufio.NewReader(file)
   242  	if call.WantsMore() {
   243  		call.Continues = true
   244  	}
   245  	for {
   246  		line, err := reader.ReadString('\n')
   247  		// We've read the entire file
   248  		if err == io.EOF {
   249  			if !call.WantsMore() {
   250  				// If this is a non-following log request, we return what we have
   251  				break
   252  			} else {
   253  				// If we want to follow, return what we have, wipe the slice, and make
   254  				// sure the container is still running before iterating.
   255  				call.ReplyGetContainerLogs(logs)
   256  				logs = []string{}
   257  				time.Sleep(1 * time.Second)
   258  				state, err := ctr.State()
   259  				if err != nil {
   260  					return call.ReplyErrorOccurred(err.Error())
   261  				}
   262  				if state != define.ContainerStateRunning && state != define.ContainerStatePaused {
   263  					return call.ReplyErrorOccurred(fmt.Sprintf("%s is no longer running", ctr.ID()))
   264  				}
   265  
   266  			}
   267  		} else if err != nil {
   268  			return call.ReplyErrorOccurred(err.Error())
   269  		} else {
   270  			logs = append(logs, line)
   271  		}
   272  	}
   273  
   274  	call.Continues = false
   275  
   276  	return call.ReplyGetContainerLogs(logs)
   277  }
   278  
   279  // ListContainerChanges ...
   280  func (i *VarlinkAPI) ListContainerChanges(call iopodman.VarlinkCall, name string) error {
   281  	changes, err := i.Runtime.GetDiff("", name)
   282  	if err != nil {
   283  		return call.ReplyErrorOccurred(err.Error())
   284  	}
   285  	result := iopodman.ContainerChanges{}
   286  	for _, change := range changes {
   287  		switch change.Kind {
   288  		case archive.ChangeModify:
   289  			result.Changed = append(result.Changed, change.Path)
   290  		case archive.ChangeDelete:
   291  			result.Deleted = append(result.Deleted, change.Path)
   292  		case archive.ChangeAdd:
   293  			result.Added = append(result.Added, change.Path)
   294  		}
   295  	}
   296  	return call.ReplyListContainerChanges(result)
   297  }
   298  
   299  // ExportContainer ...
   300  func (i *VarlinkAPI) ExportContainer(call iopodman.VarlinkCall, name, outPath string) error {
   301  	ctr, err := i.Runtime.LookupContainer(name)
   302  	if err != nil {
   303  		return call.ReplyContainerNotFound(name, err.Error())
   304  	}
   305  	outputFile, err := ioutil.TempFile("", "varlink_recv")
   306  	if err != nil {
   307  		return call.ReplyErrorOccurred(err.Error())
   308  	}
   309  
   310  	defer outputFile.Close()
   311  	if outPath == "" {
   312  		outPath = outputFile.Name()
   313  	}
   314  	if err := ctr.Export(outPath); err != nil {
   315  		return call.ReplyErrorOccurred(err.Error())
   316  	}
   317  	return call.ReplyExportContainer(outPath)
   318  
   319  }
   320  
   321  // GetContainerStats ...
   322  func (i *VarlinkAPI) GetContainerStats(call iopodman.VarlinkCall, name string) error {
   323  	if rootless.IsRootless() {
   324  		cgroupv2, err := cgroups.IsCgroup2UnifiedMode()
   325  		if err != nil {
   326  			return call.ReplyErrorOccurred(err.Error())
   327  		}
   328  		if !cgroupv2 {
   329  			return call.ReplyErrRequiresCgroupsV2ForRootless("rootless containers cannot report container stats")
   330  		}
   331  	}
   332  	ctr, err := i.Runtime.LookupContainer(name)
   333  	if err != nil {
   334  		return call.ReplyContainerNotFound(name, err.Error())
   335  	}
   336  	containerStats, err := ctr.GetContainerStats(&libpod.ContainerStats{})
   337  	if err != nil {
   338  		if errors.Cause(err) == define.ErrCtrStateInvalid {
   339  			return call.ReplyNoContainerRunning()
   340  		}
   341  		return call.ReplyErrorOccurred(err.Error())
   342  	}
   343  	cs := iopodman.ContainerStats{
   344  		Id:           ctr.ID(),
   345  		Name:         ctr.Name(),
   346  		Cpu:          containerStats.CPU,
   347  		Cpu_nano:     int64(containerStats.CPUNano),
   348  		System_nano:  int64(containerStats.SystemNano),
   349  		Mem_usage:    int64(containerStats.MemUsage),
   350  		Mem_limit:    int64(containerStats.MemLimit),
   351  		Mem_perc:     containerStats.MemPerc,
   352  		Net_input:    int64(containerStats.NetInput),
   353  		Net_output:   int64(containerStats.NetOutput),
   354  		Block_input:  int64(containerStats.BlockInput),
   355  		Block_output: int64(containerStats.BlockOutput),
   356  		Pids:         int64(containerStats.PIDs),
   357  	}
   358  	return call.ReplyGetContainerStats(cs)
   359  }
   360  
   361  // StartContainer ...
   362  func (i *VarlinkAPI) StartContainer(call iopodman.VarlinkCall, name string) error {
   363  	ctr, err := i.Runtime.LookupContainer(name)
   364  	if err != nil {
   365  		return call.ReplyContainerNotFound(name, err.Error())
   366  	}
   367  	state, err := ctr.State()
   368  	if err != nil {
   369  		return call.ReplyErrorOccurred(err.Error())
   370  	}
   371  	if state == define.ContainerStateRunning || state == define.ContainerStatePaused {
   372  		return call.ReplyErrorOccurred("container is already running or paused")
   373  	}
   374  	recursive := false
   375  	if ctr.PodID() != "" {
   376  		recursive = true
   377  	}
   378  	if err := ctr.Start(getContext(), recursive); err != nil {
   379  		return call.ReplyErrorOccurred(err.Error())
   380  	}
   381  	return call.ReplyStartContainer(ctr.ID())
   382  }
   383  
   384  // InitContainer initializes the container given by Varlink.
   385  func (i *VarlinkAPI) InitContainer(call iopodman.VarlinkCall, name string) error {
   386  	ctr, err := i.Runtime.LookupContainer(name)
   387  	if err != nil {
   388  		return call.ReplyContainerNotFound(name, err.Error())
   389  	}
   390  	if err := ctr.Init(getContext()); err != nil {
   391  		if errors.Cause(err) == define.ErrCtrStateInvalid {
   392  			return call.ReplyInvalidState(ctr.ID(), err.Error())
   393  		}
   394  		return call.ReplyErrorOccurred(err.Error())
   395  	}
   396  	return call.ReplyInitContainer(ctr.ID())
   397  }
   398  
   399  // StopContainer ...
   400  func (i *VarlinkAPI) StopContainer(call iopodman.VarlinkCall, name string, timeout int64) error {
   401  	ctr, err := i.Runtime.LookupContainer(name)
   402  	if err != nil {
   403  		return call.ReplyContainerNotFound(name, err.Error())
   404  	}
   405  	if err := ctr.StopWithTimeout(uint(timeout)); err != nil {
   406  		if errors.Cause(err) == define.ErrCtrStopped {
   407  			return call.ReplyErrCtrStopped(ctr.ID())
   408  		}
   409  		if errors.Cause(err) == define.ErrCtrStateInvalid {
   410  			return call.ReplyInvalidState(ctr.ID(), err.Error())
   411  		}
   412  		return call.ReplyErrorOccurred(err.Error())
   413  	}
   414  	return call.ReplyStopContainer(ctr.ID())
   415  }
   416  
   417  // RestartContainer ...
   418  func (i *VarlinkAPI) RestartContainer(call iopodman.VarlinkCall, name string, timeout int64) error {
   419  	ctr, err := i.Runtime.LookupContainer(name)
   420  	if err != nil {
   421  		return call.ReplyContainerNotFound(name, err.Error())
   422  	}
   423  	if err := ctr.RestartWithTimeout(getContext(), uint(timeout)); err != nil {
   424  		return call.ReplyErrorOccurred(err.Error())
   425  	}
   426  	return call.ReplyRestartContainer(ctr.ID())
   427  }
   428  
   429  // ContainerExists looks in local storage for the existence of a container
   430  func (i *VarlinkAPI) ContainerExists(call iopodman.VarlinkCall, name string) error {
   431  	_, err := i.Runtime.LookupContainer(name)
   432  	if errors.Cause(err) == define.ErrNoSuchCtr {
   433  		return call.ReplyContainerExists(1)
   434  	}
   435  	if err != nil {
   436  		return call.ReplyErrorOccurred(err.Error())
   437  	}
   438  	return call.ReplyContainerExists(0)
   439  }
   440  
   441  // KillContainer kills a running container.  If you want to use the default SIGTERM signal, just send a -1
   442  // for the signal arg.
   443  func (i *VarlinkAPI) KillContainer(call iopodman.VarlinkCall, name string, signal int64) error {
   444  	killSignal := uint(syscall.SIGTERM)
   445  	if signal != -1 {
   446  		killSignal = uint(signal)
   447  	}
   448  	ctr, err := i.Runtime.LookupContainer(name)
   449  	if err != nil {
   450  		return call.ReplyContainerNotFound(name, err.Error())
   451  	}
   452  	if err := ctr.Kill(killSignal); err != nil {
   453  		return call.ReplyErrorOccurred(err.Error())
   454  	}
   455  	return call.ReplyKillContainer(ctr.ID())
   456  }
   457  
   458  // PauseContainer ...
   459  func (i *VarlinkAPI) PauseContainer(call iopodman.VarlinkCall, name string) error {
   460  	ctr, err := i.Runtime.LookupContainer(name)
   461  	if err != nil {
   462  		return call.ReplyContainerNotFound(name, err.Error())
   463  	}
   464  	if err := ctr.Pause(); err != nil {
   465  		return call.ReplyErrorOccurred(err.Error())
   466  	}
   467  	return call.ReplyPauseContainer(ctr.ID())
   468  }
   469  
   470  // UnpauseContainer ...
   471  func (i *VarlinkAPI) UnpauseContainer(call iopodman.VarlinkCall, name string) error {
   472  	ctr, err := i.Runtime.LookupContainer(name)
   473  	if err != nil {
   474  		return call.ReplyContainerNotFound(name, err.Error())
   475  	}
   476  	if err := ctr.Unpause(); err != nil {
   477  		return call.ReplyErrorOccurred(err.Error())
   478  	}
   479  	return call.ReplyUnpauseContainer(ctr.ID())
   480  }
   481  
   482  // WaitContainer ...
   483  func (i *VarlinkAPI) WaitContainer(call iopodman.VarlinkCall, name string, interval int64) error {
   484  	ctr, err := i.Runtime.LookupContainer(name)
   485  	if err != nil {
   486  		return call.ReplyContainerNotFound(name, err.Error())
   487  	}
   488  	exitCode, err := ctr.WaitWithInterval(time.Duration(interval))
   489  	if err != nil {
   490  		return call.ReplyErrorOccurred(err.Error())
   491  	}
   492  	return call.ReplyWaitContainer(int64(exitCode))
   493  }
   494  
   495  // RemoveContainer ...
   496  func (i *VarlinkAPI) RemoveContainer(call iopodman.VarlinkCall, name string, force bool, removeVolumes bool) error {
   497  	ctx := getContext()
   498  	ctr, err := i.Runtime.LookupContainer(name)
   499  	if err != nil {
   500  		return call.ReplyContainerNotFound(name, err.Error())
   501  	}
   502  	if err := i.Runtime.RemoveContainer(ctx, ctr, force, removeVolumes); err != nil {
   503  		if errors.Cause(err) == define.ErrNoSuchCtr {
   504  			return call.ReplyContainerExists(1)
   505  		}
   506  		if errors.Cause(err) == define.ErrCtrStateInvalid {
   507  			return call.ReplyInvalidState(ctr.ID(), err.Error())
   508  		}
   509  		return call.ReplyErrorOccurred(err.Error())
   510  	}
   511  	return call.ReplyRemoveContainer(ctr.ID())
   512  }
   513  
   514  // EvictContainer ...
   515  func (i *VarlinkAPI) EvictContainer(call iopodman.VarlinkCall, name string, removeVolumes bool) error {
   516  	ctx := getContext()
   517  	id, err := i.Runtime.EvictContainer(ctx, name, removeVolumes)
   518  	if err != nil {
   519  		return call.ReplyErrorOccurred(err.Error())
   520  	}
   521  	return call.ReplyEvictContainer(id)
   522  }
   523  
   524  // DeleteStoppedContainers ...
   525  func (i *VarlinkAPI) DeleteStoppedContainers(call iopodman.VarlinkCall) error {
   526  	ctx := getContext()
   527  	var deletedContainers []string
   528  	containers, err := i.Runtime.GetAllContainers()
   529  	if err != nil {
   530  		return call.ReplyErrorOccurred(err.Error())
   531  	}
   532  	for _, ctr := range containers {
   533  		state, err := ctr.State()
   534  		if err != nil {
   535  			return call.ReplyErrorOccurred(err.Error())
   536  		}
   537  		if state != define.ContainerStateRunning {
   538  			if err := i.Runtime.RemoveContainer(ctx, ctr, false, false); err != nil {
   539  				return call.ReplyErrorOccurred(err.Error())
   540  			}
   541  			deletedContainers = append(deletedContainers, ctr.ID())
   542  		}
   543  	}
   544  	return call.ReplyDeleteStoppedContainers(deletedContainers)
   545  }
   546  
   547  // GetAttachSockets ...
   548  func (i *VarlinkAPI) GetAttachSockets(call iopodman.VarlinkCall, name string) error {
   549  	ctr, err := i.Runtime.LookupContainer(name)
   550  	if err != nil {
   551  		return call.ReplyContainerNotFound(name, err.Error())
   552  	}
   553  
   554  	status, err := ctr.State()
   555  	if err != nil {
   556  		return call.ReplyErrorOccurred(err.Error())
   557  	}
   558  
   559  	// If the container hasn't been run, we need to run init
   560  	// so the conmon sockets get created.
   561  	if status == define.ContainerStateConfigured || status == define.ContainerStateStopped {
   562  		if err := ctr.Init(getContext()); err != nil {
   563  			return call.ReplyErrorOccurred(err.Error())
   564  		}
   565  	}
   566  
   567  	sockPath, err := ctr.AttachSocketPath()
   568  	if err != nil {
   569  		return call.ReplyErrorOccurred(err.Error())
   570  	}
   571  
   572  	s := iopodman.Sockets{
   573  		Container_id:   ctr.ID(),
   574  		Io_socket:      sockPath,
   575  		Control_socket: ctr.ControlSocketPath(),
   576  	}
   577  	return call.ReplyGetAttachSockets(s)
   578  }
   579  
   580  // ContainerCheckpoint ...
   581  func (i *VarlinkAPI) ContainerCheckpoint(call iopodman.VarlinkCall, name string, keep, leaveRunning, tcpEstablished bool) error {
   582  	ctx := getContext()
   583  	ctr, err := i.Runtime.LookupContainer(name)
   584  	if err != nil {
   585  		return call.ReplyContainerNotFound(name, err.Error())
   586  	}
   587  
   588  	options := libpod.ContainerCheckpointOptions{
   589  		Keep:           keep,
   590  		TCPEstablished: tcpEstablished,
   591  		KeepRunning:    leaveRunning,
   592  	}
   593  	if err := ctr.Checkpoint(ctx, options); err != nil {
   594  		return call.ReplyErrorOccurred(err.Error())
   595  	}
   596  	return call.ReplyContainerCheckpoint(ctr.ID())
   597  }
   598  
   599  // ContainerRestore ...
   600  func (i *VarlinkAPI) ContainerRestore(call iopodman.VarlinkCall, name string, keep, tcpEstablished bool) error {
   601  	ctx := getContext()
   602  	ctr, err := i.Runtime.LookupContainer(name)
   603  	if err != nil {
   604  		return call.ReplyContainerNotFound(name, err.Error())
   605  	}
   606  
   607  	options := libpod.ContainerCheckpointOptions{
   608  		Keep:           keep,
   609  		TCPEstablished: tcpEstablished,
   610  	}
   611  	if err := ctr.Restore(ctx, options); err != nil {
   612  		return call.ReplyErrorOccurred(err.Error())
   613  	}
   614  	return call.ReplyContainerRestore(ctr.ID())
   615  }
   616  
   617  // ContainerConfig returns just the container.config struct
   618  func (i *VarlinkAPI) ContainerConfig(call iopodman.VarlinkCall, name string) error {
   619  	ctr, err := i.Runtime.LookupContainer(name)
   620  	if err != nil {
   621  		return call.ReplyContainerNotFound(name, err.Error())
   622  	}
   623  	config := ctr.Config()
   624  	b, err := json.Marshal(config)
   625  	if err != nil {
   626  		return call.ReplyErrorOccurred("unable to serialize container config")
   627  	}
   628  	return call.ReplyContainerConfig(string(b))
   629  }
   630  
   631  // ContainerArtifacts returns an untouched container's artifact in string format
   632  func (i *VarlinkAPI) ContainerArtifacts(call iopodman.VarlinkCall, name, artifactName string) error {
   633  	ctr, err := i.Runtime.LookupContainer(name)
   634  	if err != nil {
   635  		return call.ReplyContainerNotFound(name, err.Error())
   636  	}
   637  	artifacts, err := ctr.GetArtifact(artifactName)
   638  	if err != nil {
   639  		return call.ReplyErrorOccurred("unable to get container artifacts")
   640  	}
   641  	b, err := json.Marshal(artifacts)
   642  	if err != nil {
   643  		return call.ReplyErrorOccurred("unable to serialize container artifacts")
   644  	}
   645  	return call.ReplyContainerArtifacts(string(b))
   646  }
   647  
   648  // ContainerInspectData returns the inspect data of a container in string format
   649  func (i *VarlinkAPI) ContainerInspectData(call iopodman.VarlinkCall, name string, size bool) error {
   650  	ctr, err := i.Runtime.LookupContainer(name)
   651  	if err != nil {
   652  		return call.ReplyContainerNotFound(name, err.Error())
   653  	}
   654  	data, err := ctr.Inspect(size)
   655  	if err != nil {
   656  		return call.ReplyErrorOccurred("unable to inspect container")
   657  	}
   658  	b, err := json.Marshal(data)
   659  	if err != nil {
   660  		return call.ReplyErrorOccurred("unable to serialize container inspect data")
   661  	}
   662  	return call.ReplyContainerInspectData(string(b))
   663  
   664  }
   665  
   666  // ContainerStateData returns a container's state data in string format
   667  func (i *VarlinkAPI) ContainerStateData(call iopodman.VarlinkCall, name string) error {
   668  	ctr, err := i.Runtime.LookupContainer(name)
   669  	if err != nil {
   670  		return call.ReplyContainerNotFound(name, err.Error())
   671  	}
   672  	data, err := ctr.ContainerState()
   673  	if err != nil {
   674  		return call.ReplyErrorOccurred("unable to obtain container state")
   675  	}
   676  	b, err := json.Marshal(data)
   677  	if err != nil {
   678  		return call.ReplyErrorOccurred("unable to serialize container inspect data")
   679  	}
   680  	return call.ReplyContainerStateData(string(b))
   681  }
   682  
   683  // GetContainerStatsWithHistory is a varlink endpoint that returns container stats based on current and
   684  // previous statistics
   685  func (i *VarlinkAPI) GetContainerStatsWithHistory(call iopodman.VarlinkCall, prevStats iopodman.ContainerStats) error {
   686  	con, err := i.Runtime.LookupContainer(prevStats.Id)
   687  	if err != nil {
   688  		return call.ReplyContainerNotFound(prevStats.Id, err.Error())
   689  	}
   690  	previousStats := ContainerStatsToLibpodContainerStats(prevStats)
   691  	stats, err := con.GetContainerStats(&previousStats)
   692  	if err != nil {
   693  		return call.ReplyErrorOccurred(err.Error())
   694  	}
   695  	cStats := iopodman.ContainerStats{
   696  		Id:           stats.ContainerID,
   697  		Name:         stats.Name,
   698  		Cpu:          stats.CPU,
   699  		Cpu_nano:     int64(stats.CPUNano),
   700  		System_nano:  int64(stats.SystemNano),
   701  		Mem_usage:    int64(stats.MemUsage),
   702  		Mem_limit:    int64(stats.MemLimit),
   703  		Mem_perc:     stats.MemPerc,
   704  		Net_input:    int64(stats.NetInput),
   705  		Net_output:   int64(stats.NetOutput),
   706  		Block_input:  int64(stats.BlockInput),
   707  		Block_output: int64(stats.BlockOutput),
   708  		Pids:         int64(stats.PIDs),
   709  	}
   710  	return call.ReplyGetContainerStatsWithHistory(cStats)
   711  }
   712  
   713  // Spec ...
   714  func (i *VarlinkAPI) Spec(call iopodman.VarlinkCall, name string) error {
   715  	ctr, err := i.Runtime.LookupContainer(name)
   716  	if err != nil {
   717  		return call.ReplyErrorOccurred(err.Error())
   718  	}
   719  
   720  	spec := ctr.Spec()
   721  	b, err := json.Marshal(spec)
   722  	if err != nil {
   723  		return call.ReplyErrorOccurred(err.Error())
   724  	}
   725  
   726  	return call.ReplySpec(string(b))
   727  }
   728  
   729  // GetContainersLogs is the varlink endpoint to obtain one or more container logs
   730  func (i *VarlinkAPI) GetContainersLogs(call iopodman.VarlinkCall, names []string, follow, latest bool, since string, tail int64, timestamps bool) error {
   731  	var wg sync.WaitGroup
   732  	if call.WantsMore() {
   733  		call.Continues = true
   734  	}
   735  	sinceTime, err := time.Parse(time.RFC3339Nano, since)
   736  	if err != nil {
   737  		return call.ReplyErrorOccurred(err.Error())
   738  	}
   739  	options := logs.LogOptions{
   740  		Follow:     follow,
   741  		Since:      sinceTime,
   742  		Tail:       tail,
   743  		Timestamps: timestamps,
   744  	}
   745  
   746  	options.WaitGroup = &wg
   747  	if len(names) > 1 {
   748  		options.Multi = true
   749  	}
   750  	tailLen := int(tail)
   751  	if tailLen < 0 {
   752  		tailLen = 0
   753  	}
   754  	logChannel := make(chan *logs.LogLine, tailLen*len(names)+1)
   755  	containers, err := shortcuts.GetContainersByContext(false, latest, names, i.Runtime)
   756  	if err != nil {
   757  		return call.ReplyErrorOccurred(err.Error())
   758  	}
   759  	if err := i.Runtime.Log(containers, &options, logChannel); err != nil {
   760  		return err
   761  	}
   762  	go func() {
   763  		wg.Wait()
   764  		close(logChannel)
   765  	}()
   766  	for line := range logChannel {
   767  		call.ReplyGetContainersLogs(newPodmanLogLine(line))
   768  		if !call.Continues {
   769  			break
   770  		}
   771  
   772  	}
   773  	return call.ReplyGetContainersLogs(iopodman.LogLine{})
   774  }
   775  
   776  func newPodmanLogLine(line *logs.LogLine) iopodman.LogLine {
   777  	return iopodman.LogLine{
   778  		Device:       line.Device,
   779  		ParseLogType: line.ParseLogType,
   780  		Time:         line.Time.Format(time.RFC3339Nano),
   781  		Msg:          line.Msg,
   782  		Cid:          line.CID,
   783  	}
   784  }
   785  
   786  // Top displays information about a container's running processes
   787  func (i *VarlinkAPI) Top(call iopodman.VarlinkCall, nameOrID string, descriptors []string) error {
   788  	ctr, err := i.Runtime.LookupContainer(nameOrID)
   789  	if err != nil {
   790  		return call.ReplyContainerNotFound(ctr.ID(), err.Error())
   791  	}
   792  	topInfo, err := ctr.Top(descriptors)
   793  	if err != nil {
   794  		return call.ReplyErrorOccurred(err.Error())
   795  	}
   796  	return call.ReplyTop(topInfo)
   797  }
   798  
   799  // ExecContainer is the varlink endpoint to execute a command in a container
   800  func (i *VarlinkAPI) ExecContainer(call iopodman.VarlinkCall, opts iopodman.ExecOpts) error {
   801  	if !call.WantsUpgrade() {
   802  		return call.ReplyErrorOccurred("client must use upgraded connection to exec")
   803  	}
   804  
   805  	ctr, err := i.Runtime.LookupContainer(opts.Name)
   806  	if err != nil {
   807  		return call.ReplyContainerNotFound(opts.Name, err.Error())
   808  	}
   809  
   810  	state, err := ctr.State()
   811  	if err != nil {
   812  		return call.ReplyErrorOccurred(
   813  			fmt.Sprintf("exec failed to obtain container %s state: %s", ctr.ID(), err.Error()))
   814  	}
   815  
   816  	if state != define.ContainerStateRunning {
   817  		return call.ReplyErrorOccurred(
   818  			fmt.Sprintf("exec requires a running container, %s is %s", ctr.ID(), state.String()))
   819  	}
   820  
   821  	// ACK the client upgrade request
   822  	call.ReplyExecContainer()
   823  
   824  	envs := make(map[string]string)
   825  	if opts.Env != nil {
   826  		// HACK: The Varlink API uses the old []string format for env,
   827  		// storage as "k=v". Split on the = and turn into the new map
   828  		// format.
   829  		for _, env := range *opts.Env {
   830  			splitEnv := strings.SplitN(env, "=", 2)
   831  			if len(splitEnv) == 1 {
   832  				logrus.Errorf("Got badly-formatted environment variable %q in exec", env)
   833  				continue
   834  			}
   835  			envs[splitEnv[0]] = splitEnv[1]
   836  		}
   837  	}
   838  
   839  	var user string
   840  	if opts.User != nil {
   841  		user = *opts.User
   842  	}
   843  
   844  	var workDir string
   845  	if opts.Workdir != nil {
   846  		workDir = *opts.Workdir
   847  	}
   848  
   849  	resizeChan := make(chan remotecommand.TerminalSize)
   850  
   851  	reader, writer, _, pipeWriter, streams := setupStreams(call)
   852  
   853  	type ExitCodeError struct {
   854  		ExitCode uint32
   855  		Error    error
   856  	}
   857  	ecErrChan := make(chan ExitCodeError, 1)
   858  
   859  	go func() {
   860  		if err := virtwriter.Reader(reader, nil, nil, pipeWriter, resizeChan, nil); err != nil {
   861  			ecErrChan <- ExitCodeError{
   862  				define.ExecErrorCodeGeneric,
   863  				err,
   864  			}
   865  		}
   866  	}()
   867  
   868  	execConfig := new(libpod.ExecConfig)
   869  	execConfig.Command = opts.Cmd
   870  	execConfig.Terminal = opts.Tty
   871  	execConfig.Privileged = opts.Privileged
   872  	execConfig.Environment = envs
   873  	execConfig.User = user
   874  	execConfig.WorkDir = workDir
   875  	execConfig.DetachKeys = opts.DetachKeys
   876  
   877  	go func() {
   878  		ec, err := ctr.Exec(execConfig, streams, resizeChan)
   879  		if err != nil {
   880  			logrus.Errorf(err.Error())
   881  		}
   882  		ecErrChan <- ExitCodeError{
   883  			uint32(ec),
   884  			err,
   885  		}
   886  	}()
   887  
   888  	ecErr := <-ecErrChan
   889  
   890  	exitCode := define.TranslateExecErrorToExitCode(int(ecErr.ExitCode), ecErr.Error)
   891  
   892  	if err = virtwriter.HangUp(writer, uint32(exitCode)); err != nil {
   893  		logrus.Errorf("ExecContainer failed to HANG-UP on %s: %s", ctr.ID(), err.Error())
   894  	}
   895  
   896  	if err := call.Writer.Flush(); err != nil {
   897  		logrus.Errorf("Exec Container err: %s", err.Error())
   898  	}
   899  
   900  	return ecErr.Error
   901  }
   902  
   903  // HealthCheckRun executes defined container's healthcheck command and returns the container's health status.
   904  func (i *VarlinkAPI) HealthCheckRun(call iopodman.VarlinkCall, nameOrID string) error {
   905  	hcStatus, err := i.Runtime.HealthCheck(nameOrID)
   906  	if err != nil && hcStatus != libpod.HealthCheckFailure {
   907  		return call.ReplyErrorOccurred(err.Error())
   908  	}
   909  	status := libpod.HealthCheckUnhealthy
   910  	if hcStatus == libpod.HealthCheckSuccess {
   911  		status = libpod.HealthCheckHealthy
   912  	}
   913  	return call.ReplyHealthCheckRun(status)
   914  }