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 $bus=Net::DBus->system();
my $udisks=$bus->get_service('org.freedesktop.UDisks');
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->$prop };
}
sub device_added {
my ($node,$dev) = @_;
$dev ||= $udisks->get_object($node,'org.freedesktop.UDisks.Device');
return unless safe_get_property($dev,'IdUsage') eq 'filesystem';
return unless safe_get_property($dev,'DeviceIsMediaAvailable');
return if safe_get_property($dev,'DeviceIsMounted');
return if exists $mounted{$node};
my $uuid=safe_get_property($dev,'IdUuid');
my $dev_path=safe_get_property($dev,'DeviceFile')
||($uuid?'/dev/disk/by-uuid/'.$uuid:undef)
|| 'unknown';
if ($dev_path eq 'unknown') {
warn "unknown path for $node\n";
return;
}
my $label=safe_get_property($dev,'IdLabel');
my $fstype=safe_get_property($dev,'IdType');
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{$node}=$mountpoint;
mkdir $mountpoint;
chown -1,scalar(getgrnam('plugdev')),$mountpoint;
chmod 0775,$mountpoint;
return;
}
sub device_removed {
my ($node,$dev) = @_;
if (exists $mounted{$node}) {
system('umount',$mounted{$node});
if (remove_from_fstab($mounted{$node})) {
rmdir $mounted{$node};
}
delete $mounted{$node};
}
return;
}
sub device_changed {
my ($node) = @_;
my $dev=$udisks->get_object($node,'org.freedesktop.UDisks.Device');
my $has_media = safe_get_property($dev,'DeviceIsMediaAvailable');
if ($has_media) {
device_added($node,$dev);
}
else {
device_removed($node,$dev);
}
}
my $reactor=Net::DBus::Reactor->main();
my $manager = $udisks->get_object('/org/freedesktop/UDisks',
'org.freedesktop.UDisks');
$manager->connect_to_signal('DeviceAdded',\&device_added);
$manager->connect_to_signal('DeviceChanged',\&device_changed);
$manager->connect_to_signal('DeviceRemoved',\&device_removed);
$reactor->run();