<dd>If this element is present, it names the parent device (that
is, a controller to which this node belongs).
</dd>
+ <dt><code>devnode</code></dt>
+ <dd>This node appears for each associated <code>/dev</code>
+ special file. A mandatory attribute <code>type</code> specify
+ the kind of file path, which may be either <code>dev</code> for
+ the main name, or <code>link</code> for additional symlinks.
+ </dd>
<dt><code>capability</code></dt>
<dd>This node appears for each capability that libvirt
associates with a node. A mandatory
<optional>
<element name="path"><text/></element>
</optional>
+ <optional>
+ <element name="devnode">
+ <attribute name='type'>
+ <value>dev</value>
+ </attribute>
+ <text/>
+ </element>
+ </optional>
+ <zeroOrMore>
+ <element name="devnode">
+ <attribute name='type'>
+ <value>link</value>
+ </attribute>
+ <text/>
+ </element>
+ </zeroOrMore>
<optional>
<ref name="parent"/>
</optional>
#define VIR_FROM_THIS VIR_FROM_NODEDEV
+VIR_ENUM_IMPL(virNodeDevDevnode, VIR_NODE_DEV_DEVNODE_LAST,
+ "dev",
+ "link")
+
VIR_ENUM_IMPL(virNodeDevCap, VIR_NODE_DEV_CAP_LAST,
"system",
"pci",
VIR_FREE(def->driver);
VIR_FREE(def->sysfs_path);
VIR_FREE(def->parent_sysfs_path);
+ VIR_FREE(def->devnode);
+ virStringListFree(def->devlinks);
caps = def->caps;
while (caps) {
virBufferAdjustIndent(&buf, 2);
virBufferEscapeString(&buf, "<name>%s</name>\n", def->name);
virBufferEscapeString(&buf, "<path>%s</path>\n", def->sysfs_path);
+ if (def->devnode)
+ virBufferEscapeString(&buf, "<devnode type='dev'>%s</devnode>\n",
+ def->devnode);
+ if (def->devlinks) {
+ for (i = 0; def->devlinks[i]; i++)
+ virBufferEscapeString(&buf, "<devnode type='link'>%s</devnode>\n",
+ def->devlinks[i]);
+ }
if (def->parent)
virBufferEscapeString(&buf, "<parent>%s</parent>\n", def->parent);
if (def->driver) {
virNodeDeviceDefPtr def;
virNodeDevCapsDefPtr *next_cap;
xmlNodePtr *nodes;
- int n;
+ int n, m;
size_t i;
if (VIR_ALLOC(def) < 0)
goto error;
}
+ /* Parse devnodes */
+ nodes = NULL;
+ if ((n = virXPathNodeSet("./devnode", ctxt, &nodes)) < 0)
+ goto error;
+
+ if (VIR_ALLOC_N(def->devlinks, n + 1) < 0)
+ goto error;
+
+ for (i = 0, m = 0; i < n; i++) {
+ xmlNodePtr node = nodes[i];
+ char *tmp = virXMLPropString(node, "type");
+ virNodeDevDevnodeType type;
+
+ if (!tmp) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ "%s", _("missing devnode type"));
+ goto error;
+ }
+
+ if ((type = virNodeDevDevnodeTypeFromString(tmp)) < 0) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("unknown devnode type '%s'"), tmp);
+ VIR_FREE(tmp);
+ goto error;
+ }
+
+ switch (type) {
+ case VIR_NODE_DEV_DEVNODE_DEV:
+ def->devnode = (char*)xmlNodeGetContent(node);
+ break;
+ case VIR_NODE_DEV_DEVNODE_LINK:
+ def->devlinks[m++] = (char*)xmlNodeGetContent(node);
+ break;
+ case VIR_NODE_DEV_DEVNODE_LAST:
+ break;
+ }
+ }
+
/* Extract device parent, if any */
def->parent = virXPathString("string(./parent[1])", ctxt);
def->parent_wwnn = virXPathString("string(./parent[1]/@wwnn)", ctxt);
# define CREATE_DEVICE 1
# define EXISTING_DEVICE 0
+typedef enum {
+ /* Keep in sync with VIR_ENUM_IMPL in node_device_conf.c */
+ VIR_NODE_DEV_DEVNODE_DEV,
+ VIR_NODE_DEV_DEVNODE_LINK,
+
+ VIR_NODE_DEV_DEVNODE_LAST
+} virNodeDevDevnodeType;
+
+VIR_ENUM_DECL(virNodeDevDevnode)
+
typedef enum {
/* Keep in sync with VIR_ENUM_IMPL in node_device_conf.c */
VIR_NODE_DEV_CAP_SYSTEM, /* System capability */
char *parent_wwpn; /* optional parent wwpn */
char *parent_fabric_wwn; /* optional parent fabric_wwn */
char *driver; /* optional driver name */
+ char *devnode; /* /dev path */
+ char **devlinks; /* /dev links */
virNodeDevCapsDefPtr caps; /* optional device capabilities */
};
return 0;
}
+static int
+udevGetDeviceNodes(struct udev_device *device,
+ virNodeDeviceDefPtr def)
+{
+ const char *devnode = NULL;
+ struct udev_list_entry *list_entry = NULL;
+ int n = 0;
+
+ devnode = udev_device_get_devnode(device);
+
+ if (VIR_STRDUP(def->devnode, devnode) < 0)
+ return -1;
+
+ udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(device))
+ n++;
+
+ if (VIR_ALLOC_N(def->devlinks, n + 1) < 0)
+ return -1;
+
+ n = 0;
+ udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(device)) {
+ if (VIR_STRDUP(def->devlinks[n++], udev_list_entry_get_name(list_entry)) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
static int
udevGetDeviceType(struct udev_device *device,
virNodeDevCapType *type)
if (udevGetDeviceType(device, &def->caps->data.type) != 0)
goto cleanup;
+ if (udevGetDeviceNodes(device, def) != 0)
+ goto cleanup;
+
if (udevGetDeviceDetails(device, def) != 0)
goto cleanup;
<device>
<name>storage_serial_3600c0ff000d7a2a5d463ff4902000000</name>
+ <devnode type='dev'>/dev/sdb</devnode>
+ <devnode type='link'>/dev/disk/by-id/usb-SanDisk_Ultra_Fit_4C530001051009112405-0:0</devnode>
+ <devnode type='link'>/dev/disk/by-path/pci-0000:00:14.0-usb-0:1:1.0-scsi-0:0:0:0</devnode>
+ <devnode type='link'>/dev/disk/by-uuid/661A1A460111DA18</devnode>
<parent>pci_10df_fe00_scsi_host_scsi_device_lun8</parent>
<capability type='storage'>
<block>/dev/sdj</block>
DO_TEST("pci_8086_27c5_scsi_host_scsi_host");
DO_TEST("pci_8086_27c5_scsi_host");
DO_TEST("storage_serial_SATA_HTS721010G9SA00_MPCZ12Y0GNGWSE");
+ DO_TEST("storage_serial_3600c0ff000d7a2a5d463ff4902000000");
DO_TEST("usb_device_1d6b_1_0000_00_1d_0_if0");
DO_TEST("usb_device_1d6b_1_0000_00_1d_0");
DO_TEST("pci_8086_4238_pcie_wireless");