With a custom hotplug script, you can automatically synchronize your computer and USB storage device camera just by plugging in the camera.
On a particular vacation where I brought both my laptop and my digital camera, I noticed I was offloading the day’s pictures onto my laptop each night. The manual process was to plug in the camera, mount it as a USB storage device, create a new directory for the images, copy the images to the new directory, and, finally, unmount the camera. With a few tweaks to hotplug and a little detective work, I was able to make the entire process hands free: I now plug in the camera, and it automatically synchronizes everything for me. In this hack I describe the tweaks to hotplug, along with tips for creating your own custom synchronization script.
Hotplug is a program under Linux that manages hot-pluggable devices. For the most part it is a program you don’t have to think about—just plug in your USB mouse, for instance, and hotplug will make sure the applicable drivers are loaded. Hotplug is very powerful, however, in that it allows you to customize what to do when a certain device is plugged in. The first step to configuring hotplug is to plug in a camera and scan the system logs to make sure the camera is recognized. In the case of my camera, hotplug recognized that it was a USB storage device and made sure my usb-storage module was already loaded:
Jan 19 15:46:27 clover kernel: hub.c: new USB device 00:02.0-1, assigned address 4 Jan 19 15:46:27 clover kernel: WARNING: USB Mass Storage data integrity not assured Jan 19 15:46:27 clover kernel: USB Mass Storage device found at 4 Jan 19 15:46:31 clover usb.agent[10819]: kernel driver usb-storage already loaded
USB drives (and in this case cameras that function as USB drives) work as regular SCSI hard drives in this situation and require the sd_mod module to be loaded. You could just mount the device that is created (/dev/sda1 typically, unless multiple drives are plugged in simultaneously), copy files, and be finished with the camera. But if you want to automatically synchronize this camera to your computer, you must perform special commands just for this drive, and not for any other USB storage device. To set up automatic synchronization, you first need some information from /proc. The /proc/bus/usb/devices file will show information about the various USB devices on the system. For instance, here is a snip from the file when my digital camera is plugged in:
T: Bus=01 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 2 Spd=12 MxCh= 0 D: Ver= 1.10 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1 P: Vendor=07b4 ProdID=0105 Rev= 0.01 S: Manufacturer=OLYMPUS S: Product=C740UZ S: SerialNumber=000255534644 C:* #Ifs= 1 Cfg#= 1 Atr=c0 MxPwr= 0mA I: If#= 0 Alt= 0 #EPs= 2 Cls=08(stor.) Sub=06 Prot=50 Driver=usb-storage E: Ad=04(O) Atr=02(Bulk) MxPS= 64 Ivl=0ms E: Ad=83(I) Atr=02(Bulk) MxPS= 64 Ivl=0ms
Yeah, it’s a lot of gibberish, but there is information in there
that is useful for setting up hotplug. The parts
that are of interest to you are Vendor=07b4,
ProdID=0105, Product=C740UZ
, and Driver=usb-storage
. You can use these
sections to configure hotplug to do certain
things when this device is plugged in. Generally
hotplug will detect that a device is a USB
storage device automatically. In case your drive isn’t automatically
detected, you will have to add a configuration line for it in
/etc/hotplug/usb/usb.usermap. The first four
columns of usb.usermap are all that should
concern you, and the information you need for those columns can be
found in those special sections of the
/proc/bus/usb/devices file. The first column
contains the USB module to associate with the device, or what follows
the Driver
option (Driver=usb-storage
). The second column uses
0x00f
for all USB storage devices.
The third and fourth options contain the hex code for the Vendor
and Product
ID
respectively. The rest of the fields don’t matter—the
first three fields are enough to match this device—so put in 0x00
for those. For my camera, the file
would look like this:
# usb.usermap file # This is autogenerated by update-usb.usermap program # Note: you may use /etc/hotplug/usb/*.usermap # usb module match_flags idVendor idProduct bcdDevice_lo bcdDevice_ hi bDeviceClass bDeviceSubClass #bDeviceProtocol bInterfaceClass bInterfaceSubClass bInterfaceProtocol driver_info usb-storage 0x00f 0x07b4 0x0105 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
This line in usb.usermap tells
hotplug to run the
/etc/hotplug/usb/usb-storage script whenever it
sees that this device is plugged in. That script can then load modules
or run other programs (including setting up programs to run when the
drive is removed). The next step is to create this file and add an
entry that matches your camera based on the PRODUCT
environment variable the
hotplug script generates. Then create the
directory in which the camera will be mounted (in this example
/mnt/camera). The
/etc/hotplug/usb/usb-storage script for my camera
would look like this (replace username
with your user’s name):
#!/bin/sh DEVICE=`grep "kernel: Attached scsi .*disk" /var/log/syslog | tail -n 1 | cut -f 10 -d " "` case "$PRODUCT" in # Olympus C750 camera 7b4/105/1) /bin/mount -t vfat /dev/${DEVICE}1 /mnt/camera echo -e '#!/bin/sh\n/bin/umount /mnt/camera' > $REMOVER chmod a+x $REMOVER export DISPLAY=":0.0" #suusername
-c /home/username
/bin/camera_sync ;; esac
The second line needs some explanation. USB drives are assigned
SCSI drive designations on the fly starting from
sda and going up. While I could just say
/dev/sda1, if more than one USB device were
plugged in at the time, the wrong device might get mounted. With the
standard Linux device system, there isn’t an easy way to determine
what SCSI drive letter was assigned to the USB drive. A kludge around
this fact is to simply grab the last mention of a USB SCSI drive
assignment out of the system log and cut out the device information.
Based on your kernel version, your system might log this information
slightly differently, so you may have to compare the
grep pattern to your syslog
file and possibly adjust the -f 10
value to match the field that contains the SCSI device entry.
The PRODUCT
environment
variable specifies the Vendor and Product IDs for the current USB
device. To see what kind of environment variables are present when
this script is run, you can insert a set >
/tmp/settings
line above the case
statement. Read through that file to
see how PRODUCT
is set for your
product. In my case it was set to 7b4/105/1
. Then set up the case
statement to mount this camera when
that device is actually plugged in to
/mnt/camera. This method is a bit brittle, as you
will see below, but it does work. Another environment variable,
REMOVER
, specifies the name of a
script that is run after the device is removed. This script is empty
by default, so I echoed in the umount
command I wanted to use when I
removed the drive.
Next make the script executable with this command:
$ chmod a+x /etc/hotplug/usb/usb-storage
Then restart the hotplug service:
$ /etc/init.d/hotplug restart
Now plug the camera and type df
to confirm that
/mnt/camera is mounted. After you unplug the
drive, the drive will be umounted.
There are some problems with the previous method. For one, the drive isn’t unmounted until it already has been removed. This could result in file system corruption over time if the device failed to sync before being removed. The solution is to use autofs to mount the device on demand and then unmount when idle.
First, install the autofs system with your distribution’s package manager. Then modify /etc/auto.master and add a line for your removable drives:
# $Id: ch01.xml,v 1.2 2005/11/28 18:05:49 ellie Exp $ # Sample auto.master file # Format of this file: # mountpoint map options # For details of the format look at autofs(5). /var/autofs/misc /etc/auto.misc /var/autofs/net /etc/auto.net /var/autofs/removable /etc/auto.removable --timeout=2
That last line tells autofs to mount any removable devices specified in /etc/auto.removable under /var/autofs/removable, and to unmount them after two seconds of idling. Now create the /etc/auto.removable file:
sda -fstype=vfat,umask=002 :/dev/sda1 sdb -fstype=vfat,umask=002 :/dev/sdb1 sdc -fstype=vfat,umask=002 :/dev/sdc1 sdd -fstype=vfat,umask=002 :/dev/sdd1
This file sets up mount points under /var/autofs/removable/, the mounting options to use, and indicates which device to mount. All of this happens outside /etc/fstab or any other mounting, so remove the /mnt/camera mount point. Instead, have the USB-storage hotplug script you created make a symlink to the autofs mount point when the drive is inserted, and then remove the symlink when the drive is removed. That way, you can deal only with /mnt/camera and not worry about /var/autofs directories. The new and improved /etc/hotplug/usb/usb-storage follows:
#!/bin/sh DEVICE=`grep "kernel: Attached scsi .*disk" /var/log/syslog | tail -n 1 | cut -f 10 -d " "` case "$PRODUCT" in # Olympus C750 camera 7b4/105/1) ln -s /var/autofs/usb/$DEVICE/dcim/100olymp
/mnt/camera echo -e '#!/bin/sh\nrm /mnt/camera' > $REMOVER chmod a+x $REMOVER export DISPLAY=":0.0" #suusername
-c /home/username
/bin/camera_sync ;; esac
Now, plug in the camera and confirm that /mnt/camera was created. In my case, I created the symlink to dig into the camera’s directory structure and take me directly to the images (/dcim/100olymp). Change this directory path to match the path your camera uses. Because you are using autofs, the camera will not be mounted unless you access /mnt/camera. After two seconds, autofs will unmount the drive. You can monitor /var/log/syslog to watch this happening:
Jan 19 16:24:35 clover automount[14059]: mount(generic): calling mkdir_path /var/autofs/removable/sda Jan 19 16:24:35 clover automount[14059]: mount(generic): calling mount -t vfat -s -o umask=002 /dev/sda1 /var/autofs/removable/sda Jan 19 16:24:36 clover automount[14059]: mount(generic): mounted /dev/sda1 type vfat on /var/autofs/removable/sda Jan 19 16:24:39 clover automount[14066]: running expiration on path /var/autofs/removable/sda Jan 19 16:24:39 clover automount[14066]: expired /var/autofs/removable/sda
As you can see, the drive unmounted seconds after the command finished since it had been idling. It would now be safe to remove the camera.
Until now, the /etc/hotplug/usb/usb-storage script had the following line commented out:
su username -c /home/username/bin/camera_sync
This line executes a custom camera syncing script as the normal
user. This way you can change the script and add features without having to be root
. Use su to change
to your user from the root user so all of the images will be accessible by your normal user. The
synchronization script relies on rsync to perform the synchronization logic,
and the complete script looks like this:
#!/bin/sh
$DATE=`date +%Y-%m-%d`
mkdir -p ~/photos/$DATE
rsync -r --size-only -b --suffix="-1.jpg" /mnt/camera/ ~/photos
/$DATE/
This script creates a new directory, named with today’s date,
under a photos directory I created in my home directory. Then
rsync synchronizes the images with that directory
according to their file sizes (since other Unix-style file data isn’t
present on the FAT32 drives most cameras use). The --suffix
argument handles any potential
duplicate files that may occur, in case your camera doesn’t create
unique-enough filenames across multiple flash cards. You can also
launch a slideshow tool such as gThumb with this script and
immediately look at thumbnails of all your photos. Once this script is
created, uncomment the corresponding line from
/etc/hotplug/usb/usb-storage and plug in your
camera to test the synchronization. Any pictures on the camera will
appear in a new directory under ~/photos.
Get Linux Multimedia Hacks now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.