Follow Slashdot blog updates by subscribing to our blog RSS feed

 



Forgot your password?
typodupeerror
Linux

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";
}

This discussion has been archived. No new comments can be posted.

USB pen-drive and Linux Saga Part V: The final Chapter

Comments Filter:
  • to see if it works!

    Thanks for the code.
  • But does the script (once I have placed it in /etc/hotplug/usb/usb as directed) need to have any particular name?

    Thanks,

    Chris O
    • the name of the script would be /etc/hotplug/usb/usbstorage ... as refrenced in the file /etc/hotplug/usb.usermap.

      That means you will be editing the file /etc/hotplug/usb.usermap and adding the line (it's very long and may wrap in your browser):

      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...

Single tasking: Just Say No.

Working...