use lib '/home/dakkar/.perlbrew/libs/perl-5.20.2@dakkar/lib/perl5';
use strict;
use warnings;
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 %mounted;
sub sanitize {
my ($path)=@_;
$path=~s{\s}{-}g;
$path=~s{/+}{_}g;
return $path;
}
sub str_from_bytearray {
my ($aref) = @_;
return $aref unless ref($aref);
my $str = pack 'C*',@{$aref||[]};
$str =~ s{\0$}{};
return $str;
}
sub device_added {
my ($node,$interfaces_and_properties) = @_;
my $fs = $interfaces_and_properties->{'org.freedesktop.UDisks2.Filesystem'};
return unless $fs;
return if @{ $fs->{MountPoints} || [] };
return if exists $mounted{$node};
my $block = $interfaces_and_properties->{'org.freedesktop.UDisks2.Block'};
my $uuid = str_from_bytearray($block->{IdUUID});
my $dev_path= str_from_bytearray($block->{Device})
|| ($uuid?'/dev/disk/by-uuid/'.$uuid:undef)
|| 'unknown';
if ($dev_path eq 'unknown') {
warn "unknown path for $node\n";
return;
}
my $label = str_from_bytearray($block->{IdLabel});
my $fstype = str_from_bytearray($block->{IdType});
return unless defined $fstype;
my $mountpoint='/mnt/'.(sanitize($label)||$uuid||'unknown');
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{$node}=$mountpoint;
mkdir $mountpoint;
chown -1,scalar(getgrnam('plugdev')),$mountpoint;
chmod 0775,$mountpoint;
return;
}
sub device_removed {
my ($node,$interfaces) = @_;
if (exists $mounted{$node}) {
system('umount',$mounted{$node});
if (remove_from_fstab($mounted{$node})) {
rmdir $mounted{$node};
}
delete $mounted{$node};
}
return;
}
my $reactor=Net::DBus::Reactor->main();
my $bus=Net::DBus->system();
my $udisks=$bus->get_service('org.freedesktop.UDisks2');
my $manager = $udisks->get_object('/org/freedesktop/UDisks2');
$manager->connect_to_signal('InterfacesAdded',\&device_added);
$manager->connect_to_signal('InterfacesRemoved',\&device_removed);
$reactor->run();
__END__