github.com/containerd/nerdctl/v2@v2.0.0-beta.5.0.20240520001846-b5758f54fa28/pkg/cmd/container/top_unix.go (about) 1 //go:build unix 2 3 /* 4 Copyright The containerd Authors. 5 6 Licensed under the Apache License, Version 2.0 (the "License"); 7 you may not use this file except in compliance with the License. 8 You may obtain a copy of the License at 9 10 http://www.apache.org/licenses/LICENSE-2.0 11 12 Unless required by applicable law or agreed to in writing, software 13 distributed under the License is distributed on an "AS IS" BASIS, 14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 See the License for the specific language governing permissions and 16 limitations under the License. 17 */ 18 19 /* 20 Portions from: 21 - https://github.com/moby/moby/blob/v20.10.6/api/types/container/container_top.go 22 - https://github.com/moby/moby/blob/v20.10.6/daemon/top_unix.go 23 Copyright (C) The Moby authors. 24 Licensed under the Apache License, Version 2.0 25 NOTICE: https://github.com/moby/moby/blob/v20.10.6/NOTICE 26 */ 27 28 package container 29 30 import ( 31 "bytes" 32 "context" 33 "errors" 34 "fmt" 35 "io" 36 "os/exec" 37 "strings" 38 "text/tabwriter" 39 40 "github.com/containerd/containerd" 41 ) 42 43 // containerTop was inspired from https://github.com/moby/moby/blob/v20.10.6/daemon/top_unix.go#L133-L189 44 // 45 // ContainerTop lists the processes running inside of the given 46 // container by calling ps with the given args, or with the flags 47 // "-ef" if no args are given. An error is returned if the container 48 // is not found, or is not running, or if there are any problems 49 // running ps, or parsing the output. 50 // procList *ContainerTopOKBody 51 func containerTop(ctx context.Context, stdio io.Writer, client *containerd.Client, id string, psArgs string) error { 52 if psArgs == "" { 53 psArgs = "-ef" 54 } 55 56 if err := validatePSArgs(psArgs); err != nil { 57 return err 58 } 59 60 container, err := client.LoadContainer(ctx, id) 61 if err != nil { 62 return err 63 } 64 65 task, err := container.Task(ctx, nil) 66 if err != nil { 67 return err 68 } 69 70 status, err := task.Status(ctx) 71 if err != nil { 72 return err 73 } 74 75 if status.Status != containerd.Running { 76 return nil 77 } 78 79 //TO DO handle restarting case: wait for container to restart and then launch top command 80 81 procs, err := task.Pids(ctx) 82 if err != nil { 83 return err 84 } 85 86 psList := make([]uint32, 0, len(procs)) 87 for _, ps := range procs { 88 psList = append(psList, ps.Pid) 89 } 90 91 args := strings.Split(psArgs, " ") 92 pids := psPidsArg(psList) 93 output, err := exec.Command("ps", append(args, pids)...).Output() 94 if err != nil { 95 // some ps options (such as f) can't be used together with q, 96 // so retry without it 97 output, err = exec.Command("ps", args...).Output() 98 if err != nil { 99 if ee, ok := err.(*exec.ExitError); ok { 100 // first line of stderr shows why ps failed 101 line := bytes.SplitN(ee.Stderr, []byte{'\n'}, 2) 102 if len(line) > 0 && len(line[0]) > 0 { 103 return errors.New(string(line[0])) 104 } 105 } 106 return nil 107 } 108 } 109 procList, err := parsePSOutput(output, psList) 110 if err != nil { 111 return err 112 } 113 114 w := tabwriter.NewWriter(stdio, 20, 1, 3, ' ', 0) 115 fmt.Fprintln(w, strings.Join(procList.Titles, "\t")) 116 117 for _, proc := range procList.Processes { 118 fmt.Fprintln(w, strings.Join(proc, "\t")) 119 } 120 121 return w.Flush() 122 }