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