diff --git a/cmd/oci-runtime-tool/generate.go b/cmd/oci-runtime-tool/generate.go index 6be5f6dd5..e8aeaec8a 100644 --- a/cmd/oci-runtime-tool/generate.go +++ b/cmd/oci-runtime-tool/generate.go @@ -26,7 +26,9 @@ var generateFlags = []cli.Flag{ cli.StringSliceFlag{Name: "cap-drop", Usage: "drop Linux capabilities"}, cli.StringFlag{Name: "cgroups-path", Usage: "specify the path to the cgroups"}, cli.StringFlag{Name: "cwd", Value: "/", Usage: "current working directory for the process"}, - cli.StringSliceFlag{Name: "device", Usage: "specifies a device which must be made available in the container"}, + cli.StringSliceFlag{Name: "device-add", Usage: "add a device which must be made available in the container"}, + cli.StringSliceFlag{Name: "device-remove", Usage: "remove a device which must be made available in the container"}, + cli.BoolFlag{Name: "device-remove-all", Usage: "remove all devices which must be made available in the container"}, cli.BoolFlag{Name: "disable-oom-kill", Usage: "disable OOM Killer"}, cli.StringSliceFlag{Name: "env", Usage: "add environment variable e.g. key=value"}, cli.StringSliceFlag{Name: "env-file", Usage: "read in a file of environment variables"}, @@ -502,15 +504,31 @@ func setupSpec(g *generate.Generator, context *cli.Context) error { g.ClearProcessRlimits() } - if context.IsSet("device") { - devices := context.StringSlice("device") + if context.IsSet("device-add") { + devices := context.StringSlice("device-add") for _, deviceArg := range devices { - err := addDevice(deviceArg, g) + dev, err := parseDevice(deviceArg, g) if err != nil { return err } + g.AddDevice(dev) } } + + if context.IsSet("device-remove") { + devices := context.StringSlice("device-remove") + for _, device := range devices { + err := g.RemoveDevice(device) + if err != nil { + return err + } + } + } + + if context.Bool("device-remove-all") { + g.ClearLinuxDevices() + } + err := addSeccomp(context, g) return err } @@ -642,14 +660,14 @@ var deviceType = map[string]bool{ "p": true, // a FIFO } -// addDevice takes the raw string passed with the --device flag, parses it, and add it -func addDevice(device string, g *generate.Generator) error { +// parseDevice takes the raw string passed with the --device-add flag +func parseDevice(device string, g *generate.Generator) (rspec.LinuxDevice, error) { dev := rspec.LinuxDevice{} // The required part and optional part are separated by ":" argsParts := strings.Split(device, ":") if len(argsParts) < 4 { - return fmt.Errorf("Incomplete device arguments: %s", device) + return dev, fmt.Errorf("Incomplete device arguments: %s", device) } requiredPart := argsParts[0:4] optionalPart := argsParts[4:] @@ -657,18 +675,18 @@ func addDevice(device string, g *generate.Generator) error { // The required part must contain type, major, minor, and path dev.Type = requiredPart[0] if !deviceType[dev.Type] { - return fmt.Errorf("Invalid device type: %s", dev.Type) + return dev, fmt.Errorf("Invalid device type: %s", dev.Type) } i, err := strconv.ParseInt(requiredPart[1], 10, 64) if err != nil { - return err + return dev, err } dev.Major = i i, err = strconv.ParseInt(requiredPart[2], 10, 64) if err != nil { - return err + return dev, err } dev.Minor = i dev.Path = requiredPart[3] @@ -678,7 +696,7 @@ func addDevice(device string, g *generate.Generator) error { parts := strings.SplitN(s, "=", 2) if len(parts) != 2 { - return fmt.Errorf("Incomplete device arguments: %s", s) + return dev, fmt.Errorf("Incomplete device arguments: %s", s) } name, value := parts[0], parts[1] @@ -687,14 +705,14 @@ func addDevice(device string, g *generate.Generator) error { case "fileMode": i, err := strconv.ParseInt(value, 10, 32) if err != nil { - return err + return dev, err } mode := os.FileMode(i) dev.FileMode = &mode case "uid": i, err := strconv.ParseInt(value, 10, 32) if err != nil { - return err + return dev, err } uid := uint32(i) dev.UID = &uid @@ -702,18 +720,16 @@ func addDevice(device string, g *generate.Generator) error { case "gid": i, err := strconv.ParseInt(value, 10, 32) if err != nil { - return err + return dev, err } gid := uint32(i) dev.GID = &gid default: - return fmt.Errorf("'%s' is not supported by device section", name) + return dev, fmt.Errorf("'%s' is not supported by device section", name) } } - g.AddDevice(dev.Path, dev.Type, dev.Major, dev.Minor, dev.FileMode, dev.UID, dev.GID) - - return nil + return dev, nil } func addSeccomp(context *cli.Context, g *generate.Generator) error { diff --git a/completions/bash/oci-runtime-tool b/completions/bash/oci-runtime-tool index 3c259cce7..081953467 100644 --- a/completions/bash/oci-runtime-tool +++ b/completions/bash/oci-runtime-tool @@ -309,7 +309,8 @@ _oci-runtime-tool_generate() { --cap-drop --cgroups-path --cwd - --device + --device-add + --device-remove --env --env-file --gid @@ -367,6 +368,7 @@ _oci-runtime-tool_generate() { " local boolean_options=" + --device-remove-all --disable-oom-kill --help -h --linux-namespace-remove-all diff --git a/generate/generate.go b/generate/generate.go index f8a6294ec..737cd9e0c 100644 --- a/generate/generate.go +++ b/generate/generate.go @@ -1015,21 +1015,45 @@ func (g *Generator) RemoveLinuxNamespace(ns string) error { } // AddDevice - add a device into g.spec.Linux.Devices -func (g *Generator) AddDevice(path, devType string, major, minor int64, fileMode *os.FileMode, uid, gid *uint32) { +func (g *Generator) AddDevice(device rspec.LinuxDevice) { g.initSpecLinux() - device := rspec.LinuxDevice{ - Path: path, - Type: devType, - Major: major, - Minor: minor, - FileMode: fileMode, - UID: uid, - GID: gid, + for i, dev := range g.spec.Linux.Devices { + if dev.Path == device.Path { + g.spec.Linux.Devices[i] = device + return + } + if dev.Type == device.Type && dev.Major == device.Major && dev.Minor == device.Minor { + fmt.Fprintln(os.Stderr, "WARNING: The same type, major and minor should not be used for multiple devices.") + } } + g.spec.Linux.Devices = append(g.spec.Linux.Devices, device) } +//RemoveDevice remove a device from g.spec.Linux.Devices +func (g *Generator) RemoveDevice(path string) error { + if g.spec == nil || g.spec.Linux == nil || g.spec.Linux.Devices == nil { + return nil + } + + for i, device := range g.spec.Linux.Devices { + if device.Path == path { + g.spec.Linux.Devices = append(g.spec.Linux.Devices[:i], g.spec.Linux.Devices[i+1:]...) + return nil + } + } + return nil +} + +func (g *Generator) ClearLinuxDevices() { + if g.spec == nil || g.spec.Linux == nil || g.spec.Linux.Devices == nil { + return + } + + g.spec.Linux.Devices = []rspec.LinuxDevice{} +} + // strPtr returns the pointer pointing to the string s. func strPtr(s string) *string { return &s } diff --git a/man/oci-runtime-tool-generate.1.md b/man/oci-runtime-tool-generate.1.md index bd42b84a9..32bdcffa5 100644 --- a/man/oci-runtime-tool-generate.1.md +++ b/man/oci-runtime-tool-generate.1.md @@ -51,7 +51,7 @@ read the configuration from `config.json`. **--cwd**=PATH Current working directory for the process. The deafult is */*. -**--device**=*TYPE:MAJOR:MINOR:PATH[:OPTIONS...]* +**--device-add**=*TYPE:MAJOR:MINOR:PATH[:OPTIONS...]* Add a device file in container. e.g. --device=c:10:229:/dev/fuse:fileMode=438:uid=0:gid=0 The *TYPE*, *MAJOR*, *MINOR*, *PATH* are required. *TYPE* is the device type. The acceptable values are b (block), c (character), u (unbuffered), p (FIFO). @@ -60,6 +60,14 @@ read the configuration from `config.json`. The *fileMode*, *uid*, *gid* are optional. *fileMode* is the file mode of the device file. *uid*/*gid* is the user/group id of the device file. + This option can be specified multiple times. + +**--device-remove**=*PATH* + Remove a device file in container. + This option can be specified multiple times. + +**--device-remove-all**=true|false + Remove all devices for linux inside the container. The default is *false*. **--disable-oom-kill**=true|false Whether to disable OOM Killer for the container or not.