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

     1  package ps
     2  
     3  import (
     4  	"os"
     5  	"path/filepath"
     6  	"regexp"
     7  	"sort"
     8  	"strconv"
     9  	"strings"
    10  	"time"
    11  
    12  	"github.com/containers/podman/v2/libpod"
    13  	"github.com/containers/podman/v2/libpod/define"
    14  	lpfilters "github.com/containers/podman/v2/libpod/filters"
    15  	"github.com/containers/podman/v2/pkg/domain/entities"
    16  	psdefine "github.com/containers/podman/v2/pkg/ps/define"
    17  	"github.com/containers/storage"
    18  	"github.com/pkg/errors"
    19  	"github.com/sirupsen/logrus"
    20  )
    21  
    22  func GetContainerLists(runtime *libpod.Runtime, options entities.ContainerListOptions) ([]entities.ListContainer, error) {
    23  	var (
    24  		pss = []entities.ListContainer{}
    25  	)
    26  	filterFuncs := make([]libpod.ContainerFilter, 0, len(options.Filters))
    27  	all := options.All || options.Last > 0
    28  	if len(options.Filters) > 0 {
    29  		for k, v := range options.Filters {
    30  			generatedFunc, err := lpfilters.GenerateContainerFilterFuncs(k, v, runtime)
    31  			if err != nil {
    32  				return nil, err
    33  			}
    34  			filterFuncs = append(filterFuncs, generatedFunc)
    35  		}
    36  	}
    37  
    38  	// Docker thinks that if status is given as an input, then we should override
    39  	// the all setting and always deal with all containers.
    40  	if len(options.Filters["status"]) > 0 {
    41  		all = true
    42  	}
    43  	if !all {
    44  		runningOnly, err := lpfilters.GenerateContainerFilterFuncs("status", []string{define.ContainerStateRunning.String()}, runtime)
    45  		if err != nil {
    46  			return nil, err
    47  		}
    48  		filterFuncs = append(filterFuncs, runningOnly)
    49  	}
    50  
    51  	cons, err := runtime.GetContainers(filterFuncs...)
    52  	if err != nil {
    53  		return nil, err
    54  	}
    55  	if options.Last > 0 {
    56  		// Sort the libpod containers
    57  		sort.Sort(SortCreateTime{SortContainers: cons})
    58  		// we should perform the lopping before we start getting
    59  		// the expensive information on containers
    60  		if options.Last < len(cons) {
    61  			cons = cons[:options.Last]
    62  		}
    63  	}
    64  	for _, con := range cons {
    65  		listCon, err := ListContainerBatch(runtime, con, options)
    66  		if err != nil {
    67  			return nil, err
    68  		}
    69  		pss = append(pss, listCon)
    70  	}
    71  
    72  	if options.All && options.Storage {
    73  		externCons, err := runtime.StorageContainers()
    74  		if err != nil {
    75  			return nil, err
    76  		}
    77  
    78  		for _, con := range externCons {
    79  			listCon, err := ListStorageContainer(runtime, con, options)
    80  			if err != nil {
    81  				return nil, err
    82  			}
    83  			pss = append(pss, listCon)
    84  		}
    85  	}
    86  
    87  	// Sort the containers we got
    88  	sort.Sort(SortPSCreateTime{SortPSContainers: pss})
    89  
    90  	if options.Last > 0 {
    91  		// only return the "last" containers caller requested
    92  		if options.Last < len(pss) {
    93  			pss = pss[:options.Last]
    94  		}
    95  	}
    96  	return pss, nil
    97  }
    98  
    99  // BatchContainerOp is used in ps to reduce performance hits by "batching"
   100  // locks.
   101  func ListContainerBatch(rt *libpod.Runtime, ctr *libpod.Container, opts entities.ContainerListOptions) (entities.ListContainer, error) {
   102  	var (
   103  		conConfig                               *libpod.ContainerConfig
   104  		conState                                define.ContainerStatus
   105  		err                                     error
   106  		exitCode                                int32
   107  		exited                                  bool
   108  		pid                                     int
   109  		size                                    *psdefine.ContainerSize
   110  		startedTime                             time.Time
   111  		exitedTime                              time.Time
   112  		cgroup, ipc, mnt, net, pidns, user, uts string
   113  	)
   114  
   115  	batchErr := ctr.Batch(func(c *libpod.Container) error {
   116  		conConfig = c.Config()
   117  		conState, err = c.State()
   118  		if err != nil {
   119  			return errors.Wrapf(err, "unable to obtain container state")
   120  		}
   121  
   122  		exitCode, exited, err = c.ExitCode()
   123  		if err != nil {
   124  			return errors.Wrapf(err, "unable to obtain container exit code")
   125  		}
   126  		startedTime, err = c.StartedTime()
   127  		if err != nil {
   128  			logrus.Errorf("error getting started time for %q: %v", c.ID(), err)
   129  		}
   130  		exitedTime, err = c.FinishedTime()
   131  		if err != nil {
   132  			logrus.Errorf("error getting exited time for %q: %v", c.ID(), err)
   133  		}
   134  
   135  		pid, err = c.PID()
   136  		if err != nil {
   137  			return errors.Wrapf(err, "unable to obtain container pid")
   138  		}
   139  
   140  		if !opts.Size && !opts.Namespace {
   141  			return nil
   142  		}
   143  
   144  		if opts.Namespace {
   145  			ctrPID := strconv.Itoa(pid)
   146  			cgroup, _ = getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "cgroup"))
   147  			ipc, _ = getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "ipc"))
   148  			mnt, _ = getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "mnt"))
   149  			net, _ = getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "net"))
   150  			pidns, _ = getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "pid"))
   151  			user, _ = getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "user"))
   152  			uts, _ = getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "uts"))
   153  		}
   154  		if opts.Size {
   155  			size = new(psdefine.ContainerSize)
   156  
   157  			rootFsSize, err := c.RootFsSize()
   158  			if err != nil {
   159  				logrus.Errorf("error getting root fs size for %q: %v", c.ID(), err)
   160  			}
   161  
   162  			rwSize, err := c.RWSize()
   163  			if err != nil {
   164  				logrus.Errorf("error getting rw size for %q: %v", c.ID(), err)
   165  			}
   166  
   167  			size.RootFsSize = rootFsSize
   168  			size.RwSize = rwSize
   169  		}
   170  		return nil
   171  	})
   172  	if batchErr != nil {
   173  		return entities.ListContainer{}, batchErr
   174  	}
   175  
   176  	portMappings, err := ctr.PortMappings()
   177  	if err != nil {
   178  		return entities.ListContainer{}, err
   179  	}
   180  
   181  	ps := entities.ListContainer{
   182  		Command:   conConfig.Command,
   183  		Created:   conConfig.CreatedTime.Unix(),
   184  		Exited:    exited,
   185  		ExitCode:  exitCode,
   186  		ExitedAt:  exitedTime.Unix(),
   187  		ID:        conConfig.ID,
   188  		Image:     conConfig.RootfsImageName,
   189  		ImageID:   conConfig.RootfsImageID,
   190  		IsInfra:   conConfig.IsInfra,
   191  		Labels:    conConfig.Labels,
   192  		Mounts:    ctr.UserVolumes(),
   193  		Names:     []string{conConfig.Name},
   194  		Pid:       pid,
   195  		Pod:       conConfig.Pod,
   196  		Ports:     portMappings,
   197  		Size:      size,
   198  		StartedAt: startedTime.Unix(),
   199  		State:     conState.String(),
   200  	}
   201  	if opts.Pod && len(conConfig.Pod) > 0 {
   202  		podName, err := rt.GetName(conConfig.Pod)
   203  		if err != nil {
   204  			if errors.Cause(err) == define.ErrNoSuchCtr {
   205  				return entities.ListContainer{}, errors.Wrapf(define.ErrNoSuchPod, "could not find container %s pod (id %s) in state", conConfig.ID, conConfig.Pod)
   206  			}
   207  			return entities.ListContainer{}, err
   208  		}
   209  		ps.PodName = podName
   210  	}
   211  
   212  	if opts.Namespace {
   213  		ps.Namespaces = entities.ListContainerNamespaces{
   214  			Cgroup: cgroup,
   215  			IPC:    ipc,
   216  			MNT:    mnt,
   217  			NET:    net,
   218  			PIDNS:  pidns,
   219  			User:   user,
   220  			UTS:    uts,
   221  		}
   222  	}
   223  	return ps, nil
   224  }
   225  
   226  func ListStorageContainer(rt *libpod.Runtime, ctr storage.Container, opts entities.ContainerListOptions) (entities.ListContainer, error) {
   227  	name := "unknown"
   228  	if len(ctr.Names) > 0 {
   229  		name = ctr.Names[0]
   230  	}
   231  
   232  	ps := entities.ListContainer{
   233  		ID:      ctr.ID,
   234  		Created: ctr.Created.Unix(),
   235  		ImageID: ctr.ImageID,
   236  		State:   "storage",
   237  		Names:   []string{name},
   238  	}
   239  
   240  	buildahCtr, err := rt.IsBuildahContainer(ctr.ID)
   241  	if err != nil {
   242  		return ps, errors.Wrapf(err, "error determining buildah container for container %s", ctr.ID)
   243  	}
   244  
   245  	if buildahCtr {
   246  		ps.Command = []string{"buildah"}
   247  	} else {
   248  		ps.Command = []string{"storage"}
   249  	}
   250  
   251  	imageName := ""
   252  	if ctr.ImageID != "" {
   253  		names, err := rt.ImageRuntime().ImageNames(ctr.ImageID)
   254  		if err != nil {
   255  			return ps, err
   256  		}
   257  		if len(names) > 0 {
   258  			imageName = names[0]
   259  		}
   260  	} else if buildahCtr {
   261  		imageName = "scratch"
   262  	}
   263  
   264  	ps.Image = imageName
   265  	return ps, nil
   266  }
   267  
   268  func getNamespaceInfo(path string) (string, error) {
   269  	val, err := os.Readlink(path)
   270  	if err != nil {
   271  		return "", errors.Wrapf(err, "error getting info from %q", path)
   272  	}
   273  	return getStrFromSquareBrackets(val), nil
   274  }
   275  
   276  // getStrFromSquareBrackets gets the string inside [] from a string.
   277  func getStrFromSquareBrackets(cmd string) string {
   278  	reg := regexp.MustCompile(`.*\[|\].*`)
   279  	arr := strings.Split(reg.ReplaceAllLiteralString(cmd, ""), ",")
   280  	return strings.Join(arr, ",")
   281  }
   282  
   283  // SortContainers helps us set-up ability to sort by createTime
   284  type SortContainers []*libpod.Container
   285  
   286  func (a SortContainers) Len() int      { return len(a) }
   287  func (a SortContainers) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
   288  
   289  type SortCreateTime struct{ SortContainers }
   290  
   291  func (a SortCreateTime) Less(i, j int) bool {
   292  	return a.SortContainers[i].CreatedTime().After(a.SortContainers[j].CreatedTime())
   293  }
   294  
   295  // SortPSContainers helps us set-up ability to sort by createTime
   296  type SortPSContainers []entities.ListContainer
   297  
   298  func (a SortPSContainers) Len() int      { return len(a) }
   299  func (a SortPSContainers) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
   300  
   301  type SortPSCreateTime struct{ SortPSContainers }
   302  
   303  func (a SortPSCreateTime) Less(i, j int) bool {
   304  	return a.SortPSContainers[i].Created > a.SortPSContainers[j].Created
   305  }