determine unique perls by their sitelibexp config setting
[scpubgit/System-Introspector.git] / lib / System / Introspector / Probe / Packages / Apt.pm
1 package System::Introspector::Probe::Packages::Apt;
2 use Moo;
3 use File::Basename;
4
5 use System::Introspector::Util qw(
6     handle_from_command
7     transform_exceptions
8     output_from_file
9     files_from_dir
10 );
11
12 has apt_lists_dir => (is => 'ro', builder => 1);
13 has apt_update_after => (is => 'ro', default => sub { 86400 });
14 has apt_update => (is => 'ro');
15
16 has apt_sources => (is => 'ro', builder => 1);
17 has apt_sources_dir => (is => 'ro', builder => 1);
18
19 sub _build_apt_lists_dir   { '/var/lib/apt/lists' }
20 sub _build_apt_sources     { '/etc/apt/sources.list' }
21 sub _build_apt_sources_dir { '/etc/apt/sources.list.d' }
22
23 sub gather {
24     my ($self) = @_;
25     return {
26         update => {
27             last => $self->_last_apt_update,
28             run => transform_exceptions {
29                 return { result => $self->_check_apt_state };
30             },
31         },
32         installed => transform_exceptions {
33             return { packages => $self->_gather_installed };
34         },
35         upgradable => transform_exceptions {
36             return { actions => $self->_gather_upgradable };
37         },
38         sources => transform_exceptions {
39             return { config => $self->_gather_sources };
40         },
41     };
42 }
43
44 sub _last_apt_update {
45     my ($self) = @_;
46     return scalar( (stat($self->apt_lists_dir))[9] );
47 }
48
49 sub _check_apt_state {
50     my ($self) = @_;
51     return 'disabled' unless $self->apt_update;
52     my $threshold   = $self->apt_update_after;
53     my $last_change = $self->_last_apt_update;
54     return 'no'if ($last_change + $threshold) > time;
55     handle_from_command 'apt-get update';
56     return 'yes';
57 }
58
59 sub _open_dpkg_query_pipe {
60     my ($self) = @_;
61     return handle_from_command 'dpkg-query --show';
62 }
63
64 sub _open_apt_get_upgrade_simulation_pipe {
65     my ($self) = @_;
66     return handle_from_command 'apt-get -s upgrade';
67 }
68
69 sub _gather_sources {
70     my ($self) = @_;
71     my $sources_dir = $self->apt_sources_dir;
72     return {
73         'sources_list' => $self->_fetch_source_list($self->apt_sources),
74         'sources_list_dir' => (-e $sources_dir) ? transform_exceptions {
75             return +{ files => +{ map {
76                 ($_, $self->_fetch_source_list("$sources_dir/$_"));
77             } files_from_dir $sources_dir } };
78         } : {},
79     };
80 }
81
82 sub _fetch_source_list {
83     my ($self, $file) = @_;
84     return transform_exceptions {
85         return {
86             file_name => $file,
87             body => scalar(output_from_file $file),
88         };
89     };
90 }
91
92 sub _gather_upgradable {
93     my ($self) = @_;
94     my $pipe = $self->_open_apt_get_upgrade_simulation_pipe;
95     my %action;
96     while (defined( my $line = <$pipe> )) {
97         chomp $line;
98         if ($line =~ m{^(inst|remv)\s+(\S+)\s+(.+)$}i) {
99             $action{ lc($1) }{ $2 } = $3;
100         }
101     }
102     return \%action;
103 }
104
105 sub _gather_installed {
106     my ($self) = @_;
107     my $pipe = $self->_open_dpkg_query_pipe;
108     my %package;
109     while (defined( my $line = <$pipe> )) {
110         chomp $line;
111         my ($package, $version) = split m{\s+}, $line;
112         $package{ $package } = {
113             version => $version,
114         };
115     }
116     return \%package;
117 }
118
119 1;
120
121 __END__
122
123 =head1 NAME
124
125 System::Introspector::Packages::Apt - Gather APT package status
126
127 =head1 DESCRIPTION
128
129 Uses C<dpkg-query> to list all installed packages.
130
131 =head1 SEE ALSO
132
133 =over
134
135 =item L<System::Introspector>
136
137 =back
138
139 =cut