You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@vcl.apache.org by ar...@apache.org on 2011/10/06 22:53:09 UTC

svn commit: r1179847 - in /incubator/vcl/trunk/managementnode/lib/VCL/Module: OS.pm OS/Linux.pm OS/Windows.pm

Author: arkurth
Date: Thu Oct  6 20:53:08 2011
New Revision: 1179847

URL: http://svn.apache.org/viewvc?rev=1179847&view=rev
Log:
VCL-523
Improved Windows code which retrieves services configured to run as a particular user. It was very slow. The service information is now exported from the registry into a text file and parsed. This is faster than running 'reg.exe query' since the services section is very large.  Moved get_file_contents from Linux.pm to OS.pm since it can also be used by the Windows code.

Modified:
    incubator/vcl/trunk/managementnode/lib/VCL/Module/OS.pm
    incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Linux.pm
    incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Windows.pm

Modified: incubator/vcl/trunk/managementnode/lib/VCL/Module/OS.pm
URL: http://svn.apache.org/viewvc/incubator/vcl/trunk/managementnode/lib/VCL/Module/OS.pm?rev=1179847&r1=1179846&r2=1179847&view=diff
==============================================================================
--- incubator/vcl/trunk/managementnode/lib/VCL/Module/OS.pm (original)
+++ incubator/vcl/trunk/managementnode/lib/VCL/Module/OS.pm Thu Oct  6 20:53:08 2011
@@ -1570,6 +1570,52 @@ sub create_text_file {
 
 #/////////////////////////////////////////////////////////////////////////////
 
+=head2 get_file_contents
+
+ Parameters  : $file_path
+ Returns     : array
+ Description : Returns an array containing the contents of the file specified by
+               the file path argument. Each array element contains a line from
+               the file.
+
+=cut
+
+sub get_file_contents {
+	my $self = shift;
+	if (ref($self) !~ /module/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+	
+	# Get the path argument
+	my $path = shift;
+	if (!$path) {
+		notify($ERRORS{'WARNING'}, 0, "path argument was not specified");
+		return;
+	}
+	
+	my $computer_short_name = $self->data->get_computer_short_name();
+	
+	# Run cat to retrieve the contents of the file
+	my $command = "cat \"$path\"";
+	my ($exit_status, $output) = $self->execute($command);
+	if (!defined($output)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to run command to read file on $computer_short_name:\n path: '$path'\ncommand: '$command'");
+		return;
+	}
+	elsif (grep(/^cat: /, @$output)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to read contents of file on $computer_short_name: '$path', exit status: $exit_status, output:\n" . join("\n", @$output));
+		return;
+	}
+	else {
+		notify($ERRORS{'DEBUG'}, 0, "retrieved " . scalar(@$output) . " lines from file on $computer_short_name: '$path'");
+		map { s/[\r\n]+$//g; } (@$output);
+		return @$output;
+	}
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
 =head2 execute
 
  Parameters  : $command, $display_output (optional)
@@ -1850,7 +1896,7 @@ sub manage_server_access {
 		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() || return;
 	my $reservation_id              = $self->data->get_reservation_id();
 	my $server_request_id           = $self->data->get_server_request_id();

Modified: incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Linux.pm
URL: http://svn.apache.org/viewvc/incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Linux.pm?rev=1179847&r1=1179846&r2=1179847&view=diff
==============================================================================
--- incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Linux.pm (original)
+++ incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Linux.pm Thu Oct  6 20:53:08 2011
@@ -1558,51 +1558,6 @@ sub move_file {
 
 #/////////////////////////////////////////////////////////////////////////////
 
-=head2 get_file_contents
-
- Parameters  : $file_path
- Returns     : array
- Description : Returns an array containing the contents of the file specified by
-               the file path argument. Each array element contains a line from
-               the file.
-
-=cut
-
-sub get_file_contents {
-	my $self = shift;
-	if (ref($self) !~ /module/i) {
-		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
-		return;
-	}
-	
-	# Get the path argument
-	my $path = shift;
-	if (!$path) {
-		notify($ERRORS{'WARNING'}, 0, "path argument was not specified");
-		return;
-	}
-	
-	my $computer_short_name = $self->data->get_computer_short_name();
-	
-	# Run cat to retrieve the contents of the file
-	my $command = "cat \"$path\"";
-	my ($exit_status, $output) = $self->execute($command);
-	if (!defined($output)) {
-		notify($ERRORS{'WARNING'}, 0, "failed to run command to read file on $computer_short_name:\n path: '$path'\ncommand: '$command'");
-		return;
-	}
-	elsif (grep(/^cat: /, @$output)) {
-		notify($ERRORS{'WARNING'}, 0, "failed to read contents of file on $computer_short_name: '$path', exit status: $exit_status, output:\n" . join("\n", @$output));
-		return;
-	}
-	else {
-		notify($ERRORS{'DEBUG'}, 0, "retrieved " . scalar(@$output) . " lines from file on $computer_short_name: '$path'");
-		return @$output;
-	}
-}
-
-#/////////////////////////////////////////////////////////////////////////////
-
 =head2 get_available_space
 
  Parameters  : $path

Modified: incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Windows.pm
URL: http://svn.apache.org/viewvc/incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Windows.pm?rev=1179847&r1=1179846&r2=1179847&view=diff
==============================================================================
--- incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Windows.pm (original)
+++ incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Windows.pm Thu Oct  6 20:53:08 2011
@@ -2444,6 +2444,9 @@ sub reg_query {
 		$command .= "/v \"$value_argument_escaped\"";
 	}
 	
+	# Ignore error lines, it will throw off parsing
+	$command .= " 2>/dev/null";
+	
 	# Run reg.exe QUERY
 	my ($exit_status, $output) = run_ssh_command($computer_node_name, $management_node_keys, $command, '', '', 0);
 	if (!defined($output)) {
@@ -2498,6 +2501,11 @@ sub reg_query {
 				$type =~ s/(^\s+|\s+$)//g;
 				$data =~ s/(^\s+|\s+$)//g;
 				
+				if ($type =~ /binary/i) {
+					#notify($ERRORS{'DEBUG'}, 0, "ignoring $type data, key: $key, value: $value");
+					next;
+				}
+				
 				$value = '(Default)' if $value =~ /NO NAME/;
 				
 				$data = $self->reg_query_convert_data($type, $data);
@@ -2570,6 +2578,12 @@ sub reg_query_convert_data {
 	}
 	
 	if ($type eq 'REG_DWORD') {
+		# Make sure a valid hex value was returned
+		if ($data !~ /^0x[a-fA-F0-9]+$/) {
+			notify($ERRORS{'WARNING'}, 0, "invalid $type value: '$data'");
+			return;
+		}
+		
 		# Convert the hex value to decimal
 		$data = hex($data);
 	}
@@ -2804,13 +2818,13 @@ sub reg_export {
 		notify($ERRORS{'WARNING'}, 0, "registry file path was not passed correctly as an argument");
 		return;
 	}
-	$registry_file_path = $self->format_path_unix($registry_file_path);
+	$registry_file_path = $self->format_path_dos($registry_file_path);
 	
-	# Escape backslashes in the root key
-	$root_key =~ s/\\+/\\\\/;
+	# Replace forward slashes with backslashes in registry key
+	$root_key =~ s/\//\\\\/g;
 	
 	# Run reg.exe EXPORT
-	my $command .= $system32_path . "/reg.exe EXPORT $root_key $registry_file_path /y";
+	my $command .= "cmd.exe /c \"$system32_path/reg.exe EXPORT $root_key $registry_file_path.tmp /y && type $registry_file_path.tmp > $registry_file_path\"";
 	my ($exit_status, $output) = run_ssh_command($computer_node_name, $management_node_keys, $command, '', '', 1);
 	if (!defined($output)) {
 		notify($ERRORS{'WARNING'}, 0, "failed to run SSH command to export registry key $root_key to file: $registry_file_path");
@@ -3830,53 +3844,7 @@ sub set_service_credentials {
 
 #/////////////////////////////////////////////////////////////////////////////
 
-=head2 get_service_list
-
- Parameters  : none
- Returns     : array
- Description : Retrieves the names of the services installed on the computer.
-
-=cut
-
-sub get_service_list {
-	my $self = shift;
-	if (ref($self) !~ /windows/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 $system32_path        = $self->get_system32_path() || return;
-	
-	# Call sc query
-	my $sc_query_command = $system32_path . "/sc.exe query";
-	my ($sc_query_exit_status, $sc_query_output) = $self->execute($sc_query_command);
-	if (defined($sc_query_exit_status) && $sc_query_exit_status == 0) {
-		#notify($ERRORS{'OK'}, 0, "retrieved service list on $computer_node_name:\n" . join("\n", @$sc_query_output));
-	}
-	elsif (defined($sc_query_exit_status)) {
-		notify($ERRORS{'WARNING'}, 0, "failed to retrieve service list from $computer_node_name, exit status: $sc_query_exit_status, output:\n@{$sc_query_output}");
-		return 0;
-	}
-	else {
-		notify($ERRORS{'WARNING'}, 0, "failed to run ssh command to failed to retrieve service list from $computer_node_name");
-		return;
-	}
-	
-	my @service_names;
-	for my $line (@$sc_query_output) {
-		if ($line =~ /SERVICE_NAME: (.*)/) {
-			push @service_names, $1;
-		}
-	}
-
-	notify($ERRORS{'DEBUG'}, 0, "found " . scalar(@service_names) . " services on $computer_node_name");
-	return @service_names;
-} ## end sub get_service_list
-
-#/////////////////////////////////////////////////////////////////////////////
-
-=head2 get_service_info
+=head2 get_service_configuration
 
  Parameters  : none
  Returns     : hash reference
@@ -3884,61 +3852,51 @@ sub get_service_list {
                reference is returned. The hash keys are service names.
                Example:
                   "sshd" => {
-                    "BINARY_PATH_NAME" => "C:\\cygwin\\bin\\cygrunsrv.exe",
-                    "DEPENDENCIES" => "tcpip",
-                    "DISPLAY_NAME" => "CYGWIN sshd",
-                    "ERROR_CONTROL" => "1   NORMAL",
-                    "LOAD_ORDER_GROUP" => "",
-                    "SERVICE_NAME" => "sshd",
                     "SERVICE_START_NAME" => ".\\root",
-                    "START_TYPE" => "2   AUTO_START",
-                    "TAG" => 0,
-                    "TYPE" => "10  WIN32_OWN_PROCESS "
                   },
 
 =cut
 
-sub get_service_info {
+sub get_service_configuration {
 	my $self = shift;
 	if (ref($self) !~ /windows/i) {
 		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
 		return;
 	}
 	
-	return $self->{service_info} if $self->{service_info};
+	return $self->{service_configuration} if $self->{service_configuration};
 
 	my $computer_node_name   = $self->data->get_computer_node_name();
-	my $system32_path        = $self->get_system32_path() || return;
 	
-	my @service_list = $self->get_service_list();
+	notify($ERRORS{'DEBUG'}, 0, "retrieving service configuration information from the registry");
+	my $services_key = 'HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services';
+	my $node_reg_file_path = "\$TMP/services_$computer_node_name.reg";
+	if (!$self->reg_export($services_key, $node_reg_file_path)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to retrieve service credential information from the registry on $computer_node_name");
+		return;
+	}
 	
-	my $service_info;
-	for my $service (@service_list) {
-		# Call sc query
-		my $command = "$system32_path/sc.exe qc \"$service\"";
-		my ($exit_status, $output) = $self->execute($command);
-		if (defined($exit_status) && $exit_status == 0) {
-			#notify($ERRORS{'DEBUG'}, 0, "retrieved '$service' service info:\n" . join("\n", @$output));
-			
-			for my $line (@$output) {
-				if (my ($property, $value) = $line =~ /^[\s\t]*(\w+)[\s\t]*:[\s\t]*(.*)/g) {
-					$service_info->{$service}{$property} = $value;
-				}
-			}
-		}
-		elsif (defined($exit_status)) {
-			notify($ERRORS{'WARNING'}, 0, "failed to retrieve '$service' service info from $computer_node_name, exit status: $exit_status, output:\n" . join("\n", @$output));
-			next SERVICE;
+	my @reg_file_contents = $self->get_file_contents($node_reg_file_path);
+	if (!@reg_file_contents) {
+		notify($ERRORS{'WARNING'}, 0, "failed to retrieve contents of file on $computer_node_name containing exported service credential information from the registry: $node_reg_file_path");
+		return;
+	}
+	
+	my $service_configuration;
+	my $service_name;
+	for my $line (@reg_file_contents) {
+		if ($line =~ /Services\\([^\\]+)\]$/i) {
+			$service_name = $1;
+			$service_configuration->{$service_name} = {};
 		}
-		else {
-			notify($ERRORS{'WARNING'}, 0, "failed to run command to retrieve '$service' service info from $computer_node_name");
-			next SERVICE;
+		elsif ($line =~ /"ObjectName"="(.+)"/i) {
+			my $object_name = $1;
+			$service_configuration->{$service_name}{SERVICE_START_NAME} = $object_name;
 		}
 	}
 	
-	$self->{service_info} = $service_info;
-	#notify($ERRORS{'DEBUG'}, 0, "retrieved service info:\n" . format_data($service_info));
-	return $service_info;
+	$self->{service_configuration} = $service_configuration;
+	return $self->{service_configuration};
 }
 
 #/////////////////////////////////////////////////////////////////////////////
@@ -3969,23 +3927,23 @@ sub get_services_using_login_id {
 		return;
 	}
 	
-	# Get infor for all the services installed on the computer
-	my $service_info = $self->get_service_info() || return;
+	# Get configuration for all the services installed on the computer
+	my $service_configuration = $self->get_service_configuration();
 	
 	my @matching_service_names;
-	for my $service_name (sort keys %$service_info) {
-		my $service_start_name = $service_info->{$service_name}{SERVICE_START_NAME};
+	for my $service_name (sort keys %$service_configuration) {
+		my $service_start_name = $service_configuration->{$service_name}{SERVICE_START_NAME};
 		
 		# The service start name may be in any of the following forms:
 		#    LocalSystem
 		#    NT AUTHORITY\LocalService
 		#    .\root
-		if ($service_start_name && $service_start_name =~ /^((NT AUTHORITY|\.)\\)?$login_id$/i) {
+		if ($service_start_name && $service_start_name =~ /^((NT AUTHORITY|\.)\\+)?$login_id$/i) {
 			push @matching_service_names, $service_name;
 		}
 	}
-	
-	notify($ERRORS{'DEBUG'}, 0, "found " . scalar(@matching_service_names) . " services using login ID '$login_id': " . join(", ", @matching_service_names));
+
+	notify($ERRORS{'DEBUG'}, 0, "services found using login ID '$login_id' (" . scalar(@matching_service_names) . "): " . join(", ", @matching_service_names));
 	return @matching_service_names;
 } ## end sub get_services_using_login_id