Repository: opencontainers/runc
File: exec.go (Go)

Code before:
	paths := make(map[string]string, len(args))
	for _, c := range args {
		// Split into controller:path.
		if ctr, path, ok := strings.Cut(c, ":"); ok {
			// There may be a few comma-separated controllers.
			for _, ctrl := range strings.Split(ctr, ",") {
				paths[ctrl] = path
			}
		} else {
			// No controller: prefix.
			if len(args) != 1 {
				return nil, fmt.Errorf("invalid --cgroup argument: %s (missing <controller>: prefix)", c)
			}
			paths[""] = c
		}
	}
	return paths, nil
}

func execProcess(context *cli.Context) (int, error) {
	container, err := getContainer(context)
	if err != nil {
		return -1, err
	}
	status, err := container.Status()
	if err != nil {
		return -1, err
	}
	if status == libcontainer.Stopped {
		return -1, errors.New("cannot exec in a stopped container")
	}
	if status == libcontainer.Paused && !context.Bool("ignore-paused") {
		return -1, errors.New("cannot exec in a paused container (use --ignore-paused to override)")
	}
	path := context.String("process")
	if path == "" && len(context.Args()) == 1 {
		return -1, errors.New("process args cannot be empty")
	}
	state, err := container.State()
	if err != nil {
		return -1, err
	}
	bundle, ok := utils.SearchLabels(state.Config.Labels, "bundle")
	if !ok {
		return -1, errors.New("bundle not found in labels")
	}
	p, err := getProcess(context, bundle)
	if err != nil {
		return -1, err
	}


Review comment (refactor):
Was trying to somewhat grasp what this case is for;

- if zero args are passed, we return early (start of function)
- if 1 arg is passed, it _MUST_ have a controller prefix
- But if 2 (or more) args are passed, then we don't have to?

So;

```bash
# valid: single arg
--cgroup a,b:/hello-world

# valid: single arg without controller
--cgroup /hello-world

# invalid: multiple args, one without controller
--cgroup a,b:/hello-world --cgroup /hello-world
--cgroup /hello-world --cgroup a,b:/hello-world 
```


So, if no controller is passed, it's for _everything_ which is only allowed if no other (per-controller) paths are specified, correct? (so not allowed to be combined).

☝️ Perhaps that's something we should capture in a comment on the function


Also;

- perhaps we need to change the `ok` check to also check if _controller_ is not empty, or is `,,,,,:/hello-world` something that should be considered valid?
- are duplicate controllers valid? They currently overwrite previous values (`foo,foo,foo:/bar`,  `foo:/baz`)









Code after:
// Returns a controller to path map. For cgroup v2, it's a single entity map
// with empty controller value.
func getSubCgroupPaths(args []string) (map[string]string, error) {
	if len(args) == 0 {
		return nil, nil
	}
	paths := make(map[string]string, len(args))
	for _, c := range args {
		// Split into controller:path.
		if ctr, path, ok := strings.Cut(c, ":"); ok {
			// There may be a few comma-separated controllers.
			for _, ctrl := range strings.Split(ctr, ",") {
				if ctrl == "" {
					return nil, fmt.Errorf("invalid --cgroup argument: %s (empty <controller> prefix)", c)
				}
				if _, ok := paths[ctrl]; ok {
					return nil, fmt.Errorf("invalid --cgroup argument(s): controller %s specified multiple times", ctrl)
				}
				paths[ctrl] = path
			}
		} else {
			// No "controller:" prefix (cgroup v2, a single path).
			if len(args) != 1 {
				return nil, fmt.Errorf("invalid --cgroup argument: %s (missing <controller>: prefix)", c)
			}
			paths[""] = c
		}
	}
	return paths, nil
}

func execProcess(context *cli.Context) (int, error) {
	container, err := getContainer(context)
	if err != nil {
		return -1, err
	}
	status, err := container.Status()
	if err != nil {
		return -1, err
	}
	if status == libcontainer.Stopped {
		return -1, errors.New("cannot exec in a stopped container")
	}
	if status == libcontainer.Paused && !context.Bool("ignore-paused") {
		return -1, errors.New("cannot exec in a paused container (use --ignore-paused to override)")
	}
	path := context.String("process")
	if path == "" && len(context.Args()) == 1 {
		return -1, errors.New("process args cannot be empty")
	}
	state, err := container.State()