chiark / gitweb /
dfb997b3403bd019ff7e341db0d06114cbc49c1f
[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 # example for all block devices on a system:
19 #       for i in `find /sys/block -name dev` ;do DEVPATH="`echo $i | sed -e 's@^/sys\|/dev@@g'`" path_id ; done
20
21 # examples:
22 # SCSI cdrom
23 # /block/sr0 -> /devices/pci0002:30/0002:30:0c.0/host0/0:0:1:0
24 # result: pci-0002:30:0c.0-scsi-0:0:1:0
25 # SCSI disk
26 # /block/sda -> /devices/pci0002:30/0002:30:0c.0/host0/0:0:4:0
27 # result: pci-0002:30:0c.0-scsi-0:0:4:0 
28 # SATA disk, 4 channels per controller
29 # /block/sda -> /devices/pci0001:00/0001:00:07.0/0001:05:0c.0/host0/0:0:0:0
30 # result: pci-0001:05:0c.0-scsi-0:0:0:0
31 # IDE disk
32 # /block/hda -> /devices/pci0002:02/0002:02:0d.0/ide0/0.0
33 # result: pci-0002:02:0d.0-ide-0.0
34 # IDE cdrom on a Mac ASIC:
35 # /block/hdc -> /devices/pci0001:01/0001:01:17.0/0.80000000:mac-io/0.00020000:ata-3/ide1/1.0
36 # result: mac-io_ata-3_master
37 # IDE cdrom on a Mac ASIC, with ide-scsi:
38 # /block/sr0 -> /devices/pci0001:01/0001:01:17.0/0.80000000:mac-io/0.0001f000:ata-4/ide0/0.1/host2/2:0:0:0
39 # result: mac-io_ata-4_slave
40
41 # USB CDrom drive without 'serial' number:
42 # reusing 'product' and 'manufacturer' string, if available
43 # /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
44 # result: usb-storage-odd-Freecom-USIDERev930:0:0:0
45
46 # devices may have several interfaces on one PCI device, like IDE:
47 # pci-0001:00:04.0_ide1-master
48 # pci-0001:00:04.0_ide2-master
49 # pci-0001:00:04.0_ide2-slave
50 # they are marked as ports, it is expected that the driver shows
51 # ide1 even if there is nothing connected to either master or slave
52 # interface
53 #
54 # match order is important.
55 # first IDE to find ide-scsi devices.
56 # then SCSI
57 # first usb-storage
58 # then firewire sbp2
59 # then the rest
60
61 SYSFS=/sys
62 RESULT=1
63 CDROM=
64 TYPE=
65 OPWD="`pwd`"
66 # Check for 'pwd -P'
67 if $(pwd -P > /dev/null 2>&1); then
68     pwd_cmd="pwd -P"
69 else
70     pwd_cmd="pwd"
71 fi
72 full_sysfs_class_path=
73 full_sysfs_device_path=
74
75 if [ -z "$DEVPATH" -a -z "$1" ] ; then
76     exit 1
77 fi
78
79 if [ -z "$DEVPATH" ] ; then
80     case "$1" in
81         $SYSFS/*)
82             DEVPATH="${1#$SYSFS}"
83             ;;
84         *)
85             DEVPATH=$1
86             ;;
87     esac
88 fi
89
90 if [ ! -d $SYSFS$DEVPATH ] ; then
91     exit 1
92 fi
93 if [ ! -f $SYSFS$DEVPATH/dev ] ; then
94     exit 1
95 fi
96
97 case "$DEVPATH" in
98     /block/*)
99         TYPE=block
100         ;;
101     /class/*)
102         TYPE="${DEVPATH#/class/}"
103         TYPE="${TYPE%%/*}"
104         ;;
105     *)
106         exit 1
107         ;;
108 esac
109         
110 #
111 ##
112 #
113
114 get_port () {
115     local type offset port
116     type=$1
117     offset=$2
118     for i in $type[0-9]* ; do
119         : i $i
120         port="${i#$type}"
121         if [ "$port" -lt "$offset" ] ; then offset=$port ; fi
122     done
123     if [ "$port" != "0" ] ; then
124         echo $(($2 - $offset))
125     fi
126 }
127
128 handle_block_ide () {
129 : handle_block_ide $*
130         local DEV=$1
131         local port idedev idecontroller
132         # IDE
133         : DEV $DEV
134         d=$DEV
135         case "$DEV" in
136         # remove ide-scsi part, leave only channel info
137                 */ide[0-9]*/host[0-9]*)
138                 while [ ! -z "$d" ] ; do
139                         case "$d" in
140                                 */host[0-9]*)
141                                 d="${d%/*}"
142                                 continue
143                                 ;;
144                                 *)
145                                 break
146                                 ;;
147                         esac
148                 done
149                 ;;
150         esac
151         idedev=$d
152         while [ ! -z "$d" ] ; do
153                 case "$d" in
154                         */ide[0-9]*)
155                         port="${d##*/}"
156                         d="${d%/*}"
157                         continue
158                         ;;
159                         *)
160                         break
161                         ;;
162                 esac
163         done
164         idecontroller=$d
165         # port info if the controller has more than one interface
166         port="${port#ide}"
167         : port $port d $d
168         : idedev $idedev kernel_port $port
169         case "${idedev##*.}" in
170                  0)
171                  channel=0
172                  ;;
173                  1)
174                  channel=1
175                  ;;
176                  *)
177                  echo "Error: $idedev is neither master or slave" >&2
178         esac
179         case "$d" in
180                 *:mac-io/*)
181                 : mac-io: $d
182                 d="`echo $d | sed -e 's@^.*:mac-io[^:]\+:\([^/]\+\).*@mac-io_\1@'`"
183                 ;;
184                 /sys/devices)
185                 # PCMCIA devices
186                 ifname=${full_sysfs_class_path##*/}
187                 set -- `sed -n "/$ifname/p" /var/lib/pcmcia/stab`
188                 d="pcmcia-$1"
189                 ;;
190                 *)
191                 d="pci-${d##*/}"
192                 # d="`echo $d | sed -e 's@^.*/\([^/]\{1,\}\)/.@pci-\1@'`"
193                 ;;
194         esac
195
196         cd $idecontroller
197         port="`get_port ide $port`"
198         cd "$OPWD"
199         :  hardware_port $port
200         if [ -z "$port" ] ; then
201                 d="${d}-ide-0:$channel"
202         else
203                 d="${d}-ide-${port}:$channel"
204         fi
205            
206         RESULT=0
207 }
208
209 handle_block_scsi () {
210 : handle_block_scsi $*
211         local DEV=$1
212         local cil controller_port controller_dev
213         # SCSI device
214         cil="${DEV##*/}"
215         cil="${cil#*:}"
216         controller_dev=$DEV
217         while [ ! -z "$controller_dev" ] ; do
218                 case "$controller_dev" in
219                         */host[0-9]*)
220                         controller_port=$controller_dev
221                         controller_dev="${controller_dev%/*}"
222                         ;;
223                         *) break ;;
224                 esac
225         done
226         : controller_dev $controller_dev
227         : controller_port $controller_port
228         # a host controller may have more than one interface/port
229         controller_port="${controller_port##*/}"
230         controller_port="${controller_port##host}"
231         #
232         case "$controller_dev" in
233                 # grand central, old powermacs
234                 *:gc/*)
235                 adapter="`echo $controller_dev |  sed -e 's@/[^/]\{1,\}$@@;s@^.*/@@;s@^.*:@@'`"
236                 bus="gc"
237                 ;;
238                 # PARISC devices
239                 *parisc*)
240                 adapter="${controler_dev##*/}"
241                 bus=parisc;
242                 ;;
243                 *)
244                 adapter="${controller_dev##*/}"
245                 bus="pci"
246                 ;;
247         esac
248         cd "$controller_dev"
249         controller_port="`get_port host $controller_port`"
250         cd "$OPWD"
251         d="$bus-$adapter"
252         if [ -z "$controller_port" ] ; then
253                 controller_port=0
254         fi
255         d="${d}-scsi-${controller_port}:${cil}"
256         RESULT=0
257 }
258
259 handle_block_usb_storage () {
260 : handle_block_usb_storage $*
261         local DEV=$1
262         cil="${DEV##*/}"
263         cil="${cil#*:}"
264         controller_dev=$DEV
265         while [ ! -z "$controller_dev" ] ; do
266                 case "$controller_dev" in
267                         */host[0-9]*)
268                         controller_dev="${controller_dev%/*}"
269                         ;;
270                         *) break ;;
271                 esac
272         done
273         : controller_dev $controller_dev
274         #
275         # usb-storage devs have a serial number, hopefully unique
276         serial=
277         if [ -f $controller_dev/../serial ] ; then
278                 serial="`sed -e 's@^[ -]\{1,\}\|[ -]\{1,\}$@@g;s@[^abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_0123456789]@@g' < $controller_dev/../serial`"
279                 : serial XXX_${serial}_XXX
280                 d="usb-$serial"
281                 serial="`echo $serial | sed -e 's@[ 0]\{1,\}@@g'`"
282         fi
283         if [ -z "$serial" ] ; then
284                 # no serial, broken device
285                 # has eventually binary junk in vpd
286                 identifier=
287                 if [ -f $controller_dev/../product ] ; then
288                 product="`sed -e 's@^[ -]\{1,\}\|[ -]\{1,\}$@@g;s@[^abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_0123456789]@@g' < $controller_dev/../product`"
289                 fi
290                 if [ -f $controller_dev/../manufacturer ] ; then
291                 manufacturer="`sed -e 's@^[ -]\{1,\}\|[ -]\{1,\}$@@g;s@[^abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_0123456789]@@g' < $controller_dev/../manufacturer`"
292                 fi
293                 if [ -z "$product" -o -z "$manufacturer" ] ; then
294                         read idvendor < $controller_dev/../idVendor
295                         read idproduct < $controller_dev/../idProduct
296                         identifier="0x${idvendor}-0x${idproduct}"
297                 else
298                         identifier="${manufacturer}-${product}"
299                 fi
300                 d="usb-${identifier}"
301         fi
302         d="$d:$cil"
303         RESULT=0
304
305
306 }
307
308 handle_block () {
309     full_sysfs_class_path="$SYSFS$DEVPATH"
310     if [ ! -f $full_sysfs_class_path/dev ] ; then return ; fi
311     # the main device has (hopefully) a symlink to the real device
312     # a partition is a subdir of the main (raw) device
313     if [ ! -L $full_sysfs_class_path/device ] ; then
314         if [ -f $full_sysfs_class_path/range ] ; then return ; fi
315         full_sysfs_class_path="${full_sysfs_class_path%/*}"
316         : full_sysfs_class_path "$full_sysfs_class_path"
317         if [ ! -L $full_sysfs_class_path/device -o ! -f $full_sysfs_class_path/dev ] ; then
318             return
319         fi
320     fi
321     cd $full_sysfs_class_path/device
322     full_sysfs_device_path="`$pwd_cmd`"
323     cd "$OPWD"
324     D=$full_sysfs_device_path
325     case "$D" in
326         */ide[0-9]/[0-9].[0-9]*|*/ide[0-9][0-9]/[0-9][0-9].[0-9]*)
327         handle_block_ide "$D"
328         ;;
329         */usb[0-9]*/[0-9]*/host[0-9]*/[0-9]*:[0-9]*:[0-9]*:[0-9]*)
330         handle_block_usb_storage "$D"
331         ;;
332         */css0/*)
333         if [ -r $full_sysfs_device_path/wwpn ]; then
334             read wwpn < $full_sysfs_device_path/wwpn
335         fi
336         if [ -r $full_sysfs_device_path/fcp_lun ]; then
337             read lun < $full_sysfs_device_path/fcp_lun
338         fi
339         if [ -r $full_sysfs_device_path/hba_id ]; then
340             read bus_id < $full_sysfs_device_path/hba_id
341         fi
342         if [ "$bus_id" -a "$wwpn" -a "$lun" ]; then
343             # S/390 zfcp adapter
344             d="ccw-$bus_id-zfcp-$wwpn:$lun"
345             RESULT=0
346         else
347             # DASD devices
348             bus="ccw"
349             adapter=${D##*/}
350             d="$bus-$adapter"
351             RESULT=0
352         fi
353         ;;
354         */host[0-9]*/[0-9]*:[0-9]*:[0-9]*:[0-9]*)
355         # check for ieee1394 sbp2 
356         if test -f $D/ieee1394_id ; then
357             read ieee1394_id < $D/ieee1394_id
358             d="`echo ieee1394-${ieee1394_id} | sed -e 's@:@-@g'`"
359             RESULT=0
360         else
361             handle_block_scsi "$D"
362         fi
363         ;;
364
365         *)
366         : not handled
367         RESULT=1
368         return
369
370         ;;
371     esac
372     # look for a partition
373     if [ "$full_sysfs_class_path" != "$SYSFS$DEVPATH" ] ; then
374         dp="`echo $SYSFS$DEVPATH | sed -e 's@^/.*/@@;s@^[^0-9]\{1,\}@@;s@.*_@@'`"
375         case "$d" in
376             *[0-9])
377             d="${d}p${dp}"
378             ;;
379             *)
380                 d="${d}${dp}"
381                 ;;
382         esac
383     fi
384     # done
385     echo "ID_PATH=$d"
386 }
387
388 case "$TYPE" in
389         block)
390         handle_block
391         ;;
392         *)
393         RESULT=1
394         ;;
395 esac
396 exit $RESULT