File Coverage

lib/Illumos/SMF.pm
Criterion Covered Total %
statement 14 251 5.5
branch 3 106 2.8
condition 0 33 0.0
subroutine 3 24 12.5
pod 21 22 95.4
total 41 436 9.4


line stmt bran cond sub pod time code
1             package Illumos::SMF;
2              
3 1     1   1561 use strict;
  1         2  
  1         24  
4 1     1   4 use warnings;
  1         1  
  1         3084  
5              
6             # version
7             our $VERSION = '0.1.7';
8              
9             # commands
10             my $SVCS = '/usr/bin/svcs';
11             my $SVCCFG = '/usr/sbin/svccfg';
12             my $SVCADM = '/usr/sbin/svcadm';
13             my $ZLOGIN = '/usr/sbin/zlogin';
14              
15             # constructor
16             sub new {
17 2     2 0 1155 my $class = shift;
18 2         5 my $self = { @_ };
19              
20             # add Illumos::Zone instance if zone support is required
21 2 100       6 $self->{zonesupport} && do {
22 1         2 eval {
23 1         439 require Illumos::Zones;
24             };
25 1 50       4421 if ($@) {
26 0         0 die "ERROR: Unable to load package Illumos::Zones.";
27             }
28              
29 1         7 $self->{zone} = Illumos::Zones->new(debug => $self->{debug});
30             };
31            
32 2         16 return bless $self, $class
33             }
34             # private methods
35             my $svcAdm = sub {
36             my $self = shift;
37             my $cmd = shift;
38             my $fmri = shift;
39              
40             my @cmd = ($SVCADM, $cmd, $fmri);
41              
42             print STDERR '# ' . join(' ', @cmd) . "\n" if $self->{debug};
43             system(@cmd) and die "ERROR: cannot $cmd '$fmri'\n";
44             };
45              
46             my $zoneCmd = sub {
47             my $self = shift;
48             my $zoneName = shift;
49              
50             print STDERR "WARNING: zonename specified but 'zonesupport' not enabled for Illumos::SMF\n"
51             . "use 'Illumos::SMF(zonesupport => 1)' to enable zone support\n" if $zoneName && !$self->{zone};
52              
53             return { cmd => [], shellquote => q{"} } if !$zoneName || !$self->{zone};
54              
55             my $zone = $self->{zone}->listZone($zoneName, { requireSMF => 1 })
56             or die "ERROR: zone '$zoneName' does not exist or not support SMF (is the zone root dataset mounted?).\n";
57              
58             if ($zone->{state} eq 'running') {
59             return { cmd => [ $ZLOGIN, $zoneName ], shellquote => q{'"'} };
60             }
61             else {
62             return { cmd => [], zpath => $zone->{zonepath}, shellquote => q{"} };
63             }
64              
65             # just in case, should never reach here...
66             return { cmd => [], shellquote => q{"} };
67             };
68              
69             # public methods
70             sub refreshFMRI {
71 0     0 1   my $self = shift;
72 0           my $fmri = shift;
73 0   0       my $opts = $_[0] // {};
74            
75 0           my $zcmd = $self->$zoneCmd($opts->{zonename});
76 0           my @cmd = @{$zcmd->{cmd}};
  0            
77             local $ENV{SVCCFG_REPOSITORY} = $zcmd->{zpath}
78 0 0         . '/root/etc/svc/repository.db' if $zcmd->{zpath};
79              
80 0           push @cmd, ($SVCCFG, '-s', $fmri, 'refresh');
81              
82 0 0         print STDERR '# ' . join(' ', @cmd) . "\n" if $self->{debug};
83 0 0         system(@cmd) and die "ERROR: cannot refresh FMRI '$fmri'\n";
84            
85 0           return 1;
86             }
87              
88             sub listFMRI {
89 0     0 1   my $self = shift;
90 0           my $fmri = shift;
91 0   0       my $opts = $_[0] // {};
92 0           my @fmris;
93            
94 0           my $zcmd = $self->$zoneCmd($opts->{zonename});
95 0           my @cmd = @{$zcmd->{cmd}};
  0            
96             local $ENV{SVCCFG_REPOSITORY} = $zcmd->{zpath}
97 0 0         . '/root/etc/svc/repository.db' if $zcmd->{zpath};
98            
99 0   0       $fmri ||= '*';
100            
101             # remove leading 'svc:/'
102 0           $fmri =~ s/^svc:\///;
103              
104 0           my @cmd1 = (@cmd, $SVCCFG, 'list', $fmri);
105              
106 0 0         print STDERR '# ' . join(' ', @cmd1) . "\n" if $self->{debug};
107 0 0         open my $fmris, '-|', @cmd1
108             or die "ERROR: cannot get list of FMRI\n";
109              
110 0           while (my $elem = <$fmris>) {
111 0           chomp $elem;
112 0 0         push @fmris, "svc:/$elem" if !$opts->{instancesonly};
113            
114 0           my @cmd2 = (@cmd, $SVCCFG, '-s', $elem, 'list');
115              
116 0 0         open my $instances, '-|', @cmd2
117             or die "ERROR: cannot get instances of '$elem'\n";
118              
119 0           while (<$instances>) {
120 0           chomp;
121 0 0         next if /:properties/;
122 0           push @fmris, "svc:/$elem:$_";
123             }
124 0           close $instances;
125             }
126              
127 0           return [ @fmris ];
128             }
129              
130             sub fmriExists {
131 0     0 1   my $self = shift;
132 0           my $fmri = shift;
133 0           my $opts = shift;
134              
135             # remove instance name
136 0           my ($baseFmri) = $fmri =~ /^((?:svc:)?[^:]+)/;
137              
138 0           return grep { $fmri eq $_ } @{$self->listFMRI($baseFmri, $opts)};
  0            
  0            
139             }
140              
141             sub fmriState {
142 0     0 1   my $self = shift;
143 0           my $fmri = shift;
144 0           my $opts = shift;
145              
146 0 0         my @cmd = ($SVCS, $opts->{zonename} ? ('-z', $opts->{zonename}) : (), qw(-H -o state), $fmri);
147              
148 0 0         print STDERR '# ' . join(' ', @cmd) . "\n" if $self->{debug};
149 0 0         open my $fmris, '-|', @cmd
150             or die "ERROR: cannot get list of FMRI\n";
151              
152 0           chomp(my $state = <$fmris>);
153 0           return $state;
154             }
155              
156             sub fmriOnline {
157 0     0 1   my $self = shift;
158            
159 0           return $self->fmriState(shift, shift) eq 'online';
160             }
161              
162             sub enable {
163 0     0 1   my $self = shift;
164 0           my $fmri = shift;
165              
166 0           $self->$svcAdm('enable', $fmri);
167             }
168              
169             sub disable {
170 0     0 1   my $self = shift;
171 0           my $fmri = shift;
172              
173 0           $self->$svcAdm('disable', $fmri);
174             }
175              
176             sub restart {
177 0     0 1   my $self = shift;
178 0           my $fmri = shift;
179              
180 0           $self->$svcAdm('restart', $fmri);
181             }
182              
183             sub addFMRI {
184 0     0 1   my $self = shift;
185 0           my $fmri = shift;
186 0   0       my $opts = $_[0] // {};
187              
188 0           my $zcmd = $self->$zoneCmd($opts->{zonename});
189 0           my @cmd = @{$zcmd->{cmd}};
  0            
190             local $ENV{SVCCFG_REPOSITORY} = $zcmd->{zpath}
191 0 0         . '/root/etc/svc/repository.db' if $zcmd->{zpath};
192              
193             # remove leading 'svc:/'
194 0           $fmri =~ s/^svc:\///;
195              
196 0           push @cmd, ($SVCCFG, 'add', $fmri);
197              
198 0 0         print STDERR '# ' . join(' ', @cmd) . "\n" if $self->{debug};
199 0 0         system(@cmd) and die "ERROR: cannot add '$fmri'\n";
200             }
201              
202             sub deleteFMRI {
203 0     0 1   my $self = shift;
204 0           my $fmri = shift;
205 0   0       my $opts = $_[0] // {};
206              
207 0           my $zcmd = $self->$zoneCmd($opts->{zonename});
208 0           my @cmd = @{$zcmd->{cmd}};
  0            
209             local $ENV{SVCCFG_REPOSITORY} = $zcmd->{zpath}
210 0 0         . '/root/etc/svc/repository.db' if $zcmd->{zpath};
211              
212 0           push @cmd, ($SVCCFG, 'delete', $fmri);
213 0 0         print STDERR '# ' . join(' ', @cmd) . "\n" if $self->{debug};
214 0 0         system(@cmd) and die "ERROR: cannot delete $fmri\n";
215             }
216              
217             sub addInstance {
218 0     0 1   my $self = shift;
219 0           my $fmri = shift;
220 0           my $instance = shift;
221 0   0       my $opts = $_[0] // {};
222              
223 0           my $zcmd = $self->$zoneCmd($opts->{zonename});
224 0           my @cmd = @{$zcmd->{cmd}};
  0            
225             local $ENV{SVCCFG_REPOSITORY} = $zcmd->{zpath}
226 0 0         . '/root/etc/svc/repository.db' if $zcmd->{zpath};
227              
228 0           push @cmd, ($SVCCFG, '-s', $fmri, 'add', $instance);
229 0 0         print STDERR '# ' . join(' ', @cmd) . "\n" if $self->{debug};
230 0 0         system(@cmd) and die "ERROR: cannot add instance '$instance' to $fmri\n";
231              
232 0           $self->addPropertyGroup("$fmri:$instance", 'general', 'framework', $opts);
233 0           $self->setProperty("$fmri:$instance", 'general/complete', $instance, undef, $opts);
234             $self->setProperty("$fmri:$instance", 'general/enabled',
235 0 0         $opts->{enabled} ? 'true' : 'false', undef, $opts);
236             }
237              
238             sub getPropertyGroups {
239 0     0 1   my $self = shift;
240 0           my $fmri = shift;
241 0   0       my $opts = $_[0] // {};
242              
243 0           my $zcmd = $self->$zoneCmd($opts->{zonename});
244 0           my @cmd = @{$zcmd->{cmd}};
  0            
245             local $ENV{SVCCFG_REPOSITORY} = $zcmd->{zpath}
246 0 0         . '/root/etc/svc/repository.db' if $zcmd->{zpath};
247              
248 0           my $pg = [];
249 0           push @cmd, ($SVCCFG, '-s', $fmri, 'listpg');
250              
251 0 0         print STDERR '# ' . join(' ', @cmd) . "\n" if $self->{debug};
252 0 0         open my $props, '-|', @cmd
253             or die "ERROR: cannot get property group of FMRI '$fmri'\n";
254              
255 0           while (my $prop = <$props>){
256 0           chomp $prop;
257 0           my ($name, $type) = split /\s+/, $prop, 2;
258 0           push @$pg, $name;
259             }
260            
261 0           return $pg;
262             }
263              
264             sub propertyExists {
265 0     0 1   my $self = shift;
266 0           my $fmri = shift;
267 0           my $property = shift;
268 0           my $opts = shift;
269            
270             # extract property group
271 0           my ($pg) = $property =~ /^([^\/]+)/;
272              
273 0           return grep { $property eq $_ } keys %{$self->getProperties($fmri, $pg, $opts)};
  0            
  0            
274             }
275              
276             sub propertyGroupExists {
277 0     0 1   my $self = shift;
278 0           my $fmri = shift;
279 0           my $pg = shift;
280 0           my $opts = shift;
281              
282 0           return grep { $pg eq $_ } @{$self->getPropertyGroups($fmri, $opts)};
  0            
  0            
283             }
284              
285             sub addPropertyGroup {
286 0     0 1   my $self = shift;
287 0           my $fmri = shift;
288 0           my $pg = shift;
289 0           my $type = shift;
290 0   0       my $opts = $_[0] // {};
291            
292 0           my $zcmd = $self->$zoneCmd($opts->{zonename});
293 0           my @cmd = @{$zcmd->{cmd}};
  0            
294             local $ENV{SVCCFG_REPOSITORY} = $zcmd->{zpath}
295 0 0         . '/root/etc/svc/repository.db' if $zcmd->{zpath};
296            
297             # set type to application if not specified
298 0   0       $type //= 'application';
299              
300 0 0         return if $self->propertyGroupExists($fmri, $pg, $opts);
301              
302 0           push @cmd, ($SVCCFG, '-s', $fmri, 'addpg', $pg, $type);
303 0 0         print STDERR '# ' . join(' ', @cmd) . "\n" if $self->{debug};
304 0 0         system(@cmd) and die "ERROR: cannot add property group to $fmri\n";
305             }
306              
307             sub deletePropertyGroup {
308 0     0 1   my $self = shift;
309 0           my $fmri = shift;
310 0           my $pg = shift;
311 0   0       my $opts = $_[0] // {};
312            
313 0           my $zcmd = $self->$zoneCmd($opts->{zonename});
314 0           my @cmd = @{$zcmd->{cmd}};
  0            
315             local $ENV{SVCCFG_REPOSITORY} = $zcmd->{zpath}
316 0 0         . '/root/etc/svc/repository.db' if $zcmd->{zpath};
317            
318 0           push @cmd, ($SVCCFG, '-s', $fmri, 'delpg', $pg);
319 0 0         print STDERR '# ' . join(' ', @cmd) . "\n" if $self->{debug};
320 0 0         system(@cmd) and die "ERROR: cannot delete property group from $fmri\n";
321             }
322              
323             sub setProperty {
324 0     0 1   my $self = shift;
325 0           my $fmri = shift;
326 0           my $property = shift;
327 0           my $value = shift;
328 0           my $type = shift;
329 0   0       my $opts = $_[0] // {};
330            
331 0           my $zcmd = $self->$zoneCmd($opts->{zonename});
332 0           my @cmd = @{$zcmd->{cmd}};
  0            
333             local $ENV{SVCCFG_REPOSITORY} = $zcmd->{zpath}
334 0 0         . '/root/etc/svc/repository.db' if $zcmd->{zpath};
335              
336             # guess property type if not provided
337 0 0         $type || do {
338 0           $type = 'astring';
339              
340 0           for ($value){
341 0 0         /^\d+$/ && do {
342 0           $type = 'count';
343 0           last;
344             };
345              
346 0 0         /^(?:true|false)$/i && do {
347 0           $type = 'boolean';
348 0           last;
349             };
350             }
351             };
352              
353             push @cmd, $self->propertyExists($fmri, $property, $opts) ?
354             ($SVCCFG, '-s', $fmri, 'setprop', $property, '=',
355             $zcmd->{shellquote} . $value . $zcmd->{shellquote})
356             : ($SVCCFG, '-s', $fmri, 'addpropvalue', $property, "$type:",
357 0 0         $zcmd->{shellquote} . $value . $zcmd->{shellquote});
358 0 0         print STDERR '# ' . join(' ', @cmd) . "\n" if $self->{debug};
359 0 0         system(@cmd) and die "ERROR: cannot set property $property of $fmri\n";
360             }
361              
362             sub setProperties {
363 0     0 1   my $self = shift;
364 0           my $fmri = shift;
365 0           my $properties = shift;
366 0           my $opts = shift;
367              
368 0           for my $key (keys %$properties){
369 0           $self->setProperty($fmri, $key, $properties->{$key}, undef, $opts)
370             }
371             }
372              
373             sub getProperties {
374 0     0 1   my $self = shift;
375 0           my $fmri = shift;
376 0           my $pg = shift;
377 0   0       my $opts = $_[0] // {};
378            
379 0           my $zcmd = $self->$zoneCmd($opts->{zonename});
380 0           my @cmd = @{$zcmd->{cmd}};
  0            
381             local $ENV{SVCCFG_REPOSITORY} = $zcmd->{zpath}
382 0 0         . '/root/etc/svc/repository.db' if $zcmd->{zpath};
383              
384 0           my $properties = {};
385              
386 0           push @cmd, ($SVCCFG, '-s', $fmri, 'listprop', $pg);
387              
388 0 0         print STDERR '# ' . join(' ', @cmd) . "\n" if $self->{debug};
389 0 0         open my $props, '-|', @cmd
390             or die "ERROR: cannot get properties of FMRI '$fmri'\n";
391              
392 0           while (<$props>){
393 0           chomp;
394 0           my ($name, $type, $value) = split /\s+/, $_, 3;
395 0 0         next if $name eq $pg;
396             #remove quotes
397 0           $value =~ s/^"|"$//g;
398 0           $properties->{$name} = $value;
399              
400             }
401            
402 0           return $properties;
403             }
404              
405             sub setFMRIProperties {
406 0     0 1   my $self = shift;
407 0           my $fmri = shift;
408 0           my $properties = shift;
409 0   0       my $opts = $_[0] // {};
410            
411 0 0         $self->addFMRI($fmri, $opts) if !$self->fmriExists($fmri, $opts);
412             # extract property groups
413 0 0         my @pg = map { $properties->{$_}->{members} ? $_ : () } keys %$properties;
  0            
414              
415 0           for my $pg (@pg) {
416 0           $self->addPropertyGroup($fmri, $pg, $properties->{$pg}->{type}, $opts);
417 0           for my $prop (keys %{$properties->{$pg}->{members}}) {
  0            
418             $self->setProperty($fmri, "$pg/$prop",
419             $properties->{$pg}->{members}->{$prop}->{value},
420             $properties->{$pg}->{members}->{$prop}->{type},
421 0           $opts);
422             }
423 0           delete $properties->{$pg};
424             }
425              
426 0           for my $prop (keys %$properties) {
427             $self->setProperty($fmri, $prop,
428             $properties->{$prop}->{value},
429             $properties->{$prop}->{type},
430 0           $opts);
431             }
432             }
433              
434             sub getFMRIProperties {
435 0     0 1   my $self = shift;
436 0           my $fmri = shift;
437 0   0       my $opts = $_[0] // {};
438            
439 0           my $zcmd = $self->$zoneCmd($opts->{zonename});
440 0           my @cmd = @{$zcmd->{cmd}};
  0            
441             local $ENV{SVCCFG_REPOSITORY} = $zcmd->{zpath}
442 0 0         . '/root/etc/svc/repository.db' if $zcmd->{zpath};
443              
444 0           my $properties = {};
445              
446 0           push @cmd, ($SVCCFG, '-s', $fmri, 'listprop');
447              
448 0 0         print STDERR '# ' . join(' ', @cmd) . "\n" if $self->{debug};
449 0 0         open my $props, '-|', @cmd
450             or die "ERROR: cannot get properties of FMRI\n";
451              
452 0           while (<$props>) {
453 0           chomp;
454 0           my ($pg, $prop, $type, $value) = /^(?:([-\w]+)\/)?([-\w]+)\s+([-\w]+)(?:\s+(.+))?$/;
455 0 0 0       next if !$prop || !$type;
456             # remove quotes from $value
457 0 0         $value =~ s/^"|"$//g if $value;
458 0 0         if ($pg) {
459 0           $properties->{$pg}->{members}->{$prop}->{type} = $type;
460 0           $properties->{$pg}->{members}->{$prop}->{value} = $value;
461             }
462             else {
463 0           $properties->{$prop}->{type} = $type;
464 0   0       $properties->{$prop}->{value} = $value // '';
465             }
466             }
467              
468 0           return $properties;
469             }
470              
471             1;
472              
473             __END__