2014-02-18 07:14:30 +08:00
package devices
import (
2014-05-31 09:30:27 +08:00
"errors"
2014-02-18 07:14:30 +08:00
"fmt"
2014-05-31 09:30:27 +08:00
"io/ioutil"
2014-02-18 07:14:30 +08:00
"os"
2014-05-31 09:30:27 +08:00
"path/filepath"
2014-02-18 07:14:30 +08:00
"syscall"
)
const (
Wildcard = - 1
)
2014-05-31 09:30:27 +08:00
var (
ErrNotADeviceNode = errors . New ( "not a device node" )
)
2014-09-05 07:34:34 +08:00
// Testing dependencies
var (
2014-09-05 08:00:09 +08:00
osLstat = os . Lstat
ioutilReadDir = ioutil . ReadDir
2014-09-05 07:34:34 +08:00
)
2014-02-18 07:14:30 +08:00
type Device struct {
Type rune ` json:"type,omitempty" `
Path string ` json:"path,omitempty" ` // It is fine if this is an empty string in the case that you are using Wildcards
MajorNumber int64 ` json:"major_number,omitempty" ` // Use the wildcard constant for wildcards.
MinorNumber int64 ` json:"minor_number,omitempty" ` // Use the wildcard constant for wildcards.
CgroupPermissions string ` json:"cgroup_permissions,omitempty" ` // Typically just "rwm"
FileMode os . FileMode ` json:"file_mode,omitempty" ` // The permission bits of the file's mode
2014-09-02 08:29:50 +08:00
Uid uint32 ` json:"uid,omitempty" `
Gid uint32 ` json:"gid,omitempty" `
2014-02-18 07:14:30 +08:00
}
func GetDeviceNumberString ( deviceNumber int64 ) string {
if deviceNumber == Wildcard {
return "*"
} else {
return fmt . Sprintf ( "%d" , deviceNumber )
}
}
2014-05-31 09:30:27 +08:00
func ( device * Device ) GetCgroupAllowString ( ) string {
2014-02-18 07:14:30 +08:00
return fmt . Sprintf ( "%c %s:%s %s" , device . Type , GetDeviceNumberString ( device . MajorNumber ) , GetDeviceNumberString ( device . MinorNumber ) , device . CgroupPermissions )
}
// Given the path to a device and it's cgroup_permissions(which cannot be easilly queried) look up the information about a linux device and return that information as a Device struct.
2014-06-17 05:51:02 +08:00
func GetDevice ( path , cgroupPermissions string ) ( * Device , error ) {
2014-09-05 07:34:34 +08:00
fileInfo , err := osLstat ( path )
2014-05-31 09:30:27 +08:00
if err != nil {
return nil , err
}
2014-02-18 07:14:30 +08:00
var (
devType rune
2014-05-31 09:30:27 +08:00
mode = fileInfo . Mode ( )
fileModePermissionBits = os . FileMode . Perm ( mode )
2014-02-18 07:14:30 +08:00
)
switch {
2014-05-31 09:30:27 +08:00
case mode & os . ModeDevice == 0 :
return nil , ErrNotADeviceNode
case mode & os . ModeCharDevice != 0 :
2014-02-18 07:14:30 +08:00
fileModePermissionBits |= syscall . S_IFCHR
devType = 'c'
default :
fileModePermissionBits |= syscall . S_IFBLK
devType = 'b'
}
2014-05-31 09:30:27 +08:00
stat_t , ok := fileInfo . Sys ( ) . ( * syscall . Stat_t )
2014-02-18 07:14:30 +08:00
if ! ok {
2014-05-31 09:30:27 +08:00
return nil , fmt . Errorf ( "cannot determine the device number for device %s" , path )
2014-02-18 07:14:30 +08:00
}
2014-05-31 09:30:27 +08:00
devNumber := int ( stat_t . Rdev )
2014-02-18 07:14:30 +08:00
2014-05-31 09:30:27 +08:00
return & Device {
2014-02-18 07:14:30 +08:00
Type : devType ,
Path : path ,
MajorNumber : Major ( devNumber ) ,
MinorNumber : Minor ( devNumber ) ,
CgroupPermissions : cgroupPermissions ,
FileMode : fileModePermissionBits ,
2014-09-02 08:29:50 +08:00
Uid : stat_t . Uid ,
Gid : stat_t . Gid ,
2014-05-31 09:30:27 +08:00
} , nil
2014-02-18 07:14:30 +08:00
}
2014-05-31 09:30:27 +08:00
func GetHostDeviceNodes ( ) ( [ ] * Device , error ) {
return getDeviceNodes ( "/dev" )
}
func getDeviceNodes ( path string ) ( [ ] * Device , error ) {
2014-09-05 08:00:09 +08:00
files , err := ioutilReadDir ( path )
2014-05-31 09:30:27 +08:00
if err != nil {
return nil , err
2014-02-18 07:14:30 +08:00
}
2014-05-31 09:30:27 +08:00
out := [ ] * Device { }
for _ , f := range files {
2014-10-25 02:04:20 +08:00
switch {
case f . IsDir ( ) :
2014-05-31 09:30:27 +08:00
switch f . Name ( ) {
2014-10-29 07:35:35 +08:00
case "pts" , "shm" , "fd" , "mqueue" :
2014-05-31 09:30:27 +08:00
continue
default :
sub , err := getDeviceNodes ( filepath . Join ( path , f . Name ( ) ) )
if err != nil {
return nil , err
}
out = append ( out , sub ... )
continue
}
2014-10-25 02:04:20 +08:00
case f . Name ( ) == "console" :
continue
2014-05-31 09:30:27 +08:00
}
2014-10-25 02:04:20 +08:00
device , err := GetDevice ( filepath . Join ( path , f . Name ( ) ) , "rwm" )
if err != nil {
if err == ErrNotADeviceNode {
continue
2014-05-31 09:30:27 +08:00
}
2014-10-25 02:04:20 +08:00
return nil , err
2014-05-31 09:30:27 +08:00
}
2014-10-25 02:04:20 +08:00
out = append ( out , device )
2014-05-31 09:30:27 +08:00
}
return out , nil
}