Author: arkurth Date: Fri Sep 9 17:36:38 2016 New Revision: 1760092 URL: http://svn.apache.org/viewvc?rev=1760092&view=rev Log: VCL-989 Updated systemd.pm to handle Ubuntu 15.04+'s usage of ssh as a service name instead of sshd.
Added code to delete_service to attempt to delete both ext_ssh and ext_sshd. Added _get_service_unit_file_path subroutine which retrieves the name of the unit file used by a service. This is called from add_ext_sshd_service instead of assuming the file is named sshd.service. Modified regular expressions in add_ext_sshd_service which manipulate ext_sshd.service. They now: * Remove any $ variables from the ExecStart line containing "OPT" including $OPTIONS and $SSHD_OPTS * Remove any explicit -f <sshd_config path> sections from ExecStart line * Remove all Alias= lines Modified: vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/init/systemd.pm Modified: vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/init/systemd.pm URL: http://svn.apache.org/viewvc/vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/init/systemd.pm?rev=1760092&r1=1760091&r2=1760092&view=diff ============================================================================== --- vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/init/systemd.pm (original) +++ vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/init/systemd.pm Fri Sep 9 17:36:38 2016 @@ -299,7 +299,7 @@ sub enable_service { 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, output:\n" . join("\n", @$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 { @@ -375,24 +375,32 @@ sub delete_service { return 0; } - my $service_name = shift; - if (!$service_name) { + my $service_name_argument = shift; + if (!$service_name_argument) { 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 - if ($self->service_exists($service_name)) { - $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; + # Attempt to delete both ext_sshd and ext_ssh if the argument is + my @service_names = ($service_name_argument); + if ($service_name_argument =~ /ext_ssh/) { + @service_names = ('ext_sshd', 'ext_ssh'); + } + + for my $service_name (@service_names) { + # Disable the service before deleting it + if ($self->service_exists($service_name)) { + $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(); @@ -570,7 +578,15 @@ sub add_ext_sshd_service { my $computer_node_name = $self->data->get_computer_node_name(); - my $sshd_service_file_path = '/lib/systemd/system/sshd.service'; + # 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 @@ -581,22 +597,36 @@ sub add_ext_sshd_service { } 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)|ext_sshd|g; + $ext_sshd_service_file_contents =~ s|(?<!bin/)sshd(?!_config\|-keygen)|ext_sshd|g; - # Replace: $OPTIONS --> -f /etc/ssh/external_sshd_config - $ext_sshd_service_file_contents =~ s|(ExecStart=.+)\s+\$OPTIONS|$1 -f /etc/ssh/external_sshd_config|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 + $ext_sshd_service_file_contents =~ s/^\s*(ExecStart=.+\S)\s+-f\s+\S+(.*)$/$1$2/gm; + + # Add -f argument + $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; - $ext_sshd_service_file_contents .= "\n"; + # 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; + + 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"); @@ -651,6 +681,64 @@ sub _daemon_reload { } #///////////////////////////////////////////////////////////////////////////// + +=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__