diff --git a/README.md b/README.md index 88daa6e..462f3f9 100644 --- a/README.md +++ b/README.md @@ -16,62 +16,107 @@ $ tar zxvf blkinfo_linux_x86_64.tar.gz ## Usage -### As A CLI - ``` -$ blkinfo /dev/vda +$ blkinfo /dev/vda3 ``` ```json { - "major_minor": "252:0", - "path": "/dev/vda", - "real_path": "/dev/vda", - "parent_path": "", - "child_paths": [ - "/dev/vda1" - ], - "sys_path": "/sys/devices/pci0000:00/0000:00:08.0/virtio2/block/vda", + "path": "/dev/vda3", + "resolved_path": "/dev/vda3", + "parent_path": "/dev/vda", + "child_paths": [], + "sys_path": "/sys/block/vda/vda3", + "resolved_sys_path": "/sys/devices/pci0000:00/0000:00:05.0/virtio2/block/vda/vda3", "sys": { "uevent": [ "MAJOR=252", - "MINOR=0", - "DEVNAME=vda", - "DEVTYPE=disk" + "MINOR=3", + "DEVNAME=vda3", + "DEVTYPE=partition", + "PARTN=3" ], "slaves": [], "holders": [] }, - "udev_data_path": "/run/udev/data/b252:0", + "major_minor": "252:3", + "udev_data_path": "/run/udev/data/b252:3", "udev_data": [ - "S:disk/by-path/virtio-pci-0000:00:08.0", - "S:disk/by-path/pci-0000:00:08.0", - "W:3", - "I:2430960", - "E:ID_PATH=pci-0000:00:08.0", - "E:ID_PATH_TAG=pci-0000_00_08_0", - "E:ID_PART_TABLE_UUID=xxxxxxxx", - "E:ID_PART_TABLE_TYPE=dos", - "E:ID_FS_TYPE=", + "S:disk/by-uuid/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "S:disk/by-partuuid/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "S:disk/by-path/virtio-pci-0000:00:05.0-part3", + "S:disk/by-path/pci-0000:00:05.0-part3", + "W:4", + "I:1583813", + "E:ID_SCSI=1", + "E:ID_PART_TABLE_TYPE=gpt", + "E:ID_PART_TABLE_UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "E:ID_PATH=pci-0000:00:05.0", + "E:ID_PATH_TAG=pci-0000_00_05_0", + "E:ID_FS_UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "E:ID_FS_UUID_ENC=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "E:ID_FS_VERSION=1.0", + "E:ID_FS_TYPE=ext4", + "E:ID_FS_USAGE=filesystem", + "E:ID_PART_ENTRY_SCHEME=gpt", + "E:ID_PART_ENTRY_UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "E:ID_PART_ENTRY_TYPE=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "E:ID_PART_ENTRY_NUMBER=3", + "E:ID_PART_ENTRY_OFFSET=8392704", + "E:ID_PART_ENTRY_SIZE=33548288", + "E:ID_PART_ENTRY_DISK=252:0", + "E:net.ifnames=0", "G:systemd" ], "mount_info_path": "/proc/self/mountinfo", "mount_info": { - "mount_id": "", - "parent_id": "", - "major_minor": "", - "root": "", - "mount_point": "", - "mount_options": [], - "optional_fields": [], - "filesystem_type": "", - "mount_source": "", - "super_options": [] + "mount_id": "28", + "parent_id": "0", + "major_minor": "252:3", + "root": "/", + "mount_point": "/", + "mount_options": [ + "rw", + "relatime" + ], + "optional_fields": [ + "shared:1" + ], + "filesystem_type": "ext4", + "mount_source": "/dev/vda3", + "super_options": [ + "rw", + "errors=remount-ro", + "data=ordered" + ] + }, + "os_release_path": "/etc/os-release", + "os_release": { + "BUG_REPORT_URL": "https://bugs.launchpad.net/ubuntu/", + "HOME_URL": "https://www.ubuntu.com/", + "ID": "ubuntu", + "ID_LIKE": "debian", + "NAME": "Ubuntu", + "PRETTY_NAME": "Ubuntu 18.04.3 LTS", + "PRIVACY_POLICY_URL": "https://www.ubuntu.com/legal/terms-and-policies/privacy-policy", + "SUPPORT_URL": "https://help.ubuntu.com/", + "UBUNTU_CODENAME": "bionic", + "VERSION": "18.04.3 LTS (Bionic Beaver)", + "VERSION_CODENAME": "bionic", + "VERSION_ID": "18.04" } } ``` -### As A Package +### Options + +|Option|Description| +|---|---| +|--help, -h|show help| +|--output value, -o value|output as "json" or "yaml" (default: "json")| +|--version, -v|print the version| + +## Package ```go package main @@ -84,7 +129,7 @@ import ( ) func main() { - bi, _ := blkinfo.New("/dev/vda") + bi, _ := blkinfo.New("/dev/vda3") b, _ := json.Marshal(bi) fmt.Println(string(b)) } diff --git a/blkinfo.go b/blkinfo.go index c49aef0..32d224b 100644 --- a/blkinfo.go +++ b/blkinfo.go @@ -2,7 +2,7 @@ package blkinfo import ( - "errors" + "fmt" "io/ioutil" "os" "path/filepath" @@ -11,17 +11,20 @@ import ( // BlkInfo shows block device information. type BlkInfo struct { - Path string `json:"path" ` - RealPath string `json:"real_path" ` - ParentPath string `json:"parent_path" ` - ChildPaths []string `json:"child_paths" ` - SysPath string `json:"sys_path" ` - Sys *Sys `json:"sys" ` - MajorMinor string `json:"major_minor" ` - UdevDataPath string `json:"udev_data_path" ` - UdevData []string `json:"udev_data" ` - MountInfoPath string `json:"mount_info_path"` - MountInfo *MountInfo `json:"mount_info" ` + Path string `json:"path" ` + ResolvedPath string `json:"resolved_path" ` + ParentPath string `json:"parent_path" ` + ChildPaths []string `json:"child_paths" ` + SysPath string `json:"sys_path" ` + ResolevedSysPath string `json:"resolved_sys_path"` + Sys *Sys `json:"sys" ` + MajorMinor string `json:"major_minor" ` + UdevDataPath string `json:"udev_data_path" ` + UdevData []string `json:"udev_data" ` + MountInfoPath string `json:"mount_info_path" ` + MountInfo *MountInfo `json:"mount_info" ` + OSReleasePath string `json:"os_release_path" ` + OSRelease map[string]string `json:"os_release" ` } // Sys shows sys information. @@ -50,11 +53,11 @@ type MountInfo struct { } // New initializes *BlkInfo. -func New(path string) (*BlkInfo, error) { +func New(path string) (*BlkInfo, error) { // nolint: funlen var err error if path == "" { - return nil, errors.New("a path is not given") + return nil, fmt.Errorf("a path is not given") } bi := &BlkInfo{ @@ -62,13 +65,13 @@ func New(path string) (*BlkInfo, error) { } bi.Path = path - bi.RealPath, err = filepath.EvalSymlinks(bi.Path) + bi.ResolvedPath, err = filepath.EvalSymlinks(bi.Path) if err != nil { return nil, err } - bi.SysPath, bi.ParentPath, bi.ChildPaths, err = relatedPaths(bi.RealPath) + bi.SysPath, bi.ResolevedSysPath, bi.ParentPath, bi.ChildPaths, err = relatedPaths(bi.ResolvedPath) if err != nil { return nil, err } @@ -101,7 +104,15 @@ func New(path string) (*BlkInfo, error) { } bi.MountInfoPath = filepath.Join("/", "proc", "self", "mountinfo") - bi.MountInfo, err = newMountInfo(bi.MountInfoPath, bi.RealPath) + bi.MountInfo, err = newMountInfo(bi.MountInfoPath, bi.ResolvedPath) + + if err != nil { + return nil, err + } + + bi.OSReleasePath = osReleasePath(bi.MountInfo.MountPoint) + + bi.OSRelease, err = newOSRelease(bi.OSReleasePath) if err != nil { return nil, err @@ -161,7 +172,7 @@ func ls(path string) ([]string, error) { } func newMountInfo(mountInfoPath string, path string) (*MountInfo, error) { - realPath, err := filepath.EvalSymlinks(path) + resolvedPath, err := filepath.EvalSymlinks(path) if err != nil { return nil, err } @@ -192,7 +203,7 @@ func newMountInfo(mountInfoPath string, path string) (*MountInfo, error) { return nil, err } - if realPath == realMountSource { + if resolvedPath == realMountSource { mountInfo.MountID = separatedFirst[0] mountInfo.ParentID = separatedFirst[1] mountInfo.MajorMinor = separatedFirst[2] @@ -211,18 +222,18 @@ func newMountInfo(mountInfoPath string, path string) (*MountInfo, error) { return mountInfo, nil } -func relatedPaths(path string) (sysPath string, parentPath string, childPaths []string, err error) { - realPath, err := filepath.EvalSymlinks(path) +func relatedPaths(path string) (sysPath string, resolvedSysPath string, parentPath string, childPaths []string, err error) { + resolvedPath, err := filepath.EvalSymlinks(path) if err != nil { - return "", "", []string{}, err + return "", "", "", []string{}, err } - devName := filepath.Base(realPath) + devName := filepath.Base(resolvedPath) blockPath := filepath.Join("/", "sys", "block") fileInfoList, err := ioutil.ReadDir(blockPath) if err != nil { - return "", "", []string{}, err + return "", "", "", []string{}, err } for _, fileInfo := range fileInfoList { @@ -235,7 +246,7 @@ func relatedPaths(path string) (sysPath string, parentPath string, childPaths [] fileInfoList, err = ioutil.ReadDir(sysPath) if err != nil { - return "", "", []string{}, err + return "", "", "", []string{}, err } childPaths = []string{} @@ -253,16 +264,16 @@ func relatedPaths(path string) (sysPath string, parentPath string, childPaths [] childPaths = []string{} } - sysPath, err = filepath.EvalSymlinks(sysPath) + resolvedSysPath, err := filepath.EvalSymlinks(sysPath) if err != nil { - return "", "", []string{}, err + return "", "", "", []string{}, err } - return sysPath, parentPath, childPaths, nil + return sysPath, resolvedSysPath, parentPath, childPaths, nil } } - return "", "", []string{}, errors.New("sysPath, parentPath, and childPaths are not found") + return "", "", "", []string{}, fmt.Errorf("sysPath, parentPath, and childPaths are not found") } func majorMinor(sysPath string) (string, error) { @@ -273,3 +284,43 @@ func majorMinor(sysPath string) (string, error) { return majorMinor, nil } + +func osReleasePath(mountPoint string) (path string) { + if mountPoint != "" { + path = filepath.Join(mountPoint, "etc", "os-release") + } + + return path +} + +func newOSRelease(osReleasePath string) (osRelease map[string]string, err error) { + osRelease = map[string]string{} + + if osReleasePath != "" { + osReleaseLines, err := lines(osReleasePath) + if err != nil { + return map[string]string{}, err + } + + for _, osReleaseLine := range osReleaseLines { + line := strings.TrimSpace(osReleaseLine) + if line == "" || strings.HasPrefix(line, "#") { + continue + } + + kv := strings.SplitN(osReleaseLine, "=", 2) + expectedKVSize := 2 + + if len(kv) != expectedKVSize { + return map[string]string{}, fmt.Errorf(`unexpected osReleaseLine, "%s"`, osReleaseLine) + } + + key := kv[0] + value := kv[1] + + osRelease[key] = trimQuotationMarks(value) + } + } + + return osRelease, nil +} diff --git a/cmd/blkinfo/blkinfo.go b/cmd/blkinfo/blkinfo.go index 7758806..1eabaf1 100644 --- a/cmd/blkinfo/blkinfo.go +++ b/cmd/blkinfo/blkinfo.go @@ -28,7 +28,7 @@ func main() { // nolint: funlen }, } app.HideHelp = true - allowedOutput := "[json|yaml]" + allowedOutput := `"json" or "yaml"` app.Flags = []cli.Flag{ cli.BoolFlag{ Name: "help, h", @@ -70,7 +70,7 @@ func main() { // nolint: funlen case "yaml": bytes, err = yaml.JSONToYAML(bytes) default: - err = fmt.Errorf("unknown output '%s', expected %s", output, allowedOutput) + err = fmt.Errorf(`unknown output "%s", expected %s`, output, allowedOutput) } if err != nil {