github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/interfaces/hotplug/deviceinfo.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2018 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package hotplug
    21  
    22  import (
    23  	"fmt"
    24  	"path/filepath"
    25  
    26  	"github.com/snapcore/snapd/dirs"
    27  )
    28  
    29  type hotplugDeviceInfoData struct {
    30  	// map of all attributes returned for given uevent.
    31  	Data map[string]string `json:"data"`
    32  }
    33  
    34  // HotplugDeviceInfo carries information about added/removed device detected at runtime.
    35  type HotplugDeviceInfo struct {
    36  	hotplugDeviceInfoData
    37  }
    38  
    39  // NewHotplugDeviceInfo creates HotplugDeviceInfo structure related to udev add or remove event.
    40  func NewHotplugDeviceInfo(env map[string]string) (*HotplugDeviceInfo, error) {
    41  	if _, ok := env["DEVPATH"]; !ok {
    42  		return nil, fmt.Errorf("missing device path attribute")
    43  	}
    44  	return &HotplugDeviceInfo{
    45  		hotplugDeviceInfoData: hotplugDeviceInfoData{Data: env},
    46  	}, nil
    47  }
    48  
    49  // Returns the value of "SUBSYSTEM" attribute of the udev event associated with the device, e.g. "usb".
    50  // Subsystem value is always present.
    51  func (h *HotplugDeviceInfo) Subsystem() string {
    52  	return h.Data["SUBSYSTEM"]
    53  }
    54  
    55  // Returns full device path under /sysfs, e.g /sys/devices/pci0000:00/0000:00:14.0/usb1/1-2.
    56  // The path is derived from DEVPATH attribute of the udev event.
    57  func (h *HotplugDeviceInfo) DevicePath() string {
    58  	// DEVPATH is guaranteed to exist (checked in the ctor).
    59  	path, _ := h.Attribute("DEVPATH")
    60  	return filepath.Join(dirs.SysfsDir, path)
    61  }
    62  
    63  // Returns the value of "MINOR" attribute of the udev event associated with the device.
    64  // The Minor value may be empty.
    65  func (h *HotplugDeviceInfo) Minor() string {
    66  	return h.Data["MINOR"]
    67  }
    68  
    69  // Returns the value of "MAJOR" attribute of the udev event associated with the device.
    70  // The Major value may be empty.
    71  func (h *HotplugDeviceInfo) Major() string {
    72  	return h.Data["MAJOR"]
    73  }
    74  
    75  // Returns the value of "DEVNAME" attribute of the udev event associated with the device, e.g. "/dev/ttyUSB0".
    76  // The DeviceName value may be empty.
    77  func (h *HotplugDeviceInfo) DeviceName() string {
    78  	return h.Data["DEVNAME"]
    79  }
    80  
    81  // Returns the value of "DEVTYPE" attribute of the udev event associated with the device, e.g. "usb_device".
    82  // The DeviceType value may be empty.
    83  func (h *HotplugDeviceInfo) DeviceType() string {
    84  	return h.Data["DEVTYPE"]
    85  }
    86  
    87  // Generic method for getting arbitrary attribute from the uevent data.
    88  func (h *HotplugDeviceInfo) Attribute(name string) (string, bool) {
    89  	val, ok := h.Data[name]
    90  	return val, ok
    91  }
    92  
    93  func (h *HotplugDeviceInfo) firstAttrValueOf(tryAttrs ...string) string {
    94  	for _, attr := range tryAttrs {
    95  		if val, _ := h.Attribute(attr); val != "" {
    96  			return val
    97  		}
    98  	}
    99  	return ""
   100  }
   101  
   102  func (h *HotplugDeviceInfo) String() string {
   103  	return h.str(70)
   104  }
   105  
   106  // ShortString returns a string representation of the device with more aggressive truncating of model/vendor name.
   107  func (h *HotplugDeviceInfo) ShortString() string {
   108  	return h.str(16)
   109  }
   110  
   111  func (h *HotplugDeviceInfo) str(maxModelOrVendorLen int) string {
   112  	var nameOrPath string
   113  
   114  	// devname is the name of the device under /dev, eg. /dev/ttyS0;
   115  	// prefer devname over devpath as this is the one used to talk to the device.
   116  	if nameOrPath = h.DeviceName(); nameOrPath == "" {
   117  		// devpath is the path of the device under /sys, eg. /sys/devices/pnp0/00:04/tty/ttyS0.
   118  		nameOrPath = h.DevicePath()
   119  	}
   120  
   121  	modelOrVendor := h.firstAttrValueOf("ID_MODEL_FROM_DATABASE", "ID_MODEL", "ID_MODEL_ID", "ID_VENDOR_FROM_DATABASE", "ID_VENDOR", "ID_VENDOR_ID")
   122  	if len(modelOrVendor) > maxModelOrVendorLen {
   123  		modelOrVendor = modelOrVendor[0:maxModelOrVendorLen] + "…"
   124  	}
   125  
   126  	var serial string
   127  	if modelOrVendor != "" {
   128  		serial = h.firstAttrValueOf("ID_SERIAL_SHORT", "ID_SERIAL")
   129  	} else {
   130  		serial = h.firstAttrValueOf("ID_SERIAL", "ID_SERIAL_SHORT")
   131  	}
   132  	hasSerial := (serial != "" && serial != "noserial")
   133  
   134  	s := nameOrPath
   135  	if modelOrVendor != "" || hasSerial {
   136  		s += " ("
   137  		if modelOrVendor != "" {
   138  			s += modelOrVendor
   139  			if hasSerial {
   140  				s += "; "
   141  			}
   142  		}
   143  		if hasSerial {
   144  			s += "serial: " + serial
   145  		}
   146  		s += ")"
   147  	}
   148  
   149  	return s
   150  }