File Coverage

blib/lib/Protocol/DBus/Peer.pm
Criterion Covered Total %
statement 65 85 76.4
branch 6 16 37.5
condition 8 14 57.1
subroutine 17 24 70.8
pod 11 12 91.6
total 107 151 70.8


line stmt bran cond sub pod time code
1             package Protocol::DBus::Peer;
2              
3 5     5   2369 use strict;
  5         10  
  5         137  
4 5     5   25 use warnings;
  5         27  
  5         150  
5              
6             =encoding utf-8
7              
8             =head1 NAME
9              
10             Protocol::DBus::Peer - base class for a D-Bus peer
11              
12             =head1 SYNOPSIS
13              
14             $dbus->send_call(
15             interface => 'org.freedesktop.DBus.Properties',
16             member => 'GetAll',
17             signature => 's',
18             path => '/org/freedesktop/DBus',
19             destination => 'org.freedesktop.DBus',
20             body => [ 'org.freedesktop.DBus' ],
21             )->then( sub { .. } );
22              
23             my $msg = $dbus->get_message();
24              
25             # Same pattern as the IO::Handle method.
26             $dbus->blocking(0);
27              
28             my $fileno = $dbus->fileno();
29              
30             $dbus->flush_write_queue() if $dbus->pending_send();
31              
32             # I’m not sure why you’d want to do this, but …
33             $dbus->big_endian();
34              
35             =head1 DESCRIPTION
36              
37             This class contains D-Bus logic that is useful in both client and
38             server contexts. (Currently this distribution does not include a server
39             implementation.)
40              
41             =cut
42              
43 5     5   2485 use Call::Context;
  5         1947  
  5         140  
44 5     5   2295 use Promise::ES6;
  5         6660  
  5         177  
45              
46 5     5   2214 use Protocol::DBus::Message;
  5         11  
  5         147  
47 5     5   2062 use Protocol::DBus::Parser;
  5         16  
  5         141  
48 5     5   2125 use Protocol::DBus::WriteMsg;
  5         14  
  5         850  
49              
50             #----------------------------------------------------------------------
51              
52             =head1 METHODS
53              
54             =head2 $msg = I->get_message()
55              
56             This returns a single instace of L, or undef if
57             no message is available. It will also fire the appropriate “on_return”
58             method on METHOD_RETURN or ERROR messages.
59              
60             The backend I/O logic reads data in chunks; thus, if there is a message
61             already available in the read buffer, no I/O is done. If you’re doing
62             non-blocking I/O then it is thus B that, every time the DBus socket
63             is readable, you call this function until undef is returned.
64              
65             =cut
66              
67             sub get_message {
68 3     3 1 72 my $msg = $_[0]->{'_parser'}->get_message();
69              
70 3 50       9 if ($msg) {
71 3 100       28 if (my $serial = $msg->get_header('REPLY_SERIAL')) {
72 1 50       16 if (my $cb = delete $_[0]->{'_on_return'}{$serial}) {
73 1         4 $cb->($msg);
74             }
75             }
76             }
77              
78 3         73 return $msg;
79             }
80              
81             #----------------------------------------------------------------------
82              
83             =head2 I->flush_write_queue()
84              
85             Same as L’s method of the same name.
86              
87             =cut
88              
89             sub flush_write_queue {
90 0 0   0 1 0 if ($_[0]->{'_io'}->get_write_queue_count()) {
91 0         0 return $_[0]->{'_io'}->flush_write_queue();
92             }
93              
94 0         0 return 1;
95             }
96              
97             #----------------------------------------------------------------------
98              
99             =head2 I->send_call( %OPTS )
100              
101             Send a METHOD_CALL message.
102              
103             %OPTS are C, C, C, C, C,
104             and C. These do as you’d expect, but note that C, if given,
105             must be an array reference.
106              
107             The return value is an instance of L that will resolve when
108             a METHOD_RETURN arrives in response, or reject when an ERROR arrives. The
109             promise both resolves and rejects with a L instance
110             that represents the response.
111              
112             Note that exceptions can still happen (outside of the promise), e.g., if
113             your input is invalid or if there’s a socket I/O error.
114              
115             =cut
116              
117 5     5   36 use constant _METHOD_RETURN_NUM => Protocol::DBus::Message::Header::MESSAGE_TYPE()->{'METHOD_RETURN'};
  5         14  
  5         5607  
