github.com/containers/podman/v4@v4.9.4/libpod/container_top_freebsd.go (about) 1 //go:build !remote 2 // +build !remote 3 4 package libpod 5 6 import ( 7 "bufio" 8 "errors" 9 "fmt" 10 "os/exec" 11 "strings" 12 "sync" 13 14 "github.com/containers/podman/v4/libpod/define" 15 "github.com/containers/podman/v4/pkg/util" 16 "github.com/google/shlex" 17 "github.com/sirupsen/logrus" 18 ) 19 20 var isDescriptor = map[string]bool{} 21 22 func init() { 23 allDescriptors, err := util.GetContainerPidInformationDescriptors() 24 if err != nil { 25 // Should never happen 26 logrus.Debugf("failed call to util.GetContainerPidInformationDescriptors()") 27 return 28 } 29 for _, d := range allDescriptors { 30 isDescriptor[d] = true 31 } 32 } 33 34 // Top gathers statistics about the running processes in a container. It returns a 35 // []string for output 36 func (c *Container) Top(descriptors []string) ([]string, error) { 37 conStat, err := c.State() 38 if err != nil { 39 return nil, fmt.Errorf("unable to look up state for %s: %w", c.ID(), err) 40 } 41 if conStat != define.ContainerStateRunning { 42 return nil, errors.New("top can only be used on running containers") 43 } 44 45 // Default to 'ps -ef' compatible descriptors 46 if len(strings.Join(descriptors, "")) == 0 { 47 descriptors = []string{"user", "pid", "ppid", "pcpu", "etime", "tty", "time", "args"} 48 } 49 50 // If everything in descriptors is a supported AIX format 51 // descriptor, we use 'ps -ao <descriptors>', otherwise we pass 52 // everything straight through to ps. 53 supportedDescriptors := true 54 for _, d := range descriptors { 55 if _, ok := isDescriptor[d]; !ok { 56 supportedDescriptors = false 57 break 58 } 59 } 60 if supportedDescriptors { 61 descriptors = []string{"-ao", strings.Join(descriptors, ",")} 62 } 63 64 // Note that the descriptors to ps(1) must be shlexed (see #12452). 65 psDescriptors := []string{} 66 for _, d := range descriptors { 67 shSplit, err := shlex.Split(d) 68 if err != nil { 69 return nil, fmt.Errorf("parsing ps args: %w", err) 70 } 71 for _, s := range shSplit { 72 if s != "" { 73 psDescriptors = append(psDescriptors, s) 74 } 75 } 76 } 77 78 jailName, err := c.jailName() 79 if err != nil { 80 return nil, fmt.Errorf("getting jail name: %w", err) 81 } 82 83 args := []string{ 84 "-J", 85 jailName, 86 } 87 args = append(args, psDescriptors...) 88 89 output, err := execPS(args) 90 if err != nil { 91 return nil, fmt.Errorf("executing ps(1): %w", err) 92 } 93 94 return output, nil 95 } 96 97 func execPS(args []string) ([]string, error) { 98 cmd := exec.Command("ps", args...) 99 stdoutPipe, err := cmd.StdoutPipe() 100 if err != nil { 101 return nil, err 102 } 103 stderrPipe, err := cmd.StderrPipe() 104 if err != nil { 105 return nil, err 106 } 107 108 var wg sync.WaitGroup 109 wg.Add(2) 110 stdout := []string{} 111 go func() { 112 scanner := bufio.NewScanner(stdoutPipe) 113 for scanner.Scan() { 114 stdout = append(stdout, scanner.Text()) 115 } 116 wg.Done() 117 }() 118 stderr := []string{} 119 go func() { 120 scanner := bufio.NewScanner(stderrPipe) 121 for scanner.Scan() { 122 stderr = append(stderr, scanner.Text()) 123 } 124 wg.Done() 125 }() 126 127 if err := cmd.Start(); err != nil { 128 return nil, err 129 } 130 wg.Wait() 131 if err := cmd.Wait(); err != nil { 132 return nil, fmt.Errorf("ps(1) command failed: %w, output: %s", err, strings.Join(stderr, " ")) 133 } 134 135 return stdout, nil 136 }