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 }