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

Ever notice that even the busiest people are never too busy to tell you just how busy they are?

Working...