| #!/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::Upstart.pm |
| |
| =head1 DESCRIPTION |
| |
| This module provides VCL support for the Upstart Linux init daemon used in |
| distributions including: |
| Ubuntu 6.10+ |
| |
| =cut |
| |
| ############################################################################### |
| package VCL::Module::OS::Linux::init::Upstart; |
| |
| # 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 : 10 |
| Description : Determines the order in which Linux init daemon modules are used. |
| Lower values are used first. |
| |
| =cut |
| |
| our $INIT_DAEMON_ORDER = 10; |
| |
| =head2 @REQUIRED_COMMANDS |
| |
| Data type : array |
| Values : initctl |
| Description : List of commands used within this module to configure and control |
| Upstart services. This module will not be used if any of these |
| commands are unavailable on the computer. |
| |
| =cut |
| |
| our @REQUIRED_COMMANDS = ('initctl'); |
| |
| =head2 @PROHIBITED_COMMANDS |
| |
| Data type : array |
| Values : initctl |
| Description : List of commands that must not exist on the computer if the |
| Upstart.pm module is to be used. This array contains: |
| 'chkconfig'. |
| |
| =cut |
| |
| our @PROHIBITED_COMMANDS = ('chkconfig'); |
| |
| =head2 $SERVICE_NAME_MAPPINGS |
| |
| Data type : hash reference |
| Description : Contains a mapping of common service names to the names used by |
| Upstart distibutions. Example, sshd is called ssh on Ubuntu. |
| |
| =cut |
| |
| our $SERVICE_NAME_MAPPINGS = { |
| 'sshd' => 'ssh', |
| 'ext_sshd' => 'ext_ssh', |
| }; |
| |
| ############################################################################### |
| |
| =head1 OBJECT METHODS |
| |
| =cut |
| |
| #////////////////////////////////////////////////////////////////////////////// |
| |
| =head2 get_service_names |
| |
| Parameters : none |
| Returns : array |
| Description : Calls 'initctl list' to retrieve the list of services controlled |
| by Upstart 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 $service_info = $self->_get_service_info() || {}; |
| return sort keys %$service_info; |
| } |
| |
| #////////////////////////////////////////////////////////////////////////////// |
| |
| =head2 _get_service_info |
| |
| Parameters : $use_cache (optional) |
| Returns : hash reference |
| Description : Calls 'initctl list' to retrieve the list of services controlled |
| by Upstart on the computer. Also calls 'service --status-all' to |
| determine SysV-style services controlled by Upstart. A hash |
| reference is returned. Hash keys are service names. Values are |
| either 'initctl' or 'service' indicating if Upstart's initctl |
| command can be used to control the service or the service command |
| must be used: |
| { |
| "acpid" => "initctl", |
| "open-vm-tools" => "service", |
| "ssh" => "initctl", |
| "sshd" => "initctl", |
| "xrdp" => "service" |
| } |
| By default, the service info is retrieved every time this |
| subroutine is called. To use cached info, the $use_cache argument |
| must be explicitely set to true. |
| |
| =cut |
| |
| sub _get_service_info { |
| 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; |
| } |
| |
| my $use_cache = shift; |
| if ($use_cache && defined($self->{service_info})) { |
| return $self->{service_info}; |
| } |
| else { |
| $self->{service_info} = {}; |
| } |
| |
| my $computer_node_name = $self->data->get_computer_node_name(); |
| |
| my %service_name_mappings_reversed = reverse %$SERVICE_NAME_MAPPINGS; |
| |
| my $initctl_command = "initctl list"; |
| my ($initctl_exit_status, $initctl_output) = $self->os->execute($initctl_command, 0); |
| if (!defined($initctl_output)) { |
| notify($ERRORS{'WARNING'}, 0, "failed to execute command to list Upstart services on $computer_node_name"); |
| return; |
| } |
| elsif (grep(/Connection refused/i, @$initctl_output)) { |
| # Ubuntu 16 and later display the following: |
| # initctl: Unable to connect to Upstart: Failed to connect to socket /com/ubuntu/upstart: Connection refused |
| notify($ERRORS{'DEBUG'}, 0, "initctl command cannot be used to retrieve list of all services on $computer_node_name, command: '$initctl_command', output:\n" . join("\n", @$initctl_output)); |
| } |
| elsif ($initctl_exit_status ne '0') { |
| notify($ERRORS{'WARNING'}, 0, "failed to retrieve list of all services on $computer_node_name using the initctl command, exit status: $initctl_exit_status, command:\n$initctl_command\noutput:\n" . join("\n", @$initctl_output)); |
| return; |
| } |
| else { |
| # Format of initctl list output lines: |
| # splash-manager stop/waiting |
| # network-interface-security (network-interface/eth1) start/running |
| # tty1 start/running, process 1400 |
| for my $line (@$initctl_output) { |
| my ($service_name) = $line =~ /^([^\s\t]+)/; |
| if ($service_name) { |
| #notify($ERRORS{'DEBUG'}, 0, "found '$service_name' service via '$initctl_command', line: '$line'"); |
| $self->{service_info}{$service_name} = 'initctl'; |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "failed to parse service name on $computer_node_name, command: '$initctl_command', line: '$line', output:\n" . join("\n", @$initctl_output)); |
| } |
| } |
| } |
| |
| # VCL-966 |
| # Legacy SysV-style services are not reported by 'initctl list' |
| # The SysV.pm module cannot control these services becuase the chkconfig command is not available on Ubuntu |
| |
| my $service_command = "service --status-all"; |
| my ($service_exit_status, $service_output) = $self->os->execute($service_command); |
| if (!defined($service_output)) { |
| notify($ERRORS{'WARNING'}, 0, "failed to execute command to retrieve list of all services on $computer_node_name using the service command: $service_command"); |
| return; |
| } |
| elsif ($service_exit_status ne '0') { |
| notify($ERRORS{'WARNING'}, 0, "failed to retrieve list of all services on $computer_node_name using the service command, exit status: $service_exit_status, command:\n$service_command\noutput:\n" . join("\n", @$service_output)); |
| } |
| else { |
| # Lines should be formatted as: |
| # [ + ] acpid |
| # [ ? ] apport |
| # [ - ] dbus |
| for my $line (@$service_output) { |
| my ($service_name) = $line =~ /\]\s*(\S+)\s*$/; |
| if ($service_name) { |
| # The service utility method of controlling services is a fallback |
| # Don't add if previously found by initctl list |
| if (!defined($self->{service_info}{$service_name})) { |
| #notify($ERRORS{'DEBUG'}, 0, "found '$service_name' service via '$service_command', line: '$line'"); |
| $self->{service_info}{$service_name} = 'service'; |
| } |
| } |
| else { |
| #notify($ERRORS{'WARNING'}, 0, "failed to parse service name on $computer_node_name, command: '$service_command', line: '" . string_to_ascii($line) . "'\noutput:\n" . join("\n", @$service_output)); |
| } |
| } |
| } |
| |
| for my $service_name (keys %{$self->{service_info}}) { |
| my $service_name_mapping = $service_name_mappings_reversed{$service_name}; |
| if ($service_name_mapping) { |
| $self->{service_info}{$service_name_mapping} = $self->{service_info}{$service_name}; |
| } |
| } |
| |
| notify($ERRORS{'DEBUG'}, 0, "retrieved services info from $computer_node_name:\n" . format_data($self->{service_info})); |
| return $self->{service_info}; |
| } |
| |
| #////////////////////////////////////////////////////////////////////////////// |
| |
| =head2 delete_service |
| |
| Parameters : $service_name |
| Returns : boolean |
| Description : Stops the service if it is running. Deletes the |
| '/etc/init/<$service_name>.conf' file. |
| |
| =cut |
| |
| sub delete_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; |
| } |
| |
| my $service_name_argument = shift; |
| if (!$service_name_argument) { |
| notify($ERRORS{'WARNING'}, 0, "service name argument was not supplied"); |
| return; |
| } |
| |
| # Need to attempt to delete both the service with a name matching the argument as well as the mapped service name |
| my @service_names = ($service_name_argument); |
| |
| # If a mapped service name also exists, attempt to delete it as well |
| if ($SERVICE_NAME_MAPPINGS->{$service_name_argument}) { |
| push @service_names, $SERVICE_NAME_MAPPINGS->{$service_name_argument}; |
| } |
| |
| my $computer_node_name = $self->data->get_computer_node_name(); |
| |
| for my $service_name (@service_names) { |
| $self->stop_service($service_name) || return; |
| |
| # Delete the service configuration file |
| my $service_file_path = "/etc/init/$service_name.conf"; |
| $self->os->delete_file($service_file_path) || return; |
| |
| notify($ERRORS{'DEBUG'}, 0, "deleted '$service_name' service on $computer_node_name"); |
| } |
| |
| return 1; |
| } |
| |
| #////////////////////////////////////////////////////////////////////////////// |
| |
| =head2 start_service |
| |
| Parameters : $service_name |
| Returns : boolean |
| Description : Calls 'initctl start <$service_name>' to start the service. |
| |
| =cut |
| |
| sub start_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; |
| } |
| |
| my $service_name = shift; |
| if (!$service_name) { |
| notify($ERRORS{'WARNING'}, 0, "service name argument was not supplied"); |
| return; |
| } |
| $service_name = $SERVICE_NAME_MAPPINGS->{$service_name} || $service_name; |
| |
| # Check if initctl cannot be used to control service |
| if ($self->_controlled_by_service_command($service_name)) { |
| return $self->_call_service_start($service_name); |
| } |
| |
| my $computer_node_name = $self->data->get_computer_node_name(); |
| |
| my $command = "initctl start $service_name"; |
| my ($exit_status, $output) = $self->os->execute($command); |
| if (!defined($output)) { |
| notify($ERRORS{'WARNING'}, 0, "failed to execute command to start '$service_name' service on $computer_node_name"); |
| return; |
| } |
| elsif (grep(/Unknown job/i, @$output)) { |
| # Output if the service doesn't exist: 'initctl: Unknown job: <service name>' |
| notify($ERRORS{'WARNING'}, 0, "'$service_name' service does not exist on $computer_node_name"); |
| return; |
| } |
| elsif (grep(/already running/i, @$output)) { |
| # Output if the service is already running: 'initctl: Job is already running: <service name>' |
| notify($ERRORS{'DEBUG'}, 0, "'$service_name' is already running on $computer_node_name"); |
| return 1; |
| } |
| elsif (grep(/running/i, @$output)) { |
| # Output if the service was started: '<service name> start/running, process <PID>' |
| notify($ERRORS{'DEBUG'}, 0, "started '$service_name' service on $computer_node_name"); |
| return 1; |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "failed to start '$service_name' service on $computer_node_name, exit status: $exit_status, command: '$command', output:\n" . join("\n", @$output)); |
| return; |
| } |
| } |
| |
| #////////////////////////////////////////////////////////////////////////////// |
| |
| =head2 stop_service |
| |
| Parameters : $service_name |
| Returns : boolean |
| Description : Calls 'initctl stop <$service_name>' to stop the service. |
| |
| =cut |
| |
| sub stop_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; |
| } |
| |
| my $service_name = shift; |
| if (!$service_name) { |
| notify($ERRORS{'WARNING'}, 0, "service name argument was not supplied"); |
| return; |
| } |
| $service_name = $SERVICE_NAME_MAPPINGS->{$service_name} || $service_name; |
| |
| # Check if initctl cannot be used to control service |
| if ($self->_controlled_by_service_command($service_name)) { |
| return $self->_call_service_stop($service_name); |
| } |
| |
| my $computer_node_name = $self->data->get_computer_node_name(); |
| |
| my $command = "initctl stop $service_name"; |
| my ($exit_status, $output) = $self->os->execute($command); |
| if (!defined($output)) { |
| notify($ERRORS{'WARNING'}, 0, "failed to execute command to stop '$service_name' service on $computer_node_name"); |
| return; |
| } |
| elsif (grep(/Unknown job/i, @$output)) { |
| # Output if the service doesn't exist: 'initctl: Unknown job: <service name>' |
| notify($ERRORS{'DEBUG'}, 0, "'$service_name' service does not exist on $computer_node_name"); |
| } |
| elsif (grep(/Unknown instance/i, @$output)) { |
| # Output if the service is not running: 'initctl: Unknown instance:' |
| notify($ERRORS{'DEBUG'}, 0, "'$service_name' is already stopped on $computer_node_name"); |
| return 1; |
| } |
| elsif (grep(/ stop\//i, @$output)) { |
| # Output if the service was stopped: '<service name> stop/waiting' |
| notify($ERRORS{'DEBUG'}, 0, "stopped '$service_name' service on $computer_node_name"); |
| return 1; |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "failed to stop '$service_name' service on $computer_node_name, exit status: $exit_status, command: '$command', output:\n" . join("\n", @$output)); |
| return; |
| } |
| |
| return 1; |
| } |
| |
| #////////////////////////////////////////////////////////////////////////////// |
| |
| =head2 restart_service |
| |
| Parameters : $service_name |
| Returns : boolean |
| Description : Calls 'initctl restart <$service_name>' to restart the service. |
| |
| =cut |
| |
| sub restart_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; |
| } |
| |
| my $service_name = shift; |
| if (!$service_name) { |
| notify($ERRORS{'WARNING'}, 0, "service name argument was not supplied"); |
| return; |
| } |
| $service_name = $SERVICE_NAME_MAPPINGS->{$service_name} || $service_name; |
| |
| # Check if initctl cannot be used to control service |
| if ($self->_controlled_by_service_command($service_name)) { |
| return $self->_call_service_restart($service_name); |
| } |
| |
| my $computer_node_name = $self->data->get_computer_node_name(); |
| |
| my $command = "initctl restart $service_name"; |
| my ($exit_status, $output) = $self->os->execute($command); |
| if (!defined($output)) { |
| notify($ERRORS{'WARNING'}, 0, "failed to execute command to restart '$service_name' service on $computer_node_name"); |
| return; |
| } |
| elsif (grep(/Unknown job/i, @$output)) { |
| # Output if the service doesn't exist: 'initctl: Unknown job: <service name>' |
| notify($ERRORS{'WARNING'}, 0, "'$service_name' service does not exist on $computer_node_name"); |
| return; |
| } |
| elsif (grep(/Unknown instance/i, @$output)) { |
| # Output if the service is not running: 'initctl: Unknown instance:' |
| return $self->start_service($service_name); |
| } |
| elsif (grep(/process \d+/i, @$output)) { |
| # Output if the service was restarted: '<service name> start/running, process <PID>' |
| notify($ERRORS{'DEBUG'}, 0, "restarted '$service_name' service on $computer_node_name"); |
| return 1; |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "failed to restart '$service_name' service on $computer_node_name, exit status: $exit_status, command: '$command', output:\n" . join("\n", @$output)); |
| return; |
| } |
| } |
| |
| #////////////////////////////////////////////////////////////////////////////// |
| |
| =head2 add_ext_sshd_service |
| |
| Parameters : none |
| Returns : boolean |
| Description : Generates and configures '/etc/init/ext_ssh.conf' based off of |
| the existing '/etc/init/ext_ssh.conf' file. Adds the ext_ssh |
| service to the computer. |
| |
| =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(); |
| |
| my $sshd_service_file_path = '/etc/init/ssh.conf'; |
| my $ext_sshd_service_file_path = '/etc/init/ext_ssh.conf'; |
| my $ext_sshd_config_file_path = '/etc/ssh/external_sshd_config'; |
| |
| # Get the contents of the sshd service startup 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: ' ssh ' --> ' ext_ssh ' |
| $ext_sshd_service_file_contents =~ s| ssh | ext_ssh |g; |
| |
| # Add config file path argument |
| $ext_sshd_service_file_contents =~ s|(exec.*/sshd .*)|$1 -f $ext_sshd_config_file_path|g; |
| |
| # Replace /var/run/sshd --> /var/run/ext_sshd |
| $ext_sshd_service_file_contents =~ s|(/var/run/)sshd|$1ext_sshd|g; |
| |
| 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 on ext_sshd service file to 644 on $computer_node_name: $ext_sshd_service_file_path"); |
| return; |
| } |
| |
| return 1; |
| } |
| |
| #////////////////////////////////////////////////////////////////////////////// |
| |
| =head2 service_running |
| |
| Parameters : $service_name |
| Returns : boolean |
| Description : Calls 'initctl status <$service_name>' to determine if the |
| 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; |
| } |
| $service_name = $SERVICE_NAME_MAPPINGS->{$service_name} || $service_name; |
| |
| # Check if initctl cannot be used to control service |
| if ($self->_controlled_by_service_command($service_name)) { |
| return $self->_call_service_status($service_name); |
| } |
| |
| my $computer_node_name = $self->data->get_computer_node_name(); |
| |
| my $command = "initctl status $service_name"; |
| my ($exit_status, $output) = $self->os->execute($command); |
| if (!defined($output)) { |
| notify($ERRORS{'WARNING'}, 0, "failed to execute command to determine if '$service_name' service is enabled on $computer_node_name"); |
| return; |
| } |
| elsif (grep(/Unknown job/i, @$output)) { |
| # Output if the service doesn't exist: 'initctl: Unknown job: <service name>' |
| notify($ERRORS{'WARNING'}, 0, "'$service_name' service does not exist on $computer_node_name"); |
| return; |
| } |
| elsif (grep(/running/i, @$output)) { |
| # Output if the service is running: '<service name> start/running, process <PID>' |
| notify($ERRORS{'DEBUG'}, 0, "'$service_name' service is running on $computer_node_name"); |
| return 1; |
| } |
| elsif (grep(/stop/i, @$output)) { |
| # Output if the service is not running: '<service name>stop/waiting' |
| notify($ERRORS{'DEBUG'}, 0, "'$service_name' service is not running $computer_node_name"); |
| return 1; |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "unable to determine if '$service_name' service is running on $computer_node_name, exit status: $exit_status, command: '$command', output:\n" . join("\n", @$output)); |
| return; |
| } |
| } |
| |
| #////////////////////////////////////////////////////////////////////////////// |
| |
| =head2 service_enabled |
| |
| Parameters : $service_name |
| Returns : boolean |
| Description : Calls 'initctl show-config <$service_name>' to determine if the |
| 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; |
| } |
| $service_name = $SERVICE_NAME_MAPPINGS->{$service_name} || $service_name; |
| |
| my $computer_node_name = $self->data->get_computer_node_name(); |
| |
| # Check if an override file exists and contains 'manual' |
| my $service_override_file_path = "/etc/init/$service_name.override"; |
| if ($self->os->file_exists($service_override_file_path)) { |
| my @override_file_contents = $self->os->get_file_contents($service_override_file_path); |
| if (!@override_file_contents) { |
| notify($ERRORS{'WARNING'}, 0, "failed to retrieve contents of $service_override_file_path from $computer_node_name"); |
| } |
| else { |
| if (grep(/manual/i, @override_file_contents)) { |
| notify($ERRORS{'DEBUG'}, 0, "'$service_name' service is not enabled on $computer_node_name, $service_override_file_path exists and contains 'manual'"); |
| return 0; |
| } |
| } |
| } |
| |
| my $command = "initctl show-config $service_name"; |
| my ($exit_status, $output) = $self->os->execute($command); |
| if (!defined($output)) { |
| notify($ERRORS{'WARNING'}, 0, "failed to execute command to determine if '$service_name' service is enabled on $computer_node_name"); |
| return; |
| } |
| elsif (grep(/Unknown job/i, @$output)) { |
| # Output if the service doesn't exist: 'initctl: Unknown job: <service name>' |
| notify($ERRORS{'WARNING'}, 0, "'$service_name' service does not exist on $computer_node_name"); |
| return; |
| } |
| elsif (grep(/start on/i, @$output)) { |
| # Output if the service is enabled: |
| # <service name> |
| # start on (filesystem or runlevel [2345]) |
| # stop on runlevel [!2345] |
| notify($ERRORS{'DEBUG'}, 0, "'$service_name' service is enabled on $computer_node_name"); |
| return 1; |
| } |
| elsif (!grep(/^initctl:/, @$output)) { |
| # Output if the service is not enabled: |
| # <service name> |
| # stop on runlevel [06] |
| notify($ERRORS{'DEBUG'}, 0, "'$service_name' service is not enabled $computer_node_name"); |
| return 1; |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "unable to determine if '$service_name' service is enabled on $computer_node_name, exit status: $exit_status, command: '$command', output:\n" . join("\n", @$output)); |
| return; |
| } |
| } |
| |
| #////////////////////////////////////////////////////////////////////////////// |
| |
| =head2 enable_service |
| |
| Parameters : $service_name |
| Returns : boolean |
| Description : |
| |
| =cut |
| |
| sub enable_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 $service_name = shift; |
| if (!$service_name) { |
| notify($ERRORS{'WARNING'}, 0, "service name argument was not supplied"); |
| return; |
| } |
| $service_name = $SERVICE_NAME_MAPPINGS->{$service_name} || $service_name; |
| |
| my $computer_node_name = $self->data->get_computer_node_name(); |
| |
| my $service_override_file_path = "/etc/init/$service_name.override"; |
| |
| if (!$self->os->file_exists($service_override_file_path)) { |
| return 1; |
| } |
| if ($self->os->delete_file($service_override_file_path)) { |
| return 1; |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "unable to enable '$service_name' service, unable to delete override file: $service_override_file_path"); |
| return; |
| } |
| } |
| |
| #////////////////////////////////////////////////////////////////////////////// |
| |
| =head2 disable_service |
| |
| Parameters : $service_name |
| Returns : boolean |
| Description : |
| |
| =cut |
| |
| sub disable_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 $service_name = shift; |
| if (!$service_name) { |
| notify($ERRORS{'WARNING'}, 0, "service name argument was not supplied"); |
| return; |
| } |
| $service_name = $SERVICE_NAME_MAPPINGS->{$service_name} || $service_name; |
| |
| my $computer_node_name = $self->data->get_computer_node_name(); |
| |
| my $service_override_file_path = "/etc/init/$service_name.override"; |
| |
| if ($self->os->create_text_file($service_override_file_path, "manual\n")) { |
| return 1; |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "unable to disable '$service_name' service, failed to create override file: $service_override_file_path"); |
| return; |
| } |
| } |
| |
| #////////////////////////////////////////////////////////////////////////////// |
| |
| =head2 _controlled_by_service_command |
| |
| Parameters : $service_name |
| Returns : boolean |
| Description : Returns true if the service exists but cannot be controlled by |
| the 'initctl' command. The 'service' command must be used for |
| basic service control. |
| |
| =cut |
| |
| sub _controlled_by_service_command { |
| 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 $service_info = $self->_get_service_info(1) || return; |
| if ($service_info->{$service_name} && $service_info->{$service_name} eq 'service') { |
| notify($ERRORS{'DEBUG'}, 0, "'$service_name' service cannot be controlled by the initctl command, the service command will be used"); |
| return 1; |
| } |
| else { |
| notify($ERRORS{'DEBUG'}, 0, "'$service_name' service will be controlled by the initctl command"); |
| return 0; |
| } |
| } |
| |
| #////////////////////////////////////////////////////////////////////////////// |
| |
| =head2 _call_service_start |
| |
| Parameters : $service_name |
| Returns : boolean |
| Description : Calls 'service <$service_name> start' to start a service that |
| can't be controlled by initctl. |
| |
| =cut |
| |
| sub _call_service_start { |
| 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; |
| |
| my $computer_node_name = $self->data->get_computer_node_name(); |
| |
| my $service_command = "service $service_name start"; |
| my ($service_exit_status, $service_output) = $self->os->execute($service_command); |
| if (!defined($service_output)) { |
| notify($ERRORS{'WARNING'}, 0, "failed to execute command to start '$service_name' service on $computer_node_name using the service command: $service_command"); |
| return; |
| } |
| elsif ($service_exit_status eq '0' || grep(/done/, @$service_output)) { |
| notify($ERRORS{'OK'}, 0, "started '$service_name' service on $computer_node_name using the service command: '$service_command', output:\n" . join("\n", @$service_output)); |
| return 1; |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "failed to start '$service_name' service on $computer_node_name using the service command, exit status: $service_exit_status, command:\n$service_command\noutput:\n" . join("\n", @$service_output)); |
| return; |
| } |
| } |
| |
| #////////////////////////////////////////////////////////////////////////////// |
| |
| =head2 _call_service_stop |
| |
| Parameters : $service_name |
| Returns : boolean |
| Description : Calls 'service <$service_name> stop' to stop a service that |
| can't be controlled by initctl. |
| |
| =cut |
| |
| sub _call_service_stop { |
| 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; |
| |
| my $computer_node_name = $self->data->get_computer_node_name(); |
| |
| my $service_command = "service $service_name stop"; |
| my ($service_exit_status, $service_output) = $self->os->execute($service_command); |
| if (!defined($service_output)) { |
| notify($ERRORS{'WARNING'}, 0, "failed to execute command to stop '$service_name' service on $computer_node_name using the service command: $service_command"); |
| return; |
| } |
| elsif ($service_exit_status eq '0' || grep(/done/, @$service_output)) { |
| notify($ERRORS{'OK'}, 0, "stopped '$service_name' service on $computer_node_name using the service command: '$service_command', output:\n" . join("\n", @$service_output)); |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "failed to stop '$service_name' service on $computer_node_name using the service command, exit status: $service_exit_status, command:\n$service_command\noutput:\n" . join("\n", @$service_output)); |
| return; |
| } |
| |
| # Try to fix common xRDP bug on Ubuntu, service start/stop/restart often leaves behind the .pid file |
| # Service can't be completly restarted until file is manually deleted |
| my @pid_files = $self->os->find_files('/var/run', "$service_name.pid", 1, 'f'); |
| if (scalar(@pid_files) == 1) { |
| my $pid_file = $pid_files[0]; |
| notify($ERRORS{'DEBUG'}, 0, "'$service_name' may not have cleaned up .pid file when service was stopped, attempting to delete file: $pid_file"); |
| $self->os->delete_file($pid_file) |
| } |
| |
| return 1; |
| } |
| |
| #////////////////////////////////////////////////////////////////////////////// |
| |
| =head2 _call_service_restart |
| |
| Parameters : $service_name |
| Returns : boolean |
| Description : Calls 'service <$service_name> stop' and then |
| 'service <$service_name> start' to restart a service that can't |
| be controlled by initctl. |
| |
| =cut |
| |
| sub _call_service_restart { |
| 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; |
| |
| my $computer_node_name = $self->data->get_computer_node_name(); |
| |
| my $service_command = "service $service_name restart"; |
| my ($service_exit_status, $service_output) = $self->os->execute($service_command); |
| if (!defined($service_output)) { |
| notify($ERRORS{'WARNING'}, 0, "failed to execute command to restart '$service_name' service on $computer_node_name using the service command: $service_command"); |
| return; |
| } |
| elsif ($service_exit_status eq '0' || grep(/done/, @$service_output)) { |
| notify($ERRORS{'OK'}, 0, "restarted '$service_name' service on $computer_node_name using the service command: '$service_command', output:\n" . join("\n", @$service_output)); |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "failed to restart '$service_name' service on $computer_node_name using the service command, exit status: $service_exit_status, command:\n$service_command\noutput:\n" . join("\n", @$service_output)); |
| return; |
| } |
| |
| if ($self->_call_service_status($service_name)) { |
| return 1; |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "'$service_name' service does not seem to be running after restarting it, attempting to stop and start service"); |
| $self->_call_service_stop($service_name); |
| $self->_call_service_start($service_name); |
| return $self->_call_service_status($service_name); |
| } |
| } |
| |
| #////////////////////////////////////////////////////////////////////////////// |
| |
| =head2 _call_service_status |
| |
| Parameters : $service_name |
| Returns : boolean |
| Description : Calls 'service <$service_name> status' to get the status of a |
| service that can't be controlled by initctl. |
| |
| =cut |
| |
| sub _call_service_status { |
| 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; |
| |
| my $computer_node_name = $self->data->get_computer_node_name(); |
| |
| my $service_command = "service $service_name status"; |
| my ($service_exit_status, $service_output) = $self->os->execute($service_command); |
| if (!defined($service_output)) { |
| notify($ERRORS{'WARNING'}, 0, "failed to execute command to retrieve status of '$service_name' service on $computer_node_name using the service command: $service_command"); |
| return; |
| } |
| |
| # Output if service is running: |
| # * Checking status of Remote Desktop Protocol server xrdp |
| # ...done. |
| # * Checking status of RDP Session Manager sesman |
| # ...done. |
| |
| # Output if service is stopped: |
| # * Checking status of Remote Desktop Protocol server xrdp |
| # ...fail! |
| # * Checking status of RDP Session Manager sesman |
| # ...fail! |
| |
| if (grep(/done/, @$service_output) && !grep(/fail/, @$service_output)) { |
| notify($ERRORS{'OK'}, 0, "'$service_name' service is running on $computer_node_name, output of '$service_command':\n" . join("\n", @$service_output)); |
| return 1; |
| } |
| elsif (grep(/fail/, @$service_output)) { |
| notify($ERRORS{'OK'}, 0, "'$service_name' service is NOT running on $computer_node_name, output of '$service_command':\n" . join("\n", @$service_output)); |
| return 0; |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "failed to determine if '$service_name' service is running on $computer_node_name using the service command, output does not contain 'done' or 'fail', exit status: $service_exit_status, command:\n$service_command\noutput:\n" . join("\n", @$service_output)); |
| return; |
| } |
| } |
| |
| #////////////////////////////////////////////////////////////////////////////// |
| |
| 1; |
| __END__ |
| |
| =head1 SEE ALSO |
| |
| L<http://cwiki.apache.org/VCL/> |
| |
| =cut |