File Coverage

blib/lib/Udev/FFI/Functions.pm
Criterion Covered Total %
statement 43 70 61.4
branch 7 26 26.9
condition 1 5 20.0
subroutine 9 11 81.8
pod 0 3 0.0
total 60 115 52.1


line stmt bran cond sub pod time code
1             package Udev::FFI::Functions;
2              
3 2     2   18 use strict;
  2         6  
  2         77  
4 2     2   15 use warnings;
  2         5  
  2         206  
5              
6             our (@ISA, @EXPORT_OK, %EXPORT_TAGS);
7              
8             require Exporter;
9             @ISA = qw(Exporter);
10              
11 2     2   1149 use IPC::Cmd qw(can_run run);
  2         118599  
  2         156  
12              
13 2     2   1268 use FFI::Platypus;
  2         13478  
  2         84  
14 2     2   829 use FFI::CheckLib;
  2         5351  
  2         1445  
15              
16              
17             use constant {
18 2         1951 FUNCTIONS => {
19             # struct udev *udev_new(void);
20             'udev_new' => {
21             ffi_data => [ [], 'opaque' ]
22             },
23              
24             # struct udev *udev_ref(struct udev *udev);
25             'udev_ref' => {
26             ffi_data => [ ['opaque'], 'opaque' ]
27             },
28              
29             # struct udev *udev_unref(struct udev *udev);
30             'udev_unref' => {
31             ffi_data => [ ['opaque'], 'opaque' ]
32             },
33              
34             # access to libudev generated lists ====================================
35              
36             # struct udev_list_entry *udev_list_entry_get_next(struct udev_list_entry *list_entry);
37             'udev_list_entry_get_next' => {
38             ffi_data => [ ['opaque'], 'opaque' ]
39             },
40              
41             # struct udev_list_entry *udev_list_entry_get_by_name(struct udev_list_entry *list_entry, const char *name);
42             'udev_list_entry_get_by_name' => {
43             ffi_data => [ ['opaque', 'string'], 'opaque']
44             },
45              
46             # const char *udev_list_entry_get_name(struct udev_list_entry *list_entry);
47             'udev_list_entry_get_name' => {
48             ffi_data => [ ['opaque'], 'string' ]
49             },
50              
51             # const char *udev_list_entry_get_value(struct udev_list_entry *list_entry);
52             'udev_list_entry_get_value' => {
53             ffi_data => [ ['opaque'], 'string' ]
54             },
55              
56              
57             # udev_device ==========================================================
58              
59             # struct udev_device *udev_device_ref(struct udev_device *udev_device);
60             'udev_device_ref' => {
61             ffi_data => [ ['opaque'], 'opaque' ]
62             },
63              
64             # struct udev_device *udev_device_unref(struct udev_device *udev_device);
65             'udev_device_unref' => {
66             ffi_data => [ ['opaque'], 'opaque' ]
67             },
68              
69             # struct udev *udev_device_get_udev(struct udev_device *udev_device);
70             'udev_device_get_udev' => {
71             ffi_data => [ ['opaque'], 'opaque' ]
72             },
73              
74             # struct udev_device *udev_device_new_from_syspath(struct udev *udev, const char *syspath);
75             'udev_device_new_from_syspath' => {
76             ffi_data => [ ['opaque', 'string'], 'opaque' ]
77             },
78              
79             # struct udev_device *udev_device_new_from_devnum(struct udev *udev, char type, dev_t devnum);
80             'udev_device_new_from_devnum' => {
81             ffi_data => [ ['opaque', 'signed char', 'uint64_t'], 'opaque' ]
82             },
83              
84             # struct udev_device *udev_device_new_from_subsystem_sysname(struct udev *udev, const char *subsystem, const char *sysname);
85             'udev_device_new_from_subsystem_sysname' => {
86             ffi_data => [ ['opaque', 'string', 'string'], 'opaque' ]
87             },
88              
89             # libudev >= 189
90             # struct udev_device *udev_device_new_from_device_id(struct udev *udev, const char *id);
91             'udev_device_new_from_device_id' => {
92             ffi_data => [ ['opaque', 'string'], 'opaque' ],
93             since => 189
94             },
95              
96             # struct udev_device *udev_device_new_from_environment(struct udev *udev);
97             'udev_device_new_from_environment' => {
98             ffi_data => [ ['opaque'], 'opaque' ]
99             },
100              
101             # struct udev_device *udev_device_get_parent(struct udev_device *udev_device);
102             'udev_device_get_parent' => {
103             ffi_data => [ ['opaque'], 'opaque' ]
104             },
105              
106             # struct udev_device *udev_device_get_parent_with_subsystem_devtype(struct udev_device *udev_device,
107             # const char *subsystem, const char *devtype);
108             'udev_device_get_parent_with_subsystem_devtype' => {
109             ffi_data => [ ['opaque', 'string', 'string'], 'opaque' ]
110             },
111              
112             # retrieve device properties
113              
114             # const char *udev_device_get_devpath(struct udev_device *udev_device);
115             'udev_device_get_devpath' => {
116             ffi_data => [ ['opaque'], 'string' ]
117             },
118              
119             # const char *udev_device_get_subsystem(struct udev_device *udev_device);
120             'udev_device_get_subsystem' => {
121             ffi_data => [ ['opaque'], 'string' ]
122             },
123              
124             # const char *udev_device_get_devtype(struct udev_device *udev_device);
125             'udev_device_get_devtype' => {
126             ffi_data => [ ['opaque'], 'string' ]
127             },
128              
129             # const char *udev_device_get_syspath(struct udev_device *udev_device);
130             'udev_device_get_syspath' => {
131             ffi_data => [ ['opaque'], 'string' ]
132             },
133              
134             # const char *udev_device_get_sysname(struct udev_device *udev_device);
135             'udev_device_get_sysname' => {
136             ffi_data => [ ['opaque'], 'string' ]
137             },
138              
139             # const char *udev_device_get_sysnum(struct udev_device *udev_device);
140             'udev_device_get_sysnum' => {
141             ffi_data => [ ['opaque'], 'string' ]
142             },
143              
144             # const char *udev_device_get_devnode(struct udev_device *udev_device);
145             'udev_device_get_devnode' => {
146             ffi_data => [ ['opaque'], 'string' ]
147             },
148              
149             #int udev_device_get_is_initialized(struct udev_device *udev_device);
150             'udev_device_get_is_initialized' => {
151             ffi_data => [ ['opaque'], 'int' ]
152             },
153              
154             # struct udev_list_entry *udev_device_get_devlinks_list_entry(struct udev_device *udev_device);
155             'udev_device_get_devlinks_list_entry' => {
156             ffi_data => [ ['opaque'], 'opaque' ]
157             },
158              
159             # struct udev_list_entry *udev_device_get_properties_list_entry(struct udev_device *udev_device);
160             'udev_device_get_properties_list_entry' => {
161             ffi_data => [ ['opaque'], 'opaque' ]
162             },
163              
164             # struct udev_list_entry *udev_device_get_tags_list_entry(struct udev_device *udev_device);
165             'udev_device_get_tags_list_entry' => {
166             ffi_data => [ ['opaque'], 'opaque' ]
167             },
168              
169             # struct udev_list_entry *udev_device_get_sysattr_list_entry(struct udev_device *udev_device);
170             'udev_device_get_sysattr_list_entry' => {
171             ffi_data => [ ['opaque'], 'opaque' ]
172             },
173              
174             #const char *udev_device_get_property_value(struct udev_device *udev_device, const char *key);
175             'udev_device_get_property_value' => {
176             ffi_data => [ ['opaque', 'string'], 'string' ]
177             },
178              
179             #const char *udev_device_get_driver(struct udev_device *udev_device);
180             'udev_device_get_driver' => {
181             ffi_data => [ ['opaque'], 'string' ]
182             },
183              
184             # dev_t udev_device_get_devnum(struct udev_device *udev_device);
185             'udev_device_get_devnum' => {
186             ffi_data => [ ['opaque'], 'uint64_t' ]
187             },
188              
189             #const char *udev_device_get_action(struct udev_device *udev_device);
190             'udev_device_get_action' => {
191             ffi_data => [ ['opaque'], 'string' ]
192             },
193              
194             #unsigned long long int udev_device_get_seqnum(struct udev_device *udev_device);
195             'udev_device_get_seqnum' => {
196             ffi_data => [ ['opaque'], 'unsigned long long' ]
197             },
198              
199             #unsigned long long int udev_device_get_usec_since_initialized(struct udev_device *udev_device);
200             'udev_device_get_usec_since_initialized' => {
201             ffi_data => [ ['opaque'], 'unsigned long long' ]
202             },
203              
204             #const char *udev_device_get_sysattr_value(struct udev_device *udev_device, const char *sysattr);
205             'udev_device_get_sysattr_value' => {
206             ffi_data => [ ['opaque', 'string'] => 'string' ]
207             },
208              
209             #int udev_device_set_sysattr_value(struct udev_device *udev_device, const char *sysattr, char *value);
210             'udev_device_set_sysattr_value' => {
211             ffi_data => [ ['opaque', 'string', 'string'], 'int' ],
212             since => 199
213             },
214              
215             #int udev_device_has_tag(struct udev_device *udev_device, const char *tag);
216             'udev_device_has_tag' => {
217             ffi_data => [ ['opaque', 'string'], 'int' ]
218             },
219              
220              
221             # udev_monitor =========================================================
222              
223             # access to kernel uevents and udev events
224              
225             # struct udev_monitor *udev_monitor_ref(struct udev_monitor *udev_monitor);
226             'udev_monitor_ref' => {
227             ffi_data => [ ['opaque'], 'opaque' ]
228             },
229              
230             # struct udev_monitor *udev_monitor_unref(struct udev_monitor *udev_monitor);
231             'udev_monitor_unref' => {
232             ffi_data => [ ['opaque'], 'opaque' ]
233             },
234              
235             # struct udev *udev_monitor_get_udev(struct udev_monitor *udev_monitor);
236             'udev_monitor_get_udev' => {
237             ffi_data => [ ['opaque'], 'opaque' ]
238             },
239              
240             #kernel and udev generated events over netlink
241              
242             # struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char *name);
243             'udev_monitor_new_from_netlink' => {
244             ffi_data => [ ['opaque', 'string'], 'opaque' ]
245             },
246              
247             # bind socket
248              
249             # int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor);
250             'udev_monitor_enable_receiving' => {
251             ffi_data => [ ['opaque'], 'int' ]
252             },
253              
254             # int udev_monitor_set_receive_buffer_size(struct udev_monitor *udev_monitor, int size);
255             'udev_monitor_set_receive_buffer_size' => {
256             ffi_data => [ ['opaque', 'int'], 'int' ]
257             },
258              
259             # int udev_monitor_get_fd(struct udev_monitor *udev_monitor);
260             'udev_monitor_get_fd' => {
261             ffi_data => [ ['opaque'], 'int' ]
262             },
263              
264             # struct udev_device *udev_monitor_receive_device(struct udev_monitor *udev_monitor);
265             'udev_monitor_receive_device' => {
266             ffi_data => [ ['opaque'], 'opaque']
267             },
268              
269             # n-kernel socket filters to select messages that get delivered to a listener
270              
271             # int udev_monitor_filter_add_match_subsystem_devtype(struct udev_monitor *udev_monitor,
272             # const char *subsystem, const char *devtype);
273             'udev_monitor_filter_add_match_subsystem_devtype' => {
274             ffi_data => [ ['opaque', 'string', 'string'], 'int' ]
275             },
276              
277             # int udev_monitor_filter_add_match_tag(struct udev_monitor *udev_monitor, const char *tag);
278             'udev_monitor_filter_add_match_tag' => {
279             ffi_data => [ ['opaque', 'string'], 'int' ]
280             },
281              
282             # int udev_monitor_filter_update(struct udev_monitor *udev_monitor);
283             'udev_monitor_filter_update' => {
284             ffi_data => [ ['opaque'], 'int' ]
285             },
286              
287             # int udev_monitor_filter_remove(struct udev_monitor *udev_monitor);
288             'udev_monitor_filter_remove' => {
289             ffi_data => [ ['opaque'], 'int' ]
290             },
291              
292              
293             # udev_enumerate =======================================================
294              
295             # search sysfs for specific devices and provide a sorted list
296              
297             # struct udev_enumerate *udev_enumerate_ref(struct udev_enumerate *udev_enumerate);
298             'udev_enumerate_ref' => {
299             ffi_data => [ ['opaque'], 'opaque' ]
300             },
301              
302             # struct udev_enumerate *udev_enumerate_unref(struct udev_enumerate *udev_enumerate);
303             'udev_enumerate_unref' => {
304             ffi_data => [ ['opaque'], 'opaque' ]
305             },
306              
307             # struct udev *udev_enumerate_get_udev(struct udev_enumerate *udev_enumerate);
308             'udev_enumerate_get_udev' => {
309             ffi_data => [ ['opaque'], 'opaque' ]
310             },
311              
312             # struct udev_enumerate *udev_enumerate_new(struct udev *udev);
313             'udev_enumerate_new' => {
314             ffi_data => [ ['opaque'], 'opaque' ]
315             },
316              
317             # device properties filter
318              
319             # int udev_enumerate_add_match_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem);
320             'udev_enumerate_add_match_subsystem' => {
321             ffi_data => [ ['opaque', 'string'], 'int' ]
322             },
323              
324             # int udev_enumerate_add_nomatch_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem);
325             'udev_enumerate_add_nomatch_subsystem' => {
326             ffi_data => [ ['opaque', 'string'], 'int' ]
327             },
328              
329             # int udev_enumerate_add_match_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value);
330             'udev_enumerate_add_match_sysattr' => {
331             ffi_data => [ ['opaque', 'string', 'string'], 'int' ]
332             },
333              
334             # int udev_enumerate_add_nomatch_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value);
335             'udev_enumerate_add_nomatch_sysattr' => {
336             ffi_data => [ ['opaque', 'string', 'string'], 'int' ]
337             },
338              
339             # int udev_enumerate_add_match_property(struct udev_enumerate *udev_enumerate, const char *property, const char *value);
340             'udev_enumerate_add_match_property' => {
341             ffi_data => [ ['opaque', 'string', 'string'], 'int' ]
342             },
343              
344             # int udev_enumerate_add_match_sysname(struct udev_enumerate *udev_enumerate, const char *sysname);
345             'udev_enumerate_add_match_sysname' => {
346             ffi_data => [ ['opaque', 'string'], 'int' ]
347             },
348              
349             # int udev_enumerate_add_match_tag(struct udev_enumerate *udev_enumerate, const char *tag);
350             'udev_enumerate_add_match_tag' => {
351             ffi_data => [ ['opaque', 'string'], 'int' ]
352             },
353              
354             # int udev_enumerate_add_match_parent(struct udev_enumerate *udev_enumerate, struct udev_device *parent);
355             'udev_enumerate_add_match_parent' => {
356             ffi_data => [ ['opaque', 'opaque'], 'int' ]
357             },
358              
359             # int udev_enumerate_add_match_is_initialized(struct udev_enumerate *udev_enumerate);
360             'udev_enumerate_add_match_is_initialized' => {
361             ffi_data => [ ['opaque'], 'int' ]
362             },
363              
364             # int udev_enumerate_add_syspath(struct udev_enumerate *udev_enumerate, const char *syspath);
365             'udev_enumerate_add_syspath' => {
366             ffi_data => [ ['opaque', 'string'], 'int' ]
367             },
368              
369             # run enumeration with active filters
370              
371             # int udev_enumerate_scan_devices(struct udev_enumerate *udev_enumerate);
372             'udev_enumerate_scan_devices' => {
373             ffi_data => [ ['opaque'], 'int' ]
374             },
375              
376             # int udev_enumerate_scan_subsystems(struct udev_enumerate *udev_enumerate);
377             'udev_enumerate_scan_subsystems' => {
378             ffi_data => [ ['opaque'], 'int' ]
379             },
380              
381             # return device list
382              
383             # struct udev_list_entry *udev_enumerate_get_list_entry(struct udev_enumerate *udev_enumerate);
384             'udev_enumerate_get_list_entry' => {
385             ffi_data => [ ['opaque'], 'opaque' ]
386             }
387             },
388              
389             UDEVADM_LOCATIONS => [
390             '/bin/udevadm',
391             '/sbin/udevadm'
392             ]
393 2     2   23 };
  2         7  
