Hotplugging disks on a headless Ubuntu Box

Since I’ve been using Ubuntu, I’ve been blessed in comparison with Linux of even just a few years ago. Hotplugging cameras, disks, and almost any other USB device “just works”. As a general rule, I don’t have to worry about configuring it, installing drivers, or compiling my kernel. Still, there are some times I need to figure out how to do things at the lower level. Today, I wanted to figure out how to mount a usb drive automatically on a headless Linux box. Since Gnome isn’t running and no one is logged in, the usual methods don’t work. After some poking around, plugging and unplugging a disk several times, I have a working solution. The first thing to know is udev. udev is a user-space process that interacts with the Linux kernel to set up devices at boot time and, in the case of USB devices, as they are plugged in. On Ubuntu, you can drop a file in /etc/udev/rules.d to tell udev to execute commands when it sees a particular device. Setting up a USB drive is a two stage process. First, udev sees the disk and fires off a series of events you can hook into. Then, after you probe the disk, udev will fire off another list of events for the partitions. To intercept these and call a script to probe the drive and mount the partitions, create a file named /etc/udev/rules.d/50-usbdisk.rules (there is a README file in that directory that explains the naming convention). Into 50-usbdisk.rules put the following text:

ACTION=="add", DEVTYPE=="disk", RUN="/usr/local/bin/usb-add-disk"  ACTION=="add", DEVTYPE=="partition", RUN="/usr/local/bin/usb-mount-partition"  ACTION=="remove", DEVTYPE=="partition", RUN="/usr/local/bin/usb-unmount-partition"

This will cause udev to run usb-add-disk when a disk is plugged in, usb-mount-partition when it sees the partitions, and usb-umount-partition when the drive is unplugged. Next, create /usr/local/bin/usb-add-disk with the following contents:

#!/bin/sh -e    # Maximum times to probe a disk  MAX=30    logger "Probing ${DEVNAME} using parted"  COUNT=0  ERR=1    while [ $COUNT -lt $MAX -a $ERR -ne 0 ]; do      sleep 1      ERR=0      parted ${DEVNAME} || ERR=$?      COUNT=$(($COUNT + 1))  done    if [ $ERR -ne 0 ]; then      logger "Couldn't probe $DEVNAME for media after $COUNT times"  fi    exit 0  

This script will log a message that it is “Probing…” to syslog, probe a a disk up to MAX times (30 in this case), and, if it isn’t successful after 30 probes, log a message to syslog about its failure. A couple of notes. I’m not sure about the $((…)) syntax. It works in bash and dash, but I’m not sure it is a POSIX standard. Second, you may be wondering why I’m pausing and probing so many times. Usually this should work on the first try. Still the “disk” I was using in this case was my Blackberry‘s SD card. Since I’m a little paranoid, I have a password on my Blackberry. Every time I plug it into a computer, it prompts me for the password. Until I enter the password, Ubuntu can see the drive, but thinks the drive is empty. Once I enter the password, the disk’s partitions appear. (Ubuntu doesn’t appear to see the partitions until after parted probes the drive. If you know a better way to get the partions to show up besides probing the drive like this, please let me know.) At this point (and, on most usb disks, this is almost immediately since you don’t have to provide a password), udev will call usb-mount-partition. Let’s give udev something to run. In /usr/local/bin/usb-mount-partition put the following:

#!/bin/sh -e    BASE=`basename $DEVNAME`  if [ -x /media/$BASE ]; then      logger "Can't mount usbdisk, /media/$BASE already exists"  else      mkdir -p /media/$BASE      mount ${DEVNAME} /media/$BASE      logger "Mounted usbdisk at /media/$BASE"  fi    exit 0

If everything works smoothly, the disk will now be mounted under /media. Whether the script is able to mount the disk or not, a message will be sent to syslog letting you know what happens. When you want to unmount the disk, you should run umount first and then remove the USB drive. That’s what you should do. But you might forget. If you do forget, then you’ll be left with a dead mount point. In that case, we have one more script to handle the clean up: usb-umount-partition. In /usr/local/bin/usb-umount-partition, put the following text:

#!/bin/sh -e    BASE=`basename $DEVNAME`  if [ -d /media/$BASE ]; then      logger "Unmounting usbdisk from /media/$BASE"      umount /media/$BASE || true      rmdir /media/$BASE  else      logger "Couldn't find mount point for $DEVNAME"  fi    exit 0

This script will umount any dead mount points and remove the mountpoint that usb-mount-partition created. Make sure the scripts you just created in /usr/local/bin are executable (sudo chmod +x /usr/local/bin/usb-*)and that’s it: your headless Ubuntu box should now be automatically mount disks. If you want to signal some other program or run a script when the drive is mounted, you can add that to usb-mount-partition. A couple of notes on these scripts. First, it is a good idea to start your shell scripts with the -e flag. This will force you to handle any error conditions. For example, in usb-umount-partition, I run umount to unmount the drive. But suppose you already did this (as you should have). The umount command would return an error. Since I’m using the -e flag, I need to handle that, so I added || true. Handling errors like this really helps during testing to make sure errors don’t hide in your scripts. logger is extremely helpful for debugging. When I was testing my udev rule files, I found it helpful to pipe env to logger. I could just tail -f /var/log/messages and find out what environment my scripts were getting.

One thought on “Hotplugging disks on a headless Ubuntu Box”

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.