4

In my question Bash script to output path to USB flash memory stick I got stuck on a problem nobody else seems to be having. (The issue also impedes my desire to use this answer: https://unix.stackexchange.com/a/60335/15010.)

So I made that specific problem into this new question.

Apparently removable devices listed in /sys/block end with 1. It is stated here: https://unix.stackexchange.com/a/80880/15010 and several other places in this http://unix.stackexchange.com and this principle is used in the answers I referenced above.

My removable device, a Sandisk 64GB flash memory stick, is listed as:

/sys/block/sdl/removable:0

Apparently, removable devices should end in 1 (and my others do). Why does my USB memory stick not follow the rule?

It was automounted by Dolphin. I'm running Kubuntu 12.04.

Dolphin shows it as "59.6GiB Removable Media".

And it is mounted (automatically) at /media/me/70E8-1567

sudo blkid shows it as:

/dev/sdl1: UUID="70E8-1567" TYPE="vfat".

'lsblk -do name,rm` shows:

sdl   0

And lsusb -vv shows:

Bus 001 Device 008: ID 0781:5530 SanDisk Corp. Cruzer
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.00
  bDeviceClass            0 (Defined at Interface level)
  bDeviceSubClass         0 
  bDeviceProtocol         0 
  bMaxPacketSize0        64
  idVendor           0x0781 SanDisk Corp.
  idProduct          0x5530 Cruzer
  bcdDevice            2.01
  iManufacturer           1 
  iProduct                2 
  iSerial                 3 
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength           32
    bNumInterfaces          1
    bConfigurationValue     1
    iConfiguration          0 
    bmAttributes         0x80
      (Bus Powered)
    MaxPower              200mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           2
      bInterfaceClass         8 Mass Storage
      bInterfaceSubClass      6 SCSI
      bInterfaceProtocol     80 Bulk-Only
      iInterface              0 
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0200  1x 512 bytes
        bInterval               0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x02  EP 2 OUT
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0200  1x 512 bytes
        bInterval               1
  • How is it listed if you do: lsblk -do name,rm? 1 or 0 ? – mikeserv Mar 15 '14 at 1:36
  • Could you give us lsusb ID corresponding? – F. Hauri Mar 15 '14 at 1:39
  • @F.Hauri added lsusb output. Thanks – MountainX Mar 15 '14 at 3:32
  • @mikeserv I added output of lsblk -do name,rm. Thanks. – MountainX Mar 15 '14 at 3:32
  • Ok, could you please give me the output of: grep -H . /sys/block/sdl/events (meaning sdl is your usb key;)... – F. Hauri Mar 15 '14 at 10:30
2

USB removable storage selector: USBKeyChooser

I think I have it:

Replacing grep -Hv 0 /sys/block/*/removable by grep -Hv ^ATA\ *$ /sys/block/*/device/vendor seem work:

export USBKEYS=($(
    grep -Hv ^ATA\ *$ /sys/block/*/device/vendor |
    sed s/device.vendor:.*$/device\\/uevent/ |
    xargs grep -H ^DRIVER=sd |
    sed s/device.uevent.*$/size/ |
    xargs grep -Hv ^0$ |
    cut -d / -f 4
))
for dev in ${USBKEYS[@]} ;do
    echo $dev \"$(
        sed -e s/\ *$//g </sys/block/$dev/device/model
        )\" ;
  done

Or even using readlink to ensure this is USB could be more accurate...

export USBKEYS=($(
    xargs -n1 readlink < <(echo /sys/block/*) |
    sed -ne 's+^.*/usb[0-9].*/\([^/]*\)$+/sys/block/\1/device/uevent+p' |
    xargs grep -H ^DRIVER=sd |
    sed s/device.uevent.*$/size/ |
    xargs grep -Hv ^0$ |
    cut -d / -f 4
))
for dev in ${USBKEYS[@]} ;do
    echo $dev \"$(
        sed -e s/\ *$//g </sys/block/$dev/device/model
        )\" ;
  done