394              
395              
396             @EXPORT_OK = ( keys(%{+FUNCTIONS}), qw(get_entries));
397              
398             %EXPORT_TAGS = (
399             'all' => \@EXPORT_OK
400             );
401              
402              
403             my $init = 0;
404             my $is_libudev_load = 1;
405              
406              
407              
408             sub udev_version {
409 2     2 0 171 my $full_path = can_run('udevadm');
410              
411 2 50       113357 if(!$full_path) {
412 2         9 for(@{ +UDEVADM_LOCATIONS }) {
  2         11  
413 4 50       33 if(-f) {
414 0         0 $full_path = $_;
415 0         0 last;
416             }
417             }
418             }
419              
420 2 50       12 if(!$full_path) {
421 2         10 $@ = "Can't find udevadm utility";
422 2         19 return undef;
423             }
424              
425              
426 0         0 my ( $success, $error_message, undef, $stdout_buf, $stderr_buf ) =
427             run( command => [$full_path, '--version'], timeout => 60, verbose => 0 );
428              
429 0 0       0 if(!$success) {
430 0         0 $@ = $error_message;
431 0         0 return undef;
432             }
433 0 0       0 if($stdout_buf->[0] !~ /^(\d+)\s*$/) {
434 0         0 $@ = "Can't get udev version from udevadm utility";
435 0         0 return undef;
436             }
437              
438 0         0 return $1;
439             }
440              
441              
442              
443             my $_function_not_attach = sub {
444             die "Function '".$_[0]."' not attached from udev library\n".
445             "`udevadm` version: ".(defined(udev_version()) ?udev_version() :'unknown')."\n";
446             };
447              
448              
449              
450             sub get_entries {
451 0     0 0 0 my $entry = shift;
452              
453 0 0       0 if(wantarray) {
454 0         0 my @a = ();
455              
456 0 0       0 if(defined($entry)) {
457 0         0 push @a, udev_list_entry_get_name($entry)
458             while defined($entry = udev_list_entry_get_next($entry));
459             }
460              
461 0         0 return @a;
462             }
463              
464              
465 0         0 my %h = ();
466              
467 0 0       0 if(defined($entry)) {
468 0         0 $h{ udev_list_entry_get_name($entry) } = udev_list_entry_get_value($entry)
469             while defined($entry = udev_list_entry_get_next($entry));
470             }
471              
472 0         0 return \%h;
473             }
474              
475              
476              
477             sub init {
478 1 50   1 0 7 return $is_libudev_load if $init;
479 1         3 ++$init;
480              
481              
482 1         10 my $libudev = find_lib(
483             lib => 'udev'
484             );
485 1 50       8706 if(!$libudev) {
486 0         0 $is_libudev_load = 0;
487 0         0 return 0;
488             }
489              
490 1   50     6 my $udev_version = udev_version() || 0;
491              
492 1         17 my $ffi = FFI::Platypus->new;
493 1         34 $ffi->lib($libudev);
494              
495 1 50       37 if(8 != $ffi->sizeof('dev_t')) {
496 0         0 $is_libudev_load = 0;
497 0         0 return 0;
498             }
499              
500 1         1356568 for my $funct (keys %{+FUNCTIONS} ) {
  1         18  
501 67         75 eval {
502 67         200 $ffi->attach($funct => FUNCTIONS->{$funct}{ffi_data}[0] => FUNCTIONS->{$funct}{ffi_data}[1]);
503             };
504 67 50       4699 if($@) {
505             die "Can't attach '$funct' function from udev library\n"
506 0 0 0     0 if !exists(FUNCTIONS->{$funct}{since}) || $udev_version >= FUNCTIONS->{$funct}{since};
507              
508 2     2   20 no strict 'refs';
  2         7  
  2         265  
509 0     0   0 *$funct = sub { $_function_not_attach->($funct) };
  0         0  
510             }
511             }
512              
513 1         12 return 1;
514             }
515              
516              
517              
518             1;