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