github.com/opencontainers/runc@v1.2.0-rc.1.0.20240520010911-492dc558cdd6/list.go (about) 1 package main 2 3 import ( 4 "encoding/json" 5 "errors" 6 "fmt" 7 "os" 8 "syscall" 9 "text/tabwriter" 10 "time" 11 12 "github.com/moby/sys/user" 13 "github.com/opencontainers/runc/libcontainer" 14 "github.com/opencontainers/runc/libcontainer/utils" 15 "github.com/urfave/cli" 16 ) 17 18 const formatOptions = `table or json` 19 20 // containerState represents the platform agnostic pieces relating to a 21 // running container's status and state 22 type containerState struct { 23 // Version is the OCI version for the container 24 Version string `json:"ociVersion"` 25 // ID is the container ID 26 ID string `json:"id"` 27 // InitProcessPid is the init process id in the parent namespace 28 InitProcessPid int `json:"pid"` 29 // Status is the current status of the container, running, paused, ... 30 Status string `json:"status"` 31 // Bundle is the path on the filesystem to the bundle 32 Bundle string `json:"bundle"` 33 // Rootfs is a path to a directory containing the container's root filesystem. 34 Rootfs string `json:"rootfs"` 35 // Created is the unix timestamp for the creation time of the container in UTC 36 Created time.Time `json:"created"` 37 // Annotations is the user defined annotations added to the config. 38 Annotations map[string]string `json:"annotations,omitempty"` 39 // The owner of the state directory (the owner of the container). 40 Owner string `json:"owner"` 41 } 42 43 var listCommand = cli.Command{ 44 Name: "list", 45 Usage: "lists containers started by runc with the given root", 46 ArgsUsage: ` 47 48 Where the given root is specified via the global option "--root" 49 (default: "/run/runc"). 50 51 EXAMPLE 1: 52 To list containers created via the default "--root": 53 # runc list 54 55 EXAMPLE 2: 56 To list containers created using a non-default value for "--root": 57 # runc --root value list`, 58 Flags: []cli.Flag{ 59 cli.StringFlag{ 60 Name: "format, f", 61 Value: "table", 62 Usage: `select one of: ` + formatOptions, 63 }, 64 cli.BoolFlag{ 65 Name: "quiet, q", 66 Usage: "display only container IDs", 67 }, 68 }, 69 Action: func(context *cli.Context) error { 70 if err := checkArgs(context, 0, exactArgs); err != nil { 71 return err 72 } 73 s, err := getContainers(context) 74 if err != nil { 75 return err 76 } 77 78 if context.Bool("quiet") { 79 for _, item := range s { 80 fmt.Println(item.ID) 81 } 82 return nil 83 } 84 85 switch context.String("format") { 86 case "table": 87 w := tabwriter.NewWriter(os.Stdout, 12, 1, 3, ' ', 0) 88 fmt.Fprint(w, "ID\tPID\tSTATUS\tBUNDLE\tCREATED\tOWNER\n") 89 for _, item := range s { 90 fmt.Fprintf(w, "%s\t%d\t%s\t%s\t%s\t%s\n", 91 item.ID, 92 item.InitProcessPid, 93 item.Status, 94 item.Bundle, 95 item.Created.Format(time.RFC3339Nano), 96 item.Owner) 97 } 98 if err := w.Flush(); err != nil { 99 return err 100 } 101 case "json": 102 if err := json.NewEncoder(os.Stdout).Encode(s); err != nil { 103 return err 104 } 105 default: 106 return errors.New("invalid format option") 107 } 108 return nil 109 }, 110 } 111 112 func getContainers(context *cli.Context) ([]containerState, error) { 113 root := context.GlobalString("root") 114 list, err := os.ReadDir(root) 115 if err != nil { 116 if errors.Is(err, os.ErrNotExist) && context.IsSet("root") { 117 // Ignore non-existing default root directory 118 // (no containers created yet). 119 return nil, nil 120 } 121 // Report other errors, including non-existent custom --root. 122 return nil, err 123 } 124 var s []containerState 125 for _, item := range list { 126 if !item.IsDir() { 127 continue 128 } 129 st, err := item.Info() 130 if err != nil { 131 if errors.Is(err, os.ErrNotExist) { 132 // Possible race with runc delete. 133 continue 134 } 135 return nil, err 136 } 137 // This cast is safe on Linux. 138 uid := st.Sys().(*syscall.Stat_t).Uid 139 owner, err := user.LookupUid(int(uid)) 140 if err != nil { 141 owner.Name = fmt.Sprintf("#%d", uid) 142 } 143 144 container, err := libcontainer.Load(root, item.Name()) 145 if err != nil { 146 fmt.Fprintf(os.Stderr, "load container %s: %v\n", item.Name(), err) 147 continue 148 } 149 containerStatus, err := container.Status() 150 if err != nil { 151 fmt.Fprintf(os.Stderr, "status for %s: %v\n", item.Name(), err) 152 continue 153 } 154 state, err := container.State() 155 if err != nil { 156 fmt.Fprintf(os.Stderr, "state for %s: %v\n", item.Name(), err) 157 continue 158 } 159 pid := state.BaseState.InitProcessPid 160 if containerStatus == libcontainer.Stopped { 161 pid = 0 162 } 163 bundle, annotations := utils.Annotations(state.Config.Labels) 164 s = append(s, containerState{ 165 Version: state.BaseState.Config.Version, 166 ID: state.BaseState.ID, 167 InitProcessPid: pid, 168 Status: containerStatus.String(), 169 Bundle: bundle, 170 Rootfs: state.BaseState.Config.Rootfs, 171 Created: state.BaseState.Created, 172 Annotations: annotations, 173 Owner: owner.Name, 174 }) 175 } 176 return s, nil 177 }