Rewind:

In this I

  1. Ensure this is USB (or removable

  2. Ensure this work as an hard drive (not a CD-Rom)

  3. Ensure they have size greater than 0 (not an empty Card reader)

Golfed version:

US=($(cut -d/ -f4 <(grep -vl ^0$ $(sed s@device/.*@size@ <(grep -l ^DRIVER=sd $(
  sed s+/device.*$+/dev*/ue*+ <(grep -Hv ^ATA\ *$ /sys/block/*/device/vendor)) <(:))) <(:))))

set | grep ^US=

This could be written

US=($(
    cut -d/ -f4 <(
        grep -vl ^0$ $(
            sed s@device/.*@size@ <(
                grep -l ^DRIVER=sd $(
                    sed -ne 's+^.*/usb[0-9].*/\([^/]*\)$+/sys/block/\1/dev*/ue*+p' <(
                        xargs -n1 readlink < <(echo /sys/block/*)
                    )
                ) /dev/null # equivalant but quicker than <(:)
            )) /dev/null)))

With readlink, golfed version become:

US=($(cut -d/ -f4 <(grep -vl ^0$ $(sed s@device/.*@size@ <(grep -l ^DRIVER=sd $(
    sed -ne 's+^.*/usb[0-9].*/\([^/]*\)$+/sys/block/\1/dev*/ue*+p' <(
    xargs -n1 readlink < <(echo /sys/block/*)) ) <(:))) <(:))))

In fine: usbKeyChooser

There is the final version of usbKeyChooser subroutine in my live installer:

#!/bin/bash
DIALOG=whiptail

usbKeyChoose() {
    while [ ! -b /dev/$STICK ] ;do
        USBKEYS=($(
                xargs -n1 readlink < <(echo /sys/block/*) |
                sed -ne 's+^.*/usb[0-9].*/\([^/]*\)$+/sys/block/\1/device/uevent+p' |
                xargs grep -H ^DRIVER=sd |
                sed s/device.uevent.*$/size/ |
                xargs grep -Hv ^0$ |
                cut -d / -f 4                 ))
        if [ ${#USBKEYS[@]} -eq 0 ];then
            title="No key found"
        else
            title="Choose wich USB stick have to be installed"
        fi

        menu=(R "Re scan devices")
        for dev in ${USBKEYS[@]} ;do
            read model </sys/block/$dev/device/model
            menu+=($dev "$model")
        done
        num=$($DIALOG --menu "$title" 21 72 14 "${menu[@]}" 2>&1 >/dev/tty)
        if [ ! "$num" ] ; then
            echo "User aborted."
            exit 0;
        fi
        [ ! "$num" = "R" ] && [ "${USBKEYS[num]}" ] && STICK=${USBKEYS[num]}
    done; }

usbKeyChoose
echo $STICK

I like this looping solution because they

  • let insert many keys,
  • wait for kernel registration,
  • valid the choice,
  • default to nothing and
  • permit user abort.

Anyway, even if user did wrong ok choice, next screen is another choice asking user for which image have to be written on key defaulting to create new image wich is a very long process where user could hit Ctrl+c

  • So you negate ATA devices from your possible results? What if you have an nfs mount? Will your script diskdump on it? – mikeserv Mar 15 '14 at 13:36
  • @mikeserv nfs mount could not be listed by /sys/block/*/device/... maybe dr::bd (I will look), but not nfs! – F. Hauri Mar 15 '14 at 14:23
  • @mikeserv /sys/block/drbd1/removable hold 0 and /sys/block/drbd1/device/vendor don't exist anyway. – F. Hauri Mar 15 '14 at 14:27
  • What about real SCSI? Or serial? – mikeserv Mar 15 '14 at 15:24
  • this works for me. I like it. – MountainX Mar 15 '14 at 16:26
4

Ok, so to pull this in a list, you can use the same command I gave you before, but just drop the removeable requirement:

% for blk in $(lsblk -ndo name) ; do
>        udevadm info --query=all --name "$/dev/$blk" |\
>        grep -q ID_BUS=usb && printf \
>            'findmnt %s -no TARGET ;'\
>                "/dev/$blk" /dev/"$blk"[0-9]
>        } ; done 2>&- |. /dev/stdin 

It's no big deal - it will now only output active mountpoints for block devices on the usb bus and I, for one, can't think of many examples of non-removable usb block devices anyway.

Regarding why your current device does not register as removeable, the most likely answer is that its driver simply doesn't list it that way. When devices are detected by udev it pulls in information on them as soon as it can based on its rules database. You can see all of those classifications with:

% udevadm info --query=all --name /dev/$BLOCK_DEVICE

That will return a list of all of the key=value entries the system uses to classify the hardware referenced by /dev/$BLOCK_DEVICE. At the bottom of this post slm advises how this information can be easily parsed to serve your needs - and it's pretty simple indeed. The documentation rightly describes it as "human-readable, grep-friendly." Above I filter all block devices to only those on the usb bus with:

% grep -q ID_BUS=usb

You can also attribute-walk all the way up the device tree for your current device and another whose attributes you think it should reflect. If it doesn't, it is because udev assigned it differently than you think it should have. Thankfully, though, you can see every datapoint that affected its assignment with:

udevadm info --attribute-walk --name /dev/$BLOCK_DEVICE

You can use this data to model new assignment rules as you please.

  • Unfortunely, this may show empty card reader or empty cdrom. – F. Hauri Mar 15 '14 at 9:48
  • This is not an answer to the question: Why USB stick listed as non-removable... – F. Hauri Mar 15 '14 at 10:22
  • I hope I fixed that just now. – mikeserv Mar 15 '14 at 13:13
  • If you wanna have a look, there is the whole script: – F. Hauri Mar 18 '14 at 6:16
  • I saw it. It's pretty good, but you'd be a lot better off working with the tools as intended instead of manipulating their side effects. The problem with side effects is that they tend to change without notice. – mikeserv Mar 18 '14 at 6:46
0

This is F. Hauri's answer adapted for kdialog:

#!/bin/bash

DIALOG=kdialog

usbKeyChoose() {
    while [ ! -b /dev/$STICK ] ;do
        USBKEYS=($(
                xargs -n1 readlink < <(echo /sys/block/*) |
                sed -ne 's+^.*/usb[0-9].*/\([^/]*\)$+/sys/block/\1/device/uevent+p' |
                xargs grep -H ^DRIVER=sd |
                sed s/device.uevent.*$/size/ |
                xargs grep -Hv ^0$ |
                cut -d / -f 4                 ))
        if [ ${#USBKEYS[@]} -eq 0 ];then
            title="No key found"
        else
            title="Choose wich USB stick have to be installed"
        fi

        menu=(R "Re scan devices")
        i=0
        for dev in ${USBKEYS[@]} ;do
            read model </sys/block/$dev/device/model
            #echo $i $dev "$model"
            menu+=("$i" "$dev $model")
            i=$[i + 1]
        done
        num=$($DIALOG --menu "$title"  "${menu[@]}")
        #echo "num=$num"
        #echo "USBKEYS[num]=${USBKEYS[num]}"
        if [ ! "$num" ] ; then
            echo "User aborted."
            exit 0;
        fi
        [ ! "$num" = "R" ] && [ "${USBKEYS[num]}" ] && STICK=${USBKEYS[num]}
    done; }

usbKeyChoose
echo $STICK
  • If you've choosed my anser for use, why did you not accept them? – F. Hauri Mar 17 '14 at 9:15
  • @F.Hauri I did choose your answer now. I learned from both answers (yours and @mikeserv) , so thanks to both of you! – MountainX Mar 17 '14 at 23:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.