|
| 1 | +//go:build linux |
| 2 | + |
| 3 | +package libpod |
| 4 | + |
| 5 | +import ( |
| 6 | + "fmt" |
| 7 | + "os" |
| 8 | + "path/filepath" |
| 9 | + "strconv" |
| 10 | + "strings" |
| 11 | + "syscall" |
| 12 | + |
| 13 | + "github.com/containers/podman/v5/libpod/define" |
| 14 | + spec "github.com/opencontainers/runtime-spec/specs-go" |
| 15 | + "github.com/sirupsen/logrus" |
| 16 | + "golang.org/x/sys/unix" |
| 17 | +) |
| 18 | + |
| 19 | +// applyRlimitsToRunningContainer applies rlimits to all processes in the container's cgroup |
| 20 | +// using the prlimit syscall. This is Linux-specific functionality. |
| 21 | +func (c *Container) applyRlimitsToRunningContainer(rlimits []spec.POSIXRlimit) error { |
| 22 | + // Only apply to running containers |
| 23 | + if !c.ensureState(define.ContainerStateRunning, define.ContainerStatePaused) { |
| 24 | + return nil |
| 25 | + } |
| 26 | + |
| 27 | + // Get the container's cgroup path |
| 28 | + cgroupPath, err := c.cGroupPath() |
| 29 | + if err != nil { |
| 30 | + return fmt.Errorf("getting cgroup path for container %s: %w", c.ID(), err) |
| 31 | + } |
| 32 | + |
| 33 | + // Read all PIDs from the cgroup.procs file |
| 34 | + pids, err := c.getAllPIDsFromCgroup(cgroupPath) |
| 35 | + if err != nil { |
| 36 | + return fmt.Errorf("getting PIDs from cgroup %s: %w", cgroupPath, err) |
| 37 | + } |
| 38 | + |
| 39 | + // Map rlimit type strings to syscall resource numbers |
| 40 | + rlimitMap := map[string]int{ |
| 41 | + "as": syscall.RLIMIT_AS, |
| 42 | + "core": syscall.RLIMIT_CORE, |
| 43 | + "cpu": syscall.RLIMIT_CPU, |
| 44 | + "data": syscall.RLIMIT_DATA, |
| 45 | + "fsize": syscall.RLIMIT_FSIZE, |
| 46 | + "nofile": syscall.RLIMIT_NOFILE, |
| 47 | + "stack": syscall.RLIMIT_STACK, |
| 48 | + "nproc": 6, // RLIMIT_NPROC = 6 on Linux (not available in syscall package on all platforms) |
| 49 | + } |
| 50 | + |
| 51 | + // Apply each rlimit to each PID |
| 52 | + for _, rlimit := range rlimits { |
| 53 | + resource, exists := rlimitMap[rlimit.Type] |
| 54 | + if !exists { |
| 55 | + logrus.Warnf("Unknown rlimit type %s, skipping", rlimit.Type) |
| 56 | + continue |
| 57 | + } |
| 58 | + |
| 59 | + // Create the new rlimit structure |
| 60 | + newLimit := &syscall.Rlimit{ |
| 61 | + Cur: rlimit.Soft, |
| 62 | + Max: rlimit.Hard, |
| 63 | + } |
| 64 | + |
| 65 | + // Apply to all PIDs in the container |
| 66 | + for _, pid := range pids { |
| 67 | + err := c.applyRlimitToPID(pid, resource, newLimit) |
| 68 | + if err != nil { |
| 69 | + // ESRCH means the process has exited, which is fine |
| 70 | + if err == unix.ESRCH { |
| 71 | + logrus.Debugf("Process %d has exited, skipping rlimit application", pid) |
| 72 | + continue |
| 73 | + } |
| 74 | + // For other errors, log but continue with other processes |
| 75 | + logrus.Warnf("Failed to apply rlimit %s to PID %d: %v", rlimit.Type, pid, err) |
| 76 | + } |
| 77 | + } |
| 78 | + } |
| 79 | + |
| 80 | + return nil |
| 81 | +} |
| 82 | + |
| 83 | +// applyRlimitToPID applies a single rlimit to a single PID using the prlimit syscall. |
| 84 | +// This is Linux-specific functionality. |
| 85 | +func (c *Container) applyRlimitToPID(pid, resource int, newLimit *syscall.Rlimit) error { |
| 86 | + // Convert syscall.Rlimit to unix.Rlimit |
| 87 | + unixLimit := &unix.Rlimit{ |
| 88 | + Cur: newLimit.Cur, |
| 89 | + Max: newLimit.Max, |
| 90 | + } |
| 91 | + // Use the unix package's Prlimit function |
| 92 | + // Note: This function is only available on Linux |
| 93 | + return unix.Prlimit(pid, resource, unixLimit, nil) |
| 94 | +} |
| 95 | + |
| 96 | +// getAllPIDsFromCgroup reads all PIDs from a cgroup's cgroup.procs file. |
| 97 | +// This is Linux-specific functionality. |
| 98 | +func (c *Container) getAllPIDsFromCgroup(cgroupPath string) ([]int, error) { |
| 99 | + procsFile := filepath.Join(cgroupPath, "cgroup.procs") |
| 100 | + data, err := os.ReadFile(procsFile) |
| 101 | + if err != nil { |
| 102 | + return nil, err |
| 103 | + } |
| 104 | + |
| 105 | + var pids []int |
| 106 | + lines := strings.Split(string(data), "\n") |
| 107 | + for _, line := range lines { |
| 108 | + line = strings.TrimSpace(line) |
| 109 | + if line == "" { |
| 110 | + continue |
| 111 | + } |
| 112 | + pid, err := strconv.Atoi(line) |
| 113 | + if err != nil { |
| 114 | + logrus.Warnf("Invalid PID in cgroup.procs: %s", line) |
| 115 | + continue |
| 116 | + } |
| 117 | + pids = append(pids, pid) |
| 118 | + } |
| 119 | + |
| 120 | + return pids, nil |
| 121 | +} |
0 commit comments