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 }