File Coverage

blib/lib/Hal/Cdroms.pm
Criterion Covered Total %
statement 0 107 0.0
branch 0 34 0.0
condition 0 9 0.0
subroutine 0 29 0.0
pod 9 12 75.0
total 9 191 4.7


line stmt bran cond sub pod time code
1             package Hal::Cdroms;
2              
3             our $VERSION = 0.04;
4              
5             # Copyright (C) 2008 Mandriva
6             #
7             # This program is free software; You can redistribute it and/or modify
8             # it under the same terms as Perl itself. Either:
9             #
10             # a) the GNU General Public License as published by the Free
11             # Software Foundation; either version 2, or (at your option) any
12             # later version,
13             #
14             # or
15             #
16             # b) the "Artistic License"
17             #
18             # The file "COPYING" distributed along with this file provides full
19             # details of the terms and conditions of the two licenses.
20              
21             =head1 NAME
22              
23             Hal::Cdroms - access cdroms through HAL and D-Bus
24              
25             =head1 SYNOPSIS
26              
27             use Hal::Cdroms;
28              
29             my $hal_cdroms = Hal::Cdroms->new;
30              
31             foreach my $hal_path ($hal_cdroms->list) {
32             my $m = $hal_cdroms->get_mount_point($hal_path);
33             print "$hal_path ", $m ? "is mounted in $m" : "is not mounted", "\n";
34             }
35              
36             my $hal_path = $hal_cdroms->wait_for_insert;
37             my $m = $hal_cdroms->mount($hal_path);
38             print "$hal_path is now mounted in $m\n";
39              
40             =head1 DESCRIPTION
41              
42             Access cdroms through HAL and D-Bus.
43              
44             =cut
45              
46             # internal constant
47             my $hal_dn = 'org.freedesktop.UDisks';
48              
49              
50             =head2 Hal::Cdroms->new
51              
52             Creates the object
53              
54             =cut
55              
56             sub new {
57 0     0 1   my ($class) = @_;
58              
59 0           require Net::DBus;
60 0           require Net::DBus::Reactor; # must be done before line below:
61 0           my $dbus = Net::DBus->system;
62 0           my $hal = $dbus->get_service($hal_dn);
63              
64 0           bless { dbus => $dbus, hal => $hal }, $class;
65             }
66              
67             =head2 $hal_cdroms->list
68              
69             Returns the list of C of the cdroms (mounted or not).
70              
71             =cut
72              
73             sub list {
74 0     0 1   my ($o) = @_;
75              
76 0           my $manager = $o->{hal}->get_object("/org/freedesktop/UDisks",
77             $hal_dn);
78              
79            
80 0           grep { _GetProperty(_get_device($o, $_), 'DeviceIsOpticalDisc') } @{$manager->EnumerateDevices};
  0            
  0            
81             }
82              
83             =head2 $hal_cdroms->get_mount_point($hal_path)
84              
85             Return the mount point associated to the C, or undef it is not mounted.
86              
87             =cut
88              
89             sub _get_udisks_device {
90 0     0     my ($o, $hal_path) = @_;
91 0           $o->{hal}->get_object($hal_path, "$hal_dn.Device");
92             }
93              
94             sub _get_device {
95 0     0     my ($o, $hal_path) = @_;
96 0           $o->{hal}->get_object($hal_path, 'org.freedesktop.DBus.Properties');
97             }
98              
99             sub _get_volume {
100 0     0     my ($o, $hal_path) = @_;
101 0           $o->{hal}->get_object($hal_path, "$hal_dn.Device.Volume");
102             }
103              
104             sub _GetProperty {
105 0     0     my ($device, $pname) = @_;
106 0           $device->Get('org.freedesktop.DBus.Properties', $pname);
107             }
108              
109             sub get_mount_point {
110 0     0 1   my ($o, $hal_path) = @_;
111              
112 0           my $device = _get_device($o, $hal_path);
113 0           eval { _GetProperty($device, 'DeviceIsMounted')
  0            
114 0 0         && @{_GetProperty($device, 'DeviceMountPaths')}[0] };
115             }
116              
117             sub _try {
118 0     0     my ($o, $f) = @_;
119              
120 0 0         if (eval { $f->(); 1 }) {
  0            
  0            
121 0           1;
122             } else {
123 0           $o->{error} = $@;
124 0           undef;
125             }
126             }
127              
128             =head2 $hal_cdroms->ensure_mounted($hal_path)
129              
130             Mount the C if not already mounted.
131             Return the mount point associated to the C, or undef it cannot be mounted successfully (see $hal_cdroms->{error}).
132              
133             =cut
134              
135             sub ensure_mounted {
136 0     0 1   my ($o, $hal_path) = @_;
137            
138 0 0 0       $o->get_mount_point($hal_path) # check if it is already mounted
139             || $o->mount($hal_path) # otherwise try to mount
140             || $o->get_mount_point($hal_path); # checking wether a volume manager did it for us
141             }
142              
143              
144             =head2 $hal_cdroms->mount_through_hal($hal_path)
145              
146             Mount the C through HAL
147             Return the mount point associated to the C, or undef it cannot be mounted successfully (see $hal_cdroms->{error}).
148             If the cdrom is listed in fstab, HAL will refuse to mount it.
149              
150             =cut
151              
152             sub mount_hal {
153 0     0 0   my ($o, $hal_path) = @_;
154              
155 0           my $device = _get_device($o, $hal_path);
156 0           my $real_device = _get_udisks_device($o, $hal_path);
157              
158 0           my $mountpoint;
159 0 0   0     _try($o, sub { $mountpoint = $real_device->FilesystemMount($fstype, []) }) or return;
  0            
160 0           $mountpoint;
161             }
162              
163             =head2 $hal_cdroms->mount($hal_path)
164              
165             Mount the C through HAL or fallback to plain mount(8).
166             Return the mount point associated to the C, or undef it cannot be mounted successfully (see $hal_cdroms->{error})
167              
168             =cut
169              
170             sub mount {
171 0     0 1   my ($o, $hal_path) = @_;
172              
173 0           my $mntpoint = mount_hal($o, $hal_path);
174 0 0         if (!$mntpoint) {
175             # this usually means HAL refused to mount a cdrom listed in fstab
176 0           my $dev = _GetProperty(_get_device($o, $hal_path), 'NativePath');
177             # try to get real path:
178 0           $dev =~ s!.*/!/dev/!;
179 0 0 0       if (my $wanted = $dev && _rdev($dev)) {
180 0           my ($fstab_dev) = grep { $wanted == _rdev($_) } _fstab_devices();
  0            
181 0 0         system("mount", $fstab_dev) == 0
182             and $mntpoint = get_mount_point($o, $hal_path);
183             }
184             }
185 0           $mntpoint;
186             }
187              
188             sub _rdev {
189 0     0     my ($dev) = @_;
190 0           (stat($dev))[6];
191             }
192             sub _fstab_devices() {
193 0 0   0     open(my $F, '<', '/etc/fstab') or return;
194 0           map { /(\S+)/ } <$F>;
  0            
195             }
196              
197             =head2 $hal_cdroms->unmount($hal_path)
198              
199             Unmount the C. Return true on success (see $hal_cdroms->{error} on failure)
200             If the cdrom is listed in not mounted by HAL, HAL will refuse to unmount it.
201              
202             =cut
203              
204             sub unmount_hal {
205 0     0 0   my ($o, $hal_path) = @_;
206              
207 0           my $volume = _get_udisks_device($o, $hal_path);
208 0     0     _try($o, sub { $volume->FilesystemUnmount([]) });
  0            
209             }
210              
211             =head2 $hal_cdroms->unmount($hal_path)
212              
213             Unmount the C through HAL or fallback on umount(8).
214             Return true on success (see $hal_cdroms->{error} on failure)
215              
216             =cut
217              
218             sub unmount {
219 0     0 1   my ($o, $hal_path) = @_;
220              
221 0 0         unmount_hal($o, $hal_path) and return 1;
222              
223 0           system('umount', get_mount_point($o, $hal_path)) == 0;
224             }
225              
226             =head2 $hal_cdroms->eject($hal_path)
227              
228             Ejects the C. Return true on success (see $hal_cdroms->{error} on failure)
229              
230             =cut
231              
232             sub eject {
233 0     0 1   my ($o, $hal_path) = @_;
234              
235 0           my $volume = _get_udisks_device($o, $hal_path);
236 0     0     _try($o, sub { $volume->FilesystemUnmount([]); $volume->DriveEject([]) });
  0            
  0            
237             }
238              
239             =head2 $hal_cdroms->wait_for_insert([$timeout])
240              
241             Waits until a cdrom is inserted.
242             Returns the inserted C on success. Otherwise returns undef.
243              
244             You can give an optional timeout in milliseconds.
245              
246             =cut
247              
248             sub wait_for_insert {
249 0     0 1   my ($o, $o_timeout) = @_;
250              
251 0 0         return if $o->list;
252              
253             _reactor_wait($o->{dbus}, $hal_dn, $o_timeout, sub {
254 0     0     my ($msg) = @_;
255 0           my $path;
256 0 0 0       return unless member($msg->get_member, 'DeviceChanged', 'DeviceAdded') && ($path = ($msg->get_args_list)[0]);
257 0           _GetProperty(_get_device($o, $path), 'DeviceIsOpticalDisc');
258 0           });
259             }
260              
261             =head2 $hal_cdroms->wait_for_mounted([$timeout])
262              
263             Waits until a cdrom is inserted and mounted by a volume manager (eg: gnome-volume-manager).
264             Returns the mounted C on success. Otherwise returns undef.
265              
266             You can give an optional timeout in milliseconds.
267              
268             =cut
269              
270             sub wait_for_mounted {
271 0     0 1   my ($o, $o_timeout) = @_;
272              
273             _reactor_wait($o->{dbus}, $hal_dn, $o_timeout, sub {
274 0     0     my ($msg) = @_;
275 0 0         $msg->get_member eq 'PropertyModified' or return;
276              
277 0           my (undef, $modified_properties) = $msg->get_args_list;
278 0 0         grep { $_->[0] eq 'volume.is_mounted' } @$modified_properties or return;
  0            
279              
280 0           my $hal_path = $msg->get_path;
281 0           my $device = _get_device($o, $hal_path);
282              
283 0 0         eval { _GetProperty(_get_device($o, $hal_path), 'DeviceIsMounted') } && $hal_path;
  0            
284 0           });
285             }
286              
287             sub _reactor_wait {
288 0     0     my ($dbus, $interface, $timeout, $check_found) = @_;
289              
290 0           my $val;
291 0           my $reactor = Net::DBus::Reactor->main;
292              
293 0           my $con = $dbus->get_connection;
294 0           $con->add_match("type='signal',interface='$interface'");
295             $con->add_filter(sub {
296 0     0     my ($_con, $msg) = @_;
297              
298 0 0         if ($val = $check_found->($msg)) {
299 0           _reactor_shutdown($reactor);
300             }
301 0           });
302 0 0         if ($timeout) {
303             $reactor->add_timeout($timeout, Net::DBus::Callback->new(method => sub {
304 0     0     _reactor_shutdown($reactor);
305 0           }));
306             }
307 0           $reactor->run;
308              
309 0           $val;
310             }
311              
312             sub _reactor_shutdown {
313 0     0     my ($reactor) = @_;
314              
315 0           $reactor->shutdown;
316              
317             # ugly, but needed for shutdown to work...
318 0     0     $reactor->add_timeout(1, Net::DBus::Callback->new(method => sub {}));
  0            
319             }
320              
321 0 0   0 0   sub member { my $e = shift; foreach (@_) { $e eq $_ and return 1 } 0 }
  0            
  0            
  0            
322              
323             =head1 AUTHOR
324              
325             Pascal Rigaux
326              
327             =cut