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