File Coverage

blib/lib/Hal/Cdroms.pm
Criterion Covered Total %
statement 0 102 0.0
branch 0 42 0.0
condition 0 6 0.0
subroutine 0 25 0.0
pod 10 10 100.0
total 10 185 5.4


line stmt bran cond sub pod time code
1             package Hal::Cdroms;
2              
3             our $VERSION = 0.05;
4              
5             # Copyright (C) 2008 Mandriva
6             # Copyright (C) 2020 Mageia
7             #
8             # This program is free software; You can redistribute it and/or modify
9             # it under the same terms as Perl itself. Either:
10             #
11             # a) the GNU General Public License as published by the Free
12             # Software Foundation; either version 2, or (at your option) any
13             # later version,
14             #
15             # or
16             #
17             # b) the "Artistic License"
18             #
19             # The file "COPYING" distributed along with this file provides full
20             # details of the terms and conditions of the two licenses.
21              
22             =head1 NAME
23              
24             Hal::Cdroms - access removable media containing CD filesystems through UDisks2 and D-Bus
25              
26             =head1 SYNOPSIS
27              
28             use Hal::Cdroms;
29              
30             my $cdroms = Hal::Cdroms->new;
31              
32             foreach my $udisks_path ($cdroms->list) {
33             my $m = $cdroms->get_mount_point($udisks_path);
34             print "$udisks_path ", $m ? "is mounted in $m" : "is not mounted", "\n";
35             }
36              
37             my $udisks_path = $cdroms->wait_for_insert;
38             my $m = $cdroms->mount($udisks_path);
39             print "$udisks_path is now mounted in $m\n";
40              
41             =head1 DESCRIPTION
42              
43             Access removable media containing CD filesystems (iso9660 and udf) through
44             UDisks2 and D-Bus. This includes CD-ROMS, DVD-ROMS, and USB flash drives.
45              
46             =cut
47              
48             # internal constant
49             my $dn = 'org.freedesktop.UDisks2';
50              
51              
52             =head2 Hal::Cdroms->new
53              
54             Creates the object
55              
56             =cut
57              
58             sub new {
59 0     0 1   my ($class) = @_;
60              
61 0           require Net::DBus;
62 0           require Net::DBus::Reactor; # must be done before line below:
63 0           my $dbus = Net::DBus->system;
64 0           my $service = $dbus->get_service($dn);
65              
66 0           bless { dbus => $dbus, service => $service }, $class;
67             }
68              
69             =head2 $cdroms->list
70              
71             Return the list of C of the removable media (mounted or not).
72              
73             =cut
74              
75             sub list {
76 0     0 1   my ($o) = @_;
77              
78 0           my $manager = $o->{service}->get_object('/org/freedesktop/UDisks2/Manager');
79              
80 0           grep { _is_cdrom($o, $_); } @{$manager->GetBlockDevices(undef)};
  0            
  0            
81             }
82              
83             =head2 $cdroms->get_mount_point($udisks_path)
84              
85             Return the mount point associated to the C, or undef it is not mounted.
86              
87             =cut
88              
89             sub _is_cdrom {
90 0     0     my ($o, $udisks_path) = @_;
91 0           my $device = _get_device($o, $udisks_path);
92 0           my $drive = _get_drive($o, $device);
93 0 0 0       return unless $drive && _get_property($drive, 'Drive', 'Removable');
94 0 0         return unless member(_get_property($device, 'Block', 'IdType'), 'iso9660', 'udf');
95 0           eval { _get_property($device, 'Filesystem', 'MountPoints') };
  0            
96             }
97              
98             sub _get_device {
99 0     0     my ($o, $udisks_path, $o_interface_name) = @_;
100 0           $o->{service}->get_object($udisks_path, $o_interface_name);
101             }
102              
103             sub _get_drive {
104 0     0     my ($o, $device) = @_;
105 0           my $drive_path = _get_property($device, 'Block', 'Drive');
106 0 0         return if $drive_path eq '/';
107 0           $o->{service}->get_object($drive_path);
108             }
109              
110             sub _get_property {
111 0     0     my ($device, $interface_name, $property_name) = @_;
112 0           $device->Get("$dn.$interface_name", $property_name);
113             }
114              
115             sub get_mount_point {
116 0     0 1   my ($o, $udisks_path) = @_;
117 0           my $mounts = _get_mount_points($o, $udisks_path);
118 0 0         _int_array_to_string($$mounts[0]) if @{$mounts};
  0            
119             }
120              
121             sub _get_mount_points {
122 0     0     my ($o, $udisks_path) = @_;
123 0           my $device = _get_device($o, $udisks_path);
124 0 0         eval { _get_property($device, 'Filesystem', 'MountPoints') } || [];
  0            
125             }
126              
127             sub _int_array_to_string {
128 0     0     my ($array) = @_;
129 0 0         join('', map { $_ ? chr($_) : '' } @{$array});
  0            
  0            
130             }
131              
132             sub _try {
133 0     0     my ($o, $f) = @_;
134              
135 0 0         if (eval { $f->(); 1 }) {
  0            
  0            
136 0           1;
137             } else {
138 0           $o->{error} = $@;
139 0           undef;
140             }
141             }
142              
143             =head2 $cdroms->ensure_mounted($udisks_path)
144              
145             Mount the C if not already mounted.
146             Return the mount point associated to the C, or undef it cannot be mounted successfully (see $cdroms->{error}).
147              
148             =cut
149              
150             sub ensure_mounted {
151 0     0 1   my ($o, $udisks_path) = @_;
152            
153 0 0 0       $o->get_mount_point($udisks_path) # check if it is already mounted
154             || $o->mount($udisks_path) # otherwise try to mount
155             || $o->get_mount_point($udisks_path); # checking wether a volume manager did it for us
156             }
157              
158              
159             =head2 $cdroms->mount($udisks_path)
160              
161             Mount the C through UDisks2.
162             Return the mount point associated to the C, or undef it cannot be mounted successfully (see $cdroms->{error}).
163              
164             =cut
165              
166             sub mount {
167 0     0 1   my ($o, $udisks_path) = @_;
168              
169 0           my $device = _get_device($o, $udisks_path, "$dn.Filesystem");
170              
171 0           my $mountpoint;
172 0 0   0     _try($o, sub { $mountpoint = $device->Mount(undef) }) or return;
  0            
173 0           $mountpoint;
174             }
175              
176             =head2 $cdroms->unmount($udisks_path)
177              
178             Unmount the C through UDisks2.
179             Return true on success (see $cdroms->{error} on failure)
180              
181             =cut
182              
183             sub unmount {
184 0     0 1   my ($o, $udisks_path) = @_;
185              
186 0           my $device = _get_device($o, $udisks_path, "$dn.Filesystem");
187 0     0     _try($o, sub { $device->Unmount(undef) });
  0            
188             }
189              
190             =head2 $cdroms->eject($udisks_path)
191              
192             Eject the C. Return true on success (see $cdroms->{error} on failure).
193              
194             =cut
195              
196             sub eject {
197 0     0 1   my ($o, $udisks_path) = @_;
198              
199 0           my $device = _get_device($o, $udisks_path);
200 0           my $drive = _get_drive($o, $device);
201 0     0     _try($o, sub { $device->as_interface("$dn.Filesystem")->Unmount(undef); $drive->Eject(undef) });
  0            
  0            
202             }
203              
204             =head2 $cdroms->wait_for_insert([$timeout])
205              
206             Wait until media containing a CD filesystem is inserted.
207             Return the inserted C on success. Otherwise return undef.
208              
209             You can give an optional timeout in milliseconds.
210              
211             =cut
212              
213             sub wait_for_insert {
214 0     0 1   my ($o, $o_timeout) = @_;
215              
216 0 0         return if $o->list;
217              
218             _reactor_wait($o->{dbus}, $o_timeout, sub {
219 0     0     my ($msg) = @_;
220 0 0         return unless $msg->get_member eq 'InterfacesAdded';
221 0           my $udisks_path = ($msg->get_args_list)[0];
222 0 0         return unless $udisks_path =~ /block_devices/;
223 0 0         return unless _is_cdrom($o, $udisks_path);
224 0           $udisks_path;
225 0           });
226             }
227              
228             =head2 $cdroms->wait_for_mounted([$timeout])
229              
230             Wait until media containing a CD filesystem is inserted and mounted by a volume manager (eg: gnome-volume-manager).
231             Return the mounted C on success. Otherwise return undef.
232              
233             You can give an optional timeout in milliseconds.
234              
235             =cut
236              
237             sub wait_for_mounted {
238 0     0 1   my ($o, $o_timeout) = @_;
239              
240             _reactor_wait($o->{dbus}, $o_timeout, sub {
241 0     0     my ($msg) = @_;
242 0 0         return unless member($msg->get_member, 'InterfacesAdded', 'PropertiesChanged');
243 0 0         my $udisks_path = $msg->get_member eq 'InterfacesAdded' ? ($msg->get_args_list)[0] : $msg->get_path;
244 0 0         return unless $udisks_path =~ /block_devices/;
245 0 0         return unless _is_cdrom($o, $udisks_path);
246 0 0         return unless @{_get_mount_points($o, $udisks_path)} > 0;
  0            
247 0           $udisks_path;
248 0           });
249             }
250              
251             sub _reactor_wait {
252 0     0     my ($dbus, $timeout, $check_found) = @_;
253              
254 0           my $found_val;
255 0           my $reactor = Net::DBus::Reactor->main;
256              
257 0           my $con = $dbus->get_connection;
258 0           $con->add_match("type='signal',sender='$dn'");
259             $con->add_filter(sub {
260 0     0     my ($_con, $msg) = @_;
261              
262 0 0         if (my $val = $check_found->($msg)) {
263 0           $found_val = $val;
264 0           $reactor->shutdown;
265             }
266 0           1;
267 0           });
268 0 0         if ($timeout) {
269             $reactor->add_timeout($timeout, Net::DBus::Callback->new(method => sub {
270 0     0     $reactor->shutdown;
271 0           }));
272             }
273 0           $reactor->run;
274              
275 0           $found_val;
276             }
277              
278             =head2 member(SCALAR, LIST)
279              
280             is the value in the list?
281              
282             =cut
283              
284             # From MDK::Common::DataStructure :
285 0 0   0 1   sub member { my $e = shift; foreach (@_) { $e eq $_ and return 1 } 0 }
  0            
  0            
  0            
286              
287             =head1 AUTHOR
288              
289             Pascal Rigaux
290             Martin Whitaker
291              
292             =cut