From 185ea6a76d3f3a9e9708239d2302bd98e00460fa Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Sat, 6 Jun 2009 16:07:06 +0200 Subject: path_id: implement in C using libudev --- TODO | 1 + extras/path_id/.gitignore | 1 + extras/path_id/Makefile.am | 12 + extras/path_id/path_id | 601 ------------------------- extras/path_id/path_id.c | 365 +++++++++++++++ extras/path_id/path_id.sh | 601 +++++++++++++++++++++++++ rules/rules.d/60-persistent-storage-tape.rules | 2 +- rules/rules.d/60-persistent-storage.rules | 4 +- 8 files changed, 984 insertions(+), 603 deletions(-) create mode 100644 extras/path_id/.gitignore delete mode 100755 extras/path_id/path_id create mode 100644 extras/path_id/path_id.c create mode 100755 extras/path_id/path_id.sh diff --git a/TODO b/TODO index bedccdb635..dfbb18d0cc 100644 --- a/TODO +++ b/TODO @@ -1,4 +1,5 @@ + o kill path_id.sh (add fc, sas, iscsi to C version) o add tests for kernel provided DEVNAME logic o drop modprobe floppy alias (SUSE), it will be in the module (2.6.30) o remove MMC rules, they got a modalias now (2.6.30) diff --git a/extras/path_id/.gitignore b/extras/path_id/.gitignore new file mode 100644 index 0000000000..6fd2f89761 --- /dev/null +++ b/extras/path_id/.gitignore @@ -0,0 +1 @@ +path_id diff --git a/extras/path_id/Makefile.am b/extras/path_id/Makefile.am index f489290399..2e936234ff 100644 --- a/extras/path_id/Makefile.am +++ b/extras/path_id/Makefile.am @@ -2,7 +2,19 @@ include $(top_srcdir)/Makefile.am.inc udevhomedir = $(udev_prefix)/lib/udev dist_udevhome_SCRIPTS = \ + path_id.sh + +udevhome_PROGRAMS = \ path_id +path_id_SOURCES = \ + path_id.c \ + ../../udev/lib/libudev.h \ + ../../udev/lib/libudev.c \ + ../../udev/lib/libudev-device.c \ + ../../udev/lib/libudev-enumerate.c \ + ../../udev/lib/libudev-list.c \ + ../../udev/lib/libudev-util.c + dist_man_MANS = \ path_id.8 diff --git a/extras/path_id/path_id b/extras/path_id/path_id deleted file mode 100755 index 7b4973fa03..0000000000 --- a/extras/path_id/path_id +++ /dev/null @@ -1,601 +0,0 @@ -#!/bin/sh - -# provide the shortest possible unique hardware path to a device -# for the Linux Persistent Device Naming scheme -# -# Copyright (C) 2005-2006 SUSE Linux Products GmbH -# Author: -# Hannes Reinecke -# -# This program is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation version 2 of the License. - -SYSFS=/sys -RESULT=1 -TYPE= -OPWD="`pwd`" -full_sysfs_path= -full_sysfs_device_path= - -if [ -z "$DEVPATH" -a -z "$1" ] ; then - exit 1 -fi - -if [ -z "$DEVPATH" ] ; then - case "$1" in - $SYSFS/*) - DEVPATH="${1#$SYSFS}" - ;; - *) - DEVPATH=$1 - ;; - esac -fi - -if [ ! -e $SYSFS$DEVPATH/dev ] ; then - exit 1 -fi - -case "$DEVPATH" in - /devices/*) - cd "$SYSFS$DEVPATH/subsystem"; - TYPE="`pwd -P`" - cd "$OPWD" - TYPE="${TYPE##*/}" - ;; - /class/*) - TYPE="${DEVPATH#/class/}" - TYPE="${TYPE%%/*}" - ;; - /block/*) - TYPE=block - ;; - *) - exit 1 - ;; -esac - -get_port_offset () { - local type offset port - type=$1 - offset=$2 - for i in $type[0-9]* ; do - : i $i - port="${i#$type}" - if [ "$port" -lt "$offset" ] ; then offset=$port ; fi - done - echo $offset -} - -handle_pci () { - local DEV=$1 - cd -P $1 - DEV=${PWD} - pci_id=${DEV##*/} - host_dev_path=$DEV - - # cciss devices don't have a separate sysfs node - for blk_link in block*; do - if [ -L "$blk_link" ]; then - case "$blk_link" in - *cciss*) - d=cciss-${blk_link#*cciss\!} - ;; - esac - fi - done - while [ ! -z "$host_dev_path" ] ; do - case "$host_dev_path" in - */pci[0-9]*) - host_dev_path=${host_dev_path%/*} - ;; - *) - break - ;; - esac - done - if [ "$d" ]; then - d="pci-$pci_id-$d" - else - d="pci-$pci_id" - fi - D="$host_dev_path" - RESULT=0 -} - -handle_platform () { - local DEV=$1 - cd -P $1 - DEV=${PWD} - platform_id=${DEV##*/} - host_dev_path=$DEV - while [ ! -z "$host_dev_path" ] ; do - case "$host_dev_path" in - */platform*) - host_dev_path=${host_dev_path%/*} - ;; - *) - break - ;; - esac - done - if [ "$d" ]; then - d="platform-$platform_id-$d" - else - d="platform-$platform_id" - fi - D="$host_dev_path" - RESULT=0 -} - -handle_xen () { - local DEV=$1 - cd -P $1 - vbd_id=${DEV##*/} - host_dev_path=$DEV - while [ ! -z "$host_dev_path" ] ; do - case "$host_dev_path" in - */vbd*) - host_dev_path=${host_dev_path%/*} - ;; - *) - break - ;; - esac - done - if [ "$d" ]; then - d="xen-$vbd_id-$d" - else - d="xen-$vbd_id" - fi - D="$host_dev_path" - RESULT=0 -} - -handle_serio () { - local DEV=$1 - cd -P $1 - DEV=${PWD} - serio_id=${DEV##*/serio} - host_dev_path=$DEV - while [ ! -z "$host_dev_path" ] ; do - case "$host_dev_path" in - */serio*) - host_dev_path=${host_dev_path%/*} - ;; - *) - break - ;; - esac - done - if [ "$d" ]; then - d="serio-$serio_id-$d" - else - d="serio-$serio_id" - fi - D="$host_dev_path" - RESULT=0 -} - -handle_ide () { - : handle_ide $* - local DEV=$1 - local port idedev idecontroller - # IDE - : DEV $DEV - port=${DEV##*/} - idedev=${DEV%/*} - idecontroller=${idedev%/*} - # port info if the controller has more than one interface - port="${port#ide}" - : port $port d $d - : idedev $idedev kernel_port $port - case "${port#*.}" in - 0) - channel=0 - ;; - 1) - channel=1 - ;; - *) - echo "Error: $idedev is neither master or slave" >&2 - ;; - esac - cd $idecontroller - offset="`get_port_offset ide ${port%.*}`" - cd "$OPWD" - : port offset $offset - port=$((${port%.*} - $offset)) - if [ "$d" ] ; then - d="ide-${port}:$channel-$d" - else - d="ide-${port}:$channel" - fi - D=$idecontroller - RESULT=0 -} - -handle_scsi () { - : handle_scsi $* - local DEV=$1 - local cil controller_port controller_dev - # SCSI device - cil="${DEV##*/}" - cil="${cil#*:}" - target_dev=${DEV%/*} - target_id=${target_dev##*/target} - cd "$target_dev" - target_num=0 - for tid in ${target_id}* ; do - target_num=$(( $target_num + 1 )) - done - controller_port=${target_dev%/*} - controller_dev="${controller_port%/*}" - : controller_dev $controller_dev - : controller_port $controller_port - # a host controller may have more than one interface/port - controller_port="${controller_port##*/host}" - # - cd "$controller_dev" - controller_offset=$(get_port_offset host $controller_port) - cd "$OPWD" - controller_port=$(( $controller_port - $controller_offset)) - scsi_id="scsi-${controller_port}:${cil}" - if [ "$d" ] ; then - d="${scsi_id}-$d" - else - d="$scsi_id" - fi - D="$controller_dev" - RESULT=0 -} - -handle_firewire () { - : handle_firewire $* - local DEV=$1 - if [ -f "$D/ieee1394_id" ] ; then - read ieee1394_id < $D/ieee1394_id - fi - if [ -z "$ieee1394_id" ] ; then - : no IEEE1394 ID - RESULT=1 - return - fi - fw_host_dev=${DEV%/fw-host*} - # IEEE1394 devices are always endpoints - d="ieee1394-0x$ieee1394_id" - D="$fw_host_dev" - RESULT=0 -} - -handle_fc () { - : handle_fc $* - local DEV=$1 - local cil controller_port controller_dev - # SCSI-FC device - fc_tgt_hcil="${DEV##*/}" - fc_tgt_lun="${fc_tgt_hcil##*:}" - fc_tgt_path="${DEV%/*}" - fc_tgt_num="${fc_tgt_path##*/}" - fc_tgt_dev="${fc_tgt_path}/fc_transport/${fc_tgt_num}" - if [ -e "$fc_tgt_dev/port_name" ]; then - read wwpn < $fc_tgt_dev/port_name - fi - if [ -z "$wwpn" ] ; then - : no WWPN - D= - RESULT=1 - return - fi - # Linux currently knows about 32bit luns - tmp_lun3=$(printf "%04x" $(($fc_tgt_lun & 0xFFFF))) - tmp_lun2=$(printf "%04x" $(( ($fc_tgt_lun >> 16) & 0xFFFF))) - tmp_lun1="0000" - tmp_lun0="0000" - if (($fc_tgt_lun == 0)) ; then - lun="0x0000000000000000" - else - lun="0x${tmp_lun3}${tmp_lun2}${tmp_lun1}${tmp_lun0}" - fi - controller_dev="${fc_tgt_path%/host[0-9]*}" - # FC devices are always endpoints - d="fc-${wwpn}:${lun}" - D="$controller_dev" - RESULT=0 -} - -handle_sas () { - : handle_sas $* - local DEV=$1 - local cil adapter controller_dev - local lun - lun=${DEV##*:} - # SAS device - sas_end_path="${DEV%%/target*}" - sas_host_path="${sas_end_path%%/port*}" - sas_phy_path="${sas_end_path#*/host*/}" - sas_phy_path="${sas_phy_path%%/*}" - sas_phy_path="${sas_host_path}/${sas_phy_path}" - - sas_phy_id=255 - for phy in $sas_phy_path/phy-*/sas_phy/phy-* ; do - if [ -d "$phy" ] ; then - read phy_id < $phy/phy_identifier - if [ $phy_id -lt $sas_phy_id ]; then - sas_phy_id=$phy_id - fi - fi - done - - if [ $sas_phy_id -eq 255 ] ; then - : no initiator address - D= - RESULT=1 - return - fi - - sas_port_id="${sas_phy_path##*/port-}" - sas_port_dev="/sys/class/sas_port/port-${sas_port_id}" - if [ -e "$sas_port_dev/num_phys" ] ; then - read phy_port < $sas_port_dev/num_phys - fi - - sas_end_id="${sas_end_path##*end_device-}" - sas_end_dev="/sys/class/sas_device/end_device-${sas_end_id}" - if [ -e "$sas_end_dev/sas_address" ]; then - read end_address < $sas_end_dev/sas_address - read end_id < $sas_end_dev/phy_identifier - fi - if [ -z "$end_address" ] ; then - : no end device address - D= - RESULT=1 - return - fi - sas_end_address="$end_address:$end_id" - controller_dev="${sas_host_path%/host[0-9]*}" - # SAS devices are always endpoints - d="sas-phy${sas_phy_id}:${phy_port}-${sas_end_address}-lun$lun" - D="$controller_dev" - RESULT=0 -} - -handle_iscsi() { - local DEV=$1 - local iscsi_session_dir - local iscsi_session iscsi_session_path - local iscsi_connection iscsi_connection_path - local iscsi_scsi_lun - # iSCSI device - iscsi_session_dir="${DEV%%/target*}" - iscsi_session="${iscsi_session_dir##*/}" - iscsi_session_path=/sys/class/iscsi_session/${iscsi_session} - if [ ! -d "$iscsi_session_path" ] ; then - : no iSCSI session path - RESULT=1 - return - fi - # Currently we're not doing MC/S - for conn in ${iscsi_session_dir}/connection* ; do - iscsi_conn_num=${conn##*:} - if [ "$iscsi_conn_num" = '0' ] ; then - iscsi_connection=$(basename $conn) - fi - done - if [ -z "$iscsi_connection" ] ; then - : no iSCSI connection found - RESULT=1 - return - fi - iscsi_connection_path=/sys/class/iscsi_connection/${iscsi_connection} - if [ ! -d "$iscsi_connection_path" ] ; then - : no iSCSI connection path - RESULT=1 - return - fi - if [ -e "${iscsi_session_path}/targetname" ]; then - read iscsi_tgtname < ${iscsi_session_path}/targetname - fi - if [ -z "$iscsi_tgtname" ] ; then - : No iSCSI Targetname - RESULT=1 - return - fi - if [ -e "${iscsi_connection_path}/persistent_address" ] ; then - read iscsi_address < ${iscsi_connection_path}/persistent_address - fi - if [ -z "$iscsi_address" ] ; then - : No iSCSI Target address - RESULT=1 - return - fi - if [ -e "${iscsi_connection_path}/persistent_port" ] ; then - read iscsi_port < ${iscsi_connection_path}/persistent_port - fi - iscsi_scsi_lun="${DEV##*:}" - d="ip-${iscsi_address}:${iscsi_port}-iscsi-${iscsi_tgtname}-lun-${iscsi_scsi_lun}" - RESULT=0 -} - -handle_usb () { -: handle_usb $* - local DEV=$1 - cd -P $1 - DEV=${PWD} - port_id=${DEV##*/} - port_num=${port_id#*-} - host_dev_path=$DEV - while [ ! -z "$host_dev_path" ] ; do - case "$host_dev_path" in - */usb*) - usb_host_path=$host_dev_path - host_dev_path="${host_dev_path%/*}" - ;; - *) - break - ;; - esac - done - : host_dev_path $host_dev_path - usb_host_num=${usb_host_path##*/usb} - - cd "$host_dev_path" - usb_host_offset=$(get_port_offset usb $usb_host_num) - usb_host_port=$(($usb_host_num - $usb_host_offset)) - cd "$OPWD" - if [ "$d" ] ; then - d="usb-$usb_host_port:$port_num-${d}" - else - d="usb-$usb_host_port:$port_num" - fi - D="$host_dev_path" - RESULT=0 -} - -handle_device () { - full_sysfs_path="$SYSFS$DEVPATH" - case "$DEVPATH" in - /devices/*) - full_sysfs_path="${full_sysfs_path%/*}" - # skip parent device of the same subsystem - if [ -L $full_sysfs_path/subsystem ]; then - cd "$full_sysfs_path/subsystem"; - subsys="`pwd -P`" - cd "$OPWD" - subsys="${subsys##*/}" - if [ "$subsys" = "$TYPE" ]; then - : skip same subsystem parent - full_sysfs_path="${full_sysfs_path%/*}" - fi - fi - # skip subsystem directory - subsys="${full_sysfs_path##*/}" - if [ "$subsys" = "$TYPE" ]; then - : skip subsystem directory - full_sysfs_path="${full_sysfs_path%/*}" - fi - cd $full_sysfs_path - ;; - *) - # old sysfs layout - if [ ! -L $full_sysfs_path/device ]; then - full_sysfs_path="${full_sysfs_path%/*}" - : full_sysfs_path "$full_sysfs_path" - if [ ! -L $full_sysfs_path/device -o ! -f $full_sysfs_path/dev ]; then - return - fi - fi - if [ -L $full_sysfs_path/device/device ]; then - cd $full_sysfs_path/device/device - else - cd $full_sysfs_path/device - fi - ;; - esac - full_sysfs_device_path="`pwd -P`" - cd "$OPWD" - - D=$full_sysfs_device_path - while [ ! -z "$D" ] ; do - case "$D" in - */ide[0-9]/[0-9].[0-9]*|*/ide[0-9][0-9]/[0-9][0-9].[0-9]*) - handle_ide "$D" - ;; - */css0/*) - if [ -r $full_sysfs_device_path/wwpn ]; then - read wwpn < $full_sysfs_device_path/wwpn - fi - if [ -r $full_sysfs_device_path/fcp_lun ]; then - read lun < $full_sysfs_device_path/fcp_lun - fi - if [ -r $full_sysfs_device_path/hba_id ]; then - read bus_id < $full_sysfs_device_path/hba_id - fi - if [ "$bus_id" -a "$wwpn" -a "$lun" ]; then - # S/390 zfcp adapter - d="ccw-$bus_id-zfcp-$wwpn:$lun" - RESULT=0 - else - # DASD devices - bus="ccw" - adapter=${D##*/} - d="$bus-$adapter" - RESULT=0 - fi - D= - ;; - */rport-[0-9]*:[0-9]*-[0-9]*/*) - handle_fc "$D" - ;; - */end_device-[0-9]*:[0-9]*:[0-9]*/*) - handle_sas "$D" - ;; - */fw-host[0-9]*/*) - handle_firewire "$D" - ;; - */session[0-9]*/*) - handle_iscsi "$D" - D= - ;; - */host[0-9]*/[0-9]*:[0-9]*:[0-9]*:[0-9]*) - handle_scsi "$D" - ;; - */ttyUSB*) - D=${D%/ttyUSB*} - ;; - */usb[0-9]*/[0-9]*/*) - handle_usb "$D" - ;; - */pci[0-9]*:[0-9]*) - handle_pci "$D" - ;; - */serio[0-9]*) - handle_serio "$D" - ;; - */platform/*) - handle_platform "$D" - ;; - */vbd-[0-9]*) - handle_xen "$D" - ;; - */devices) - D= - ;; - *) - : not handled - RESULT=1 - return - ;; - esac - done - if [ "$TYPE" = "scsi_tape" ] ; then - devname=${full_sysfs_path##*/} - rewind="${devname%%st*}" - mode="${devname##*st}" - case "$mode" in - *l) - mode="l" - ;; - *m) - mode="m" - ;; - *a) - mode="a" - ;; - *) - mode="" - ;; - esac - if [ "$d" ]; then - d="$d-${rewind}st${mode}" - fi - fi -} - -handle_device -if [ -z "$d" ]; then - exit 1 -fi -echo "ID_PATH=$d" -exit 0 diff --git a/extras/path_id/path_id.c b/extras/path_id/path_id.c new file mode 100644 index 0000000000..872f0c932e --- /dev/null +++ b/extras/path_id/path_id.c @@ -0,0 +1,365 @@ +/* + * compose persisistent device path + * + * Copyright (C) 2009 Kay Sievers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include <../../udev/udev.h> + +int debug; + +static void log_fn(struct udev *udev, int priority, + const char *file, int line, const char *fn, + const char *format, va_list args) +{ + if (debug) { + fprintf(stderr, "%s: ", fn != NULL ? fn : file); + vfprintf(stderr, format, args); + } else { + vsyslog(priority, format, args); + } +} + +static int path_prepend(char **path, const char *fmt, ...) +{ + va_list va; + char *old; + char *pre; + int err; + + old = *path; + + va_start(va, fmt); + err = vasprintf(&pre, fmt, va); + va_end(va); + if (err < 0) + return err; + + if (old != NULL) { + err = asprintf(path, "%s-%s", pre, old); + if (err < 0) + return err; + free(pre); + } else { + *path = pre; + } + + free(old); + return 0; +} + +static struct udev_device *skip_subsystem(struct udev_device *dev, const char *subsys) +{ + struct udev_device *parent = dev; + + while (parent != NULL) { + const char *subsystem; + + subsystem = udev_device_get_subsystem(parent); + if (subsystem == NULL || strcmp(subsystem, subsys) != 0) + break; + dev = parent; + parent = udev_device_get_parent(parent); + } + return dev; +} + +/* find smallest number of instances of / */ +static int base_number(const char *syspath, const char *name) +{ + char *base; + char *pos; + DIR *dir; + struct dirent *dent; + size_t len; + int number = -1; + + base = strdup(syspath); + if (base == NULL) + goto out; + + pos = strrchr(base, '/'); + if (pos == NULL) + goto out; + pos[0] = '\0'; + + len = strlen(name); + dir = opendir(base); + if (dir == NULL) + goto out; + for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { + char *rest; + int i; + + if (dent->d_name[0] == '.') + continue; + if (dent->d_type != DT_DIR && dent->d_type != DT_LNK) + continue; + if (strncmp(dent->d_name, name, len) != 0) + continue; + i = strtoul(&dent->d_name[len], &rest, 10); + if (rest[0] != '\0') + continue; + if (number == -1 || i < number) + number = i; + } + closedir(dir); +out: + free(base); + return number; +} + +static struct udev_device *handle_scsi(struct udev_device *dev, char **path) +{ + const char *devtype; + struct udev_device *hostdev; + const char *name; + int host, bus, target, lun; + int base; + + devtype = udev_device_get_devtype(dev); + if (devtype == NULL || strcmp(devtype, "scsi_device") != 0) + return dev; + + hostdev = udev_device_get_parent_with_subsystem_devtype(dev, "scsi", "scsi_host"); + if (hostdev == NULL) + return dev; + + name = udev_device_get_sysname(dev); + if (sscanf(name, "%d:%d:%d:%d", &host, &bus, &target, &lun) != 4) + return dev; + + /* rebase host offset to get the local relative number */ + base = base_number(udev_device_get_syspath(hostdev), "host"); + if (base < 0) + return dev; + host -= base; + + path_prepend(path, "scsi-%u:%u:%u:%u", host, bus, target, lun); + dev = skip_subsystem(dev, "scsi"); + return dev; +} + +static void handle_scsi_tape(struct udev_device *dev, char **suffix) +{ + const char *name; + + name = udev_device_get_sysname(dev); + if (strncmp(name, "nst", 3) == 0 && strchr("lma", name[3]) != NULL) + asprintf(suffix, "nst%c", name[3]); + else if (strncmp(name, "st", 2) == 0 && strchr("lma", name[2]) != NULL) + asprintf(suffix, "st%c", name[2]); +} + +static struct udev_device *handle_usb(struct udev_device *dev, char **path) +{ + const char *devtype; + const char *str; + const char *port; + + devtype = udev_device_get_devtype(dev); + if (devtype == NULL || strcmp(devtype, "usb_interface") != 0) + return dev; + + str = udev_device_get_sysname(dev); + port = strchr(str, '-'); + if (port == NULL) + return dev; + port++; + + dev = skip_subsystem(dev, "usb"); + path_prepend(path, "usb-0:%s", port); + return dev; +} + +static struct udev_device *handle_firewire(struct udev_device *parent, struct udev_device *dev, char **path) +{ + struct udev_device *scsi_dev; + + scsi_dev = udev_device_get_parent_with_subsystem_devtype(dev, "scsi", "scsi_device"); + if (scsi_dev != NULL) { + const char *id; + + id = udev_device_get_sysattr_value(scsi_dev, "ieee1394_id"); + if (id != NULL) + path_prepend(path, "ieee1394-0x%s", id); + } + + parent = skip_subsystem(parent, "firewire"); + return parent; +} + +static struct udev_device *handle_ccw(struct udev_device *parent, struct udev_device *dev, char **path) +{ + struct udev_device *scsi_dev; + + scsi_dev = udev_device_get_parent_with_subsystem_devtype(dev, "scsi", "scsi_device"); + if (scsi_dev != NULL) { + const char *wwpn; + const char *lun; + const char *hba_id; + + hba_id = udev_device_get_sysattr_value(scsi_dev, "hba_id"); + wwpn = udev_device_get_sysattr_value(scsi_dev, "wwpn"); + lun = udev_device_get_sysattr_value(scsi_dev, "fcp_lun"); + if (hba_id != NULL && lun != NULL && wwpn != NULL) { + path_prepend(path, "ccw-%s-zfcp-%s:%s", hba_id, wwpn, lun); + goto out; + } + } + + path_prepend(path, "ccw-%s", udev_device_get_sysname(parent)); +out: + parent = skip_subsystem(parent, "ccw"); + return parent; +} + +int main(int argc, char **argv) +{ + static const struct option options[] = { + { "debug", no_argument, NULL, 'd' }, + { "help", no_argument, NULL, 'h' }, + {} + }; + struct udev *udev; + struct udev_device *dev; + struct udev_device *parent; + char syspath[UTIL_PATH_SIZE]; + const char *devpath; + char *path; + char *path_suffix; + int rc = 1; + + udev = udev_new(); + if (udev == NULL) + goto exit; + + logging_init("usb_id"); + udev_set_log_fn(udev, log_fn); + + while (1) { + int option; + + option = getopt_long(argc, argv, "dh", options, NULL); + if (option == -1) + break; + + switch (option) { + case 'd': + debug = 1; + if (udev_get_log_priority(udev) < LOG_INFO) + udev_set_log_priority(udev, LOG_INFO); + break; + case 'h': + printf("Usage: path_id [--debug] [--help] \n" + " --debug print debug information\n" + " --help print this help text\n\n"); + default: + rc = 1; + goto exit; + } + } + + devpath = argv[optind]; + if (devpath == NULL) { + fprintf(stderr, "No device specified\n"); + rc = 2; + goto exit; + } + + util_strscpyl(syspath, sizeof(syspath), udev_get_sys_path(udev), devpath, NULL); + dev = udev_device_new_from_syspath(udev, syspath); + if (dev == NULL) { + fprintf(stderr, "unable to access '%s'\n", devpath); + rc = 3; + goto exit; + } + + path = NULL; + path_suffix = NULL; + + parent = dev; + while (parent != NULL) { + const char *subsys; + + subsys = udev_device_get_subsystem(parent); + + if (subsys == NULL) { + ; + } else if (strcmp(subsys, "scsi_tape") == 0) { + handle_scsi_tape(parent, &path_suffix); + } else if (strcmp(subsys, "scsi") == 0) { + parent = handle_scsi(parent, &path); + } else if (strcmp(subsys, "fc_transport") == 0) { + ; //handle_fc(); + } else if (strcmp(subsys, "sas_end_device") == 0) { + ; //handle_sas(); + } else if (strcmp(subsys, "iscsi_session") == 0) { + ; //handle_iscsi() + } else if (strcmp(subsys, "ccw") == 0) { + handle_ccw(parent, dev, &path); + } else if (strcmp(subsys, "cciss") == 0) { + ; //handle_cciss(); + } else if (strcmp(subsys, "usb") == 0) { + parent = handle_usb(parent, &path); + } else if (strcmp(subsys, "serio") == 0) { + path_prepend(&path, "serio-%s", udev_device_get_sysnum(parent)); + parent = skip_subsystem(parent, "serio"); + } else if (strcmp(subsys, "firewire") == 0 || strcmp(subsys, "ieee1394") == 0) { + parent = handle_firewire(parent, dev, &path); + } else if (strcmp(subsys, "pci") == 0) { + path_prepend(&path, "pci-%s", udev_device_get_sysname(parent)); + parent = skip_subsystem(parent, "pci"); + } else if (strcmp(subsys, "platform") == 0) { + path_prepend(&path, "platform-%s", udev_device_get_sysname(parent)); + parent = skip_subsystem(parent, "platform"); + } else if (strcmp(subsys, "xen") == 0) { + path_prepend(&path, "xen-%s", udev_device_get_sysname(parent)); + parent = skip_subsystem(parent, "xen"); + } + + parent = udev_device_get_parent(parent); + } + + if (path != NULL) { + if (path_suffix != NULL) { + printf("ID_PATH=%s%s\n", path, path_suffix); + free(path_suffix); + } else { + printf("ID_PATH=%s\n", path); + } + free(path); + rc = 0; + } + + udev_device_unref(dev); +exit: + udev_unref(udev); + logging_close(); + return rc; +} diff --git a/extras/path_id/path_id.sh b/extras/path_id/path_id.sh new file mode 100755 index 0000000000..7b4973fa03 --- /dev/null +++ b/extras/path_id/path_id.sh @@ -0,0 +1,601 @@ +#!/bin/sh + +# provide the shortest possible unique hardware path to a device +# for the Linux Persistent Device Naming scheme +# +# Copyright (C) 2005-2006 SUSE Linux Products GmbH +# Author: +# Hannes Reinecke +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation version 2 of the License. + +SYSFS=/sys +RESULT=1 +TYPE= +OPWD="`pwd`" +full_sysfs_path= +full_sysfs_device_path= + +if [ -z "$DEVPATH" -a -z "$1" ] ; then + exit 1 +fi + +if [ -z "$DEVPATH" ] ; then + case "$1" in + $SYSFS/*) + DEVPATH="${1#$SYSFS}" + ;; + *) + DEVPATH=$1 + ;; + esac +fi + +if [ ! -e $SYSFS$DEVPATH/dev ] ; then + exit 1 +fi + +case "$DEVPATH" in + /devices/*) + cd "$SYSFS$DEVPATH/subsystem"; + TYPE="`pwd -P`" + cd "$OPWD" + TYPE="${TYPE##*/}" + ;; + /class/*) + TYPE="${DEVPATH#/class/}" + TYPE="${TYPE%%/*}" + ;; + /block/*) + TYPE=block + ;; + *) + exit 1 + ;; +esac + +get_port_offset () { + local type offset port + type=$1 + offset=$2 + for i in $type[0-9]* ; do + : i $i + port="${i#$type}" + if [ "$port" -lt "$offset" ] ; then offset=$port ; fi + done + echo $offset +} + +handle_pci () { + local DEV=$1 + cd -P $1 + DEV=${PWD} + pci_id=${DEV##*/} + host_dev_path=$DEV + + # cciss devices don't have a separate sysfs node + for blk_link in block*; do + if [ -L "$blk_link" ]; then + case "$blk_link" in + *cciss*) + d=cciss-${blk_link#*cciss\!} + ;; + esac + fi + done + while [ ! -z "$host_dev_path" ] ; do + case "$host_dev_path" in + */pci[0-9]*) + host_dev_path=${host_dev_path%/*} + ;; + *) + break + ;; + esac + done + if [ "$d" ]; then + d="pci-$pci_id-$d" + else + d="pci-$pci_id" + fi + D="$host_dev_path" + RESULT=0 +} + +handle_platform () { + local DEV=$1 + cd -P $1 + DEV=${PWD} + platform_id=${DEV##*/} + host_dev_path=$DEV + while [ ! -z "$host_dev_path" ] ; do + case "$host_dev_path" in + */platform*) + host_dev_path=${host_dev_path%/*} + ;; + *) + break + ;; + esac + done + if [ "$d" ]; then + d="platform-$platform_id-$d" + else + d="platform-$platform_id" + fi + D="$host_dev_path" + RESULT=0 +} + +handle_xen () { + local DEV=$1 + cd -P $1 + vbd_id=${DEV##*/} + host_dev_path=$DEV + while [ ! -z "$host_dev_path" ] ; do + case "$host_dev_path" in + */vbd*) + host_dev_path=${host_dev_path%/*} + ;; + *) + break + ;; + esac + done + if [ "$d" ]; then + d="xen-$vbd_id-$d" + else + d="xen-$vbd_id" + fi + D="$host_dev_path" + RESULT=0 +} + +handle_serio () { + local DEV=$1 + cd -P $1 + DEV=${PWD} + serio_id=${DEV##*/serio} + host_dev_path=$DEV + while [ ! -z "$host_dev_path" ] ; do + case "$host_dev_path" in + */serio*) + host_dev_path=${host_dev_path%/*} + ;; + *) + break + ;; + esac + done + if [ "$d" ]; then + d="serio-$serio_id-$d" + else + d="serio-$serio_id" + fi + D="$host_dev_path" + RESULT=0 +} + +handle_ide () { + : handle_ide $* + local DEV=$1 + local port idedev idecontroller + # IDE + : DEV $DEV + port=${DEV##*/} + idedev=${DEV%/*} + idecontroller=${idedev%/*} + # port info if the controller has more than one interface + port="${port#ide}" + : port $port d $d + : idedev $idedev kernel_port $port + case "${port#*.}" in + 0) + channel=0 + ;; + 1) + channel=1 + ;; + *) + echo "Error: $idedev is neither master or slave" >&2 + ;; + esac + cd $idecontroller + offset="`get_port_offset ide ${port%.*}`" + cd "$OPWD" + : port offset $offset + port=$((${port%.*} - $offset)) + if [ "$d" ] ; then + d="ide-${port}:$channel-$d" + else + d="ide-${port}:$channel" + fi + D=$idecontroller + RESULT=0 +} + +handle_scsi () { + : handle_scsi $* + local DEV=$1 + local cil controller_port controller_dev + # SCSI device + cil="${DEV##*/}" + cil="${cil#*:}" + target_dev=${DEV%/*} + target_id=${target_dev##*/target} + cd "$target_dev" + target_num=0 + for tid in ${target_id}* ; do + target_num=$(( $target_num + 1 )) + done + controller_port=${target_dev%/*} + controller_dev="${controller_port%/*}" + : controller_dev $controller_dev + : controller_port $controller_port + # a host controller may have more than one interface/port + controller_port="${controller_port##*/host}" + # + cd "$controller_dev" + controller_offset=$(get_port_offset host $controller_port) + cd "$OPWD" + controller_port=$(( $controller_port - $controller_offset)) + scsi_id="scsi-${controller_port}:${cil}" + if [ "$d" ] ; then + d="${scsi_id}-$d" + else + d="$scsi_id" + fi + D="$controller_dev" + RESULT=0 +} + +handle_firewire () { + : handle_firewire $* + local DEV=$1 + if [ -f "$D/ieee1394_id" ] ; then + read ieee1394_id < $D/ieee1394_id + fi + if [ -z "$ieee1394_id" ] ; then + : no IEEE1394 ID + RESULT=1 + return + fi + fw_host_dev=${DEV%/fw-host*} + # IEEE1394 devices are always endpoints + d="ieee1394-0x$ieee1394_id" + D="$fw_host_dev" + RESULT=0 +} + +handle_fc () { + : handle_fc $* + local DEV=$1 + local cil controller_port controller_dev + # SCSI-FC device + fc_tgt_hcil="${DEV##*/}" + fc_tgt_lun="${fc_tgt_hcil##*:}" + fc_tgt_path="${DEV%/*}" + fc_tgt_num="${fc_tgt_path##*/}" + fc_tgt_dev="${fc_tgt_path}/fc_transport/${fc_tgt_num}" + if [ -e "$fc_tgt_dev/port_name" ]; then + read wwpn < $fc_tgt_dev/port_name + fi + if [ -z "$wwpn" ] ; then + : no WWPN + D= + RESULT=1 + return + fi + # Linux currently knows about 32bit luns + tmp_lun3=$(printf "%04x" $(($fc_tgt_lun & 0xFFFF))) + tmp_lun2=$(printf "%04x" $(( ($fc_tgt_lun >> 16) & 0xFFFF))) + tmp_lun1="0000" + tmp_lun0="0000" + if (($fc_tgt_lun == 0)) ; then + lun="0x0000000000000000" + else + lun="0x${tmp_lun3}${tmp_lun2}${tmp_lun1}${tmp_lun0}" + fi + controller_dev="${fc_tgt_path%/host[0-9]*}" + # FC devices are always endpoints + d="fc-${wwpn}:${lun}" + D="$controller_dev" + RESULT=0 +} + +handle_sas () { + : handle_sas $* + local DEV=$1 + local cil adapter controller_dev + local lun + lun=${DEV##*:} + # SAS device + sas_end_path="${DEV%%/target*}" + sas_host_path="${sas_end_path%%/port*}" + sas_phy_path="${sas_end_path#*/host*/}" + sas_phy_path="${sas_phy_path%%/*}" + sas_phy_path="${sas_host_path}/${sas_phy_path}" + + sas_phy_id=255 + for phy in $sas_phy_path/phy-*/sas_phy/phy-* ; do + if [ -d "$phy" ] ; then + read phy_id < $phy/phy_identifier + if [ $phy_id -lt $sas_phy_id ]; then + sas_phy_id=$phy_id + fi + fi + done + + if [ $sas_phy_id -eq 255 ] ; then + : no initiator address + D= + RESULT=1 + return + fi + + sas_port_id="${sas_phy_path##*/port-}" + sas_port_dev="/sys/class/sas_port/port-${sas_port_id}" + if [ -e "$sas_port_dev/num_phys" ] ; then + read phy_port < $sas_port_dev/num_phys + fi + + sas_end_id="${sas_end_path##*end_device-}" + sas_end_dev="/sys/class/sas_device/end_device-${sas_end_id}" + if [ -e "$sas_end_dev/sas_address" ]; then + read end_address < $sas_end_dev/sas_address + read end_id < $sas_end_dev/phy_identifier + fi + if [ -z "$end_address" ] ; then + : no end device address + D= + RESULT=1 + return + fi + sas_end_address="$end_address:$end_id" + controller_dev="${sas_host_path%/host[0-9]*}" + # SAS devices are always endpoints + d="sas-phy${sas_phy_id}:${phy_port}-${sas_end_address}-lun$lun" + D="$controller_dev" + RESULT=0 +} + +handle_iscsi() { + local DEV=$1 + local iscsi_session_dir + local iscsi_session iscsi_session_path + local iscsi_connection iscsi_connection_path + local iscsi_scsi_lun + # iSCSI device + iscsi_session_dir="${DEV%%/target*}" + iscsi_session="${iscsi_session_dir##*/}" + iscsi_session_path=/sys/class/iscsi_session/${iscsi_session} + if [ ! -d "$iscsi_session_path" ] ; then + : no iSCSI session path + RESULT=1 + return + fi + # Currently we're not doing MC/S + for conn in ${iscsi_session_dir}/connection* ; do + iscsi_conn_num=${conn##*:} + if [ "$iscsi_conn_num" = '0' ] ; then + iscsi_connection=$(basename $conn) + fi + done + if [ -z "$iscsi_connection" ] ; then + : no iSCSI connection found + RESULT=1 + return + fi + iscsi_connection_path=/sys/class/iscsi_connection/${iscsi_connection} + if [ ! -d "$iscsi_connection_path" ] ; then + : no iSCSI connection path + RESULT=1 + return + fi + if [ -e "${iscsi_session_path}/targetname" ]; then + read iscsi_tgtname < ${iscsi_session_path}/targetname + fi + if [ -z "$iscsi_tgtname" ] ; then + : No iSCSI Targetname + RESULT=1 + return + fi + if [ -e "${iscsi_connection_path}/persistent_address" ] ; then + read iscsi_address < ${iscsi_connection_path}/persistent_address + fi + if [ -z "$iscsi_address" ] ; then + : No iSCSI Target address + RESULT=1 + return + fi + if [ -e "${iscsi_connection_path}/persistent_port" ] ; then + read iscsi_port < ${iscsi_connection_path}/persistent_port + fi + iscsi_scsi_lun="${DEV##*:}" + d="ip-${iscsi_address}:${iscsi_port}-iscsi-${iscsi_tgtname}-lun-${iscsi_scsi_lun}" + RESULT=0 +} + +handle_usb () { +: handle_usb $* + local DEV=$1 + cd -P $1 + DEV=${PWD} + port_id=${DEV##*/} + port_num=${port_id#*-} + host_dev_path=$DEV + while [ ! -z "$host_dev_path" ] ; do + case "$host_dev_path" in + */usb*) + usb_host_path=$host_dev_path + host_dev_path="${host_dev_path%/*}" + ;; + *) + break + ;; + esac + done + : host_dev_path $host_dev_path + usb_host_num=${usb_host_path##*/usb} + + cd "$host_dev_path" + usb_host_offset=$(get_port_offset usb $usb_host_num) + usb_host_port=$(($usb_host_num - $usb_host_offset)) + cd "$OPWD" + if [ "$d" ] ; then + d="usb-$usb_host_port:$port_num-${d}" + else + d="usb-$usb_host_port:$port_num" + fi + D="$host_dev_path" + RESULT=0 +} + +handle_device () { + full_sysfs_path="$SYSFS$DEVPATH" + case "$DEVPATH" in + /devices/*) + full_sysfs_path="${full_sysfs_path%/*}" + # skip parent device of the same subsystem + if [ -L $full_sysfs_path/subsystem ]; then + cd "$full_sysfs_path/subsystem"; + subsys="`pwd -P`" + cd "$OPWD" + subsys="${subsys##*/}" + if [ "$subsys" = "$TYPE" ]; then + : skip same subsystem parent + full_sysfs_path="${full_sysfs_path%/*}" + fi + fi + # skip subsystem directory + subsys="${full_sysfs_path##*/}" + if [ "$subsys" = "$TYPE" ]; then + : skip subsystem directory + full_sysfs_path="${full_sysfs_path%/*}" + fi + cd $full_sysfs_path + ;; + *) + # old sysfs layout + if [ ! -L $full_sysfs_path/device ]; then + full_sysfs_path="${full_sysfs_path%/*}" + : full_sysfs_path "$full_sysfs_path" + if [ ! -L $full_sysfs_path/device -o ! -f $full_sysfs_path/dev ]; then + return + fi + fi + if [ -L $full_sysfs_path/device/device ]; then + cd $full_sysfs_path/device/device + else + cd $full_sysfs_path/device + fi + ;; + esac + full_sysfs_device_path="`pwd -P`" + cd "$OPWD" + + D=$full_sysfs_device_path + while [ ! -z "$D" ] ; do + case "$D" in + */ide[0-9]/[0-9].[0-9]*|*/ide[0-9][0-9]/[0-9][0-9].[0-9]*) + handle_ide "$D" + ;; + */css0/*) + if [ -r $full_sysfs_device_path/wwpn ]; then + read wwpn < $full_sysfs_device_path/wwpn + fi + if [ -r $full_sysfs_device_path/fcp_lun ]; then + read lun < $full_sysfs_device_path/fcp_lun + fi + if [ -r $full_sysfs_device_path/hba_id ]; then + read bus_id < $full_sysfs_device_path/hba_id + fi + if [ "$bus_id" -a "$wwpn" -a "$lun" ]; then + # S/390 zfcp adapter + d="ccw-$bus_id-zfcp-$wwpn:$lun" + RESULT=0 + else + # DASD devices + bus="ccw" + adapter=${D##*/} + d="$bus-$adapter" + RESULT=0 + fi + D= + ;; + */rport-[0-9]*:[0-9]*-[0-9]*/*) + handle_fc "$D" + ;; + */end_device-[0-9]*:[0-9]*:[0-9]*/*) + handle_sas "$D" + ;; + */fw-host[0-9]*/*) + handle_firewire "$D" + ;; + */session[0-9]*/*) + handle_iscsi "$D" + D= + ;; + */host[0-9]*/[0-9]*:[0-9]*:[0-9]*:[0-9]*) + handle_scsi "$D" + ;; + */ttyUSB*) + D=${D%/ttyUSB*} + ;; + */usb[0-9]*/[0-9]*/*) + handle_usb "$D" + ;; + */pci[0-9]*:[0-9]*) + handle_pci "$D" + ;; + */serio[0-9]*) + handle_serio "$D" + ;; + */platform/*) + handle_platform "$D" + ;; + */vbd-[0-9]*) + handle_xen "$D" + ;; + */devices) + D= + ;; + *) + : not handled + RESULT=1 + return + ;; + esac + done + if [ "$TYPE" = "scsi_tape" ] ; then + devname=${full_sysfs_path##*/} + rewind="${devname%%st*}" + mode="${devname##*st}" + case "$mode" in + *l) + mode="l" + ;; + *m) + mode="m" + ;; + *a) + mode="a" + ;; + *) + mode="" + ;; + esac + if [ "$d" ]; then + d="$d-${rewind}st${mode}" + fi + fi +} + +handle_device +if [ -z "$d" ]; then + exit 1 +fi +echo "ID_PATH=$d" +exit 0 diff --git a/rules/rules.d/60-persistent-storage-tape.rules b/rules/rules.d/60-persistent-storage-tape.rules index 30a0115608..968528d845 100644 --- a/rules/rules.d/60-persistent-storage-tape.rules +++ b/rules/rules.d/60-persistent-storage-tape.rules @@ -17,7 +17,7 @@ KERNEL=="st*[0-9]", ENV{ID_SERIAL}=="?*", SYMLINK+="tape/by-id/$env{ID_BUS}-$en KERNEL=="nst*[0-9]", ENV{ID_SERIAL}=="?*", SYMLINK+="tape/by-id/$env{ID_BUS}-$env{ID_SERIAL}-nst" # by-path (parent device path) -KERNEL=="st*[0-9]|nst*[0-9]", IMPORT{program}="path_id %p" +KERNEL=="st*[0-9]|nst*[0-9]", IMPORT{program}="path_id.sh %p" KERNEL=="st*[0-9]", ENV{ID_PATH}=="?*", SYMLINK+="tape/by-path/$env{ID_PATH}" KERNEL=="nst*[0-9]", ENV{ID_PATH}=="?*", SYMLINK+="tape/by-path/$env{ID_PATH}" diff --git a/rules/rules.d/60-persistent-storage.rules b/rules/rules.d/60-persistent-storage.rules index e47cf21571..b39ea03321 100644 --- a/rules/rules.d/60-persistent-storage.rules +++ b/rules/rules.d/60-persistent-storage.rules @@ -47,7 +47,9 @@ KERNEL=="mspblk[0-9]", SUBSYSTEMS=="memstick", ATTRS{name}=="?*", ATTRS{serial}= KERNEL=="mspblk[0-9]p[0-9]", ENV{ID_NAME}=="?*", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/memstick-$env{ID_NAME}_$env{ID_SERIAL}-part%n" # by-path (parent device path) -ENV{DEVTYPE}=="disk", DEVPATH!="*/virtual/*", IMPORT{program}="path_id %p" +# old shell script for fc, sas, iscsi, s390, ide +ENV{DEVTYPE}=="disk", DEVPATH=="*/css0/*|*/rport-[0-9]*|*/end_device-*|*/session[0-9]*|*/ide[0-9]*", IMPORT{program}="path_id.sh %p" +ENV{DEVTYPE}=="disk", ENV{ID_PATH}=="", DEVPATH!="*/virtual/*", IMPORT{program}="path_id %p" ENV{DEVTYPE}=="disk", ENV{ID_PATH}=="?*", SYMLINK+="disk/by-path/$env{ID_PATH}" ENV{DEVTYPE}=="partition", ENV{ID_PATH}=="?*", SYMLINK+="disk/by-path/$env{ID_PATH}-part%n" -- cgit v1.2.3-54-g00ecf