use strict;
use warnings;
use lib '/home/dakkar/perl5/lib/perl5','/home/dakkar/perl5/lib/perl5/x86_64-linux-thread-multi';
use IO::Socket::INET;
use Net::DBus;
use Net::DBus::Reactor;
use File::Temp;
use Cwd 'realpath';
my $lock=IO::Socket::INET->new(LocalAddr=>'127.0.0.1',LocalPort=>9999,Type=>SOCK_STREAM,Listen=>1)
or die 'already running';
sub add_to_fstab {
my ($dev_path,$mount_point,$fs_type,$opts)=@_;
my @orig_stat=stat('/etc/fstab');
my $tfh=File::Temp->new(DIR=>'/etc');
$tfh->unlink_on_destroy(1);
while(1) {
seek $tfh,0,0;
truncate $tfh,0;
open my $in_fh,'<','/etc/fstab';
while (my $line=<$in_fh>) {
next if $line =~ m{\A \s* \#}smx;
my ($in_dev,$in_path,$in_fs,$in_opts)=
($line=~m{\A \s* (\S+) \s+ (\S+) \s+ (\S+) \s+ (\S+) }smx)
or next;
if (-e $in_dev
&& (realpath($in_dev) eq realpath($dev_path))) {
return $in_path;
}
}
continue {
print {$tfh} $line;
};
print {$tfh} "$dev_path $mount_point $fs_type $opts 0 0 # dakkar\n";
if ((stat('/etc/fstab'))[9] == $orig_stat[9]) {
rename "$tfh",'/etc/fstab';
chmod $orig_stat[2],'/etc/fstab';
chown @orig_stat[4,5],'/etc/fstab';
return;
}
}
}
sub remove_from_fstab {
my ($mount_point)=@_;
my @orig_stat=stat('/etc/fstab');
my $tfh=File::Temp->new(DIR=>'/etc');
$tfh->unlink_on_destroy(1);
my $was_there=0;
while(1) {
seek $tfh,0,0;
truncate $tfh,0;
open my $in_fh,'<','/etc/fstab';
while (my $line=<$in_fh>) {
next if $line =~ m{\A \s* \#}smx;
my ($in_dev,$in_path,$in_fs,$in_opts,$comms)=
($line=~m{\A \s* (\S+) \s+ (\S+) \s+ (\S+) \s+ (\S+) \s+ \d+ \s+ \d+ (?: \s* \# (.*) )?}smx);
if (defined($comms) && ($comms eq " dakkar\n")
&& ($in_path eq $mount_point)) {
$line='';$was_there=1;
}
}
continue {
print {$tfh} $line;
};
if ((stat('/etc/fstab'))[9] == $orig_stat[9]) {
rename "$tfh",'/etc/fstab';
chmod $orig_stat[2],'/etc/fstab';
chown @orig_stat[4,5],'/etc/fstab';
return 1 if $was_there;
return;
}
}
}
my $bus=Net::DBus->system();
my $hal=$bus->get_service('org.freedesktop.Hal');
my %ignore;my %mounted;
sub sanitize {
my ($path)=@_;
$path=~s{\s}{-}g;
$path=~s{/+}{_}g;
return $path;
}
sub safe_get_property {
my ($dev,$prop)=@_;
local $@;
return eval {$dev->GetProperty($prop)}
}
sub device_added {
my $dev=$hal->get_object($_[0],'org.freedesktop.Hal.Device');
my $caps=safe_get_property($dev,'info.capabilities');
return unless $caps;
my %caps;@caps{@$caps}=();
if (exists($caps{storage})) {
my $should_automount=
safe_get_property($dev,'storage.automount_enabled_hint');
if (!$should_automount) {
$ignore{$_[0]}=1;
}
return;
}
if (exists($caps{volume})) {
my $parent=safe_get_property($dev,'info.parent');
if ($ignore{$parent}) {
warn "ignoring $_[0]\n";
return;
}
if (safe_get_property($dev,'volume.ignore')) {
warn "ignoring $_[0]\n";
return;
}
my $uuid=safe_get_property($dev,'volume.uuid');
my $dev_path=safe_get_property($dev,'linux.device_file')
||safe_get_property($dev,'volume.linux.device_file')
||($uuid?'/dev/disk/by-uuid/'.$uuid:undef)
|| 'unknown';
if ($dev_path eq 'unknown') {
warn "unknown path for $_[0]\n";
return;
}
my $usage=safe_get_property($dev,'volume.fsusage');
return unless $usage eq 'filesystem';
my $label=safe_get_property($dev,'volume.label');
my $fstype=safe_get_property($dev,'volume.fstype');
return unless defined $fstype;
my $mountpoint='/mnt/'.(sanitize($label)||$uuid);
if (-e $mountpoint) {
$mountpoint.='-0';
while (-e $mountpoint) {++$mountpoint};
}
my $manual_mountpoint=add_to_fstab($dev_path,$mountpoint,$fstype,'users,noauto,noatime,nodiratime,nosuid,nodev');
$mountpoint=$manual_mountpoint if defined $manual_mountpoint;
$mounted{$_[0]}=$mountpoint;
mkdir $mountpoint;
chown -1,scalar(getgrnam('plugdev')),$mountpoint;
chmod 0775,$mountpoint;
}
return;
}
sub device_removed {
delete $ignore{$_[0]};
if (exists $mounted{$_[0]}) {
system('umount',$mounted{$_[0]});
if (remove_from_fstab($mounted{$_[0]})) {
rmdir $mounted{$_[0]};
}
delete $mounted{$_[0]};
}
return;
}
my $reactor=Net::DBus::Reactor->main();
my $manager = $hal->get_object('/org/freedesktop/Hal/Manager',
'org.freedesktop.Hal.Manager');
$manager->connect_to_signal('DeviceAdded',\&device_added);
$manager->connect_to_signal('DeviceRemoved',\&device_removed);
$reactor->run();