chiark / gitweb /
persistent links: add scsi tape links and usb path support
[elogind.git] / extras / path_id
1 #!/bin/sh
2
3 # provide the shortest possible unique hardware path to a block device
4 # for the udev persistent disk device naming scheme
5 #
6 # Copyright (C) 2005 SUSE Linux Products GmbH
7 # Author:
8 #       Hannes Reinecke <hare@suse.de>
9 #
10 #       This program is free software; you can redistribute it and/or modify it
11 #       under the terms of the GNU General Public License as published by the
12 #       Free Software Foundation version 2 of the License.
13 #
14 # to be called from a udev rule to return the name for a symlink
15 #       DEVPATH=/block/sda/sda3 path_id
16 #       path_id <devpath>
17
18 # examples for all block devices on a system:
19 #       for i in `find /sys/class/block`; do DEVPATH="`echo $i | sed -e 's@^/sys\|/dev@@g'`"; path_id; done
20
21 # SCSI cdrom
22 # /class/block/sr0 -> /devices/pci0002:30/0002:30:0c.0/host0/target0:0:0/0:0:1:0
23 # pci-0002:30:0c.0-scsi-0:0:1:0
24 #
25 # SCSI disk
26 # /class/block/sda -> /devices/pci0002:30/0002:30:0c.0/host0/target0:0:0/0:0:4:0
27 # pci-0002:30:0c.0-scsi-0:0:4:0
28 #
29 # SATA disk, 4 channels per controller
30 # /class/block/sda -> /devices/pci0001:00/0001:00:07.0/0001:05:0c.0/host0/target0:0:0/0:0:0:0
31 # pci-0001:05:0c.0-scsi-0:0:0:0
32 #
33 # IDE disk
34 # /class/block/hda -> /devices/pci0002:02/0002:02:0d.0/ide0/0.0
35 # pci-0002:02:0d.0-ide-0.0
36 #
37 # IDE cdrom on a Mac ASIC:
38 # /class/block/hdc -> /devices/pci0001:01/0001:01:17.0/0.80000000:mac-io/0.00020000:ata-3/ide1/1.0
39 # mac-io_ata-3_master
40 #
41 # IDE cdrom on a Mac ASIC, with ide-scsi:
42 # /class/block/sr0 -> /devices/pci0001:01/0001:01:17.0/0.80000000:mac-io/0.0001f000:ata-4/ide0/0.1/host2/target2:0:0/2:0:0:0
43 # mac-io_ata-4_slave
44
45 # USB CDrom drive without 'serial' number:
46 # reusing 'product' and 'manufacturer' string, if available
47 # /class/block/sr0 -> /devices/pci0001:00/0001:00:04.0/0001:02:0b.0/usb4/4-2/4-2:1.0/host4/4:0:0:0
48 # usb-storage-odd-Freecom-USIDERev930:0:0:0
49
50 # devices may have several interfaces on one PCI device, like IDE:
51 # pci-0001:00:04.0_ide1-master
52 # pci-0001:00:04.0_ide2-master
53 # pci-0001:00:04.0_ide2-slave
54 # they are marked as ports, it is expected that the driver shows
55 # ide1 even if there is nothing connected to either master or slave
56 # interface
57 #
58 # match order is important.
59 # first IDE to find ide-scsi devices, then SCSI
60 # first usb-storage, then firewire sbp2, then the rest
61
62 SYSFS=/sys
63 RESULT=1
64 TYPE=
65 OPWD="`pwd`"
66 full_sysfs_path=
67 full_sysfs_device_path=
68
69 if [ -z "$DEVPATH" -a -z "$1" ] ; then
70     exit 1
71 fi
72
73 if [ -z "$DEVPATH" ] ; then
74     case "$1" in
75         $SYSFS/*)
76             DEVPATH="${1#$SYSFS}"
77             ;;
78         *)
79             DEVPATH=$1
80             ;;
81     esac
82 fi
83
84 if [ ! -e $SYSFS$DEVPATH/dev ] ; then
85     exit 1
86 fi
87
88 case "$DEVPATH" in
89     /devices/*)
90         cd "$SYSFS$DEVPATH/subsystem";
91         TYPE="`pwd -P`"
92         cd "$OPWD"
93         TYPE="${TYPE##*/}"
94         ;;
95     /class/*)
96         TYPE="${DEVPATH#/class/}"
97         TYPE="${TYPE%%/*}"
98         ;;
99     /block/*)
100         TYPE=block
101         ;;
102     *)
103         exit 1
104         ;;
105 esac
106
107 get_port_offset () {
108     local type offset port
109     type=$1
110     offset=$2
111     for i in $type[0-9]* ; do
112         : i $i
113         port="${i#$type}"
114         if [ "$port" -lt "$offset" ] ; then offset=$port ; fi
115     done
116     echo $offset
117 }
118
119 handle_pci () {
120     local DEV=$1
121     cd -P $1
122     DEV=${PWD}
123     pci_id=${DEV##*/}
124
125     host_dev_path=$DEV
126     while [ ! -z "$host_dev_path" ] ; do
127         case "$host_dev_path" in
128             */pci[0-9]*)
129                 host_dev_path=${host_dev_path%/*}
130                 ;;
131             *) break;;
132         esac
133     done
134
135     d="pci-$pci_id-$d"
136     D="$host_dev_path"
137     RESULT=0
138 }
139
140 handle_block_ide () {
141 : handle_block_ide $*
142         local DEV=$1
143         local port idedev idecontroller
144         # IDE
145         : DEV $DEV
146
147         port=${DEV##*/}
148         idedev=${DEV%/*}
149         idecontroller=${idedev%/*}
150         # port info if the controller has more than one interface
151         port="${port#ide}"
152         : port $port d $d
153         : idedev $idedev kernel_port $port
154         case "${port#*.}" in
155                  0)
156                  channel=0
157                  ;;
158                  1)
159                  channel=1
160                  ;;
161                  *)
162                  echo "Error: $idedev is neither master or slave" >&2
163         esac
164
165         cd $idecontroller
166         offset="`get_port_offset ide ${port%.*}`"
167         cd "$OPWD"
168         :  port offset $offset
169         port=$((${port%.*} - $offset))
170         if [ "$d" ] ; then
171             d="ide-${port}:$channel-$d"
172         else
173             d="ide-${port}:$channel"
174         fi
175         D=$idecontroller
176         RESULT=0
177 }
178
179 handle_block_scsi () {
180     : handle_block_scsi $*
181         local DEV=$1
182         local cil controller_port controller_dev
183         # SCSI device
184         cil="${DEV##*/}"
185         cil="${cil#*:}"
186         target_dev=${DEV%/*}
187         target_id=${target_dev##*/target}
188         cd "$target_dev"
189         target_num=0
190         for tid in ${target_id}* ; do
191             target_num=$(( $target_num + 1 ))
192         done
193
194         controller_port=${target_dev%/*}
195         controller_dev="${controller_port%/*}"
196
197         : controller_dev $controller_dev
198         : controller_port $controller_port
199         # a host controller may have more than one interface/port
200         controller_port="${controller_port##*/host}"
201         #
202         cd "$controller_dev"
203         controller_offset=$(get_port_offset host $controller_port)
204         cd "$OPWD"
205         controller_port=$(( $controller_port - $controller_offset))
206         scsi_id="scsi-${controller_port}:${cil}"
207
208         if [ "$d" ] ; then
209             d="${scsi_id}-$d"
210         else
211             d="$scsi_id"
212         fi
213
214         D="$controller_dev"
215         RESULT=0
216 }
217
218 handle_block_firewire () {
219     :handle_block_firewire $*
220     local DEV=$1
221
222     if [ -f "$D/ieee1394_id" ] ; then
223         read ieee1394_id < $D/ieee1394_id
224     fi
225     
226     if [ -z "$ieee1394_id" ] ; then
227         : no IEEE1394 ID
228         RESULT=1
229         return
230     fi
231
232     fw_host_dev=${DEV%/fw-host*}
233     # IEEE1394 devices are always endpoints
234     d="ieee1394-0x$ieee1394_id"
235     D="$fw_host_dev"
236
237     RESULT=0
238 }
239
240 handle_block_fc () {
241 : handle_block_fc $*
242         local DEV=$1
243         local cil controller_port controller_dev
244         # SCSI-FC device
245         fc_tgt_hcil="${DEV##*/}"
246         fc_tgt_lun="${fc_tgt_hcil##*:}"
247         fc_tgt_path="${DEV%/*}"
248         fc_tgt_num="${fc_tgt_path##*/}"
249         fc_tgt_dev="${fc_tgt_path}/fc_transport:${fc_tgt_num}"
250         if [ -e "$fc_tgt_dev/port_name" ]; then
251             read wwpn < $fc_tgt_dev/port_name
252         fi
253         if [ -z "$wwpn" ] ; then
254             : no WWPN
255             RESULT=1
256             return
257         fi
258         # Linux currently knows about 32bit luns
259         tmp_lun3=$(printf "%04x" $(($fc_tgt_lun & 0xFFFF)))
260         tmp_lun2=$(printf "%04x" $(( ($fc_tgt_lun >> 16) & 0xFFFF)))
261         tmp_lun1="0000"
262         tmp_lun0="0000"
263
264         if (($fc_tgt_lun == 0)) ; then
265             lun="0x0000000000000000"
266         else
267             lun="0x${tmp_lun3}${tmp_lun2}${tmp_lun1}${tmp_lun0}"
268         fi
269         controller_dev="${fc_tgt_path%/host[0-9]*}"
270
271         # FC devices are always endpoints
272         d="fc-${wwpn}:${lun}"
273         D="$controller_dev"
274         RESULT=0
275 }
276
277 handle_block_sas () {
278     : handle_block_sas $*
279         local DEV=$1
280         local cil adapter controller_dev
281         # SAS device
282         sas_host_path="${DEV%%/phy*}"
283         sas_phy_path="${DEV#*/host*/}"
284         sas_phy_path="${sas_phy_path%%/target*}"
285         sas_phy_id="${sas_phy_path%%/*}"
286         sas_rphy_id="${sas_phy_path##*/}"
287         sas_phy_dev="${sas_host_path}/${sas_phy_id}/sas_phy:${sas_phy_id}"
288         if [ -e "$sas_phy_dev/sas_address" ]; then
289             read phy_address < $sas_phy_dev/sas_address
290             read phy_port < $sas_phy_dev/port_identifier
291             read phy_id < $sas_phy_dev/phy_identifier
292         fi
293         if [ -z "$phy_address" ] ; then
294             : no initiator address
295             RESULT=1
296             return
297         fi
298         sas_phy_address="$phy_address:$phy_port:$phy_id"
299         sas_rphy_dev="${sas_host_path}/${sas_phy_id}/${sas_rphy_id}/sas_rphy:${sas_rphy_id}"
300         if [ -e "$sas_rphy_dev/sas_address" ]; then
301             read rphy_address < $sas_rphy_dev/sas_address
302             read rphy_id < $sas_rphy_dev/phy_identifier
303         fi
304         if [ -z "$rphy_address" ] ; then
305             : no initiator address
306             RESULT=1
307             return
308         fi
309         sas_rphy_address="$rphy_address:$rphy_id"
310
311         controller_dev="${sas_host_path%/host[0-9]*}"
312
313         # SAS devices are always endpoints
314         d="sas-${sas_phy_address}-${sas_rphy_address}"
315         D="$controller_dev"
316
317         RESULT=0
318 }
319
320 handle_usb () {
321 : handle_usb $*
322         local DEV=$1
323         cd -P $1
324         DEV=${PWD}
325         port_id=${DEV##*/}
326         port_num=${port_id#*-}
327         host_dev_path=$DEV
328         while [ ! -z "$host_dev_path" ] ; do
329                 case "$host_dev_path" in
330                         */usb*)
331                         usb_host_path=$host_dev_path
332                         host_dev_path="${host_dev_path%/*}"
333                         ;;
334                         *) break ;;
335                 esac
336         done
337         : host_dev_path $host_dev_path
338         usb_host_num=${usb_host_path##*/usb}
339
340         cd "$host_dev_path"
341         usb_host_offset=$(get_port_offset usb $usb_host_num)
342         usb_host_port=$(($usb_host_num - $usb_host_offset))
343         cd "$OPWD"
344
345         if [ "$d" ] ; then
346             d="usb-$usb_host_port:$port_num-${d}"
347         else
348             d="usb-$usb_host_port:$port_num"
349         fi
350
351         D="$host_dev_path"
352         RESULT=0
353 }
354
355 handle_block () {
356     full_sysfs_path="$SYSFS$DEVPATH"
357     if [ -L $full_sysfs_path/subsystem ]; then
358         # new sysfs block layout
359         full_sysfs_path="${full_sysfs_path%/*}"
360         cd "$full_sysfs_path/subsystem";
361         subsys="`pwd -P`"
362         cd "$OPWD"
363         subsys="${subsys##*/}"
364         if [ "$subsys" == "block" ]; then
365             # parent is "block", it's a partition, move one up
366             full_sysfs_path="${full_sysfs_path%/*}"
367         fi
368         cd $full_sysfs_path
369     else
370         # old sysfs block layout
371         if [ ! -L $full_sysfs_path/device ] ; then
372             if [ -f $full_sysfs_path/range ] ; then return ; fi
373             full_sysfs_path="${full_sysfs_path%/*}"
374             : full_sysfs_path "$full_sysfs_path"
375             if [ ! -L $full_sysfs_path/device -o ! -f $full_sysfs_path/dev ] ; then
376                 return
377             fi
378         fi
379         cd $full_sysfs_path/device
380     fi
381     full_sysfs_device_path="`pwd -P`"
382     cd "$OPWD"
383     D=$full_sysfs_device_path
384     while [ ! -z "$D" ] ; do
385     case "$D" in
386         */ide[0-9]/[0-9].[0-9]*|*/ide[0-9][0-9]/[0-9][0-9].[0-9]*)
387         handle_block_ide "$D"
388         ;;
389         */css0/*)
390         if [ -r $full_sysfs_device_path/wwpn ]; then
391             read wwpn < $full_sysfs_device_path/wwpn
392         fi
393         if [ -r $full_sysfs_device_path/fcp_lun ]; then
394             read lun < $full_sysfs_device_path/fcp_lun
395         fi
396         if [ -r $full_sysfs_device_path/hba_id ]; then
397             read bus_id < $full_sysfs_device_path/hba_id
398         fi
399         if [ "$bus_id" -a "$wwpn" -a "$lun" ]; then
400             # S/390 zfcp adapter
401             d="ccw-$bus_id-zfcp-$wwpn:$lun"
402             RESULT=0
403         else
404             # DASD devices
405             bus="ccw"
406             adapter=${D##*/}
407             d="$bus-$adapter"
408             RESULT=0
409         fi
410         D=
411         ;;
412         */rport-[0-9]*:[0-9]*-[0-9]*/*)
413         handle_block_fc "$D"
414         ;;
415         */phy-[0-9]*:[0-9]*/*)
416         handle_block_sas "$D"
417         ;;
418         */fw-host[0-9]*/*)
419         handle_block_firewire "$D"
420         ;;
421         */host[0-9]*/[0-9]*:[0-9]*:[0-9]*:[0-9]*)
422         handle_block_scsi "$D"
423         ;;
424         */usb[0-9]*/[0-9]*/*)
425         handle_usb "$D"
426         ;;
427         */pci[0-9]*:[0-9]*)
428         handle_pci "$D"
429         ;;
430         */devices)
431         D=
432         ;;
433         *)
434         : not handled
435         RESULT=1
436         return
437         ;;
438     esac
439     done
440
441     if [ "$TYPE" == "scsi_tape" ] ; then
442         devname=${full_sysfs_path##*/}
443         rewind="${devname%%st*}"
444         mode="${devname##*st}"
445         case "$mode" in
446             *l)
447                 mode="l"
448                 ;;
449             *m)
450                 mode="m"
451                 ;;
452             *a)
453                 mode="a"
454                 ;;
455             *)
456                 mode=""
457                 ;;
458         esac
459         if [ "$d" ]; then
460             d="$d-${rewind}st${mode}"
461         fi
462     fi
463     echo "ID_PATH=$d"
464 }
465
466 case "$TYPE" in
467     block)
468         handle_block
469         ;;
470     scsi_tape)
471         handle_block
472         ;;
473     input)
474         handle_usb $SYSFS$DEVPATH/device
475         ;;
476     *)
477         RESULT=1
478         ;;
479 esac
480 exit $RESULT