Author: arkurth
Date: Thu Jul 14 17:18:57 2016
New Revision: 1752706
URL: http://svn.apache.org/viewvc?rev=1752706&view=rev
Log:
VCL-915
Added start_nfs_client_service and stop_nfs_client_service to Windows.pm which
use nfsadmin.exe to control the service. Updated start_service and stop_service
to call these new subroutines if the service name is NfsClnt.
Other
Cleaned up old code in add_user_group_policy_script and
remove_user_group_policy_script. Added get_user_group_policy_script_info.
Modified:
vcl/trunk/managementnode/lib/VCL/Module/OS/Windows.pm
Modified: vcl/trunk/managementnode/lib/VCL/Module/OS/Windows.pm
URL:
http://svn.apache.org/viewvc/vcl/trunk/managementnode/lib/VCL/Module/OS/Windows.pm?rev=1752706&r1=1752705&r2=1752706&view=diff
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/Module/OS/Windows.pm (original)
+++ vcl/trunk/managementnode/lib/VCL/Module/OS/Windows.pm Thu Jul 14 17:18:57
2016
@@ -5894,8 +5894,8 @@ sub delete_capture_configuration_files {
$self->delete_files_by_pattern($system32_path .
'/GroupPolicy/User/Scripts',
'.*\(Prepare\|prepare\|Cleanup\|cleanup\|post_load\).*');
# Remove VCLprepare.cmd and VCLcleanup.cmd lines from
scripts.ini file
- $self->remove_group_policy_script('logon', 'VCLprepare.cmd');
- $self->remove_group_policy_script('logoff', 'VCLcleanup.cmd');
+ $self->remove_user_group_policy_script('logon',
'VCLprepare.cmd');
+ $self->remove_user_group_policy_script('logoff',
'VCLcleanup.cmd');
}
# Remove old scripts and utilities
@@ -5922,15 +5922,33 @@ sub delete_capture_configuration_files {
#/////////////////////////////////////////////////////////////////////////////
-=head2 add_group_policy_script
+=head2 get_user_group_policy_script_info
- Parameters :
- Returns :
- Description :
+ Parameters : none
+ Returns : hash reference
+ Description : Parses the script.ini file and constructs a hash reference:
+ {
+ "logoff" => {
+ 0 => {
+ "cmdline" => "C:\\logoff.cmd",
+ "parameters" => ">> C:\\logoff.log"
+ },
+ 1 => {
+ "cmdline" => "logoff2.cmd",
+ "parameters" => ""
+ },
+ },
+ "logon" => {
+ 0 => {
+ "cmdline" => "logon.cmd",
+ "parameters" => ""
+ }
+ }
+ }
=cut
-sub add_group_policy_script {
+sub get_user_group_policy_script_info {
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");
@@ -5939,246 +5957,192 @@ sub add_group_policy_script {
my $system32_path = $self->get_system32_path() || return;
- # Get the arguments
- my $stage_argument = shift;
- my $cmdline_argument = shift;
- my $parameters_argument = shift;
- if (!$stage_argument || $stage_argument !~ /^(logon|logoff)$/i) {
- notify($ERRORS{'WARNING'}, 0, "stage (logon/logoff) argument
was not specified");
- return;
- }
- if (!$cmdline_argument) {
- notify($ERRORS{'WARNING'}, 0, "CmdLine argument was not
specified");
- return;
- }
- if (!$parameters_argument) {
- $parameters_argument = '';
- }
-
- # Capitalize the first letter of logon/logoff
- $stage_argument = lc($stage_argument);
- $stage_argument = "L" . substr($stage_argument, 1);
-
- # Store the stage name (logon/logoff) not being modified
- my $opposite_stage_argument;
- if ($stage_argument =~ /logon/i) {
- $opposite_stage_argument = 'Logoff';
- }
- else {
- $opposite_stage_argument = 'Logon';
- }
-
- # Path to scripts.ini file
- my $scripts_ini = $system32_path .
'/GroupPolicy/User/Scripts/scripts.ini';
-
- # Set the owner of scripts.ini to root
- my $chown_command = "touch $scripts_ini && chown root $scripts_ini";
- my ($chown_status, $chown_output) = $self->execute($chown_command);
- if (defined($chown_status) && $chown_status == 0) {
- notify($ERRORS{'DEBUG'}, 0, "set root as owner of scripts.ini");
- }
- elsif (defined($chown_status)) {
- notify($ERRORS{'WARNING'}, 0, "failed to set root as owner of
scripts.ini, exit status: $chown_status, output:\n@{$chown_output}");
- }
- else {
- notify($ERRORS{'WARNING'}, 0, "unable to run ssh command to set
root as owner of scripts.ini");
- }
-
- # Set the permissions of scripts.ini to 664
- my $chmod_command = "chmod 664 $scripts_ini";
- my ($chmod_status, $chmod_output) = $self->execute($chmod_command);
- if (defined($chmod_status) && $chmod_status == 0) {
- notify($ERRORS{'DEBUG'}, 0, "ran chmod on scripts.ini");
- }
- elsif (defined($chmod_status)) {
- notify($ERRORS{'WARNING'}, 0, "failed to run chmod 664 on
scripts.ini, exit status: $chmod_status, output:\n@{$chmod_output}");
- }
- else {
- notify($ERRORS{'WARNING'}, 0, "unable to run ssh command to run
chmod 664 on scripts.ini");
- }
-
- # Clear hidden, system, and readonly flags on scripts.ini
- my $attrib_command = "attrib -H -S -R $scripts_ini";
- my ($attrib_status, $attrib_output) = $self->execute($attrib_command);
- if (defined($attrib_status) && $attrib_status == 0) {
- notify($ERRORS{'DEBUG'}, 0, "ran attrib -H -S -R on
scripts.ini");
- }
- elsif (defined($attrib_status)) {
- notify($ERRORS{'WARNING'}, 0, "failed to run attrib -H -S -R on
scripts.ini, exit status: $attrib_status, output:\n@{$attrib_output}");
- }
- else {
- notify($ERRORS{'WARNING'}, 0, "unable to run ssh command to run
attrib -H -S -R on scripts.ini");
- }
-
- # Get the contents of scripts.ini
- my $cat_command = "cat $scripts_ini";
- my ($cat_status, $cat_output) = $self->execute($cat_command, 1);
- if (defined($cat_status) && $cat_status == 0) {
- notify($ERRORS{'DEBUG'}, 0, "retrieved scripts.ini contents:\n"
. join("\n", @{$cat_output}));
- }
- elsif (defined($cat_status)) {
- notify($ERRORS{'WARNING'}, 0, "failed to cat scripts.ini
contents");
- }
- else {
- notify($ERRORS{'WARNING'}, 0, "unable to run ssh command to
scripts.ini contents");
- }
-
- # Create a string containing all of the lines in scripts.ini
- my $scripts_ini_string = join("\n", @{$cat_output}) || '';
-
- # Remove any carriage returns to make pattern matching easier
- $scripts_ini_string =~ s/\r//gs;
-
- # Get a string containing just the section being modified (logon/logoff)
- my ($section_string) = $scripts_ini_string =~
/(\[$stage_argument\][^\[\]]*)/is;
- $section_string = "[$stage_argument]" if !$section_string;
- notify($ERRORS{'DEBUG'}, 0, "scripts.ini $stage_argument section:\n" .
string_to_ascii($section_string));
-
- my ($opposite_section_string) = $scripts_ini_string =~
/(\[$opposite_stage_argument\][^\[\]]*)/is;
- $opposite_section_string = "[$opposite_stage_argument]" if
!$opposite_section_string;
- notify($ERRORS{'DEBUG'}, 0, "scripts.ini $opposite_stage_argument
section:\n" . string_to_ascii($opposite_section_string));
-
- my @section_lines = split(/[\r\n]+/, $section_string);
- notify($ERRORS{'DEBUG'}, 0, "scripts.ini $stage_argument section line
count: " . scalar @section_lines);
-
- my %scripts_original;
- for my $section_line (@section_lines) {
- if ($section_line =~ /(\d+)Parameters\s*=(.*)/i) {
- my $index = $1;
- my $parameters = $2;
- if (!defined $scripts_original{$index}{Parameters}) {
- $scripts_original{$index}{Parameters} =
$parameters;
- #notify($ERRORS{'DEBUG'}, 0, "found
$stage_argument parameters:\nline: '$section_line'\nparameters:
'$parameters'\nindex: $index");
- }
- else {
- notify($ERRORS{'WARNING'}, 0, "found duplicate
$stage_argument parameters line for index $index");
- }
- }
- elsif ($section_line =~ /(\d+)CmdLine\s*=(.*)/i) {
- my $index = $1;
- my $cmdline = $2;
- if (!defined $scripts_original{$index}{CmdLine}) {
- $scripts_original{$index}{CmdLine} = $cmdline;
- #notify($ERRORS{'DEBUG'}, 0, "found
$stage_argument cmdline:\nline: '$section_line'\ncmdline: '$cmdline'\nindex:
$index");
- }
- else {
- notify($ERRORS{'WARNING'}, 0, "found duplicate
$stage_argument CmdLine line for index $index");
- }
- }
- elsif ($section_line =~ /\[$stage_argument\]/i) {
- #notify($ERRORS{'DEBUG'}, 0, "found $stage_argument
heading:\nline: '$section_line'");
- }
- else {
- notify($ERRORS{'WARNING'}, 0, "found unexpected line:
'$section_line'");
- }
- }
+ my $scripts_ini_file_path =
"$system32_path/GroupPolicy/User/Scripts/scripts.ini";
- my %scripts_modified;
- my $index_modified = 0;
- foreach my $index (sort keys %scripts_original) {
- if (!defined $scripts_original{$index}{CmdLine}) {
- notify($ERRORS{'WARNING'}, 0, "CmdLine not specified
for index $index");
- next;
- }
- elsif ($scripts_original{$index}{CmdLine} =~ /^\s*$/) {
- notify($ERRORS{'WARNING'}, 0, "CmdLine blank for index
$index");
+ my @lines;
+ if ($self->file_exists($scripts_ini_file_path)) {
+ @lines = $self->get_file_contents($scripts_ini_file_path);
+ notify($ERRORS{'DEBUG'}, 0, "retrieved contents of
scripts.ini:\n" . join("\n", @lines));
+ }
+
+ # Format of scripts.ini
+ # <--- BLANK FIRST LINE
+ # [Logon]
+ # 0CmdLine=logon.cmd
+ # 0Parameters=>> C:\logon.log
+ # 1CmdLine=logon1.cmd
+ # 1Parameters=
+ # [Logoff]
+ # 0CmdLine=C:\logoff.cmd
+ # 0Parameters=>> C:\logoff.log
+
+ my $info = {
+ 'logon' => {},
+ 'logoff' => {},
+ };
+
+ my $current_stage;
+ for my $line (@lines) {
+ # Ignore blank lines
+ next unless $line =~ /\w/;
+
+ # Remove Unicode nul and special characters added to beginning
of file
+ $line =~ s/(\x00|\xFE|\xFF)//g;
+
+ # Find script stage section heading: [Logon] or [Logoff]
+ if ($line =~ /\[(logon|logoff)\]/i) {
+ $current_stage = lc($1);
next;
}
- if (!defined $scripts_original{$index}{Parameters}) {
- notify($ERRORS{'WARNING'}, 0, "Parameters not specified
for index $index");
- $scripts_original{$index}{Parameters} = '';
- }
- if ($scripts_original{$index}{CmdLine} =~ /$cmdline_argument/i
&& $scripts_original{$index}{Parameters} =~ /$parameters_argument/i) {
- notify($ERRORS{'DEBUG'}, 0, "replacing existing
$stage_argument script at index $index:\ncmdline:
$scripts_original{$index}{CmdLine}\nparameters:
$scripts_original{$index}{Parameters}");
+ # Parse the line, format should be either:
+ # 0CmdLine=logon.cmd
+ # 0Parameters=>> C:\logon.log
+ my ($index, $property, $value) = $line =~
/^\s*(\d+)(\w+)=(.*)$/;
+ if (!defined($index)) {
+ notify($ERRORS{'WARNING'}, 0, "failed to parse
scripts.ini, unable to parse line: '$line'\n" . join("\n", @lines));
+ return;
}
- else {
- notify($ERRORS{'DEBUG'}, 0, "retaining existing
$stage_argument script at index $index:\ncmdline:
$scripts_original{$index}{CmdLine}\nparameters:
$scripts_original{$index}{Parameters}");
- $scripts_modified{$index_modified}{CmdLine} =
$scripts_original{$index}{CmdLine};
- $scripts_modified{$index_modified}{Parameters} =
$scripts_original{$index}{Parameters};
- $index_modified++;
+
+ $property = lc($property);
+
+ if (defined($info->{$current_stage}{$index}{$property})) {
+ notify($ERRORS{'WARNING'}, 0, "failed to parse
scripts.ini, multiple [$current_stage] '$property' lines exist for index
$index:\n" . join("\n", @lines));
+ return;
}
+
+ $info->{$current_stage}{$index}{$property} = $value;
}
- # Add the argument script to the hash
- $scripts_modified{$index_modified}{CmdLine} = $cmdline_argument;
- $scripts_modified{$index_modified}{Parameters} = $parameters_argument;
- $index_modified++;
-
- #notify($ERRORS{'DEBUG'}, 0, "arguments:\ncmdline:
$cmdline_argument\nparameters: $parameters_argument");
- #notify($ERRORS{'DEBUG'}, 0, "original $stage_argument scripts data:\n"
. format_data(\%scripts_original));
- #notify($ERRORS{'DEBUG'}, 0, "modified $stage_argument scripts data:\n"
. format_data(\%scripts_modified));
-
- my $section_string_new = "[$stage_argument]\n";
- foreach my $index_new (sort keys(%scripts_modified)) {
- $section_string_new .= $index_new .
"CmdLine=$scripts_modified{$index_new}{CmdLine}\n";
- $section_string_new .= $index_new .
"Parameters=$scripts_modified{$index_new}{Parameters}\n";
+ # Guarantee each script index has both a CmdLine and Parameters entry
+ for my $check_stage (keys %$info) {
+ for my $index (keys %{$info->{$check_stage}}) {
+ $info->{$check_stage}{$index}{'cmdline'} = '' unless
defined($info->{$check_stage}{$index}{'cmdline'});
+ $info->{$check_stage}{$index}{'parameters'} = '' unless
defined($info->{$check_stage}{$index}{'parameters'});
+ }
}
- notify($ERRORS{'DEBUG'}, 0, "original $stage_argument scripts
section:\n$section_string");
- notify($ERRORS{'DEBUG'}, 0, "modified $stage_argument scripts
section:\n$section_string_new");
-
- my $scripts_ini_modified;
- if ($stage_argument =~ /logon/i) {
- $scripts_ini_modified =
"$section_string_new\n$opposite_section_string";
- }
- else {
- $scripts_ini_modified =
"$opposite_section_string\n$section_string_new";
+ notify($ERRORS{'DEBUG'}, 0, "retrieved user group policy info:\n" .
format_data($info));
+ return $info;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 add_user_group_policy_script
+
+ Parameters : $stage_argument, $cmdline_argument, $parameters_argument
+ Returns : boolean
+ Description : Adds a traditional (non-Powershell) user group policy script to
+ the computer to be automatically executed at logon or logoff.
The
+ stage argument must either be 'logon' or 'logoff'.
+
+=cut
+
+sub add_user_group_policy_script {
+ 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;
}
- notify($ERRORS{'DEBUG'}, 0, "modified scripts.ini
contents:\n$scripts_ini_modified");
-
- # Escape quote characters
- $scripts_ini_modified =~ s/"/\\"/gs;
+
+ my $system32_path = $self->get_system32_path() || return;
- # Echo the modified contents to scripts.ini
- my $echo_command = "echo \"$scripts_ini_modified\" > $scripts_ini";
- my ($echo_status, $echo_output) = $self->execute($echo_command);
- if (defined($echo_status) && $echo_status == 0) {
- notify($ERRORS{'DEBUG'}, 0, "echo'd modified contents to
scripts.ini");
+ # Get the arguments
+ my ($stage_argument, $cmdline_argument, $parameters_argument) = @_;
+ if (!$stage_argument) {
+ notify($ERRORS{'WARNING'}, 0, "stage (logon/logoff) argument
was not specified");
+ return;
}
- elsif (defined($echo_status)) {
- notify($ERRORS{'WARNING'}, 0, "failed to echo modified contents
to scripts.ini, exit status: $echo_status, output:\n@{$echo_output}");
+ elsif (!$stage_argument || $stage_argument !~ /^(logon|logoff)$/i) {
+ notify($ERRORS{'WARNING'}, 0, "stage argument is not valid:
$stage_argument, it must be 'logon' or 'logoff'");
return;
}
- else {
- notify($ERRORS{'WARNING'}, 0, "unable to run ssh command to
echo modified contents to scripts.ini");
+ elsif (!$cmdline_argument) {
+ notify($ERRORS{'WARNING'}, 0, "CmdLine argument was not
specified");
return;
}
- # Apply Windows-style line endings to scripts.ini
- $self->set_text_file_line_endings($scripts_ini);
+ # Use lower case for all comparisons
+ $stage_argument = lc($stage_argument);
- # Get the modified contents of scripts.ini
- my $cat_modified_command = "cat $scripts_ini";
- my ($cat_modified_status, $cat_modified_output) =
$self->execute($cat_modified_command, 1);
- if (defined($cat_modified_status) && $cat_modified_status == 0) {
- notify($ERRORS{'DEBUG'}, 0, "retrieved modified scripts.ini
contents");
- }
- elsif (defined($cat_modified_status)) {
- notify($ERRORS{'WARNING'}, 0, "failed to cat scripts.ini
contents");
- }
- else {
- notify($ERRORS{'WARNING'}, 0, "unable to run ssh command to
scripts.ini contents");
- }
+ # Replace slashes with double backslashes
+ $cmdline_argument =~ s/[\\\/]+/\\/g;
+ my $cmdline_argument_escaped = quotemeta($cmdline_argument);
+
+ # Construct a hash which will be inserted into the scripts.ini info hash
+ my $script_argument = {
+ 'cmdline' => $cmdline_argument,
+ 'parameters' => $parameters_argument,
+ };
- ## Run gpupdate so the new settings take effect immediately
- #$self->run_gpupdate();
+ # Path to scripts.ini file
+ my $scripts_ini_directory_path =
"$system32_path/GroupPolicy/User/Scripts";
+ my $scripts_ini_file_path = "$scripts_ini_directory_path/scripts.ini";
- notify($ERRORS{'OK'}, 0, "added '$cmdline_argument' $stage_argument
script to scripts.ini\noriginal contents:\n$scripts_ini_string\n-----\nnew
contents:\n" . join("\n", @{$cat_modified_output}));
- return 1;
+ my $info = $self->get_user_group_policy_script_info() || return;
+
+ # Figure out the index to use
+ # Check if a script exists with an idential command line
+ my $add_index;
+ for my $index (sort {$a <=> $b} keys %{$info->{$stage_argument}}) {
+ my $cmdline = $info->{$stage_argument}{$index}{cmdline};
+ my $cmdline_escaped = quotemeta($cmdline);
+
+ if (lc($cmdline_escaped) eq lc($cmdline_argument_escaped)) {
+ $add_index = $index;
+ notify($ERRORS{'DEBUG'}, 0, "replacing existing
$stage_argument script at index $add_index:\n" .
+ "existing script:\n" .
format_data($info->{$stage_argument}{$index}) . "\n" .
+ "argument:\n" . format_data($script_argument)
+ );
+ last;
+ }
+ else {
+ notify($ERRORS{'DEBUG'}, 0, "command line of existing
$stage_argument script at index $index does not match argument:\n" .
+ "existing: '$cmdline_escaped'\n" .
+ "argument: '$cmdline_argument_escaped'"
+ );
+ }
+ }
+ if (!defined($add_index)) {
+ # Existing matching script not found, add to end
+ $add_index = scalar keys %{$info->{$stage_argument}};
+ notify($ERRORS{'DEBUG'}, 0, "existing $stage_argument script
was not found with command line matching argument, script will be added with
index $add_index");
+ }
+
+ # Add or replace the script defined by the arguments
+ $info->{$stage_argument}{$add_index} = $script_argument;
+ notify($ERRORS{'DEBUG'}, 0, "updated scripts.ini content:\n" .
format_data($info));
+
+ # Assemble the updated scripts.ini content
+ my $updated_contents;
+ for my $stage ('logon', 'logoff') {
+ # Capitalize the first letter and add it to the contents
+ my $stage_uc = ucfirst($stage);
+ $updated_contents .= "[$stage_uc]\n";
+
+ for my $index (sort {$a <=> $b} keys %{$info->{$stage}}) {
+ my $script = $info->{$stage}{$index};
+ $updated_contents .= $index . "CmdLine=" .
$script->{cmdline} . "\n";
+ $updated_contents .= $index . "Parameters=" .
$script->{parameters} . "\n";
+ }
+ }
+ notify($ERRORS{'DEBUG'}, 0, "updated $scripts_ini_file_path
contents:\n$updated_contents");
+ return $self->create_text_file($scripts_ini_file_path,
$updated_contents);
}
#/////////////////////////////////////////////////////////////////////////////
-=head2 remove_group_policy_script
+=head2 remove_user_group_policy_script
- Parameters :
- Returns :
- Description :
+ Parameters : $stage_argument, $cmdline_argument
+ Returns : boolean
+ Description : Removes a traditional (non-Powershell) user logon or logoff
+ script group policy from the computer. The stage argument must
+ either be 'logon' or 'logoff'.
=cut
-sub remove_group_policy_script {
+sub remove_user_group_policy_script {
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");
@@ -6188,226 +6152,97 @@ sub remove_group_policy_script {
my $system32_path = $self->get_system32_path() || return;
# Get the arguments
- my $stage_argument = shift;
- my $cmdline_argument = shift;
- if (!$stage_argument || $stage_argument !~ /^(logon|logoff)$/i) {
+ my ($stage_argument, $cmdline_argument, $parameters_argument) = @_;
+ if (!$stage_argument) {
notify($ERRORS{'WARNING'}, 0, "stage (logon/logoff) argument
was not specified");
return;
}
- if (!$cmdline_argument) {
+ elsif (!$stage_argument || $stage_argument !~ /^(logon|logoff)$/i) {
+ notify($ERRORS{'WARNING'}, 0, "stage argument is not valid:
$stage_argument, it must be 'logon' or 'logoff'");
+ return;
+ }
+ elsif (!$cmdline_argument) {
notify($ERRORS{'WARNING'}, 0, "CmdLine argument was not
specified");
return;
}
- # Capitalize the first letter of logon/logoff
+ # Use lower case for all comparisons
$stage_argument = lc($stage_argument);
- $stage_argument = "L" . substr($stage_argument, 1);
- # Store the stage name (logon/logoff) not being modified
- my $opposite_stage_argument;
- if ($stage_argument =~ /logon/i) {
- $opposite_stage_argument = 'Logoff';
- }
- else {
- $opposite_stage_argument = 'Logon';
- }
+ # Replace slashes with double backslashes
+ $cmdline_argument =~ s/[\\\/]+/\\/g;
+ my $cmdline_argument_escaped = quotemeta($cmdline_argument);
- # Attempt to delete batch or script files specified by the argument
-
$self->delete_files_by_pattern("$system32_path/GroupPolicy/User/Scripts",
".*$cmdline_argument.*", 2);
-
- # Path to scripts.ini file
- my $scripts_ini = $system32_path .
'/GroupPolicy/User/Scripts/scripts.ini';
-
- # Set the owner of scripts.ini to root
- my $chown_command = "touch $scripts_ini && chown root $scripts_ini";
- my ($chown_status, $chown_output) = $self->execute($chown_command);
- if (defined($chown_output) && grep(/no such file/i, @$chown_output)) {
- notify($ERRORS{'DEBUG'}, 0, "scripts.ini file does not exist,
nothing to remove");
- return 1;
- }
- elsif (defined($chown_status) && $chown_status == 0) {
- notify($ERRORS{'DEBUG'}, 0, "set root as owner of scripts.ini");
- }
- elsif (defined($chown_status)) {
- notify($ERRORS{'WARNING'}, 0, "failed to set root as owner of
scripts.ini, exit status: $chown_status, output:\n@{$chown_output}");
- }
- else {
- notify($ERRORS{'WARNING'}, 0, "unable to run ssh command to set
root as owner of scripts.ini");
- }
+ # Extract the last part of the command line if a full path was specified
+ my ($cmdline_argument_executable) = $cmdline_argument =~ /([^\\]+)$/;
- # Set the permissions of scripts.ini to 664
- my $chmod_command = "chmod 664 $scripts_ini";
- my ($chmod_status, $chmod_output) = $self->execute($chmod_command);
- if (defined($chmod_status) && $chmod_status == 0) {
- notify($ERRORS{'DEBUG'}, 0, "ran chmod on scripts.ini");
- }
- elsif (defined($chmod_status)) {
- notify($ERRORS{'WARNING'}, 0, "failed to run chmod 664 on
scripts.ini, exit status: $chmod_status, output:\n@{$chmod_output}");
- }
- else {
- notify($ERRORS{'WARNING'}, 0, "unable to run ssh command to run
chmod 664 on scripts.ini");
- }
+ # Path to scripts.ini file
+ my $scripts_ini_directory_path =
"$system32_path/GroupPolicy/User/Scripts";
+ my $scripts_ini_file_path = "$scripts_ini_directory_path/scripts.ini";
- # Clear hidden, system, and readonly flags on scripts.ini
- my $attrib_command = "attrib -H -S -R $scripts_ini";
- my ($attrib_status, $attrib_output) = $self->execute($attrib_command);
- if (defined($attrib_status) && $attrib_status == 0) {
- notify($ERRORS{'DEBUG'}, 0, "ran attrib -H -S -R on
scripts.ini");
- }
- elsif (defined($attrib_status)) {
- notify($ERRORS{'WARNING'}, 0, "failed to run attrib -H -S -R on
scripts.ini, exit status: $attrib_status, output:\n@{$attrib_output}");
- }
- else {
- notify($ERRORS{'WARNING'}, 0, "unable to run ssh command to run
attrib -H -S -R on scripts.ini");
- }
+ my $info = $self->get_user_group_policy_script_info() || return;
- # Get the contents of scripts.ini
- my $cat_command = "cat $scripts_ini";
- my ($cat_status, $cat_output) = $self->execute($cat_command, 1);
- if (defined($cat_status) && $cat_status == 0) {
- notify($ERRORS{'DEBUG'}, 0, "retrieved scripts.ini contents:\n"
. join("\n", @{$cat_output}));
- }
- elsif (defined($cat_status)) {
- notify($ERRORS{'WARNING'}, 0, "failed to cat scripts.ini
contents");
+ # Attempt to delete batch or script files specified by the argument
+ if ($cmdline_argument =~ /\\/) {
+ $self->delete_file($cmdline_argument);
}
else {
- notify($ERRORS{'WARNING'}, 0, "unable to run ssh command to
scripts.ini contents");
+
$self->delete_files_by_pattern("$system32_path/GroupPolicy/User/Scripts",
".*$cmdline_argument.*", 2);
}
- # Create a string containing all of the lines in scripts.ini
- my $scripts_ini_string = join("\n", @{$cat_output}) || '';
-
- # Remove any carriage returns to make pattern matching easier
- $scripts_ini_string =~ s/\r//gs;
-
- # Get a string containing just the section being modified (logon/logoff)
- my ($section_string) = $scripts_ini_string =~
/(\[$stage_argument\][^\[\]]*)/is;
- $section_string = "[$stage_argument]" if !$section_string;
- notify($ERRORS{'DEBUG'}, 0, "scripts.ini $stage_argument section:\n" .
string_to_ascii($section_string));
-
- my ($opposite_section_string) = $scripts_ini_string =~
/(\[$opposite_stage_argument\][^\[\]]*)/is;
- $opposite_section_string = "[$opposite_stage_argument]" if
!$opposite_section_string;
- notify($ERRORS{'DEBUG'}, 0, "scripts.ini $opposite_stage_argument
section:\n" . string_to_ascii($opposite_section_string));
-
- my @section_lines = split(/[\r\n]+/, $section_string);
- notify($ERRORS{'DEBUG'}, 0, "scripts.ini $stage_argument section line
count: " . scalar @section_lines);
-
- my %scripts_original;
- for my $section_line (@section_lines) {
- if ($section_line =~ /(\d+)Parameters\s*=(.*)/i) {
- my $index = $1;
- my $parameters = $2;
- if (!defined $scripts_original{$index}{Parameters}) {
- $scripts_original{$index}{Parameters} =
$parameters;
- #notify($ERRORS{'DEBUG'}, 0, "found
$stage_argument parameters:\nline: '$section_line'\nparameters:
'$parameters'\nindex: $index");
- }
- else {
- notify($ERRORS{'WARNING'}, 0, "found duplicate
$stage_argument parameters line for index $index");
- }
- }
- elsif ($section_line =~ /(\d+)CmdLine\s*=(.*)/i) {
- my $index = $1;
- my $cmdline = $2;
- if (!defined $scripts_original{$index}{CmdLine}) {
- $scripts_original{$index}{CmdLine} = $cmdline;
- #notify($ERRORS{'DEBUG'}, 0, "found
$stage_argument cmdline:\nline: '$section_line'\ncmdline: '$cmdline'\nindex:
$index");
- }
- else {
- notify($ERRORS{'WARNING'}, 0, "found duplicate
$stage_argument CmdLine line for index $index");
- }
- }
- elsif ($section_line =~ /\[$stage_argument\]/i) {
- #notify($ERRORS{'DEBUG'}, 0, "found $stage_argument
heading:\nline: '$section_line'");
- }
- else {
- notify($ERRORS{'WARNING'}, 0, "found unexpected line:
'$section_line'");
- }
- }
-
- my %scripts_modified;
- my $index_modified = 0;
- foreach my $index (sort keys %scripts_original) {
- if (!defined $scripts_original{$index}{CmdLine}) {
- notify($ERRORS{'WARNING'}, 0, "CmdLine not specified
for index $index");
+ # Find matching scripts, delete from hash if found
+ my $found_match = 0;
+ for my $index (sort {$a <=> $b} keys %{$info->{$stage_argument}}) {
+ my $cmdline = $info->{$stage_argument}{$index}{cmdline};
+ my $cmdline_escaped = quotemeta($cmdline);
+
+ # Extract the last part of the command line if it contains a
full path
+ my ($cmdline_executable) = $cmdline =~ /([^\\]+)$/;
+
+ if (lc($cmdline_escaped) eq lc($cmdline_argument_escaped)) {
+ $found_match = 1;
+ notify($ERRORS{'DEBUG'}, 0, "existing command line
matches argument, removing $stage_argument script at index $index:\n" .
+ "argument: $cmdline_argument\n" .
+ "existing script:\n" .
format_data($info->{$stage_argument}{$index})
+ );
+ delete $info->{$stage_argument}{$index};
next;
}
- elsif ($scripts_original{$index}{CmdLine} =~ /^\s*$/) {
- notify($ERRORS{'WARNING'}, 0, "CmdLine blank for index
$index");
+ elsif ($cmdline_argument_executable && $cmdline_executable &&
lc($cmdline_argument_executable) eq lc($cmdline_executable)) {
+ $found_match = 1;
+ notify($ERRORS{'DEBUG'}, 0, "existing command line
executable matches argument executable, removing $stage_argument script at
index $index:\n" .
+ "argument: $cmdline_argument\n" .
+ "argument executable:
$cmdline_argument_executable\n" .
+ "existing script:\n" .
format_data($info->{$stage_argument}{$index})
+ );
+ delete $info->{$stage_argument}{$index};
next;
}
- if (!defined $scripts_original{$index}{Parameters}) {
- notify($ERRORS{'WARNING'}, 0, "Parameters not specified
for index $index");
- $scripts_original{$index}{Parameters} = '';
- }
-
- if ($scripts_original{$index}{CmdLine} =~ /$cmdline_argument/i)
{
- notify($ERRORS{'DEBUG'}, 0, "removing $stage_argument
script at index $index:\ncmdline:
$scripts_original{$index}{CmdLine}\nparameters:
$scripts_original{$index}{Parameters}");
- }
- else {
- notify($ERRORS{'DEBUG'}, 0, "retaining existing
$stage_argument script at index $index:\ncmdline:
$scripts_original{$index}{CmdLine}\nparameters:
$scripts_original{$index}{Parameters}");
- $scripts_modified{$index_modified}{CmdLine} =
$scripts_original{$index}{CmdLine};
- $scripts_modified{$index_modified}{Parameters} =
$scripts_original{$index}{Parameters};
- $index_modified++;
- }
- }
-
- my $section_string_new = "[$stage_argument]\n";
- foreach my $index_new (sort keys(%scripts_modified)) {
- $section_string_new .= $index_new .
"CmdLine=$scripts_modified{$index_new}{CmdLine}\n";
- $section_string_new .= $index_new .
"Parameters=$scripts_modified{$index_new}{Parameters}\n";
- }
-
- notify($ERRORS{'DEBUG'}, 0, "original $stage_argument scripts
section:\n$section_string");
- notify($ERRORS{'DEBUG'}, 0, "modified $stage_argument scripts
section:\n$section_string_new");
-
- my $scripts_ini_modified;
- if ($stage_argument =~ /logon/i) {
- $scripts_ini_modified =
"$section_string_new\n$opposite_section_string";
- }
- else {
- $scripts_ini_modified =
"$opposite_section_string\n$section_string_new";
}
- notify($ERRORS{'DEBUG'}, 0, "modified scripts.ini
contents:\n$scripts_ini_modified");
- $scripts_ini_modified =~ s/"/\\"/gs;
-
- # Echo the modified contents to scripts.ini
- my $echo_command = "echo \"$scripts_ini_modified\" > $scripts_ini";
- my ($echo_status, $echo_output) = $self->execute($echo_command);
- if (defined($echo_status) && $echo_status == 0) {
- notify($ERRORS{'DEBUG'}, 0, "echo'd modified contents to
scripts.ini");
- }
- elsif (defined($echo_status)) {
- notify($ERRORS{'WARNING'}, 0, "failed to echo modified contents
to scripts.ini, exit status: $echo_status, output:\n@{$echo_output}");
- return;
- }
- else {
- notify($ERRORS{'WARNING'}, 0, "unable to run ssh command to
echo modified contents to scripts.ini");
- return;
+ if (!$found_match) {
+ notify($ERRORS{'DEBUG'}, 0, "scripts.ini update not necessary,
did not find existing script matching command line argument:
$cmdline_argument");
+ return 1;
}
- # Apply Windows-style line endings to scripts.ini
- $self->set_text_file_line_endings($scripts_ini);
-
- # Get the modified contents of scripts.ini
- my $cat_modified_command = "cat $scripts_ini";
- my ($cat_modified_status, $cat_modified_output) =
$self->execute($cat_modified_command, 1);
- if (defined($cat_modified_status) && $cat_modified_status == 0) {
- notify($ERRORS{'DEBUG'}, 0, "retrieved modified scripts.ini
contents");
- }
- elsif (defined($cat_modified_status)) {
- notify($ERRORS{'WARNING'}, 0, "failed to cat scripts.ini
contents");
- }
- else {
- notify($ERRORS{'WARNING'}, 0, "unable to run ssh command to
scripts.ini contents");
+ # Assemble the updated scripts.ini content
+ my $updated_contents;
+ for my $stage ('logon', 'logoff') {
+ # Capitalize the first letter and add it to the contents
+ my $stage_uc = ucfirst($stage);
+ $updated_contents .= "[$stage_uc]\n";
+
+ # Reindex in case there are now gaps
+ my $new_index = 0;
+ for my $original_index (sort {$a <=> $b} keys
%{$info->{$stage}}) {
+ my $script = $info->{$stage}{$original_index};
+ $updated_contents .= $new_index . "CmdLine=" .
$script->{cmdline} . "\n";
+ $updated_contents .= $new_index . "Parameters=" .
$script->{parameters} . "\n";
+ $new_index++;
+ }
}
-
- notify($ERRORS{'OK'}, 0, "removed '$cmdline_argument' $stage_argument
script from scripts.ini\noriginal contents:\n$scripts_ini_string\n-----\nnew
contents:\n" . join("\n", @{$cat_modified_output}));
-
- ## Run gpupdate so the new settings take effect immediately
- #$self->run_gpupdate();
-
- return 1;
+ notify($ERRORS{'DEBUG'}, 0, "updated $scripts_ini_file_path
contents:\n$updated_contents");
+ return $self->create_text_file($scripts_ini_file_path,
$updated_contents);
}
#/////////////////////////////////////////////////////////////////////////////
@@ -6994,9 +6829,9 @@ sub is_process_running {
=head2 start_service
- Parameters :
- Returns :
- Description :
+ Parameters : $service_name
+ Returns : boolean
+ Description : Starts a service on the computer.
=cut
@@ -7014,24 +6849,35 @@ sub start_service {
notify($ERRORS{'WARNING'}, 0, "service name was not passed as
an argument");
return;
}
+
+ # The Client for NFS service should be controlled with the nfsadmin.exe
utility
+ if ($service_name =~ /^NfsClnt$/) {
+ if ($self->start_nfs_client_service()) {
+ return 1;
+ }
+ }
my $command = $system32_path . '/net.exe start "' . $service_name . '"';
- my ($status, $output) = $self->execute($command);
- if (defined($status) && $status == 0) {
- notify($ERRORS{'OK'}, 0, "started service: $service_name");
+ my ($exit_status, $output) = $self->execute($command);
+ if (!defined($output)) {
+ notify($ERRORS{'WARNING'}, 0, "failed to execute command to to
start service: $service_name");
+ return;
}
- elsif (defined($output) && grep(/already been started/i, @{$output})) {
- notify($ERRORS{'OK'}, 0, "service has already been started:
$service_name");
+ elsif (grep(/is not started/i, @{$output})) {
+ notify($ERRORS{'OK'}, 0, "service is not started:
$service_name");
+ return 1;
}
- elsif (defined($status)) {
- notify($ERRORS{'WARNING'}, 0, "unable to start service:
$service_name, exit status: $status, output:\n@{$output}");
+ elsif (grep(/(does not exist|service name is invalid)/i, @$output)) {
+ notify($ERRORS{'WARNING'}, 0, "service could not be started
because it does not exist: $service_name, output:\n" . join("\n", @$output));
return 0;
}
- else {
- notify($ERRORS{'WARNING'}, 0, "unable to run ssh command to to
start service: $service_name");
+ elsif ($exit_status || grep(/could not be started/i, @$output)) {
+ notify($ERRORS{'WARNING'}, 0, "failed to start service:
$service_name, exit status: $exit_status, command:\n$command\noutput:\n" .
join("\n", @$output));
return 0;
}
-
+ else {
+ notify($ERRORS{'OK'}, 0, "started service: $service_name" .
join("\n", @$output));
+ }
return 1;
} ## end sub start_service
@@ -7039,9 +6885,9 @@ sub start_service {
=head2 stop_service
- Parameters :
- Returns :
- Description :
+ Parameters : $service_name
+ Returns : boolean
+ Description : Stops a service on the computer.
=cut
@@ -7059,28 +6905,35 @@ sub stop_service {
notify($ERRORS{'WARNING'}, 0, "service name was not passed as
an argument");
return;
}
-
+
+ # The Client for NFS service should be controlled with the nfsadmin.exe
utility
+ if ($service_name =~ /^NfsClnt$/) {
+ if ($self->stop_nfs_client_service()) {
+ return 1;
+ }
+ }
+
my $command = $system32_path . '/net.exe stop "' . $service_name . '"';
- my ($status, $output) = $self->execute($command);
- if (defined($status) && $status == 0) {
- notify($ERRORS{'OK'}, 0, "stopped service: $service_name");
+ my ($exit_status, $output) = $self->execute($command);
+ if (!defined($output)) {
+ notify($ERRORS{'WARNING'}, 0, "failed to execute command to to
stop service: $service_name");
+ return;
}
- elsif (defined($output) && grep(/is not started/i, @{$output})) {
+ elsif (grep(/is not started/i, @{$output})) {
notify($ERRORS{'OK'}, 0, "service is not started:
$service_name");
+ return 1;
}
- elsif (defined($output) && grep(/does not exist/i, @{$output})) {
- notify($ERRORS{'WARNING'}, 0, "service was not stopped because
it does not exist: $service_name");
- return;
+ elsif (grep(/(does not exist|service name is invalid)/i, @$output)) {
+ notify($ERRORS{'OK'}, 0, "service was not stopped because it
does not exist: $service_name, output:\n" . join("\n", @$output));
+ return 1;
}
- elsif (defined($status)) {
- notify($ERRORS{'WARNING'}, 0, "unable to stop service:
$service_name, exit status: $status, output:\n@{$output}");
+ elsif ($exit_status || grep(/could not be stopped/i, @$output)) {
+ notify($ERRORS{'WARNING'}, 0, "failed to stop service:
$service_name, exit status: $exit_status, command:\n$command\noutput:\n" .
join("\n", @$output));
return 0;
}
else {
- notify($ERRORS{'WARNING'}, 0, "unable to run ssh command to to
stop service: $service_name");
- return 0;
+ notify($ERRORS{'OK'}, 0, "stopped service: $service_name" .
join("\n", @$output));
}
-
return 1;
} ## end sub stop_service
@@ -7101,7 +6954,7 @@ sub restart_service {
notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a
function, it must be called as a class method");
return;
}
-
+
my $system32_path = $self->get_system32_path() || return;
my $service_name = shift;
@@ -7110,21 +6963,8 @@ sub restart_service {
return;
}
- my $command = "$system32_path/net.exe stop \"$service_name\" ;
$system32_path/net.exe start \"$service_name\"";
- my ($status, $output) = $self->execute($command, 1);
- if (!defined($output)) {
- notify($ERRORS{'WARNING'}, 0, "failed to run command to restart
service: $service_name");
- return;
- }
- elsif ($status == 0) {
- notify($ERRORS{'OK'}, 0, "restarted service: $service_name,
output:\n" . join("\n", @$output));
- }
- else {
- notify($ERRORS{'WARNING'}, 0, "failed to restart service:
$service_name, exit status: $status, output:\n" . join("\n", @$output));
- return 0;
- }
-
- return 1;
+ $self->stop_service($service_name);
+ return $self->start_service($service_name);
}
#/////////////////////////////////////////////////////////////////////////////
@@ -7175,6 +7015,94 @@ sub service_exists {
return 1;
}
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 start_nfs_client_service
+
+ Parameters : none
+ Returns : boolean
+ Description : Starts the Client for NFS (NfsClnt) service on the computer
using
+ the nfsadmin.exe utility.
+
+=cut
+
+sub start_nfs_client_service {
+ 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 $system32_path = $self->get_system32_path() || return;
+
+ my $command = $system32_path . '/nfsadmin.exe client start';
+ my ($exit_status, $output) = $self->execute($command);
+ if (!defined($output)) {
+ notify($ERRORS{'WARNING'}, 0, "failed to execute command to to
start Client for NFS service");
+ return;
+ }
+ elsif (grep(/already started/i, @$output)) {
+ notify($ERRORS{'OK'}, 0, "Client for NFS service is already
started");
+ return 1;
+ }
+ elsif (grep(/no such file/i, @$output)) {
+ notify($ERRORS{'OK'}, 0, "failed to start Client for NFS
service using nfsadmin.exe because utility does not exist on computer");
+ return;
+ }
+ elsif ($exit_status) {
+ notify($ERRORS{'WARNING'}, 0, "failed to start Client for NFS
service, exit status: $exit_status, command: '$command', output:\n" .
join("\n", @$output));
+ return 0;
+ }
+ else {
+ notify($ERRORS{'OK'}, 0, "started Client for NFS service,
output:\n" . join("\n", @$output));
+ }
+ return 1;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 stop_nfs_client_service
+
+ Parameters : none
+ Returns : boolean
+ Description : Stops the Client for NFS (NfsClnt) service on the computer using
+ the nfsadmin.exe utility.
+
+=cut
+
+sub stop_nfs_client_service {
+ 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 $system32_path = $self->get_system32_path() || return;
+
+ my $command = $system32_path . '/nfsadmin.exe client stop';
+ my ($exit_status, $output) = $self->execute($command);
+ if (!defined($output)) {
+ notify($ERRORS{'WARNING'}, 0, "failed to execute command to to
stop Client for NFS service");
+ return;
+ }
+ elsif (grep(/not been started/i, @$output)) {
+ notify($ERRORS{'OK'}, 0, "Client for NFS service is not
started");
+ return 1;
+ }
+ elsif (grep(/no such file/i, @$output)) {
+ notify($ERRORS{'OK'}, 0, "failed to stop Client for NFS service
using nfsadmin.exe because utility does not exist on computer");
+ return;
+ }
+ elsif ($exit_status) {
+ notify($ERRORS{'WARNING'}, 0, "failed to stop Client for NFS
service, exit status: $exit_status, command: '$command', output:\n" .
join("\n", @$output));
+ return 0;
+ }
+ else {
+ notify($ERRORS{'OK'}, 0, "stopped Client for NFS service,
output:\n" . join("\n", @$output));
+ }
+ return 1;
+}
+
#/////////////////////////////////////////////////////////////////////////////
=head2 get_installed_applications