Journal Zarf's Journal: USB pen-drive and Linux Saga Part V: The final Chapter 5
#!/usr/bin/perl
# http://slashdot.org/~Zarf/
# http://slashdot.org/~Zarf/journal/44708
# to use this script modify usb.usermap with this line (sans "#" comment marker):
# usbstorage 0x0030 0x0c76 0x0005 0x0000 0x0000 0x00 0x00 0x00 0x08 0x00 0x00 0x00000000
# then name this script /etc/hotplug/usb/usbstorage
my $remover = $ENV{"REMOVER"};
my $action = $ENV{"ACTION"};
if ( $action =~ m/remove/i ) {
my $dev = get_dev(); # some system want /var/log/dmesg
$umount = `umount -l $dev`; # unmount
exit $umount;
}
if($action =~ m/add/i){
# make sure all modules are loaded...
$res = `/sbin/modprobe sr_mod`;
$res = `/sbin/modprobe sd_mod`;
$res = `/sbin/modprobe usbcore`;
$res = `/sbin/modprobe uhci`;
$res = `/sbin/modprobe usb-storage`;
$res = `/sbin/modprobe vfat`;
my $dev = get_dev(); # some system want /var/log/dmesg
die("$dev already mounted!") if is_mounted($dev);
my $name = get_product_name($ENV{PRODUCT});
my $consoleowner = get_consoleowner();
my $dir = "/mnt/$name";
my $i = 0;
while( (is_mounted($dev,$dir) ) ){ # Check for any device that might mount
$i++; # to this dir. If you find one, increment
$dir = "/mnt/$name$i"; # the name until you find a dir that's free.
last if $i > 10000;
}
mkdir($dir, 0755) unless -e $dir;
add_to_fstab($dev,$dir); # Associate this dev to this dir
`umount -l $dev`; # doing this first fixes a lot of errors.
# By unmounting first you can avoid errors associated with
# the fringe case where the user has
# uncerimoniously yoinked the drive out of the system and frantically
# jabbed the drive back in.
`su $consoleowner -c \"sleep 1; mount $dir\"`; # delay to give the system
# a chance to catch up... make sure to mount as the consoleowner.
make_remover($remover,$dir);
}
exit;
sub get_consoleowner{
my $consoleowner = "root";
if( -f "/var/run/console.lock" ){
open(CO, "</var/run/console.lock") or die "Trying console.lock: $!";
$consoleowner = <CO>;
close CO;
}
elsif( -f "/var/lock/console.lock" ){
open(CO, "</var/lock/console.lock") or die "Trying console.lock: $!";
$consoleowner = <CO>;
close CO;
}
# otherwise leave the consoleowner as the user 'nobody'.
return $consoleowner;
}
# is_mounted($dev,$dir) returns true if either the dir or the dev is spoken for.
# if a $dev is supplied without a dir, we check for only the device.
sub is_mounted{
my $dir = shift;
my $dev = shift || "/dev/null"; # default to /dev/null because noone will ever
# mount onto dev null and this code effectively only looks for the device now.
open(MTAB, "</etc/mtab") or die "Trying to open /etc/mtab: $!";
my $found = 0;
while( my $line = <MTAB> ){
$found = 1 if ( $line =~ m/$dir/g || $line =~ m/$dev/g );
}
close MTAB;
return $found;
}
sub add_to_fstab{
my $device = shift;
my $dir = shift;
my $added = 1;
# now we check the fstab to see if this device is listed:
open(FSTAB, "</etc/fstab") or die "Trying fstab: $!";
while( my $line = <FSTAB> ){
$added = 0 if ($line =~ m/$device/);
}
close FSTAB;
if($added){
open(FSTAB, ">>/etc/fstab") or die "Trying fstab: $!";
print FSTAB "$device\t$dir\tauto\tnoauto,user,kudzu\t0 0\n";
#NOTE: the kudzu causes this entry to get automatically removed by
# updfstab -- the remover may want to call updfstab or let it
# automatically manage the fstab itself.
close FSTAB;
print STATUS "wrote to fstab, $device,dir\n";
}
else{ print STATUS "$device already in fstab\n";}
return $added;
}
sub get_product_name{
my $code = shift;
my $name = "usb-storage";
my ($ven,$mod) = ($1,$2) if $code =~ m/(\w+)\/(\w+)\/\w+/;
my $info = `/sbin/lsusb -d 0x$ven:0x$mod 2>1`;
my ($idVendor, $vendor) = ($1,$2)
if $info =~ m/idVendor\s+(\w+)\W+(\w+).{0,}(\n|$)/s;
my ($idProduct,$product) = ($1,$2)
if $info =~ m/idProduct\s+(\w+)\W+(\w+).{0,}(\n|$)/s;
my $tmp = $vendor; # ."_".$product;
$tmp =~ s/\W/_/g;
$tmp =~ s/^_|_$//g;
$name = $tmp if length($tmp);
return $name;
}
# make_remover($remover,$dir); #################################################
sub make_remover{
my $remover = shift;
my $dir = shift;
check_dirs($remover); # make sure the path to $remover exists!
if( open(REMOVER, ">$remover") ){
print REMOVER
qq|#!/usr/bin/perl\n|,
qq|# The user should have unmounted this device by now.\n|,
qq|# This script is here for when they forget.\n|,
qq|`umount -l $dir`;\n|,
qq|`/usr/sbin/updfstab -n`;\n|,
qq|`/bin/rmdir $dir`;\n|,
qq|#auto-generated unmounter by $0\n|;
close(REMOVER);
$chmod = `chmod a+x $remover`;
} else {warn "Can't open $remover!"; }
}
# specifically for the $remover which doesn't always give a good dir location.
sub check_dirs{
my $file = shift;
my @dirs = split(/\//,$file); # split on /
my $dir = shift(@dirs);
while(@dirs){
`mkdir $dir` unless -e $dir;
$dir = $dir . "/" . shift(@dirs);
}
}
# make_remover($remover,$dir); #################################################
sub get_dev{
my $file = shift || "/var/log/messages";
my $bytes = 200;
my $lines = "";
my $start = time();
while(
($lines !~ m/hub.c:\s+new\s+USB\s+device/i) || # keep seeking while we
($lines !~ m/SCSI\s+device\s+(\w+):/i) || # can't see all the data
($lines !~ m/(\w+):\s+(\w+\d+)\W/i) # that we need.
){
open(LOG, "<$file") || die("$file: $!");
seek(LOG, -$bytes, 2); # seek in FH, $bytes, from end of file.
my @arr = <LOG>;
close LOG;
$lines = join("", (@arr));
$bytes+=200;
$bytes = 200 if $bytes > 10000; # limit bytes to between 200 and 10000
die( "Can't find device in $file!" )# if running for more than 300
if(time - $start > 300); # seconds die horribly.
}
my $dev = "sda1";
$lines =~ m/SCSI\s+device\s+(\w+):/; # pull out the name of the
$dev = $1; # scsi device.
$lines =~ m/($dev):\s+($dev\d+)\W/; # find the first partition
my $part = $2; # return that partition as thedevice
return "/dev/$part";
}
# http://slashdot.org/~Zarf/
# http://slashdot.org/~Zarf/journal/44708
# to use this script modify usb.usermap with this line (sans "#" comment marker):
# usbstorage 0x0030 0x0c76 0x0005 0x0000 0x0000 0x00 0x00 0x00 0x08 0x00 0x00 0x00000000
# then name this script
my $remover = $ENV{"REMOVER"};
my $action = $ENV{"ACTION"};
if ( $action =~ m/remove/i ) {
my $dev = get_dev(); # some system want
$umount = `umount -l $dev`; # unmount
exit $umount;
}
if($action =~ m/add/i){
# make sure all modules are loaded...
$res = `/sbin/modprobe sr_mod`;
$res = `/sbin/modprobe sd_mod`;
$res = `/sbin/modprobe usbcore`;
$res = `/sbin/modprobe uhci`;
$res = `/sbin/modprobe usb-storage`;
$res = `/sbin/modprobe vfat`;
my $dev = get_dev(); # some system want
die("$dev already mounted!") if is_mounted($dev);
my $name = get_product_name($ENV{PRODUCT});
my $consoleowner = get_consoleowner();
my $dir = "/mnt/$name";
my $i = 0;
while( (is_mounted($dev,$dir) ) ){ # Check for any device that might mount
$i++; # to this dir. If you find one, increment
$dir = "/mnt/$name$i"; # the name until you find a dir that's free.
last if $i > 10000;
}
mkdir($dir, 0755) unless -e $dir;
add_to_fstab($dev,$dir); # Associate this dev to this dir
`umount -l $dev`; # doing this first fixes a lot of errors.
# By unmounting first you can avoid errors associated with
# the fringe case where the user has
# uncerimoniously yoinked the drive out of the system and frantically
# jabbed the drive back in.
`su $consoleowner -c \"sleep 1; mount $dir\"`; # delay to give the system
# a chance to catch up... make sure to mount as the consoleowner.
make_remover($remover,$dir);
}
exit;
sub get_consoleowner{
my $consoleowner = "root";
if( -f "/var/run/console.lock" ){
open(CO, "</var/run/console.lock") or die "Trying console.lock: $!";
$consoleowner = <CO>;
close CO;
}
elsif( -f "/var/lock/console.lock" ){
open(CO, "</var/lock/console.lock") or die "Trying console.lock: $!";
$consoleowner = <CO>;
close CO;
}
# otherwise leave the consoleowner as the user 'nobody'.
return $consoleowner;
}
# is_mounted($dev,$dir) returns true if either the dir or the dev is spoken for.
# if a $dev is supplied without a dir, we check for only the device.
sub is_mounted{
my $dir = shift;
my $dev = shift || "/dev/null"; # default to
# mount onto dev null and this code effectively only looks for the device now.
open(MTAB, "</etc/mtab") or die "Trying to open
my $found = 0;
while( my $line = <MTAB> ){
$found = 1 if ( $line =~ m/$dir/g || $line =~ m/$dev/g );
}
close MTAB;
return $found;
}
sub add_to_fstab{
my $device = shift;
my $dir = shift;
my $added = 1;
# now we check the fstab to see if this device is listed:
open(FSTAB, "</etc/fstab") or die "Trying fstab: $!";
while( my $line = <FSTAB> ){
$added = 0 if ($line =~ m/$device/);
}
close FSTAB;
if($added){
open(FSTAB, ">>/etc/fstab") or die "Trying fstab: $!";
print FSTAB "$device\t$dir\tauto\tnoauto,user,kudzu\t0 0\n";
#NOTE: the kudzu causes this entry to get automatically removed by
# updfstab -- the remover may want to call updfstab or let it
# automatically manage the fstab itself.
close FSTAB;
print STATUS "wrote to fstab, $device,dir\n";
}
else{ print STATUS "$device already in fstab\n";}
return $added;
}
sub get_product_name{
my $code = shift;
my $name = "usb-storage";
my ($ven,$mod) = ($1,$2) if $code =~ m/(\w+)\/(\w+)\/\w+/;
my $info = `/sbin/lsusb -d 0x$ven:0x$mod 2>1`;
my ($idVendor, $vendor) = ($1,$2)
if $info =~ m/idVendor\s+(\w+)\W+(\w+).{0,}(\n|$)/s;
my ($idProduct,$product) = ($1,$2)
if $info =~ m/idProduct\s+(\w+)\W+(\w+).{0,}(\n|$)/s;
my $tmp = $vendor; #
$tmp =~ s/\W/_/g;
$tmp =~ s/^_|_$//g;
$name = $tmp if length($tmp);
return $name;
}
# make_remover($remover,$dir); #################################################
sub make_remover{
my $remover = shift;
my $dir = shift;
check_dirs($remover); # make sure the path to $remover exists!
if( open(REMOVER, ">$remover") ){
print REMOVER
qq|#!/usr/bin/perl\n|,
qq|# The user should have unmounted this device by now.\n|,
qq|# This script is here for when they forget.\n|,
qq|`umount -l $dir`;\n|,
qq|`/usr/sbin/updfstab -n`;\n|,
qq|`/bin/rmdir $dir`;\n|,
qq|#auto-generated unmounter by $0\n|;
close(REMOVER);
$chmod = `chmod a+x $remover`;
} else {warn "Can't open $remover!"; }
}
# specifically for the $remover which doesn't always give a good dir location.
sub check_dirs{
my $file = shift;
my @dirs = split(/\//,$file); # split on
my $dir = shift(@dirs);
while(@dirs){
`mkdir $dir` unless -e $dir;
$dir = $dir . "/" . shift(@dirs);
}
}
# make_remover($remover,$dir); #################################################
sub get_dev{
my $file = shift || "/var/log/messages";
my $bytes = 200;
my $lines = "";
my $start = time();
while(
($lines !~ m/hub.c:\s+new\s+USB\s+device/i) || # keep seeking while we
($lines !~ m/SCSI\s+device\s+(\w+):/i) || # can't see all the data
($lines !~ m/(\w+):\s+(\w+\d+)\W/i) # that we need.
){
open(LOG, "<$file") || die("$file: $!");
seek(LOG, -$bytes, 2); # seek in FH, $bytes, from end of file.
my @arr = <LOG>;
close LOG;
$lines = join("", (@arr));
$bytes+=200;
$bytes = 200 if $bytes > 10000; # limit bytes to between 200 and 10000
die( "Can't find device in $file!" )# if running for more than 300
if(time - $start > 300); # seconds die horribly.
}
my $dev = "sda1";
$lines =~ m/SCSI\s+device\s+(\w+):/; # pull out the name of the
$dev = $1; # scsi device.
$lines =~ m/($dev):\s+($dev\d+)\W/; # find the first partition
my $part = $2; # return that partition as thedevice
return "/dev/$part";
}
Nice job. Now I have to boot into Linux (Score:2)
Thanks for the code.
Re:Please clarify (Score:2)
It depends on what your definition of "work" is. The default Redhat 9 install will take any new usbstorage class device and map it to a scsi device (typically
I will try it later... (Score:1)
Thanks,
Chris O
Re:I will try it later... (Score:2)
That means you will be editing the file
usbstorage 0x0030 0x0000 0x0000 0x0000 0x0000 0x00 0x00 0x00 0x08 0x06 0x00 0x00000000
I've noticed that my Redhat 9 requires I stick the pen-drive in once, remove it, then stick it in again the very first time after boot...