136 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			136 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			Go
		
	
	
	
//go:build !remote
 | 
						|
 | 
						|
package libpod
 | 
						|
 | 
						|
import (
 | 
						|
	"bufio"
 | 
						|
	"errors"
 | 
						|
	"fmt"
 | 
						|
	"os/exec"
 | 
						|
	"strings"
 | 
						|
	"sync"
 | 
						|
 | 
						|
	"github.com/containers/podman/v4/libpod/define"
 | 
						|
	"github.com/containers/podman/v4/pkg/util"
 | 
						|
	"github.com/google/shlex"
 | 
						|
	"github.com/sirupsen/logrus"
 | 
						|
)
 | 
						|
 | 
						|
var isDescriptor = map[string]bool{}
 | 
						|
 | 
						|
func init() {
 | 
						|
	allDescriptors, err := util.GetContainerPidInformationDescriptors()
 | 
						|
	if err != nil {
 | 
						|
		// Should never happen
 | 
						|
		logrus.Debugf("failed call to util.GetContainerPidInformationDescriptors()")
 | 
						|
		return
 | 
						|
	}
 | 
						|
	for _, d := range allDescriptors {
 | 
						|
		isDescriptor[d] = true
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Top gathers statistics about the running processes in a container. It returns a
 | 
						|
// []string for output
 | 
						|
func (c *Container) Top(descriptors []string) ([]string, error) {
 | 
						|
	conStat, err := c.State()
 | 
						|
	if err != nil {
 | 
						|
		return nil, fmt.Errorf("unable to look up state for %s: %w", c.ID(), err)
 | 
						|
	}
 | 
						|
	if conStat != define.ContainerStateRunning {
 | 
						|
		return nil, errors.New("top can only be used on running containers")
 | 
						|
	}
 | 
						|
 | 
						|
	// Default to 'ps -ef' compatible descriptors
 | 
						|
	if len(strings.Join(descriptors, "")) == 0 {
 | 
						|
		descriptors = []string{"user", "pid", "ppid", "pcpu", "etime", "tty", "time", "args"}
 | 
						|
	}
 | 
						|
 | 
						|
	// If everything in descriptors is a supported AIX format
 | 
						|
	// descriptor, we use 'ps -ao <descriptors>', otherwise we pass
 | 
						|
	// everything straight through to ps.
 | 
						|
	supportedDescriptors := true
 | 
						|
	for _, d := range descriptors {
 | 
						|
		if _, ok := isDescriptor[d]; !ok {
 | 
						|
			supportedDescriptors = false
 | 
						|
			break
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if supportedDescriptors {
 | 
						|
		descriptors = []string{"-ao", strings.Join(descriptors, ",")}
 | 
						|
	}
 | 
						|
 | 
						|
	// Note that the descriptors to ps(1) must be shlexed (see #12452).
 | 
						|
	psDescriptors := []string{}
 | 
						|
	for _, d := range descriptors {
 | 
						|
		shSplit, err := shlex.Split(d)
 | 
						|
		if err != nil {
 | 
						|
			return nil, fmt.Errorf("parsing ps args: %w", err)
 | 
						|
		}
 | 
						|
		for _, s := range shSplit {
 | 
						|
			if s != "" {
 | 
						|
				psDescriptors = append(psDescriptors, s)
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	jailName, err := c.jailName()
 | 
						|
	if err != nil {
 | 
						|
		return nil, fmt.Errorf("getting jail name: %w", err)
 | 
						|
	}
 | 
						|
 | 
						|
	args := []string{
 | 
						|
		"-J",
 | 
						|
		jailName,
 | 
						|
	}
 | 
						|
	args = append(args, psDescriptors...)
 | 
						|
 | 
						|
	output, err := execPS(args)
 | 
						|
	if err != nil {
 | 
						|
		return nil, fmt.Errorf("executing ps(1): %w", err)
 | 
						|
	}
 | 
						|
 | 
						|
	return output, nil
 | 
						|
}
 | 
						|
 | 
						|
func execPS(args []string) ([]string, error) {
 | 
						|
	cmd := exec.Command("ps", args...)
 | 
						|
	stdoutPipe, err := cmd.StdoutPipe()
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	stderrPipe, err := cmd.StderrPipe()
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	var wg sync.WaitGroup
 | 
						|
	wg.Add(2)
 | 
						|
	stdout := []string{}
 | 
						|
	go func() {
 | 
						|
		scanner := bufio.NewScanner(stdoutPipe)
 | 
						|
		for scanner.Scan() {
 | 
						|
			stdout = append(stdout, scanner.Text())
 | 
						|
		}
 | 
						|
		wg.Done()
 | 
						|
	}()
 | 
						|
	stderr := []string{}
 | 
						|
	go func() {
 | 
						|
		scanner := bufio.NewScanner(stderrPipe)
 | 
						|
		for scanner.Scan() {
 | 
						|
			stderr = append(stderr, scanner.Text())
 | 
						|
		}
 | 
						|
		wg.Done()
 | 
						|
	}()
 | 
						|
 | 
						|
	if err := cmd.Start(); err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	wg.Wait()
 | 
						|
	if err := cmd.Wait(); err != nil {
 | 
						|
		return nil, fmt.Errorf("ps(1) command failed: %w, output: %s", err, strings.Join(stderr, " "))
 | 
						|
	}
 | 
						|
 | 
						|
	return stdout, nil
 | 
						|
}
 |