line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# Sys::OsRelease |
2
|
|
|
|
|
|
|
# ABSTRACT: read operating system details from standard /etc/os-release file |
3
|
|
|
|
|
|
|
# Copyright (c) 2022 by Ian Kluft |
4
|
|
|
|
|
|
|
# Open Source license Perl's Artistic License 2.0: |
5
|
|
|
|
|
|
|
# SPDX-License-Identifier: Artistic-2.0 |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
# This module must be maintained for minimal dependencies so it can be used to build systems and containers. |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
## no critic (Modules::RequireExplicitPackage) |
10
|
|
|
|
|
|
|
# This resolves conflicting Perl::Critic rules which want package and strictures each before the other |
11
|
4
|
|
|
4
|
|
12471
|
use strict; |
|
4
|
|
|
|
|
26
|
|
|
4
|
|
|
|
|
105
|
|
12
|
4
|
|
|
4
|
|
16
|
use warnings; |
|
4
|
|
|
|
|
6
|
|
|
4
|
|
|
|
|
87
|
|
13
|
4
|
|
|
4
|
|
2245
|
use utf8; |
|
4
|
|
|
|
|
52
|
|
|
4
|
|
|
|
|
18
|
|
14
|
|
|
|
|
|
|
## use critic (Modules::RequireExplicitPackage) |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
package Sys::OsRelease; |
17
|
|
|
|
|
|
|
$Sys::OsRelease::VERSION = '0.2.2'; |
18
|
4
|
|
|
4
|
|
183
|
use Config; |
|
4
|
|
|
|
|
8
|
|
|
4
|
|
|
|
|
166
|
|
19
|
4
|
|
|
4
|
|
23
|
use Carp qw(carp croak); |
|
4
|
|
|
|
|
6
|
|
|
4
|
|
|
|
|
1632
|
|
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
# the instance - use Sys::OsRelease->instance() to get it |
22
|
|
|
|
|
|
|
my %_instances = (); |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
# default search path and file name for os-release file |
25
|
|
|
|
|
|
|
my @std_search_path = qw(/etc /usr/lib /run/host); |
26
|
|
|
|
|
|
|
my $std_file_name = "os-release"; |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
# defined attributes from FreeDesktop's os-release standard - this needs to be kept up-to-date with the standard |
29
|
|
|
|
|
|
|
my @std_attrs = qw(NAME ID ID_LIKE PRETTY_NAME CPE_NAME VARIANT VARIANT_ID VERSION VERSION_ID VERSION_CODENAME |
30
|
|
|
|
|
|
|
BUILD_ID IMAGE_ID IMAGE_VERSION HOME_URL DOCUMENTATION_URL SUPPORT_URL BUG_REPORT_URL PRIVACY_POLICY_URL |
31
|
|
|
|
|
|
|
LOGO ANSI_COLOR DEFAULT_HOSTNAME SYSEXT_LEVEL); |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
# OS ID strings which are preferred as common if found in ID_LIKE |
34
|
|
|
|
|
|
|
my %common_id = ( |
35
|
|
|
|
|
|
|
alpine => 1, |
36
|
|
|
|
|
|
|
arch => 1, |
37
|
|
|
|
|
|
|
fedora => 1, |
38
|
|
|
|
|
|
|
debian => 1, |
39
|
|
|
|
|
|
|
opensuse => 1, |
40
|
|
|
|
|
|
|
); |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
# call destructor when program ends |
43
|
|
|
|
|
|
|
END { |
44
|
4
|
|
|
4
|
|
5077
|
foreach my $class (keys %_instances) { |
45
|
1
|
|
|
|
|
9
|
$class->clear_instance(); |
46
|
|
|
|
|
|
|
} |
47
|
4
|
|
|
|
|
20
|
undef %_instances; |
48
|
|
|
|
|
|
|
} |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
# |
51
|
|
|
|
|
|
|
# singleton management methods |
52
|
|
|
|
|
|
|
# These can be imported by another class by using the import_singleton() method. That was done for Sys::OsPackage, |
53
|
|
|
|
|
|
|
# to avoid copying those methods. But other classes with a similar need to minimize module dependencies which already |
54
|
|
|
|
|
|
|
# use Sys::OsRelease can do this too. |
55
|
|
|
|
|
|
|
# |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
# alternative method to initiate initialization without returning a value |
58
|
|
|
|
|
|
|
sub init |
59
|
|
|
|
|
|
|
{ |
60
|
0
|
|
|
0
|
1
|
0
|
my ($class, @params) = @_; |
61
|
0
|
|
|
|
|
0
|
$class->instance(@params); |
62
|
0
|
|
|
|
|
0
|
return; |
63
|
|
|
|
|
|
|
} |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
# new method calls instance |
66
|
|
|
|
|
|
|
sub new |
67
|
|
|
|
|
|
|
{ |
68
|
0
|
|
|
0
|
1
|
0
|
my ($class, @params) = @_; |
69
|
0
|
|
|
|
|
0
|
return $class->instance(@params); |
70
|
|
|
|
|
|
|
} |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
# singleton class instance |
73
|
|
|
|
|
|
|
sub instance |
74
|
|
|
|
|
|
|
{ |
75
|
27
|
|
|
27
|
1
|
61528
|
my ($class, @params) = @_; |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
# initialize if not already done |
78
|
27
|
100
|
|
|
|
62
|
if (not $class->defined_instance()) { |
79
|
11
|
|
|
|
|
39
|
$_instances{$class} = $class->_new_instance(@params); |
80
|
|
|
|
|
|
|
} |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
# return singleton instance |
83
|
27
|
|
|
|
|
75
|
return $_instances{$class}; |
84
|
|
|
|
|
|
|
} |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
# test if instance is defined for testing |
87
|
|
|
|
|
|
|
sub defined_instance |
88
|
|
|
|
|
|
|
{ |
89
|
40
|
|
|
40
|
1
|
696
|
my $class = shift; |
90
|
40
|
100
|
66
|
|
|
269
|
return ((exists $_instances{$class}) and $_instances{$class}->isa($class)) ? 1 : 0; |
91
|
|
|
|
|
|
|
} |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
# clear instance for exit-cleanup or for re-use in testing |
94
|
|
|
|
|
|
|
sub clear_instance |
95
|
|
|
|
|
|
|
{ |
96
|
11
|
|
|
11
|
1
|
4357
|
my $class = shift; |
97
|
11
|
50
|
|
|
|
39
|
if ($class->defined_instance()) { |
98
|
|
|
|
|
|
|
# clean up anything that the destructor will miss, such as auto-generated methods |
99
|
11
|
50
|
|
|
|
70
|
if ($class->can("_cleanup_instance")) { |
100
|
11
|
|
|
|
|
31
|
$class->_cleanup_instance(); |
101
|
|
|
|
|
|
|
} |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
# dereferencing will destroy singleton instance |
104
|
11
|
|
|
|
|
27
|
delete $_instances{$class}; |
105
|
|
|
|
|
|
|
} |
106
|
11
|
|
|
|
|
83
|
return; |
107
|
|
|
|
|
|
|
} |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
# allow other classes which cooperate with Sys::OsRelease to import our singleton-management methods |
110
|
|
|
|
|
|
|
# This helps maintain minimal prerequisites among modules working to set up Perl on containers or new systems. |
111
|
|
|
|
|
|
|
sub import_singleton |
112
|
|
|
|
|
|
|
{ |
113
|
1
|
|
|
1
|
1
|
2042
|
my $class = shift; |
114
|
1
|
|
|
|
|
3
|
my $caller_class = caller; |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
# export singleton-management methods to caller class |
117
|
1
|
|
|
|
|
3
|
foreach my $method_name (qw(init new instance defined_instance clear_instance)) { |
118
|
|
|
|
|
|
|
## no critic (TestingAndDebugging::ProhibitNoStrict) |
119
|
4
|
|
|
4
|
|
34
|
no strict 'refs'; |
|
4
|
|
|
|
|
8
|
|
|
4
|
|
|
|
|
5869
|
|
120
|
5
|
|
|
|
|
7
|
*{$caller_class."::".$method_name} = \&{$class."::".$method_name}; |
|
5
|
|
|
|
|
44
|
|
|
5
|
|
|
|
|
13
|
|
121
|
|
|
|
|
|
|
} |
122
|
1
|
|
|
|
|
4
|
return; |
123
|
|
|
|
|
|
|
} |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
# |
126
|
|
|
|
|
|
|
# os-release data access methods |
127
|
|
|
|
|
|
|
# |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
# access module constants |
130
|
1
|
|
|
1
|
0
|
481
|
sub std_search_path { return @std_search_path; } |
131
|
1
|
|
|
1
|
0
|
72
|
sub std_attrs { return @std_attrs; } |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
# fold case for case-insensitive matching |
134
|
|
|
|
|
|
|
my $can_fc = CORE->can("fc"); # test fc() once and save result |
135
|
|
|
|
|
|
|
sub fold_case |
136
|
|
|
|
|
|
|
{ |
137
|
795
|
|
|
795
|
0
|
996
|
my $str = shift; |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
# use fc if available, otherwise lc to support older Perls |
140
|
795
|
50
|
|
|
|
3806
|
return $can_fc ? $can_fc->($str) : lc($str); |
141
|
|
|
|
|
|
|
} |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
# initialize a new instance |
144
|
|
|
|
|
|
|
sub _new_instance |
145
|
|
|
|
|
|
|
{ |
146
|
11
|
|
|
11
|
|
51
|
my ($class, @params) = @_; |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
# enforce class lineage - _new_instance() should be overloaded by other classes that import singleton methods |
149
|
11
|
50
|
|
|
|
50
|
if (not $class->isa(__PACKAGE__)) { |
150
|
0
|
0
|
|
|
|
0
|
croak "_new_instance() should be overloaded by calling class: " |
151
|
|
|
|
|
|
|
.(ref $class ? ref $class : $class)." is not a ".__PACKAGE__; |
152
|
|
|
|
|
|
|
} |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
# obtain parameters from array or hashref |
155
|
11
|
|
|
|
|
21
|
my %obj; |
156
|
11
|
100
|
|
|
|
30
|
if (scalar @params > 0) { |
157
|
9
|
50
|
|
|
|
23
|
if (ref $params[0] eq 'HASH') { |
158
|
0
|
|
|
|
|
0
|
$obj{_config} = $params[0]; |
159
|
|
|
|
|
|
|
} else { |
160
|
9
|
|
|
|
|
32
|
$obj{_config} = {@params}; |
161
|
|
|
|
|
|
|
} |
162
|
|
|
|
|
|
|
} |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
# locate os-release file in standard places |
165
|
11
|
|
|
|
|
23
|
my $osrelease_path; |
166
|
11
|
100
|
|
|
|
29
|
my @search_path = ((exists $obj{_config}{search_path}) ? @{$obj{_config}{search_path}} : @std_search_path); |
|
9
|
|
|
|
|
24
|
|
167
|
11
|
100
|
|
|
|
34
|
my $file_name = ((exists $obj{_config}{file_name}) ? $obj{_config}{file_name} : $std_file_name); |
168
|
11
|
|
|
|
|
22
|
foreach my $search_dir (@search_path) { |
169
|
10
|
50
|
|
|
|
510
|
if (-r "$search_dir/$file_name") { |
170
|
10
|
|
|
|
|
55
|
$osrelease_path = $search_dir."/".$file_name; |
171
|
10
|
|
|
|
|
21
|
last; |
172
|
|
|
|
|
|
|
} |
173
|
|
|
|
|
|
|
} |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
# If we found os-release on this system, read it |
176
|
|
|
|
|
|
|
# otherwise leave everything empty and platform() method will use Perl's $Config{osname} as a summary value |
177
|
11
|
100
|
|
|
|
22
|
if (defined $osrelease_path) { |
178
|
|
|
|
|
|
|
# save os-release file path |
179
|
10
|
|
|
|
|
29
|
$obj{_config}{osrelease_path} = $osrelease_path; |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
# read os-release file |
182
|
|
|
|
|
|
|
## no critic (InputOutput::RequireBriefOpen) |
183
|
10
|
50
|
|
|
|
541
|
if (open my $fh, "<", $osrelease_path) { |
184
|
10
|
|
|
|
|
1331
|
while (my $line = <$fh>) { |
185
|
119
|
|
|
|
|
212
|
chomp $line; # remove trailing nl |
186
|
119
|
50
|
|
|
|
209
|
if (substr($line, -1, 1) eq "\r") { |
187
|
0
|
|
|
|
|
0
|
$line = substr($line, 0, -1); # remove trailing cr |
188
|
|
|
|
|
|
|
} |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
# skip comments and blank lines |
191
|
119
|
50
|
33
|
|
|
438
|
if ($line =~ /^ \s+ #/x or $line =~ /^ \s+ $/x) { |
192
|
0
|
|
|
|
|
0
|
next; |
193
|
|
|
|
|
|
|
} |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
# read attribute assignment lines |
196
|
119
|
100
|
66
|
|
|
450
|
if ($line =~ /^ ([A-Z0-9_]+) = "(.*)" $/x |
|
|
|
100
|
|
|
|
|
197
|
|
|
|
|
|
|
or $line =~ /^ ([A-Z0-9_]+) = '(.*)' $/x |
198
|
|
|
|
|
|
|
or $line =~ /^ ([A-Z0-9_]+) = (.*) $/x) |
199
|
|
|
|
|
|
|
{ |
200
|
117
|
50
|
|
|
|
234
|
next if $1 eq "_config"; # don't overwrite _config |
201
|
117
|
|
|
|
|
167
|
$obj{fold_case($1)} = $2; |
202
|
|
|
|
|
|
|
} |
203
|
|
|
|
|
|
|
} |
204
|
10
|
|
|
|
|
176
|
close $fh; |
205
|
|
|
|
|
|
|
} |
206
|
|
|
|
|
|
|
} |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
# bless instance and generate accessor methods |
209
|
11
|
|
|
|
|
53
|
my $obj_ref = bless \%obj, $class; |
210
|
11
|
|
|
|
|
44
|
$obj_ref->_gen_accessors(); |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
# instantiate object |
213
|
11
|
|
|
|
|
31
|
return $obj_ref; |
214
|
|
|
|
|
|
|
} |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
# helper function to allow methods to get the instance ref when called via the class name |
217
|
|
|
|
|
|
|
sub class_or_obj |
218
|
|
|
|
|
|
|
{ |
219
|
993
|
|
|
993
|
0
|
1106
|
my $coo = shift; |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
# return the instance |
222
|
993
|
100
|
|
|
|
1650
|
return ((ref $coo) ? $coo : $coo->instance()); |
223
|
|
|
|
|
|
|
} |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
# clean up data in an instance before feeding it to the destructor |
226
|
|
|
|
|
|
|
sub _cleanup_instance |
227
|
|
|
|
|
|
|
{ |
228
|
11
|
|
|
11
|
|
24
|
my ($class_or_obj) = @_; |
229
|
11
|
|
|
|
|
19
|
my $self = class_or_obj($class_or_obj); |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
# enforce class lineage - _cleanup_instance() should be overloaded by other classes that import singleton methods |
232
|
11
|
50
|
|
|
|
48
|
if (not $self->isa(__PACKAGE__)) { |
233
|
0
|
|
|
|
|
0
|
croak "_new_instance() should be overloaded by calling class: " |
234
|
|
|
|
|
|
|
.(ef $self)." is not a ".__PACKAGE__; |
235
|
|
|
|
|
|
|
} |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
# clear accessor functions |
238
|
11
|
|
|
|
|
16
|
foreach my $acc (keys %{$self->{_config}{accessor}}) { |
|
11
|
|
|
|
|
106
|
|
239
|
256
|
|
|
|
|
361
|
$self->_clear_accessor($acc); |
240
|
|
|
|
|
|
|
} |
241
|
11
|
|
|
|
|
28
|
return; |
242
|
|
|
|
|
|
|
} |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
# determine platform type |
245
|
|
|
|
|
|
|
sub platform |
246
|
|
|
|
|
|
|
{ |
247
|
4
|
|
|
4
|
1
|
1223
|
my ($class_or_obj) = @_; |
248
|
4
|
|
|
|
|
7
|
my $self = class_or_obj($class_or_obj); |
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
# if we haven't already saved this result, compute and save it |
251
|
4
|
100
|
|
|
|
8
|
if (not $self->has_config("platform")) { |
252
|
2
|
100
|
|
|
|
5
|
if ($self->has_attr("id")) { |
253
|
1
|
|
|
|
|
2
|
$self->config("platform", $self->id); |
254
|
|
|
|
|
|
|
} |
255
|
2
|
100
|
|
|
|
4
|
if ($self->has_attr("id_like")) { |
256
|
|
|
|
|
|
|
# check if the configuration has additional common IDs which should be recognized if seen in ID_LIKE |
257
|
1
|
50
|
|
|
|
6
|
if ($self->has_config("common_id")) { |
258
|
0
|
|
|
|
|
0
|
my $cids = $self->config("common_id"); |
259
|
0
|
0
|
|
|
|
0
|
my @cids = (ref $cids eq "ARRAY") ? (@{$cids}) : (split /\s+/x, $cids); |
|
0
|
|
|
|
|
0
|
|
260
|
0
|
|
|
|
|
0
|
foreach my $cid (@cids) { |
261
|
0
|
|
|
|
|
0
|
$common_id{$cid} = 1; |
262
|
|
|
|
|
|
|
} |
263
|
|
|
|
|
|
|
} |
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
# check ID_LIKE for more common names which should be used instead of ID |
266
|
1
|
|
|
|
|
3
|
foreach my $like (split /\s+/x, $self->id_like) { |
267
|
1
|
50
|
|
|
|
3
|
if (exists $common_id{$like}) { |
268
|
1
|
|
|
|
|
3
|
$self->config("platform", $like); |
269
|
1
|
|
|
|
|
2
|
last; |
270
|
|
|
|
|
|
|
} |
271
|
|
|
|
|
|
|
} |
272
|
|
|
|
|
|
|
} |
273
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
# if platform is still not set, use Perl's osname config as a summary value |
275
|
2
|
100
|
|
|
|
4
|
if (not $self->has_config("platform")) { |
276
|
1
|
|
|
|
|
3
|
$self->config("platform", $Config{osname}); |
277
|
|
|
|
|
|
|
} |
278
|
|
|
|
|
|
|
} |
279
|
4
|
|
|
|
|
8
|
return $self->config("platform"); |
280
|
|
|
|
|
|
|
} |
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
# get location of the os-release file found on this system |
283
|
|
|
|
|
|
|
# return undef if the file was not found |
284
|
|
|
|
|
|
|
sub osrelease_path |
285
|
|
|
|
|
|
|
{ |
286
|
1
|
|
|
1
|
1
|
5
|
my ($class_or_obj) = @_; |
287
|
1
|
|
|
|
|
3
|
my $self = class_or_obj($class_or_obj); |
288
|
1
|
50
|
|
|
|
3
|
if (exists $self->{_config}{osrelease_path}) { |
289
|
1
|
|
|
|
|
3
|
return $self->{_config}{osrelease_path}; |
290
|
|
|
|
|
|
|
} |
291
|
0
|
|
|
|
|
0
|
return; |
292
|
|
|
|
|
|
|
} |
293
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
# attribute existence checker |
295
|
|
|
|
|
|
|
sub has_attr |
296
|
|
|
|
|
|
|
{ |
297
|
341
|
|
|
341
|
1
|
43413
|
my ($class_or_obj, $key) = @_; |
298
|
341
|
|
|
|
|
553
|
my $self = class_or_obj($class_or_obj); |
299
|
341
|
100
|
|
|
|
672
|
return ((exists $self->{fold_case($key)}) ? 1 : 0); |
300
|
|
|
|
|
|
|
} |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
# attribute read-only accessor |
303
|
|
|
|
|
|
|
sub get |
304
|
|
|
|
|
|
|
{ |
305
|
95
|
|
|
95
|
1
|
205
|
my ($class_or_obj, $key) = @_; |
306
|
95
|
|
|
|
|
161
|
my $self = class_or_obj($class_or_obj); |
307
|
95
|
|
|
|
|
163
|
return $self->{fold_case($key)}; |
308
|
|
|
|
|
|
|
} |
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
# attribute existence checker |
311
|
|
|
|
|
|
|
sub has_config |
312
|
|
|
|
|
|
|
{ |
313
|
11
|
|
|
11
|
1
|
10343
|
my ($class_or_obj, $key) = @_; |
314
|
11
|
|
|
|
|
15
|
my $self = class_or_obj($class_or_obj); |
315
|
11
|
100
|
|
|
|
36
|
return ((exists $self->{_config}{$key}) ? 1 : 0); |
316
|
|
|
|
|
|
|
} |
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
# config read/write accessor |
319
|
|
|
|
|
|
|
sub config |
320
|
|
|
|
|
|
|
{ |
321
|
7
|
|
|
7
|
1
|
21
|
my ($class_or_obj, $key, $value) = @_; |
322
|
7
|
|
|
|
|
11
|
my $self = class_or_obj($class_or_obj); |
323
|
7
|
100
|
|
|
|
15
|
if (defined $value) { |
324
|
3
|
|
|
|
|
5
|
$self->{_config}{$key} = $value; |
325
|
|
|
|
|
|
|
} |
326
|
7
|
|
|
|
|
25
|
return $self->{_config}{$key}; |
327
|
|
|
|
|
|
|
} |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
# generate accessor methods for all defined and standardized attributes |
330
|
|
|
|
|
|
|
# private internal method |
331
|
|
|
|
|
|
|
sub _gen_accessors |
332
|
|
|
|
|
|
|
{ |
333
|
11
|
|
|
11
|
|
24
|
my ($class_or_obj) = @_; |
334
|
11
|
|
|
|
|
20
|
my $self = class_or_obj($class_or_obj); |
335
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
# generate read-only accessors for attributes actually found in os-release |
337
|
11
|
|
|
|
|
19
|
foreach my $key (sort keys %{$self}) { |
|
11
|
|
|
|
|
99
|
|
338
|
128
|
100
|
|
|
|
212
|
next if $key eq "_config"; # protect special/reserved attribute |
339
|
117
|
|
|
|
|
186
|
$self->_gen_accessor($key); |
340
|
|
|
|
|
|
|
} |
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
# generate undef accessors for standardized attributes which were not found in os-release |
343
|
11
|
|
|
|
|
29
|
foreach my $std_attr (@std_attrs) { |
344
|
242
|
50
|
|
|
|
352
|
next if $std_attr eq "_config"; # protect special/reserved attribute |
345
|
242
|
|
|
|
|
316
|
my $fc_attr = fold_case($std_attr); |
346
|
242
|
100
|
|
|
|
362
|
next if $self->has_attr($fc_attr); |
347
|
139
|
|
|
|
|
206
|
$self->_gen_accessor($fc_attr); |
348
|
|
|
|
|
|
|
} |
349
|
11
|
|
|
|
|
14
|
return; |
350
|
|
|
|
|
|
|
} |
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
# generate accessor |
353
|
|
|
|
|
|
|
# private internal method |
354
|
|
|
|
|
|
|
sub _gen_accessor |
355
|
|
|
|
|
|
|
{ |
356
|
256
|
|
|
256
|
|
351
|
my ($class_or_obj, $name) = @_; |
357
|
256
|
|
|
|
|
306
|
my $self = class_or_obj($class_or_obj); |
358
|
256
|
50
|
|
|
|
372
|
my $class = (ref $self) ? (ref $self) : $self; |
359
|
256
|
|
|
|
|
349
|
my $method_name = $class."::".$name; |
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
# mark accessor flag in configuration so it can be deleted for cleanup (mainly for testing) |
362
|
256
|
100
|
|
|
|
586
|
if (not exists $self->{_config}{accessor}) { |
363
|
11
|
|
|
|
|
31
|
$self->{_config}{accessor} = {}; |
364
|
|
|
|
|
|
|
} |
365
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
# generate accessor as read-only or undef depending whether it exists in the running system |
367
|
256
|
100
|
|
|
|
356
|
if (exists $self->{$name}) { |
368
|
|
|
|
|
|
|
# generate read-only accessor for attribute which was found in os-release |
369
|
117
|
|
|
3
|
|
355
|
$self->{_config}{accessor}{$name} = sub { return $self->{$name} }; |
|
3
|
|
|
|
|
11
|
|
370
|
|
|
|
|
|
|
} else { |
371
|
|
|
|
|
|
|
# generate undef accessor for standard attribute which was not found in os-release |
372
|
139
|
|
|
0
|
|
440
|
$self->{_config}{accessor}{$name} = sub { return; }; |
|
0
|
|
|
|
|
0
|
|
373
|
|
|
|
|
|
|
} |
374
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
## no critic (TestingAndDebugging::ProhibitNoStrict) |
376
|
4
|
|
|
4
|
|
35
|
no strict 'refs'; |
|
4
|
|
|
|
|
7
|
|
|
4
|
|
|
|
|
516
|
|
377
|
256
|
|
|
|
|
369
|
*{$method_name} = $self->{_config}{accessor}{$name}; |
|
256
|
|
|
|
|
610
|
|
378
|
256
|
|
|
|
|
441
|
return; |
379
|
|
|
|
|
|
|
} |
380
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
# clean up accessor |
382
|
|
|
|
|
|
|
# private internal method |
383
|
|
|
|
|
|
|
sub _clear_accessor |
384
|
|
|
|
|
|
|
{ |
385
|
256
|
|
|
256
|
|
343
|
my ($class_or_obj, $name) = @_; |
386
|
256
|
|
|
|
|
305
|
my $self = class_or_obj($class_or_obj); |
387
|
256
|
50
|
|
|
|
362
|
my $class = (ref $self) ? (ref $self) : $self; |
388
|
256
|
50
|
|
|
|
422
|
if (exists $self->{_config}{accessor}{$name}) { |
389
|
256
|
|
|
|
|
364
|
my $method_name = $class."::".$name; |
390
|
|
|
|
|
|
|
## no critic (TestingAndDebugging::ProhibitNoStrict) |
391
|
4
|
|
|
4
|
|
26
|
no strict 'refs'; |
|
4
|
|
|
|
|
7
|
|
|
4
|
|
|
|
|
491
|
|
392
|
256
|
|
|
|
|
261
|
undef *{$method_name}; |
|
256
|
|
|
|
|
572
|
|
393
|
256
|
|
|
|
|
893
|
delete $self->{_config}{accessor}{$name}; |
394
|
|
|
|
|
|
|
} |
395
|
256
|
|
|
|
|
427
|
return; |
396
|
|
|
|
|
|
|
} |
397
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
1; |
399
|
|
|
|
|
|
|
|
400
|
|
|
|
|
|
|
=pod |
401
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
=encoding UTF-8 |
403
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
=head1 NAME |
405
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
Sys::OsRelease - read operating system details from standard /etc/os-release file |
407
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
=head1 VERSION |
409
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
version 0.2.2 |
411
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
=head1 SYNOPSIS |
413
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
non-object-oriented: |
415
|
|
|
|
|
|
|
|
416
|
|
|
|
|
|
|
Sys::OsRelease->init(); |
417
|
|
|
|
|
|
|
my $id = Sys::OsRelease->id(); |
418
|
|
|
|
|
|
|
my $id_like = Sys::OsRelease->id_like(); |
419
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
object-oriented: |
421
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
my $osrelease = Sys::OsRelease->instance(); |
423
|
|
|
|
|
|
|
my $id = $osrelease->id(); |
424
|
|
|
|
|
|
|
my $id_like = $osrelease->id_like(); |
425
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
=head1 DESCRIPTION |
427
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
Sys::OsRelease is a helper library to read the /etc/os-release file, as defined by FreeDesktop.Org. |
429
|
|
|
|
|
|
|
The os-release file is used to define an operating system environment. |
430
|
|
|
|
|
|
|
It has been in widespread use among Linux distributions since 2017 and BSD variants since 2020. |
431
|
|
|
|
|
|
|
It was started on Linux systems which use the systemd software, but then spread to other Linux, BSD and |
432
|
|
|
|
|
|
|
Unix-based systems. |
433
|
|
|
|
|
|
|
Its purpose is to identify the system to any software which needs to know. |
434
|
|
|
|
|
|
|
It differentiates between Unix-based operating systems and even between Linux distributions. |
435
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
Sys::OsRelease is implemented with a singleton model, meaning there is only one instance of the class. |
437
|
|
|
|
|
|
|
Instead of instantiating an object with new(), the instance() class method returns the one and only instance. |
438
|
|
|
|
|
|
|
The first time it's called, it instantiates it. |
439
|
|
|
|
|
|
|
On following calls, it returns a reference to the singleton instance. |
440
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
This module maintains minimal prerequisites, and only those which are usually included with Perl. |
442
|
|
|
|
|
|
|
(Suggestions of new features and code will have to follow this rule.) |
443
|
|
|
|
|
|
|
That is intended to be acceptable for establishing system or container environments which contain Perl programs. |
444
|
|
|
|
|
|
|
It can also be used for installing or configuring software that needs to know about the system environment. |
445
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
=head2 The os-release Standard |
447
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
FreeDesktop.Org's os-release standard is at L. |
449
|
|
|
|
|
|
|
|
450
|
|
|
|
|
|
|
Current attributes recognized by Sys::OsRelease are: |
451
|
|
|
|
|
|
|
NAME ID ID_LIKE PRETTY_NAME CPE_NAME VARIANT VARIANT_ID VERSION VERSION_ID VERSION_CODENAME BUILD_ID IMAGE_ID |
452
|
|
|
|
|
|
|
IMAGE_VERSION HOME_URL DOCUMENTATION_URL SUPPORT_URL BUG_REPORT_URL PRIVACY_POLICY_URL LOGO ANSI_COLOR |
453
|
|
|
|
|
|
|
DEFAULT_HOSTNAME SYSEXT_LEVEL |
454
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
If other attributes are found in the os-release file, they will be accepted. |
456
|
|
|
|
|
|
|
Folded to lower case, the attribute names are used as keys in an internal hash structure. |
457
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
=head1 NAME |
459
|
|
|
|
|
|
|
|
460
|
|
|
|
|
|
|
Sys::OsRelease - read operating system details from standard /etc/os-release file |
461
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
=head1 METHODS |
463
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
=over 1 |
465
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
=item init([key => value, ...]) |
467
|
|
|
|
|
|
|
|
468
|
|
|
|
|
|
|
initializes the singleton instance without returning a value. |
469
|
|
|
|
|
|
|
Parameters are passed to the instance() method. |
470
|
|
|
|
|
|
|
This method is for cases where method calls will be via the class name, and the program |
471
|
|
|
|
|
|
|
doesn't need a reference to the instance. |
472
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
Under normal circumstances no parameters are needed. See instance() for possible parameters. |
474
|
|
|
|
|
|
|
|
475
|
|
|
|
|
|
|
=item new([key => value, ...]) |
476
|
|
|
|
|
|
|
|
477
|
|
|
|
|
|
|
initializes the singleton instance and returns a reference to it. |
478
|
|
|
|
|
|
|
Parameters are passed to the instance() method. |
479
|
|
|
|
|
|
|
This is equivalent to using the instance() method, made available if new() sounds more comfortable. |
480
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
Under normal circumstances no parameters are needed. See instance() for possible parameters. |
482
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
=item instance([key => value, ...]) |
484
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
initializes the singleton instance and returns a reference to it. |
486
|
|
|
|
|
|
|
|
487
|
|
|
|
|
|
|
Under normal circumstances no parameters are needed. Possible optional parameters are as follows: |
488
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
=over 1 |
490
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
=item common_id |
492
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
supplies an arrayref to use as a list of additional common strings which should be recognized by the platform() |
494
|
|
|
|
|
|
|
method, if they occur in the ID_LIKE attribute in the os-release file. By default, "debian" and "fedora" are |
495
|
|
|
|
|
|
|
regonized by platform() as common names and it will return them instead of the system's ID attribute. |
496
|
|
|
|
|
|
|
|
497
|
|
|
|
|
|
|
=item search_path |
498
|
|
|
|
|
|
|
|
499
|
|
|
|
|
|
|
supplies an arrayref of strings with directories to use as the search path for the os-release file. |
500
|
|
|
|
|
|
|
|
501
|
|
|
|
|
|
|
=item file_name |
502
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
supplies a string with the basename of the file to look for the os-release file. |
504
|
|
|
|
|
|
|
Obviously the default file name is "os-release". |
505
|
|
|
|
|
|
|
Under normal circumstances there is no need to set this. |
506
|
|
|
|
|
|
|
Currently this is only used for testing, where suffixes are added for copies of various different systems' |
507
|
|
|
|
|
|
|
os-release files, to indicate which system they came from. |
508
|
|
|
|
|
|
|
|
509
|
|
|
|
|
|
|
=back |
510
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
=item platform() |
512
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
returns a string with the platform type. On systems with /etc/os-release (or os-release in any location |
514
|
|
|
|
|
|
|
from the standard) this is usually from the ID field. |
515
|
|
|
|
|
|
|
On systems that use the ID_LIKE field, systems that claim to be like "debian" or "fedora" (always in lower case) |
516
|
|
|
|
|
|
|
will return those names for the platform. |
517
|
|
|
|
|
|
|
|
518
|
|
|
|
|
|
|
The list of recognized common platforms can be modified by passing a "common_id" parameter to instance()/new() |
519
|
|
|
|
|
|
|
with an arrayref containing additional names to recognize as common. For example, "centos" is another possibility. |
520
|
|
|
|
|
|
|
It was not included in the default because CentOS is discontinued. Both Rocky Linux and Alma Linux have |
521
|
|
|
|
|
|
|
ID_LIKE fields of "rhel centos fedora", which will match "fedora" with the default setting, but could be configured |
522
|
|
|
|
|
|
|
via "common_id" to recognize "centos" since it's listed first in ID_LIKE. |
523
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
On systems where an os-release file doesn't exist or isn't found, the platform string will fall back to Perl's |
525
|
|
|
|
|
|
|
$Config{osname} setting for the system. |
526
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
=item osrelease_path() |
528
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
returns the path where os-release was found. |
530
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
The default search path is /etc, /usr/lib and /run/host as defined by the standard. |
532
|
|
|
|
|
|
|
The search path can be replaced by providing a "search_path" parameter to instance()/new() with an arrayref |
533
|
|
|
|
|
|
|
containing the directories to search. This feature is currently only used for testing purposes. |
534
|
|
|
|
|
|
|
|
535
|
|
|
|
|
|
|
=item defined_instance() |
536
|
|
|
|
|
|
|
|
537
|
|
|
|
|
|
|
returns true if the singleton instance is defined, false if it is not yet defined or has been cleared. |
538
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
=item has_attr(name) |
540
|
|
|
|
|
|
|
|
541
|
|
|
|
|
|
|
returns a boolean which is true if the attribute named by the string parameter exists in the os-release data for the |
542
|
|
|
|
|
|
|
current system. |
543
|
|
|
|
|
|
|
The attribute name is case insensitive. |
544
|
|
|
|
|
|
|
|
545
|
|
|
|
|
|
|
=item get(name) |
546
|
|
|
|
|
|
|
|
547
|
|
|
|
|
|
|
is a read-only accessor which returns the value of the os-release attribute named by the string parameter, |
548
|
|
|
|
|
|
|
or undef if it doesn't exist. |
549
|
|
|
|
|
|
|
|
550
|
|
|
|
|
|
|
=item has_config(name) |
551
|
|
|
|
|
|
|
|
552
|
|
|
|
|
|
|
returns a boolean which is true if Sys::OsRelease contains a configuration setting named by the string parameter. |
553
|
|
|
|
|
|
|
|
554
|
|
|
|
|
|
|
=item config(name, [value]) |
555
|
|
|
|
|
|
|
|
556
|
|
|
|
|
|
|
is a read/write accessor for the configuration setting named by the string parameter "name". |
557
|
|
|
|
|
|
|
If no value parameter is provided, it returns the value of the parameter, or undef if it doesn't exist. |
558
|
|
|
|
|
|
|
If a value parameter is provided, it assigns that to the configuration setting and returns the same value. |
559
|
|
|
|
|
|
|
|
560
|
|
|
|
|
|
|
=item clear_instance() |
561
|
|
|
|
|
|
|
|
562
|
|
|
|
|
|
|
removes the singleton instance of the class if it was defined. |
563
|
|
|
|
|
|
|
Under normal circumstances it is not necessary to call this since the class destructor will call it automatically. |
564
|
|
|
|
|
|
|
It is currently only used for testing, where it is necessary to clear the instance before loading a new one with |
565
|
|
|
|
|
|
|
different parameters. |
566
|
|
|
|
|
|
|
|
567
|
|
|
|
|
|
|
Since this class is based on the singleton model, there is only one instance. |
568
|
|
|
|
|
|
|
The instance(), new() and init() methods will only initialize the instance if it is not already initialized. |
569
|
|
|
|
|
|
|
|
570
|
|
|
|
|
|
|
=item import_singleton |
571
|
|
|
|
|
|
|
|
572
|
|
|
|
|
|
|
The singleton-management methods I, I, I, I and I |
573
|
|
|
|
|
|
|
can be imported by another class by using the import_singleton() method. |
574
|
|
|
|
|
|
|
That was done for L, to allow it to avoid copying those methods. |
575
|
|
|
|
|
|
|
But other classes with a similar need to minimize module dependencies which already |
576
|
|
|
|
|
|
|
use I can do this too. |
577
|
|
|
|
|
|
|
This helps maintain minimal prerequisites among modules working to set up Perl on containers or new systems. |
578
|
|
|
|
|
|
|
|
579
|
|
|
|
|
|
|
=back |
580
|
|
|
|
|
|
|
|
581
|
|
|
|
|
|
|
=head1 SEE ALSO |
582
|
|
|
|
|
|
|
|
583
|
|
|
|
|
|
|
FreeDesktop.Org's os-release standard: L |
584
|
|
|
|
|
|
|
|
585
|
|
|
|
|
|
|
GitHub repository for Sys::OsRelease: L |
586
|
|
|
|
|
|
|
|
587
|
|
|
|
|
|
|
Related modules: |
588
|
|
|
|
|
|
|
|
589
|
|
|
|
|
|
|
=over 1 |
590
|
|
|
|
|
|
|
|
591
|
|
|
|
|
|
|
=item L |
592
|
|
|
|
|
|
|
|
593
|
|
|
|
|
|
|
installs Perl modules, for example as dependencies of a script, via OS packages if available or otherwise via CPAN - |
594
|
|
|
|
|
|
|
uses Sys::OsRelease to determine OS type |
595
|
|
|
|
|
|
|
|
596
|
|
|
|
|
|
|
=item L |
597
|
|
|
|
|
|
|
|
598
|
|
|
|
|
|
|
system information collected from multiple sources including system architecture, hardware, OS release data |
599
|
|
|
|
|
|
|
|
600
|
|
|
|
|
|
|
=back |
601
|
|
|
|
|
|
|
|
602
|
|
|
|
|
|
|
=head1 BUGS AND LIMITATIONS |
603
|
|
|
|
|
|
|
|
604
|
|
|
|
|
|
|
Please report bugs via GitHub at L |
605
|
|
|
|
|
|
|
|
606
|
|
|
|
|
|
|
Patches and enhancements may be submitted via a pull request at L |
607
|
|
|
|
|
|
|
|
608
|
|
|
|
|
|
|
=head1 LICENSE INFORMATION |
609
|
|
|
|
|
|
|
|
610
|
|
|
|
|
|
|
Copyright (c) 2022 by Ian Kluft |
611
|
|
|
|
|
|
|
|
612
|
|
|
|
|
|
|
This module is distributed in the hope that it will be useful, but it is provided “as is” and without any express or implied warranties. For details, see the full text of the license in the file LICENSE or at L. |
613
|
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
=head1 AUTHOR |
615
|
|
|
|
|
|
|
|
616
|
|
|
|
|
|
|
Ian Kluft |
617
|
|
|
|
|
|
|
|
618
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
619
|
|
|
|
|
|
|
|
620
|
|
|
|
|
|
|
This software is Copyright (c) 2022 by Ian Kluft. |
621
|
|
|
|
|
|
|
|
622
|
|
|
|
|
|
|
This is free software, licensed under: |
623
|
|
|
|
|
|
|
|
624
|
|
|
|
|
|
|
The Artistic License 2.0 (GPL Compatible) |
625
|
|
|
|
|
|
|
|
626
|
|
|
|
|
|
|
=cut |
627
|
|
|
|
|
|
|
|
628
|
|
|
|
|
|
|
__END__ |