@@ -26,6 +26,7 @@ var generateFlags = []cli.Flag{
2626 cli.StringSliceFlag {Name : "cap-drop" , Usage : "drop Linux capabilities" },
2727 cli.StringFlag {Name : "cgroups-path" , Usage : "specify the path to the cgroups" },
2828 cli.StringFlag {Name : "cwd" , Value : "/" , Usage : "current working directory for the process" },
29+ cli.StringSliceFlag {Name : "device" , Usage : "specifies a device which must be made available in the container" },
2930 cli.BoolFlag {Name : "disable-oom-kill" , Usage : "disable OOM Killer" },
3031 cli.StringSliceFlag {Name : "env" , Usage : "add environment variable e.g. key=value" },
3132 cli.StringSliceFlag {Name : "env-file" , Usage : "read in a file of environment variables" },
@@ -501,6 +502,15 @@ func setupSpec(g *generate.Generator, context *cli.Context) error {
501502 g .ClearProcessRlimits ()
502503 }
503504
505+ if context .IsSet ("device" ) {
506+ devices := context .StringSlice ("device" )
507+ for _ , deviceArg := range devices {
508+ err := addDevice (deviceArg , g )
509+ if err != nil {
510+ return err
511+ }
512+ }
513+ }
504514 err := addSeccomp (context , g )
505515 return err
506516}
@@ -625,6 +635,87 @@ func parseNamespace(ns string) (string, string, error) {
625635 return nsType , nsPath , nil
626636}
627637
638+ var deviceType = map [string ]bool {
639+ "b" : true , // a block (buffered) special file
640+ "c" : true , // a character special file
641+ "u" : true , // a character (unbuffered) special file
642+ "p" : true , // a FIFO
643+ }
644+
645+ // addDevice takes the raw string passed with the --device flag, parses it, and add it
646+ func addDevice (device string , g * generate.Generator ) error {
647+ dev := rspec.Device {}
648+
649+ // The required part and optional part are seperated by ":"
650+ argsParts := strings .Split (device , ":" )
651+ if len (argsParts ) < 4 {
652+ return fmt .Errorf ("Incomplete device arguments: %s" , device )
653+ }
654+ requiredPart := argsParts [0 :4 ]
655+ optionalPart := argsParts [4 :]
656+
657+ // The required part must contain type, major, minor, and path
658+ dev .Type = requiredPart [0 ]
659+ if ! deviceType [dev .Type ] {
660+ return fmt .Errorf ("Invalid device type: %s" , dev .Type )
661+ }
662+
663+ i , err := strconv .ParseInt (requiredPart [1 ], 10 , 64 )
664+ if err != nil {
665+ return err
666+ }
667+ dev .Major = i
668+
669+ i , err = strconv .ParseInt (requiredPart [2 ], 10 , 64 )
670+ if err != nil {
671+ return err
672+ }
673+ dev .Minor = i
674+ dev .Path = requiredPart [3 ]
675+
676+ // The optional part include all optional property
677+ for _ , s := range optionalPart {
678+ parts := strings .SplitN (s , "=" , 2 )
679+
680+ if len (parts ) != 2 {
681+ return fmt .Errorf ("Incomplete device arguments: %s" , s )
682+ }
683+
684+ name , value := parts [0 ], parts [1 ]
685+
686+ switch name {
687+ case "fileMode" :
688+ i , err := strconv .ParseInt (value , 10 , 32 )
689+ if err != nil {
690+ return err
691+ }
692+ mode := os .FileMode (i )
693+ dev .FileMode = & mode
694+ case "uid" :
695+ i , err := strconv .ParseInt (value , 10 , 32 )
696+ if err != nil {
697+ return err
698+ }
699+ uid := uint32 (i )
700+ dev .UID = & uid
701+
702+ case "gid" :
703+ i , err := strconv .ParseInt (value , 10 , 32 )
704+ if err != nil {
705+ return err
706+ }
707+ gid := uint32 (i )
708+ dev .GID = & gid
709+ default :
710+ return fmt .Errorf ("'%s' is not supported by device section" , name )
711+ }
712+ }
713+
714+ g .AddDevice (dev .Path , dev .Type , dev .Major , dev .Minor , dev .FileMode , dev .UID , dev .GID )
715+
716+ return nil
717+ }
718+
628719func addSeccomp (context * cli.Context , g * generate.Generator ) error {
629720
630721 // Set the DefaultAction of seccomp
0 commit comments