118              
119             sub send_call {
120 1     1 1 22 my ($self, %opts) = @_;
121              
122 1         12 $self->_send_msg(
123             %opts,
124             type => 'METHOD_CALL',
125             );
126              
127             # Don’t create a promise if we were called in void context.
128 1   33     59 return defined(wantarray) && do {
129             my $serial = $self->{'_last_sent_serial'};
130              
131             # Keep references to $self out of the callback
132             # in order to avoid memory leaks.
133             my $on_return_hr = $self->{'_on_return'} ||= {};
134              
135             return Promise::ES6->new( sub {
136 1     1   53 my ($resolve, $reject) = @_;
137              
138             $on_return_hr->{$serial} = sub {
139 1 50       6 if ($_[0]->get_type() == _METHOD_RETURN_NUM()) {
140 1         6 $resolve->($_[0]);
141             }
142             else {
143 0         0 $reject->($_[0]);
144             }
145 1         13 };
146             } );
147             };
148             }
149              
150             =head2 I->send_return( $ORIG_MSG, %OPTS )
151              
152             Send a METHOD_RETURN message.
153              
154             Arguments are similar to C except for the header differences
155             that the D-Bus specification describes. Also, C is not given
156             directly but is instead inferred from the $ORIG_MSG. (Behavior is
157             undefined if this parameter is given directly.)
158              
159             =cut
160              
161             sub send_return {
162 1     1 1 55 my ($self, $orig_msg, @opts_kv) = @_;
163              
164 1         5 return $self->_send_msg(
165             _response_fields_from_orig_msg($orig_msg, \@opts_kv),
166             type => 'METHOD_RETURN',
167             );
168             }
169              
170             =head2 I->send_error( $ORIG_MSG, %OPTS )
171              
172             Like C, but sends an error instead. The
173             C parameter is required.
174              
175             =cut
176              
177             sub send_error {
178 0     0 1 0 my ($self, $orig_msg, @opts_kv) = @_;
179              
180 0         0 return $self->_send_msg(
181             _response_fields_from_orig_msg($orig_msg, \@opts_kv),
182             type => 'ERROR',
183             );
184             }
185              
186             sub _response_fields_from_orig_msg {
187              
188             return (
189              
190             # This has to honor a passed “destination”
191             # so that we can implement a D-Bus server in tests.
192             destination => $_[0]->get_header('SENDER'),
193              
194 1     1   5 @{ $_[1] },
  1         7  
195              
196             # Reject callers’ attempts to set this one.
197             reply_serial => $_[0]->get_serial(),
198             );
199             }
200              
201             =head2 I->send_signal( %OPTS )
202              
203             Like C but sends a signal rather than a method call,
204             and a promise is not returned.
205              
206             =cut
207              
208             sub send_signal {
209 1     1 1 1240 my ($self, @opts_kv) = @_;
210              
211 1         27 return $self->_send_msg(
212             @opts_kv,
213             type => 'SIGNAL',
214             );
215             }
216              
217             #----------------------------------------------------------------------
218              
219             =head2 I->big_endian()
220              
221             Same interface as C, but this sets/gets/toggles whether to send
222             big-endian messages instead of little-endian.
223              
224             By default this library uses the system’s native byte order, so you probably
225             have little need for this function.
226              
227             =cut
228              
229             sub big_endian {
230 0     0 1 0 my ($self) = @_;
231              
232 0 0       0 if (@_ > 0) {
233 0         0 my $old = $self->{'_big_endian'};
234 0         0 $self->{'_big_endian'} = !!$_[1];
235              
236 0 0       0 $self->{'_to_str_fn'} = 'to_string_' . ($_[1] ? 'be' : 'le');
237              
238 0         0 return $self->{'_big_endian'};
239             }
240              
241 0         0 return !!$self->{'_big_endian'};
242             }
243              
244             #----------------------------------------------------------------------
245              
246             =head2 I->preserve_variant_signatures()
247              
248             Same interface as C, but when this is enabled
249             variants are given as two-member array references ([ signature => value ]),
250             blessed as C instances.
251              
252             For most Perl applications this is probably counterproductive.
253              
254             =cut
255              
256             sub preserve_variant_signatures {
257 0     0 1 0 my $self = shift;
258              
259 0         0 return $self->{'_parser'}->preserve_variant_signatures(@_);
260             }
261              
262             #----------------------------------------------------------------------
263              
264             =head2 I->blocking()
265              
266             Same interface as L’s method of the same name.
267              
268             =cut
269              
270             sub blocking {
271 0     0 1 0 my $self = shift;
272              
273 0         0 return $self->{'_socket'}->blocking(@_);
274             }
275              
276             #----------------------------------------------------------------------
277              
278             =head2 I->fileno()
279              
280             Returns the connection socket’s file descriptor.
281              
282             =cut
283              
284             sub fileno {
285 0     0 1 0 return fileno $_[0]->{'_socket'};
286             }
287              
288             #----------------------------------------------------------------------
289              
290             =head2 I->pending_send()
291              
292             Returns a boolean that indicates whether there is data queued up to send
293             to the server.
294              
295             =cut
296              
297             sub pending_send {
298 0     0 1 0 return !!$_[0]->{'_io'}->get_write_queue_count();
299             }
300              
301             #----------------------------------------------------------------------
302              
303             # undocumented
304             sub new {
305 2     2 0 864774 my ($class, $socket) = @_;
306              
307 2         38 my $self = bless { _socket => $socket }, $class;
308              
309 2         89 $self->_set_up_peer_io( $socket );
310              
311 2         7 return $self;
312             }
313              
314             #----------------------------------------------------------------------
315              
316             sub _set_up_peer_io {
317 2     2   29 my ($self, $socket) = @_;
318              
319 2         392 $self->{'_io'} = Protocol::DBus::WriteMsg->new( $socket )->enable_write_queue();
320 2         211 $self->{'_parser'} = Protocol::DBus::Parser->new( $socket );
321              
322 2         15 return;
323             }
324              
325             sub _send_msg {
326 3     3   64 my ($self, %opts) = @_;
327              
328 3         19 my ($type, $body_ar, $flags) = delete @opts{'type', 'body', 'flags'};
329              
330             my @hargs = map {
331 3         15 my $k = $_;
  14         21  
332 14         33 $k =~ tr;
333 14         36 ( $k => $opts{$_} );
334             } keys %opts;
335              
336 3         27 my $serial = ++$self->{'_last_sent_serial'};
337              
338 3         203 my $msg = Protocol::DBus::Message->new(
339             type => $type,
340             hfields => \@hargs,
341             flags => $flags,
342             body => $body_ar,
343             serial => $serial,
344             );
345              
346             # Use native byte order by default.
347 3   100     60 $self->{'_endian'} ||= (pack 'n', 1) eq (pack 'l', 1) ? 'be' : 'le';
348              
349 3   66     34 $self->{'_to_str_fn'} ||= "to_string_$self->{'_endian'}";
350              
351 3         86 my ($buf_sr, $fds_ar) = $msg->can($self->{'_to_str_fn'})->($msg);
352              
353 3 50 66     22 if ($fds_ar && @$fds_ar && !$self->supports_unix_fd()) {
      33        
354 0         0 die "Cannot send file descriptors without UNIX FD support!";
355             }
356              
357 3         21 $self->{'_io'}->enqueue_message( $buf_sr, $fds_ar );
358              
359 3         23 return $self->{'_io'}->flush_write_queue();
360             }
361              
362             1;