github.com/containers/libpod@v1.9.4-0.20220419124438-4284fd425507/cmd/podman/ps.go (about) 1 package main 2 3 import ( 4 "fmt" 5 "html/template" 6 "os" 7 "reflect" 8 "sort" 9 "strings" 10 "text/tabwriter" 11 "time" 12 13 tm "github.com/buger/goterm" 14 "github.com/containers/buildah/pkg/formats" 15 "github.com/containers/libpod/cmd/podman/cliconfig" 16 "github.com/containers/libpod/cmd/podman/shared" 17 "github.com/containers/libpod/pkg/adapter" 18 "github.com/docker/go-units" 19 "github.com/pkg/errors" 20 "github.com/spf13/cobra" 21 ) 22 23 const ( 24 hid = "CONTAINER ID" 25 himage = "IMAGE" 26 hcommand = "COMMAND" 27 hcreated = "CREATED" 28 hstatus = "STATUS" 29 hports = "PORTS" 30 hnames = "NAMES" 31 hsize = "SIZE" 32 hinfra = "IS INFRA" //nolint 33 hpod = "POD" 34 hpodname = "POD NAME" 35 nspid = "PID" 36 nscgroup = "CGROUPNS" 37 nsipc = "IPC" 38 nsmnt = "MNT" 39 nsnet = "NET" 40 nspidns = "PIDNS" 41 nsuserns = "USERNS" 42 nsuts = "UTS" 43 ) 44 45 // Type declaration and functions for sorting the PS output 46 type psSorted []shared.PsContainerOutput 47 48 func (a psSorted) Len() int { return len(a) } 49 func (a psSorted) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 50 51 type psSortedCommand struct{ psSorted } 52 53 func (a psSortedCommand) Less(i, j int) bool { 54 return a.psSorted[i].Command < a.psSorted[j].Command 55 } 56 57 type psSortedCreated struct{ psSorted } 58 59 func (a psSortedCreated) Less(i, j int) bool { 60 return a.psSorted[i].CreatedAt.After(a.psSorted[j].CreatedAt) 61 } 62 63 type psSortedId struct{ psSorted } 64 65 func (a psSortedId) Less(i, j int) bool { return a.psSorted[i].ID < a.psSorted[j].ID } 66 67 type psSortedImage struct{ psSorted } 68 69 func (a psSortedImage) Less(i, j int) bool { return a.psSorted[i].Image < a.psSorted[j].Image } 70 71 type psSortedNames struct{ psSorted } 72 73 func (a psSortedNames) Less(i, j int) bool { return a.psSorted[i].Names < a.psSorted[j].Names } 74 75 type psSortedPod struct{ psSorted } 76 77 func (a psSortedPod) Less(i, j int) bool { return a.psSorted[i].Pod < a.psSorted[j].Pod } 78 79 type psSortedRunningFor struct{ psSorted } 80 81 func (a psSortedRunningFor) Less(i, j int) bool { 82 return a.psSorted[j].StartedAt.After(a.psSorted[i].StartedAt) 83 } 84 85 type psSortedStatus struct{ psSorted } 86 87 func (a psSortedStatus) Less(i, j int) bool { return a.psSorted[i].Status < a.psSorted[j].Status } 88 89 type psSortedSize struct{ psSorted } 90 91 func (a psSortedSize) Less(i, j int) bool { 92 if a.psSorted[i].Size == nil || a.psSorted[j].Size == nil { 93 return false 94 } 95 return a.psSorted[i].Size.RootFsSize < a.psSorted[j].Size.RootFsSize 96 } 97 98 var ( 99 psCommand cliconfig.PsValues 100 psDescription = "Prints out information about the containers" 101 _psCommand = cobra.Command{ 102 Use: "ps", 103 Args: noSubArgs, 104 Short: "List containers", 105 Long: psDescription, 106 RunE: func(cmd *cobra.Command, args []string) error { 107 psCommand.InputArgs = args 108 psCommand.GlobalFlags = MainGlobalOpts 109 psCommand.Remote = remoteclient 110 return psCmd(&psCommand) 111 }, 112 Example: `podman ps -a 113 podman ps -a --format "{{.ID}} {{.Image}} {{.Labels}} {{.Mounts}}" 114 podman ps --size --sort names`, 115 } 116 ) 117 118 func psInit(command *cliconfig.PsValues) { 119 command.SetHelpTemplate(HelpTemplate()) 120 command.SetUsageTemplate(UsageTemplate()) 121 flags := command.Flags() 122 flags.BoolVarP(&command.All, "all", "a", false, "Show all the containers, default is only running containers") 123 flags.StringSliceVarP(&command.Filter, "filter", "f", []string{}, "Filter output based on conditions given") 124 flags.StringVar(&command.Format, "format", "", "Pretty-print containers to JSON or using a Go template") 125 flags.IntVarP(&command.Last, "last", "n", -1, "Print the n last created containers (all states)") 126 flags.BoolVarP(&command.Latest, "latest", "l", false, "Show the latest container created (all states)") 127 flags.BoolVar(&command.Namespace, "namespace", false, "Display namespace information") 128 flags.BoolVar(&command.Namespace, "ns", false, "Display namespace information") 129 flags.BoolVar(&command.NoTrunct, "no-trunc", false, "Display the extended information") 130 flags.BoolVarP(&command.Pod, "pod", "p", false, "Print the ID and name of the pod the containers are associated with") 131 flags.BoolVarP(&command.Quiet, "quiet", "q", false, "Print the numeric IDs of the containers only") 132 flags.BoolVarP(&command.Size, "size", "s", false, "Display the total file sizes") 133 flags.StringVar(&command.Sort, "sort", "created", "Sort output by command, created, id, image, names, runningfor, size, or status") 134 flags.BoolVar(&command.Sync, "sync", false, "Sync container state with OCI runtime") 135 flags.UintVarP(&command.Watch, "watch", "w", 0, "Watch the ps output on an interval in seconds") 136 137 markFlagHiddenForRemoteClient("latest", flags) 138 } 139 140 func init() { 141 psCommand.Command = &_psCommand 142 psInit(&psCommand) 143 } 144 145 func psCmd(c *cliconfig.PsValues) error { 146 var ( 147 watch bool 148 runtime *adapter.LocalRuntime 149 err error 150 ) 151 152 if c.Watch > 0 { 153 watch = true 154 } 155 156 if c.Watch > 0 && c.Latest { 157 return errors.New("the watch and latest flags cannot be used together") 158 } 159 160 if err := checkFlagsPassed(c); err != nil { 161 return errors.Wrapf(err, "error with flags passed") 162 } 163 if !c.Size { 164 runtime, err = adapter.GetRuntimeNoStore(getContext(), &c.PodmanCommand) 165 } else { 166 runtime, err = adapter.GetRuntime(getContext(), &c.PodmanCommand) 167 } 168 if err != nil { 169 return errors.Wrapf(err, "error creating libpod runtime") 170 } 171 172 defer runtime.DeferredShutdown(false) 173 174 if !watch { 175 if err := psDisplay(c, runtime); err != nil { 176 return err 177 } 178 } else { 179 for { 180 tm.Clear() 181 tm.MoveCursor(1, 1) 182 tm.Flush() 183 if err := psDisplay(c, runtime); err != nil { 184 return err 185 } 186 time.Sleep(time.Duration(c.Watch) * time.Second) 187 tm.Clear() 188 tm.MoveCursor(1, 1) 189 tm.Flush() 190 } 191 } 192 return nil 193 } 194 195 func printQuiet(containers []shared.PsContainerOutput) error { 196 for _, c := range containers { 197 fmt.Println(c.ID) 198 } 199 return nil 200 } 201 202 // checkFlagsPassed checks if mutually exclusive flags are passed together 203 func checkFlagsPassed(c *cliconfig.PsValues) error { 204 // latest, and last are mutually exclusive. 205 if c.Last >= 0 && c.Latest { 206 return errors.Errorf("last and latest are mutually exclusive") 207 } 208 // Filter on status forces all 209 if len(c.Filter) > 0 { 210 for _, filter := range c.Filter { 211 splitFilter := strings.SplitN(filter, "=", 2) 212 if strings.ToLower(splitFilter[0]) == "status" { 213 c.All = true 214 break 215 } 216 } 217 } 218 // Quiet conflicts with size and namespace and is overridden by a Go 219 // template. 220 if c.Quiet { 221 if c.Size || c.Namespace { 222 return errors.Errorf("quiet conflicts with size and namespace") 223 } 224 if c.Flag("format").Changed && c.Format != formats.JSONString { 225 // Quiet is overridden by Go template output. 226 c.Quiet = false 227 } 228 } 229 // Size and namespace conflict with each other 230 if c.Size && c.Namespace { 231 return errors.Errorf("size and namespace options conflict") 232 } 233 return nil 234 } 235 236 func sortPsOutput(sortBy string, psOutput psSorted) (psSorted, error) { 237 switch sortBy { 238 case "id": 239 sort.Sort(psSortedId{psOutput}) 240 case "image": 241 sort.Sort(psSortedImage{psOutput}) 242 case "command": 243 sort.Sort(psSortedCommand{psOutput}) 244 case "runningfor": 245 sort.Sort(psSortedRunningFor{psOutput}) 246 case "status": 247 sort.Sort(psSortedStatus{psOutput}) 248 case "size": 249 sort.Sort(psSortedSize{psOutput}) 250 case "names": 251 sort.Sort(psSortedNames{psOutput}) 252 case "created": 253 sort.Sort(psSortedCreated{psOutput}) 254 case "pod": 255 sort.Sort(psSortedPod{psOutput}) 256 default: 257 return nil, errors.Errorf("invalid option for --sort, options are: command, created, id, image, names, runningfor, size, or status") 258 } 259 return psOutput, nil 260 } 261 262 func printFormat(format string, containers []shared.PsContainerOutput) error { 263 // return immediately if no containers are present 264 if len(containers) == 0 { 265 return nil 266 } 267 268 // Use a tabwriter to align column format 269 w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) 270 271 // Make a map of the field names for the headers 272 headerNames := make(map[string]string) 273 v := reflect.ValueOf(containers[0]) 274 t := v.Type() 275 for i := 0; i < t.NumField(); i++ { 276 headerNames[t.Field(i).Name] = t.Field(i).Name 277 } 278 279 // Spit out the header if "table" is present in the format 280 if strings.HasPrefix(format, "table") { 281 hformat := strings.Replace(strings.TrimSpace(format[5:]), " ", "\t", -1) 282 format = hformat 283 headerTmpl, err := template.New("header").Parse(hformat) 284 if err != nil { 285 return err 286 } 287 if err := headerTmpl.Execute(w, headerNames); err != nil { 288 return err 289 } 290 fmt.Fprintln(w, "") 291 } 292 293 // Spit out the data rows now 294 dataTmpl, err := template.New("data").Parse(format) 295 if err != nil { 296 return err 297 } 298 299 for _, container := range containers { 300 if err := dataTmpl.Execute(w, container); err != nil { 301 return err 302 } 303 fmt.Fprintln(w, "") 304 } 305 // Flush the writer 306 return w.Flush() 307 } 308 309 func dumpJSON(containers []shared.PsContainerOutput) error { 310 b, err := json.MarshalIndent(containers, "", " ") 311 if err != nil { 312 return err 313 } 314 os.Stdout.Write(b) 315 return nil 316 } 317 318 func psDisplay(c *cliconfig.PsValues, runtime *adapter.LocalRuntime) error { 319 var ( 320 err error 321 ) 322 opts := shared.PsOptions{ 323 All: c.All, 324 Format: c.Format, 325 Last: c.Last, 326 Latest: c.Latest, 327 NoTrunc: c.NoTrunct, 328 Pod: c.Pod, 329 Quiet: c.Quiet, 330 Size: c.Size, 331 Namespace: c.Namespace, 332 Sort: c.Sort, 333 Sync: c.Sync, 334 } 335 336 pss, err := runtime.Ps(c, opts) 337 if err != nil { 338 return err 339 } 340 // Here and down 341 if opts.Sort != "" { 342 pss, err = sortPsOutput(opts.Sort, pss) 343 if err != nil { 344 return err 345 } 346 } 347 348 // If quiet, print only cids and return 349 if opts.Quiet { 350 return printQuiet(pss) 351 } 352 353 // If the user wants their own GO template format 354 if opts.Format != "" { 355 if opts.Format == "json" { 356 return dumpJSON(pss) 357 } 358 return printFormat(opts.Format, pss) 359 } 360 361 // Define a tab writer with stdout as the output 362 w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) 363 364 // Output standard PS headers 365 if !opts.Namespace { 366 fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s", hid, himage, hcommand, hcreated, hstatus, hports, hnames) 367 // User wants pod info 368 if opts.Pod { 369 fmt.Fprintf(w, "\t%s\t%s", hpod, hpodname) 370 } 371 //User wants size info 372 if opts.Size { 373 fmt.Fprintf(w, "\t%s", hsize) 374 } 375 } else { 376 // Output Namespace headers 377 fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s", hid, hnames, nspid, nscgroup, nsipc, nsmnt, nsnet, nspidns, nsuserns, nsuts) 378 } 379 380 // Now iterate each container and output its information 381 for _, container := range pss { 382 383 // Standard PS output 384 if !opts.Namespace { 385 fmt.Fprintf(w, "\n%s\t%s\t%s\t%s\t%s\t%s\t%s", container.ID, container.Image, container.Command, container.Created, container.Status, container.Ports, container.Names) 386 // User wants pod info 387 if opts.Pod { 388 fmt.Fprintf(w, "\t%s\t%s", container.Pod, container.PodName) 389 } 390 //User wants size info 391 if opts.Size { 392 var size string 393 if container.Size == nil { 394 size = units.HumanSizeWithPrecision(0, 0) 395 } else { 396 size = units.HumanSizeWithPrecision(float64(container.Size.RwSize), 3) + " (virtual " + units.HumanSizeWithPrecision(float64(container.Size.RootFsSize), 3) + ")" 397 } 398 fmt.Fprintf(w, "\t%s", size) 399 } 400 401 } else { 402 // Print namespace information 403 ns := runtime.GetNamespaces(container) 404 fmt.Fprintf(w, "\n%s\t%s\t%d\t%s\t%s\t%s\t%s\t%s\t%s\t%s", container.ID, container.Names, container.Pid, ns.Cgroup, ns.IPC, ns.MNT, ns.NET, ns.PIDNS, ns.User, ns.UTS) 405 } 406 407 } 408 fmt.Fprint(w, "\n") 409 return w.Flush() 410 }