| #!/usr/bin/perl -w |
| ############################################################################### |
| # $Id$ |
| ############################################################################### |
| # Licensed to the Apache Software Foundation (ASF) under one or more |
| # contributor license agreements. See the NOTICE file distributed with |
| # this work for additional information regarding copyright ownership. |
| # The ASF licenses this file to You under the Apache License, Version 2.0 |
| # (the "License"); you may not use this file except in compliance with |
| # the License. You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| ############################################################################### |
| |
| =head1 NAME |
| |
| VCL::Module::OS::Linux::init::systemd.pm |
| |
| =head1 DESCRIPTION |
| |
| This module provides VCL support for the systemd Linux init daemon used in |
| distributions such as: |
| Fedora 15+ |
| openSUSE 12.1+ |
| |
| =cut |
| |
| ############################################################################### |
| package VCL::Module::OS::Linux::init::systemd; |
| |
| # Specify the lib path using FindBin |
| use FindBin; |
| use lib "$FindBin::Bin/../../../../.."; |
| |
| # Configure inheritance |
| use base qw(VCL::Module::OS::Linux::init); |
| |
| # Specify the version of this module |
| our $VERSION = '2.5'; |
| |
| # Specify the version of Perl to use |
| use 5.008000; |
| |
| use strict; |
| use warnings; |
| use diagnostics; |
| |
| use VCL::utils; |
| |
| ############################################################################### |
| |
| =head1 CLASS VARIABLES |
| |
| =cut |
| |
| =head2 $INIT_DAEMON_ORDER |
| |
| Data type : integer |
| Value : 20 |
| Description : Determines the order in which Linux init daemon modules are used. |
| Lower values are used first. |
| |
| =cut |
| |
| our $INIT_DAEMON_ORDER = 20; |
| |
| =head2 @REQUIRED_COMMANDS |
| |
| Data type : array |
| Values : systemctl |
| Description : List of commands used within this module to configure and control |
| systemd services. This module will not be used if any of these |
| commands are unavailable on the computer. |
| |
| =cut |
| |
| our @REQUIRED_COMMANDS = ('systemctl'); |
| |
| ############################################################################### |
| |
| =head1 OBJECT METHODS |
| |
| =cut |
| |
| #////////////////////////////////////////////////////////////////////////////// |
| |
| =head2 get_service_names |
| |
| Parameters : none |
| Returns : array |
| Description : Calls 'systemctl list-unit-files' to retrieve the list of |
| services controlled by systemd on the computer. |
| |
| =cut |
| |
| sub get_service_names { |
| my $self = shift; |
| if (ref($self) !~ /linux/i) { |
| notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); |
| return; |
| } |
| |
| my $computer_node_name = $self->data->get_computer_node_name(); |
| |
| my $command = "systemctl --no-pager list-unit-files"; |
| my ($exit_status, $output) = $self->os->execute($command, 0); |
| if (!defined($output)) { |
| notify($ERRORS{'WARNING'}, 0, "failed to execute command to retrieve systemd service names on $computer_node_name"); |
| return; |
| } |
| |
| # Format of systemctl list output lines: |
| # ssyslog.target static |
| # Add to hash then extract keys to remove duplicates |
| my %service_name_hash; |
| for my $line (@$output) { |
| my ($service_name) = $line =~ /^(.+)\.service/; |
| $service_name_hash{$service_name} = 1 if $service_name; |
| } |
| my @service_names = sort(keys %service_name_hash); |
| notify($ERRORS{'DEBUG'}, 0, "retrieved systemd service names from $computer_node_name: " . join(", ", @service_names)); |
| return @service_names; |
| } |
| |
| #////////////////////////////////////////////////////////////////////////////// |
| |
| =head2 service_running |
| |
| Parameters : $service_name |
| Returns : boolean |
| Description : Calls 'systemctl is-active' to determines if a service is |
| running. |
| |
| =cut |
| |
| sub service_running { |
| my $self = shift; |
| if (ref($self) !~ /linux/i) { |
| notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); |
| return; |
| } |
| |
| my $service_name = shift; |
| if (!$service_name) { |
| notify($ERRORS{'WARNING'}, 0, "service name argument was not supplied"); |
| return; |
| } |
| |
| my $computer_node_name = $self->data->get_computer_node_name(); |
| |
| my $command = "systemctl is-active $service_name.service"; |
| my ($exit_status, $output) = $self->os->execute($command, 0); |
| if (!defined($output)) { |
| notify($ERRORS{'WARNING'}, 0, "failed to execute command to determine if $service_name service is running on $computer_node_name"); |
| return; |
| } |
| |
| # Output should either be 'active' or 'inactive |
| if (grep(/inactive/, @$output)) { |
| notify($ERRORS{'DEBUG'}, 0, "$service_name service is not running on $computer_node_name"); |
| return 0; |
| } |
| elsif (grep(/active/, @$output)) { |
| notify($ERRORS{'DEBUG'}, 0, "$service_name service is running on $computer_node_name"); |
| return 1; |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "failed to determine if $service_name service is running on $computer_node_name, output does not contain 'active' or 'inactive':\n" . join("\n", @$output)); |
| return; |
| } |
| } |
| |
| #////////////////////////////////////////////////////////////////////////////// |
| |
| =head2 service_enabled |
| |
| Parameters : $service_name |
| Returns : boolean |
| Description : Calls 'systemctl is-enabled' to determines if a service is |
| enabled. |
| |
| =cut |
| |
| sub service_enabled { |
| my $self = shift; |
| if (ref($self) !~ /linux/i) { |
| notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); |
| return; |
| } |
| |
| my $service_name = shift; |
| if (!$service_name) { |
| notify($ERRORS{'WARNING'}, 0, "service name argument was not supplied"); |
| return; |
| } |
| |
| my $computer_node_name = $self->data->get_computer_node_name(); |
| |
| my $command = "systemctl is-enabled $service_name.service"; |
| my ($exit_status, $output) = $self->os->execute($command, 0); |
| if (!defined($output)) { |
| notify($ERRORS{'WARNING'}, 0, "failed to execute command to determine if $service_name service is running on $computer_node_name"); |
| return; |
| } |
| |
| # Output should either be 'enabled' or 'disabled |
| if (grep(/disabled/, @$output)) { |
| notify($ERRORS{'DEBUG'}, 0, "$service_name service is disabled on $computer_node_name"); |
| return 0; |
| } |
| elsif (grep(/enabled/, @$output)) { |
| notify($ERRORS{'DEBUG'}, 0, "$service_name service is enabled on $computer_node_name"); |
| return 1; |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "failed to determine if $service_name service is enabled on $computer_node_name, output does not contain 'enabled' or 'disabled':\n" . join("\n", @$output)); |
| return; |
| } |
| } |
| |
| #////////////////////////////////////////////////////////////////////////////// |
| |
| =head2 enable_service |
| |
| Parameters : $service_name |
| Returns : boolean |
| Description : Calls 'systemctl enable' to enable the service specified by the |
| argument. |
| |
| =cut |
| |
| sub enable_service { |
| my $self = shift; |
| if (ref($self) !~ /VCL::Module/i) { |
| notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); |
| return 0; |
| } |
| |
| my $service_name = shift; |
| if (!$service_name) { |
| notify($ERRORS{'WARNING'}, 0, "service name argument was not supplied"); |
| return; |
| } |
| |
| my $computer_node_name = $self->data->get_computer_node_name(); |
| |
| # Enable the service |
| my $command = "systemctl --no-reload enable $service_name.service"; |
| my ($exit_status, $output) = $self->os->execute($command, 0); |
| if (!defined($output)) { |
| notify($ERRORS{'WARNING'}, 0, "failed to execute command to enable '$service_name' service on $computer_node_name: $command"); |
| return; |
| } |
| elsif (grep(/(Failed to issue method call|No such file)/i, @$output)) { |
| # Output if the service doesn't exist: 'Failed to issue method call: No such file or directory' |
| notify($ERRORS{'WARNING'}, 0, "unable to enable '$service_name' service because it does not exist on $computer_node_name"); |
| return; |
| } |
| elsif ($exit_status ne 0 || grep(/(failed)/i, @$output)) { |
| notify($ERRORS{'WARNING'}, 0, "failed to enable '$service_name' service on $computer_node_name, exit status: $exit_status, command:\n$command\noutput:\n" . join("\n", @$output)); |
| return; |
| } |
| else { |
| notify($ERRORS{'DEBUG'}, 0, "enabled '$service_name' service on $computer_node_name"); |
| } |
| |
| return 1; |
| } |
| |
| #////////////////////////////////////////////////////////////////////////////// |
| |
| =head2 disable_service |
| |
| Parameters : $service_name |
| Returns : boolean |
| Description : Calls 'systemctl disable' to disable the service specified by the |
| argument. |
| |
| =cut |
| |
| sub disable_service { |
| my $self = shift; |
| if (ref($self) !~ /VCL::Module/i) { |
| notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); |
| return 0; |
| } |
| |
| my $service_name = shift; |
| if (!$service_name) { |
| notify($ERRORS{'WARNING'}, 0, "service name argument was not supplied"); |
| return; |
| } |
| |
| my $computer_node_name = $self->data->get_computer_node_name(); |
| |
| my $command = "systemctl --no-reload disable $service_name.service"; |
| my ($exit_status, $output) = $self->os->execute($command, 0); |
| if (!defined($output)) { |
| notify($ERRORS{'WARNING'}, 0, "failed to execute command to disable '$service_name' service on $computer_node_name: $command"); |
| return; |
| } |
| elsif (grep(/(Failed to issue method call|No such file)/i, @$output)) { |
| # Output if the service doesn't exist: 'Failed to issue method call: No such file or directory' |
| notify($ERRORS{'WARNING'}, 0, "unable to disable '$service_name' service because it does not exist on $computer_node_name"); |
| return; |
| } |
| elsif ($exit_status ne 0 || grep(/(failed)/i, @$output)) { |
| notify($ERRORS{'WARNING'}, 0, "failed to disable '$service_name' service on $computer_node_name, exit status: $exit_status, output:\n" . join("\n", @$output)); |
| return; |
| } |
| else { |
| notify($ERRORS{'DEBUG'}, 0, "disabled '$service_name' service on $computer_node_name"); |
| } |
| |
| return 1; |
| } |
| |
| #////////////////////////////////////////////////////////////////////////////// |
| |
| =head2 delete_service |
| |
| Parameters : $service_name |
| Returns : boolean |
| Description : Disables the service and deletes the service file from |
| /lib/systemd/system/. |
| |
| =cut |
| |
| sub delete_service { |
| my $self = shift; |
| if (ref($self) !~ /VCL::Module/i) { |
| notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); |
| return 0; |
| } |
| |
| my $service_name = shift; |
| if (!$service_name) { |
| notify($ERRORS{'WARNING'}, 0, "service name argument was not supplied"); |
| return; |
| } |
| |
| my $computer_node_name = $self->data->get_computer_node_name(); |
| |
| # Disable the service before deleting it |
| $self->stop_service($service_name) || return; |
| $self->disable_service($service_name) || return; |
| |
| # Delete the service configuration file |
| my $service_file_path = "/lib/systemd/system/$service_name.service"; |
| if (!$self->os->delete_file($service_file_path)) { |
| return; |
| } |
| |
| $self->_daemon_reload(); |
| |
| return 1; |
| } |
| |
| #////////////////////////////////////////////////////////////////////////////// |
| |
| =head2 start_service |
| |
| Parameters : $service_name |
| Returns : boolean |
| Description : Calls 'systemctl start' to start the service specified by the |
| argument. |
| |
| =cut |
| |
| sub start_service { |
| my $self = shift; |
| if (ref($self) !~ /VCL::Module/i) { |
| notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); |
| return 0; |
| } |
| |
| my $service_name = shift; |
| if (!$service_name) { |
| notify($ERRORS{'WARNING'}, 0, "service name argument was not supplied"); |
| return; |
| } |
| |
| my $computer_node_name = $self->data->get_computer_node_name(); |
| |
| # start the service |
| my $command = "systemctl start $service_name.service"; |
| my ($exit_status, $output) = $self->os->execute($command, 0); |
| if (!defined($output)) { |
| notify($ERRORS{'WARNING'}, 0, "failed to execute command to start '$service_name' service on $computer_node_name: $command"); |
| return; |
| } |
| elsif (grep(/(Failed to issue method call|No such file)/i, @$output)) { |
| # Output if the service doesn't exist |
| # Failed to issue method call: Unit httpdx.service failed to load: No such file or directory. |
| notify($ERRORS{'WARNING'}, 0, "unable to start '$service_name' service because it does not exist on $computer_node_name"); |
| return; |
| } |
| elsif ($exit_status ne 0 || grep(/(failed)/i, @$output)) { |
| notify($ERRORS{'WARNING'}, 0, "failed to start '$service_name' service on $computer_node_name, exit status: $exit_status, output:\n" . join("\n", @$output)); |
| return; |
| } |
| else { |
| notify($ERRORS{'DEBUG'}, 0, "started '$service_name' service on $computer_node_name"); |
| } |
| |
| return 1; |
| } |
| |
| #////////////////////////////////////////////////////////////////////////////// |
| |
| =head2 stop_service |
| |
| Parameters : $service_name |
| Returns : boolean |
| Description : Calls 'systemctl stop' to stop the service specified by the |
| argument. |
| |
| =cut |
| |
| sub stop_service { |
| my $self = shift; |
| if (ref($self) !~ /VCL::Module/i) { |
| notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); |
| return 0; |
| } |
| |
| my $service_name = shift; |
| if (!$service_name) { |
| notify($ERRORS{'WARNING'}, 0, "service name argument was not supplied"); |
| return; |
| } |
| |
| my $computer_node_name = $self->data->get_computer_node_name(); |
| |
| # stop the service |
| my $command = "systemctl stop $service_name.service"; |
| my ($exit_status, $output) = $self->os->execute($command, 0); |
| if (!defined($output)) { |
| notify($ERRORS{'WARNING'}, 0, "failed to execute command to stop '$service_name' service on $computer_node_name: $command"); |
| return; |
| } |
| elsif (grep(/(Failed to issue method call|No such file)/i, @$output)) { |
| # Output if the service doesn't exist |
| # Failed to issue method call: Unit httpdx.service failed to load: No such file or directory. |
| notify($ERRORS{'DEBUG'}, 0, "unable to stop '$service_name' service because it does not exist on $computer_node_name"); |
| return 1; |
| } |
| elsif (grep(/(not loaded)/i, @$output)) { |
| # Output if the service isn't loaded |
| # Failed to stop ext_ssh.service: Unit ext_ssh.service not loaded. |
| notify($ERRORS{'DEBUG'}, 0, "unable to stop '$service_name' service because it is not loaded $computer_node_name"); |
| return 1; |
| } |
| elsif ($exit_status ne 0 || grep(/(failed)/i, @$output)) { |
| notify($ERRORS{'WARNING'}, 0, "failed to stop '$service_name' service on $computer_node_name, exit status: $exit_status, output:\n" . join("\n", @$output)); |
| return; |
| } |
| else { |
| notify($ERRORS{'DEBUG'}, 0, "stopped '$service_name' service on $computer_node_name"); |
| } |
| |
| return 1; |
| } |
| |
| #////////////////////////////////////////////////////////////////////////////// |
| |
| =head2 restart_service |
| |
| Parameters : $service_name |
| Returns : boolean |
| Description : Calls 'systemctl restart' to restart the service specified by the |
| argument. |
| |
| =cut |
| |
| sub restart_service { |
| my $self = shift; |
| if (ref($self) !~ /VCL::Module/i) { |
| notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); |
| return 0; |
| } |
| |
| my $service_name = shift; |
| if (!$service_name) { |
| notify($ERRORS{'WARNING'}, 0, "service name argument was not supplied"); |
| return; |
| } |
| |
| my $computer_node_name = $self->data->get_computer_node_name(); |
| |
| # Restart the service |
| my $command = "systemctl restart $service_name.service"; |
| my ($exit_status, $output) = $self->os->execute($command, 0); |
| if (!defined($output)) { |
| notify($ERRORS{'WARNING'}, 0, "failed to execute command to restart '$service_name' service on $computer_node_name: $command"); |
| return; |
| } |
| elsif (grep(/(Failed to issue method call|No such file)/i, @$output)) { |
| # Output if the service doesn't exist |
| # Failed to issue method call: Unit httpdx.service failed to load: No such file or directory. |
| notify($ERRORS{'WARNING'}, 0, "unable to restart '$service_name' service because it does not exist on $computer_node_name"); |
| return; |
| } |
| elsif ($exit_status ne 0 || grep(/(failed)/i, @$output)) { |
| notify($ERRORS{'WARNING'}, 0, "failed to restart '$service_name' service on $computer_node_name, exit status: $exit_status, output:\n" . join("\n", @$output)); |
| return; |
| } |
| else { |
| notify($ERRORS{'DEBUG'}, 0, "restarted '$service_name' service on $computer_node_name"); |
| } |
| |
| return 1; |
| } |
| |
| #////////////////////////////////////////////////////////////////////////////// |
| |
| =head2 add_ext_sshd_service |
| |
| Parameters : none |
| Returns : boolean |
| Description : Constructs the ext_sshd service configuration file: |
| /lib/systemd/system/ext_sshd.service |
| |
| =cut |
| |
| sub add_ext_sshd_service { |
| my $self = shift; |
| if (ref($self) !~ /linux/i) { |
| notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); |
| return 0; |
| } |
| |
| my $computer_node_name = $self->data->get_computer_node_name(); |
| |
| # Get the unit file path for the sshd service |
| # Do not automatically assume it is /lib/systemd/system/sshd.service |
| # https://issues.apache.org/jira/browse/VCL-989 |
| my $sshd_service_file_path = $self->_get_service_unit_file_path('sshd'); |
| if (!$sshd_service_file_path) { |
| $sshd_service_file_path = '/lib/systemd/system/sshd.service'; |
| } |
| |
| # Hard-code the ext_sshd file path (intentional) |
| my $ext_sshd_service_file_path = '/lib/systemd/system/ext_sshd.service'; |
| |
| # Get the contents of the sshd service configuration file already on the computer |
| my @sshd_service_file_contents = $self->os->get_file_contents($sshd_service_file_path); |
| if (!@sshd_service_file_contents) { |
| notify($ERRORS{'WARNING'}, 0, "failed to retrieve contents of $sshd_service_file_path from $computer_node_name"); |
| return; |
| } |
| |
| my $ext_sshd_service_file_contents = join("\n", @sshd_service_file_contents); |
| |
| # Replace: OpenSSH --> External OpenSSH |
| $ext_sshd_service_file_contents =~ s|(OpenSSH)|external $1|g; |
| |
| # Replace: sshd --> ext_sshd, exceptions: |
| # /bin/sshd |
| # /sshd_config |
| $ext_sshd_service_file_contents =~ s|(?<!bin/)sshd(?!_config\|-keygen)|ext_sshd|g; |
| |
| # Remove ExecStart options variables |
| # ExecStart=/usr/sbin/sshd -D $OPTIONS |
| # ExecStart=/usr/sbin/sshd -D $SSHD_OPTS |
| $ext_sshd_service_file_contents =~ s/^\s*(ExecStart=.+\S)\s+\$\S*OPT\S*(.*)$/$1$2/gm; |
| |
| # Remove explicit -f arguments from ExecStart line |
| $ext_sshd_service_file_contents =~ s/^\s*(ExecStart=.+\S)\s+-f\s+\S+(.*)$/$1$2/gm; |
| |
| # Add -f argument to ExecStart line |
| $ext_sshd_service_file_contents =~ s|^\s*(ExecStart=.+\S)\s*$|$1 -f /etc/ssh/external_sshd_config|gm; |
| |
| # Set EnvironmentFile to /dev/null, service won't start if the file doesn't exist |
| $ext_sshd_service_file_contents =~ s|(EnvironmentFile)=.*|$1=/dev/null|g; |
| |
| # Remove Alias= line which may exist in ssh_config: |
| # Alias=ext_sshd.service |
| # Otherwise, this may occur when attempting to enable the service if the service is named the same as the alias: |
| # Failed to execute operation: Too many levels of symbolic links |
| $ext_sshd_service_file_contents =~ s/^\s*Alias=.*//gm; |
| |
| # Add explicit lines, remove first to avoid duplicates: |
| $ext_sshd_service_file_contents =~ s/^\s*(Restart|RestartSec|StartLimitInterval)=.*\n?//gm; |
| |
| # Attempt to restart if the service dies |
| $ext_sshd_service_file_contents =~ s/(\[Service\])/$1\nRestart=on-failure/gm; |
| $ext_sshd_service_file_contents =~ s/(\[Service\])/$1\nRestartSec=3s/gm; |
| |
| # (VCL-1027) Add StartLimitInterval=0 under [Service] to prevent: |
| # Job for ext_sshd.service failed because start of the service was attempted too often |
| $ext_sshd_service_file_contents =~ s/(\[Service\])/$1\nStartLimitInterval=0/gm; |
| |
| notify($ERRORS{'DEBUG'}, 0, "$ext_sshd_service_file_path:\n$ext_sshd_service_file_contents"); |
| |
| if (!$self->os->create_text_file($ext_sshd_service_file_path, $ext_sshd_service_file_contents)) { |
| notify($ERRORS{'WARNING'}, 0, "failed to create ext_sshd service file on $computer_node_name: $ext_sshd_service_file_path"); |
| return; |
| } |
| |
| if (!$self->os->set_file_permissions($ext_sshd_service_file_path, '644')) { |
| notify($ERRORS{'WARNING'}, 0, "failed to set permissions of ext_sshd service file to 644 on $computer_node_name: $ext_sshd_service_file_path"); |
| return; |
| } |
| |
| $self->_daemon_reload(); |
| |
| return $self->enable_service('ext_sshd'); |
| } |
| |
| #////////////////////////////////////////////////////////////////////////////// |
| |
| =head2 _daemon_reload |
| |
| Parameters : none |
| Returns : boolean |
| Description : Runs 'systemctl --system daemon-reload'. This is necessary when |
| adding or deleting services or else systemctl will complain: |
| Warning: Unit file changed on disk, 'systemctl --system daemon-reload' recommended. |
| |
| =cut |
| |
| sub _daemon_reload { |
| my $self = shift; |
| if (ref($self) !~ /linux/i) { |
| notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); |
| return 0; |
| } |
| |
| my $computer_node_name = $self->data->get_computer_node_name(); |
| |
| my $command = "systemctl --system daemon-reload"; |
| my ($exit_status, $output) = $self->os->execute($command, 0); |
| if (!defined($output)) { |
| notify($ERRORS{'WARNING'}, 0, "failed to execute command to reload systemd manager configuration on $computer_node_name: $command"); |
| return; |
| } |
| elsif ($exit_status ne 0 || grep(/(failed)/i, @$output)) { |
| notify($ERRORS{'WARNING'}, 0, "failed to reload systemd manager configuration on $computer_node_name, exit status: $exit_status, output:\n" . join("\n", @$output)); |
| return; |
| } |
| else { |
| notify($ERRORS{'DEBUG'}, 0, "reloaded systemd manager configuration on $computer_node_name"); |
| } |
| return 1; |
| } |
| |
| #////////////////////////////////////////////////////////////////////////////// |
| |
| =head2 _get_service_unit_file_path |
| |
| Parameters : $service_name |
| Returns : string |
| Description : Determines the unit file for the service specified by the |
| argument. This is needed because the file name is not always |
| $service_name.service. This is the case when a service has alias |
| names configured such as the ssh and sshd services on Ubuntu 16. |
| The file path for the sshd service is ssh.service. |
| |
| =cut |
| |
| sub _get_service_unit_file_path { |
| my $self = shift; |
| if (ref($self) !~ /VCL::/i) { |
| notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); |
| return 0; |
| } |
| |
| my $service_name = shift; |
| if (!$service_name) { |
| notify($ERRORS{'WARNING'}, 0, "service name argument was not supplied"); |
| return; |
| } |
| |
| my $computer_node_name = $self->data->get_computer_node_name(); |
| |
| my $command = "systemctl show $service_name.service --property=FragmentPath"; |
| my ($exit_status, $output) = $self->os->execute($command, 0); |
| if (!defined($output)) { |
| notify($ERRORS{'WARNING'}, 0, "failed to retrieve unit file path for $service_name service on $computer_node_name: $command"); |
| return; |
| } |
| elsif ($exit_status ne 0 || grep(/(failed)/i, @$output)) { |
| notify($ERRORS{'WARNING'}, 0, "failed to retrieve unit file path for $service_name service on $computer_node_name, exit status: $exit_status, output:\n" . join("\n", @$output)); |
| return; |
| } |
| |
| # Expected output: |
| # FragmentPath=/lib/systemd/system/ssh.service |
| my ($file_path_line) = grep(/FragmentPath=/, @$output); |
| if (!$file_path_line) { |
| notify($ERRORS{'WARNING'}, 0, "failed to retrieve unit file path for $service_name service on $computer_node_name, output does not contain a 'FragmentPath=' line, output:\n" . join("\n", @$output)); |
| return; |
| } |
| |
| my ($file_path) = $file_path_line =~ /FragmentPath=(.+)\s*$/g; |
| if (!$file_path) { |
| notify($ERRORS{'WARNING'}, 0, "failed to retrieve unit file path for $service_name service on $computer_node_name, failed to parse 'FragmentPath=' line, output:\n" . join("\n", @$output)); |
| return; |
| } |
| |
| notify($ERRORS{'DEBUG'}, 0, "retrieved unit file path for $service_name service on $computer_node_name: $file_path"); |
| return $file_path |
| } |
| |
| #////////////////////////////////////////////////////////////////////////////// |
| |
| 1; |
| __END__ |
| |
| =head1 SEE ALSO |
| |
| L<http://cwiki.apache.org/VCL/> |
| |
| =cut |