2014-02-17 23:14:30 +00:00
package devices
import (
2014-05-31 01:30:27 +00:00
"errors"
2014-02-17 23:14:30 +00:00
"fmt"
2014-05-31 01:30:27 +00:00
"io/ioutil"
2014-02-17 23:14:30 +00:00
"os"
2014-05-31 01:30:27 +00:00
"path/filepath"
2014-02-17 23:14:30 +00:00
"syscall"
)
const (
Wildcard = - 1
)
2014-05-31 01:30:27 +00:00
var (
ErrNotADeviceNode = errors . New ( "not a device node" )
)
2014-02-17 23:14:30 +00: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
}
func GetDeviceNumberString ( deviceNumber int64 ) string {
if deviceNumber == Wildcard {
return "*"
} else {
return fmt . Sprintf ( "%d" , deviceNumber )
}
}
2014-05-31 01:30:27 +00:00
func ( device * Device ) GetCgroupAllowString ( ) string {
2014-02-17 23:14:30 +00: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-05-31 01:30:27 +00:00
func GetDevice ( path string , cgroupPermissions string ) ( * Device , error ) {
fileInfo , err := os . Stat ( path )
if err != nil {
return nil , err
}
2014-02-17 23:14:30 +00:00
var (
devType rune
2014-05-31 01:30:27 +00:00
mode = fileInfo . Mode ( )
fileModePermissionBits = os . FileMode . Perm ( mode )
2014-02-17 23:14:30 +00:00
)
switch {
2014-05-31 01:30:27 +00:00
case mode & os . ModeDevice == 0 :
return nil , ErrNotADeviceNode
case mode & os . ModeCharDevice != 0 :
2014-02-17 23:14:30 +00:00
fileModePermissionBits |= syscall . S_IFCHR
devType = 'c'
default :
fileModePermissionBits |= syscall . S_IFBLK
devType = 'b'
}
2014-05-31 01:30:27 +00:00
stat_t , ok := fileInfo . Sys ( ) . ( * syscall . Stat_t )
2014-02-17 23:14:30 +00:00
if ! ok {
2014-05-31 01:30:27 +00:00
return nil , fmt . Errorf ( "cannot determine the device number for device %s" , path )
2014-02-17 23:14:30 +00:00
}
2014-05-31 01:30:27 +00:00
devNumber := int ( stat_t . Rdev )
2014-02-17 23:14:30 +00:00
2014-05-31 01:30:27 +00:00
return & Device {
2014-02-17 23:14:30 +00:00
Type : devType ,
Path : path ,
MajorNumber : Major ( devNumber ) ,
MinorNumber : Minor ( devNumber ) ,
CgroupPermissions : cgroupPermissions ,
FileMode : fileModePermissionBits ,
2014-05-31 01:30:27 +00:00
} , nil
2014-02-17 23:14:30 +00:00
}
2014-05-31 01:30:27 +00:00
func GetHostDeviceNodes ( ) ( [ ] * Device , error ) {
return getDeviceNodes ( "/dev" )
}
func getDeviceNodes ( path string ) ( [ ] * Device , error ) {
files , err := ioutil . ReadDir ( path )
if err != nil {
return nil , err
2014-02-17 23:14:30 +00:00
}
2014-05-31 01:30:27 +00:00
out := [ ] * Device { }
for _ , f := range files {
if f . IsDir ( ) {
switch f . Name ( ) {
case "pts" , "shm" , "fd" :
continue
default :
sub , err := getDeviceNodes ( filepath . Join ( path , f . Name ( ) ) )
if err != nil {
return nil , err
}
out = append ( out , sub ... )
continue
}
}
device , err := GetDevice ( filepath . Join ( path , f . Name ( ) ) , "rwm" )
if err != nil {
if err == ErrNotADeviceNode {
continue
}
return nil , err
}
out = append ( out , device )
}
return out , nil
}