github.com/openebs/node-disk-manager@v1.9.1-0.20230225014141-4531f06ffa1e/pkg/udev/common.go (about)

     1  /*
     2  Copyright 2018 OpenEBS Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  // +build linux,cgo
    18  
    19  package udev
    20  
    21  /*
    22    #cgo LDFLAGS: -ludev
    23    #include <stdlib.h>
    24  */
    25  import "C"
    26  import (
    27  	"os"
    28  	"strconv"
    29  	"strings"
    30  	"unsafe"
    31  
    32  	"github.com/openebs/node-disk-manager/pkg/util"
    33  )
    34  
    35  const (
    36  	NDMBlockDevicePrefix      = "blockdevice-"         // NDMBlockDevicePrefix used as device's uuid prefix
    37  	UDEV_SUBSYSTEM            = "block"                // udev to filter this device type
    38  	UDEV_SYSTEM               = "disk"                 // used to filter devices other than disk which udev tracks (eg. CD ROM)
    39  	UDEV_PARTITION            = "partition"            // used to filter out partitions
    40  	UDEV_PATH                 = "DEVPATH"              // udev attribute to get device path
    41  	UDEV_WWN                  = "ID_WWN"               // udev attribute to get device WWN number
    42  	UDEV_SERIAL               = "ID_SERIAL_SHORT"      // udev attribute to get device serial number
    43  	UDEV_SERIAL_FULL          = "ID_SERIAL"            // udev attribute to get - separated vendor, model, serial
    44  	UDEV_BUS                  = "ID_BUS"               // udev attribute to get bus name
    45  	UDEV_MODEL                = "ID_MODEL"             // udev attribute to get device model number
    46  	UDEV_VENDOR               = "ID_VENDOR"            // udev attribute to get device vendor details
    47  	UDEV_TYPE                 = "ID_TYPE"              // udev attribute to get device type
    48  	UDEV_MAJOR                = "MAJOR"                // udev attribute to get device major no
    49  	UDEV_MINOR                = "MINOR"                // udev attribute to get device minor no
    50  	UDEV_UUID                 = "UDEV_UUID"            // ndm attribute to get device uuid
    51  	UDEV_SYSPATH              = "UDEV_SYSPATH"         // udev attribute to get device syspath
    52  	UDEV_ACTION               = "UDEV_ACTION"          // udev attribute to get monitor device action
    53  	UDEV_ACTION_ADD           = "add"                  // udev attribute constant for add action
    54  	UDEV_ACTION_REMOVE        = "remove"               // udev attribute constant for remove action
    55  	UDEV_ACTION_CHANGE        = "change"               // udev attribute constant for change action
    56  	UDEV_DEVTYPE              = "DEVTYPE"              // udev attribute to get device device type ie - disk or part
    57  	UDEV_SOURCE               = "udev"                 // udev source constant
    58  	UDEV_SYSPATH_PREFIX       = "/sys/dev/block/"      // udev syspath prefix
    59  	UDEV_DEVNAME              = "DEVNAME"              // udev attribute contain disk name given by kernel
    60  	UDEV_DEVLINKS             = "DEVLINKS"             // udev attribute contain devlinks of a disk
    61  	BY_ID_LINK                = "by-id"                // by-path devlink contains this string
    62  	BY_PATH_LINK              = "by-path"              // by-path devlink contains this string
    63  	BY_UUID_LINK              = "by-uuid"              // by-uuid devlink contains the string
    64  	BY_PARTUUID_LINK          = "by-partuuid"          // by-partuuid devlink contains the string
    65  	LINK_ID_INDEX             = 4                      // this is used to get link index from dev link
    66  	UDEV_FS_TYPE              = "ID_FS_TYPE"           // file system type the partition
    67  	UDEV_FS_UUID              = "ID_FS_UUID"           // UUID of the filesystem present
    68  	UDEV_PARTITION_TABLE_TYPE = "ID_PART_TABLE_TYPE"   // udev attribute to get partition table type(gpt/dos)
    69  	UDEV_PARTITION_TABLE_UUID = "ID_PART_TABLE_UUID"   // udev attribute to get partition table UUID
    70  	UDEV_PARTITION_NUMBER     = "ID_PART_ENTRY_NUMBER" // udev attribute to get partition number
    71  	UDEV_PARTITION_UUID       = "ID_PART_ENTRY_UUID"   // udev attribute to get partition uuid
    72  	UDEV_PARTITION_TYPE       = "ID_PART_ENTRY_TYPE"   // udev attribute to get partition type
    73  	UDEV_DM_UUID              = "DM_UUID"              // udev attribute to get the device mapper uuid
    74  	// UDEV_DM_NAME is udev attribute to get the name of the dm device. This is used to generate the device mapper path
    75  	UDEV_DM_NAME = "DM_NAME"
    76  	// SYMLINK is used to represent any manually created device symlinks
    77  	SYMLINK = "symlink"
    78  )
    79  
    80  // UdevDiskDetails struct contain different attribute of disk.
    81  type UdevDiskDetails struct {
    82  	WWN            string
    83  	Model          string   // Model is Model of disk.
    84  	Serial         string   // Serial is Serial of a disk.
    85  	Vendor         string   // Vendor is Vendor of a disk.
    86  	Path           string   // Path is Path of a disk.
    87  	ByIdDevLinks   []string // ByIdDevLinks contains by-id devlinks
    88  	ByPathDevLinks []string // ByPathDevLinks contains by-path devlinks
    89  	SymLinks       []string // SymLinks contains device symlinks if any
    90  	DiskType       string   // DeviceType can be disk, partition
    91  	// IDType is used for uuid generation using the legacy algorithm
    92  	IDType     string
    93  	FileSystem string // FileSystem on the disk
    94  	// Partitiontype on the disk/device
    95  	PartitionType string
    96  	// PartitionNumber is the partition number, for /dev/sdb1, partition number is 1
    97  	PartitionNumber uint8
    98  	// PartitionTableType is the type of the partition table (dos/gpt)
    99  	PartitionTableType string
   100  	// DMPath is the /dev/mapper path if this is a dm device
   101  	DMPath string
   102  }
   103  
   104  // freeCharPtr frees c pointer
   105  func freeCharPtr(s *C.char) {
   106  	C.free(unsafe.Pointer(s))
   107  }
   108  
   109  //DiskInfoFromLibudev returns disk attribute extracted using libudev apicalls.
   110  func (device *UdevDevice) DiskInfoFromLibudev() UdevDiskDetails {
   111  	devLinks := device.GetDevLinks()
   112  	diskDetails := UdevDiskDetails{
   113  		WWN:                device.GetPropertyValue(UDEV_WWN),
   114  		Model:              device.GetPropertyValue(UDEV_MODEL),
   115  		Serial:             device.GetPropertyValue(UDEV_SERIAL),
   116  		Vendor:             device.GetPropertyValue(UDEV_VENDOR),
   117  		Path:               device.GetPropertyValue(UDEV_DEVNAME),
   118  		ByIdDevLinks:       devLinks[BY_ID_LINK],
   119  		ByPathDevLinks:     devLinks[BY_PATH_LINK],
   120  		SymLinks:           devLinks[SYMLINK],
   121  		DiskType:           device.GetDevtype(),
   122  		IDType:             device.GetPropertyValue(UDEV_TYPE),
   123  		FileSystem:         device.GetFileSystemInfo(),
   124  		PartitionType:      device.GetPartitionType(),
   125  		PartitionNumber:    device.GetPartitionNumber(),
   126  		PartitionTableType: device.GetPropertyValue(UDEV_PARTITION_TABLE_TYPE),
   127  	}
   128  	// get the devicemapper path from the dm name
   129  	dmName := device.GetPropertyValue(UDEV_DM_NAME)
   130  	if len(dmName) != 0 {
   131  		diskDetails.DMPath = "/dev/mapper/" + dmName
   132  	}
   133  	return diskDetails
   134  }
   135  
   136  // GetUid returns unique id for the disk block device
   137  func (device *UdevDevice) GetUid() string {
   138  	uid := device.GetPropertyValue(UDEV_WWN) +
   139  		device.GetPropertyValue(UDEV_MODEL) +
   140  		device.GetPropertyValue(UDEV_SERIAL) +
   141  		device.GetPropertyValue(UDEV_VENDOR)
   142  
   143  	idtype := device.GetPropertyValue(UDEV_TYPE)
   144  
   145  	model := device.GetPropertyValue(UDEV_MODEL)
   146  
   147  	// Virtual disks either have no attributes or they all have
   148  	// the same attributes. Adding hostname in uid so that disks from different
   149  	// nodes can be differentiated. Also, putting devpath in uid so that disks
   150  	// from the same node also can be differentiated.
   151  	// 	On Gke, we have the ID_TYPE property, but still disks will have
   152  	// same attributes. We have to put a special check to handle it and process
   153  	// it like a Virtual disk.
   154  	localDiskModels := make([]string, 0)
   155  	localDiskModels = append(localDiskModels, "EphemeralDisk")
   156  	localDiskModels = append(localDiskModels, "Virtual_disk")
   157  	localDiskModels = append(localDiskModels, "QEMU_HARDDISK")
   158  	if len(idtype) == 0 || util.Contains(localDiskModels, model) {
   159  		// as hostNetwork is true, os.Hostname will give you the node's Hostname
   160  		host, _ := os.Hostname()
   161  		uid += host + device.GetPropertyValue(UDEV_DEVNAME)
   162  	}
   163  
   164  	return NDMBlockDevicePrefix + util.Hash(uid)
   165  }
   166  
   167  // IsDisk returns true if device is a disk
   168  func (device *UdevDevice) IsDisk() bool {
   169  	return device.GetDevtype() == UDEV_SYSTEM
   170  }
   171  
   172  // IsPartition return true if device is a partition
   173  func (device *UdevDevice) IsParitition() bool {
   174  	return device.GetDevtype() == UDEV_PARTITION
   175  }
   176  
   177  // GetFileSystemInfo returns filesystem type on disk/partition if it exists.
   178  func (device *UdevDevice) GetFileSystemInfo() string {
   179  	fileSystem := device.GetPropertyValue(UDEV_FS_TYPE)
   180  	return fileSystem
   181  }
   182  
   183  // GetPartitionType returns the partition type of the partition, like DOS, lvm2 etc
   184  func (device *UdevDevice) GetPartitionType() string {
   185  	partitionType := device.GetPropertyValue(UDEV_PARTITION_TYPE)
   186  	return partitionType
   187  }
   188  
   189  // GetPartitionNumber returns the partition number of the device, if the device is partition
   190  // eg: /dev/sdb2 -> 2
   191  func (device *UdevDevice) GetPartitionNumber() uint8 {
   192  	partNo, err := strconv.Atoi(device.GetPropertyValue(UDEV_PARTITION_NUMBER))
   193  	if err != nil {
   194  		return 0
   195  	}
   196  	return uint8(partNo)
   197  }
   198  
   199  // GetSyspath returns syspath of a disk using syspath we can fell details
   200  // in diskInfo struct using udev probe
   201  func (device *UdevDevice) GetSyspath() string {
   202  	major := device.GetPropertyValue(UDEV_MAJOR)
   203  	minor := device.GetPropertyValue(UDEV_MINOR)
   204  	syspath := UDEV_SYSPATH_PREFIX + major + ":" + minor
   205  	return syspath
   206  }
   207  
   208  // GetPath returns the path of device in /dev directory
   209  func (device *UdevDevice) GetPath() string {
   210  	return device.GetPropertyValue(UDEV_DEVNAME)
   211  }
   212  
   213  // GetDevLinks returns syspath of a disk using syspath we can fell details
   214  // in diskInfo struct using udev probe
   215  func (device *UdevDevice) GetDevLinks() map[string][]string {
   216  	devLinkMap := make(map[string][]string)
   217  	byIdLink := make([]string, 0)
   218  	byPathLink := make([]string, 0)
   219  	symLink := make([]string, 0)
   220  	for _, link := range strings.Split(device.GetPropertyValue(UDEV_DEVLINKS), " ") {
   221  		/*
   222  			devlink is like - /dev/disk/by-id/scsi-0Google_PersistentDisk_demo-disk
   223  			parts = ["", "dev", "disk", "by-id", "scsi-0Google_PersistentDisk_demo-disk"]
   224  			parts[4] contains link index like model or wwn or sysPath (wwn-0x5000c5009e3a8d2b) (ata-ST500LM021-1KJ152_W6HFGR)
   225  		*/
   226  		parts := strings.Split(link, "/")
   227  		if util.Contains(parts, BY_ID_LINK) {
   228  			/*
   229  				A default by-id link is observed to be created for all types of disks (physical, virtual and cloud).
   230  				This link has the format - bus, vendor, model, serial - all appended in the same order. Keeping this
   231  				link as the first element of array for consistency purposes.
   232  			*/
   233  			if strings.HasPrefix(parts[LINK_ID_INDEX], device.GetPropertyValue(UDEV_BUS)) && strings.HasSuffix(parts[LINK_ID_INDEX], device.GetPropertyValue(UDEV_SERIAL_FULL)) {
   234  				byIdLink = append([]string{link}, byIdLink...)
   235  			} else {
   236  				byIdLink = append(byIdLink, link)
   237  			}
   238  		} else if util.Contains(parts, BY_PATH_LINK) {
   239  			byPathLink = append(byPathLink, link)
   240  		}
   241  		// we can add very dev link in symlink map as there is a 1:1 mapping between the two links
   242  		symLink = append(symLink, link)
   243  	}
   244  	devLinkMap[BY_ID_LINK] = byIdLink
   245  	devLinkMap[BY_PATH_LINK] = byPathLink
   246  	devLinkMap[SYMLINK] = symLink
   247  	return devLinkMap
   248  }