From afb84ee82d9a664388f949423a02e6e4a62764a0 Mon Sep 17 00:00:00 2001 From: David Durieux Date: Tue, 23 Feb 2021 11:16:20 +0100 Subject: [PATCH] Inventory Linux and Windows crontasks --- lib/FusionInventory/Agent/Inventory.pm | 6 +- .../Agent/Task/Inventory/Generic/Crontasks.pm | 175 +++++++++++++ .../Agent/Task/Inventory/Win32/Crontasks.pm | 239 ++++++++++++++++++ 3 files changed, 419 insertions(+), 1 deletion(-) create mode 100644 lib/FusionInventory/Agent/Task/Inventory/Generic/Crontasks.pm create mode 100644 lib/FusionInventory/Agent/Task/Inventory/Win32/Crontasks.pm diff --git a/lib/FusionInventory/Agent/Inventory.pm b/lib/FusionInventory/Agent/Inventory.pm index 70c1986491..305677af91 100644 --- a/lib/FusionInventory/Agent/Inventory.pm +++ b/lib/FusionInventory/Agent/Inventory.pm @@ -102,7 +102,11 @@ my %fields = ( VOLUME_GROUPS => [ qw/VG_NAME PV_COUNT LV_COUNT ATTR SIZE FREE VG_UUID VG_EXTENT_SIZE/ ], VERSIONPROVIDER => [ qw/NAME VERSION COMMENTS PERL_EXE PERL_VERSION PERL_ARGS - PROGRAM PERL_CONFIG PERL_INC PERL_MODULE/ ] + PROGRAM PERL_CONFIG PERL_INC PERL_MODULE/ ], + CRONTASKS => [ qw/NAME DESCRIPTION COMMAND EXECUTION_MONTH + EXECUTION_DAY EXECUTION_HOUR EXECUTION_MINUTE + EXECUTION_WEEKDAY USER_EXECUTION STORAGE + USER_STORAGE STATUS/ ] ); my %checks = ( diff --git a/lib/FusionInventory/Agent/Task/Inventory/Generic/Crontasks.pm b/lib/FusionInventory/Agent/Task/Inventory/Generic/Crontasks.pm new file mode 100644 index 0000000000..34aba2d3b7 --- /dev/null +++ b/lib/FusionInventory/Agent/Task/Inventory/Generic/Crontasks.pm @@ -0,0 +1,175 @@ +package FusionInventory::Agent::Task::Inventory::Generic::Crontasks; + +use strict; +use warnings; + +use parent 'FusionInventory::Agent::Task::Inventory::Module'; + +use English qw(-no_match_vars); + +use File::Find; + +use FusionInventory::Agent::Tools; + +sub isEnabled { + my (%params) = @_; + + # return if $params{no_category}->{crontasks}; + + # Not working under win32 + return 0 if $OSNAME eq 'MSWin32'; + + return + canRun('crontab'); +} + +sub doInventory { + my (%params) = @_; + + my $inventory = $params{inventory}; + my $logger = $params{logger}; + + my %tasks; + + foreach my $task (_getTasks(logger => $logger)) { + $inventory->addEntry( + section => 'CRONTASKS', + entry => $task + ); + } + foreach my $task (_getUsersTasks(logger => $logger)) { + $inventory->addEntry( + section => 'CRONTASKS', + entry => $task + ); + } +} + +sub _getTasks { + + my @files; + my @folders; + push @files, '/etc/crontab'; + push @folders, '/etc/cron.d'; + if (-d '/etc/cron.daily') { + push @folders, '/etc/cron.daily'; + } + if (-d '/etc/cron.hourly') { + push @folders, '/etc/cron.hourly'; + } + if (-d '/etc/cron.monthly') { + push @folders, '/etc/cron.monthly'; + } + if (-d '/etc/cron.weekly') { + push @folders, '/etc/cron.weekly'; + } + + my $searchFiles = sub { + return if (($_ eq '.') || ($_ eq '..')); + if (-d && $_ eq 'fp') { + $File::Find::prune = 1; + return; + } + return if (-d); + push @files, $File::Find::name; + }; + + find($searchFiles, @folders); + + my @tasks; + foreach my $filename (@files) { + + my (%params) = ( + file => '/etc/crontab', + @_ + ); + + my $handle = getFileHandle(( + file => $filename, + @_ + )); + continue unless $handle; + + my $description = ''; + my $pathFound = 0; + while (my $line = <$handle>) { + if ($line =~ /^PATH/) { + $pathFound = 1; + next; + } + next if !$pathFound; + if ($line =~ /^[#\s*|]$/) { + $description = ''; + next; + } + if ($line =~ /^#\s*\w/) { + $description .= substr($line, 1); + } else { + my @args = split(/\s+/, $line, 7); + push @tasks, { + NAME => $args[6], + DESCRIPTION => trimWhitespace($description), + COMMAND => $args[6], + EXECUTION_MONTH => $args[3], + EXECUTION_DAY => $args[2], + EXECUTION_HOUR => $args[1], + EXECUTION_MINUTE => $args[0], + EXECUTION_WEEKDAY => $args[4], + USER_EXECUTION => $args[5], + STORAGE => $filename, + USER_STORAGE => 'system', + STATUS => 1 + }; + } + } + close $handle; + } + return @tasks; +} + +sub _getUsersTasks { + my (%params) = @_; + + my $logger = $params{logger}; + + my @tasks; + foreach my $user (FusionInventory::Agent::Task::Inventory::Generic::Users::_getLocalUsers(logger => $logger)) { + + my $handle = getFileHandle(( + command => 'crontab -u '.$user->{LOGIN}.' -l', + @_ + )); + continue unless $handle; + + my $description = ''; + while (my $line = <$handle>) { + $line =~ s/\R//g; + if ($line =~ /^[#\s*|]$/) { + $description = ''; + next; + } + if ($line =~ /^#\s*\w/) { + $description .= substr($line, 1); + } else { + my @args = split(/\s+/, $line, 6); + push @tasks, { + NAME => $args[5], + DESCRIPTION => trimWhitespace($description), + COMMAND => $args[5], + EXECUTION_MONTH => $args[3], + EXECUTION_DAY => $args[2], + EXECUTION_HOUR => $args[1], + EXECUTION_MINUTE => $args[0], + EXECUTION_WEEKDAY => $args[4], + USER_EXECUTION => $user->{LOGIN}, + STORAGE => $user->{LOGIN}, + USER_STORAGE => 'user', + STATUS => 1 + }; + } + } + close $handle; + } + return @tasks; +} +1; diff --git a/lib/FusionInventory/Agent/Task/Inventory/Win32/Crontasks.pm b/lib/FusionInventory/Agent/Task/Inventory/Win32/Crontasks.pm new file mode 100644 index 0000000000..47dc98635e --- /dev/null +++ b/lib/FusionInventory/Agent/Task/Inventory/Win32/Crontasks.pm @@ -0,0 +1,239 @@ +package FusionInventory::Agent::Task::Inventory::Win32::Crontasks; + +use strict; +use warnings; + +use parent 'FusionInventory::Agent::Task::Inventory::Module'; + +use English qw(-no_match_vars); + +use File::Find; +use XML::XPath; +use XML::XPath::XMLParser; + +use FusionInventory::Agent::Tools; +use FusionInventory::Agent::Tools::Win32; + +sub isEnabled { + my (%params) = @_; + return 1; +} + +sub doInventory { + my (%params) = @_; + + my $inventory = $params{inventory}; + my $logger = $params{logger}; + + my %tasks; + + foreach my $task (_getTasks(logger => $logger)) { + $inventory->addEntry( + section => 'CRONTASKS', + entry => $task + ); + } + +} + +sub _getTasks { + + my @files; + + my $searchFiles = sub { + return if (($_ eq '.') || ($_ eq '..')); + if (-d && $_ eq 'fp') { + $File::Find::prune = 1; + return; + } + return if (-d); + push @files, $File::Find::name; + }; + + find($searchFiles, 'C:\Windows\System32\Tasks'); + + my @tasks; + foreach my $filename (@files) { + my $xp = XML::XPath->new(filename => $filename); + my @path = split(/\//, $filename); + + my $completeCommand = ''; + my $nodes = $xp->findnodes('/Task/Actions/Exec'); + foreach my $node ($nodes->get_nodelist) { + if ($completeCommand ne '') { + $completeCommand .= ' && '; + } + my $command = $node->find('Command')->string_value; + my $arguments = $node->find('Arguments')->string_value; + my $workingDir = $node->find('WorkingDirectory')->string_value; + if ($workingDir ne '') { + $completeCommand .= $workingDir.' && '; + } + if ($command ne '') { + $completeCommand .= $command; + } + if ($arguments ne '') { + $completeCommand .= ' '.$arguments; + } + } + + my $executionMinute = ''; + my $executionHour = ''; + my $executionDay = ''; + my $executionMonth = ''; + my $executionWeekday = ''; + + my $triggers = $xp->findnodes('/Task/Triggers/CalendarTrigger'); + foreach my $trigger ($triggers->get_nodelist) { + my $interval = $trigger->find('Repetition/Interval')->string_value; + my $daysInterval = $trigger->find('ScheduleByDay/DaysInterval')->string_value; + my $startDate = $trigger->find('StartBoundary')->string_value; + + if ($interval =~ /(\w+)(\d+)(\w+)/) { + if ($3 eq 'M') { + $executionMinute = '*/'.$2; + $executionHour = '*'; + $executionDay = '*'; + $executionMonth = '*'; + $executionWeekday = '*'; + } elsif ($3 eq 'H') { + $executionMinute = '0'; + $executionHour = '*/'.$2; + $executionDay = '*'; + $executionMonth = '*'; + $executionWeekday = '*'; + } elsif ($3 eq 'D') { + $executionMinute = '0'; + $executionHour = '0'; + $executionDay = '*/'.$2; + $executionMonth = '*'; + $executionWeekday = '*'; + } + } else { + # no interval, so get the start hour + if ($startDate =~ /T(\d{2}):(\d{2})/) { + $executionMinute = ($2 + 0); + $executionHour = ($1 + 0); + } + } + if ($trigger->find('ScheduleByDay')) { + $executionDay = '*/'.$trigger->find('ScheduleByDay/DaysInterval')->string_value; + } + if ($trigger->find('DaysOfWeek')) { + my @wdays; + if ($trigger->find('DaysOfWeek/Sunday')) { + push(@wdays, 0); + } + if ($trigger->find('DaysOfWeek/Monday')) { + push(@wdays, 1); + } + if ($trigger->find('DaysOfWeek/Tuesday')) { + push(@wdays, 2); + } + if ($trigger->find('DaysOfWeek/Wednesday')) { + push(@wdays, 3); + } + if ($trigger->find('DaysOfWeek/Thursday')) { + push(@wdays, 4); + } + if ($trigger->find('DaysOfWeek/Friday')) { + push(@wdays, 5); + } + if ($trigger->find('DaysOfWeek/Saturday')) { + push(@wdays, 6); + } + $executionWeekday = join(",", @wdays); + } + if ($trigger->find('ScheduleByMonth')) { + if ($trigger->find('ScheduleByMonth/DaysOfMonth')) { + my @wdays; + my $daysOfMonth = $trigger->findnodes('ScheduleByMonth/DaysOfMonth'); + foreach my $day ($daysOfMonth->get_nodelist) { + push(@wdays, $day->string_value); + } + $executionDay = join(",", @wdays); + } + if ($trigger->find('ScheduleByMonth/Months')) { + my @months; + my $byMonths = $trigger->findnodes('ScheduleByMonth/Months'); + if ($trigger->find('ScheduleByMonth/Months/January')) { + push(@months, 1); + } + if ($trigger->find('ScheduleByMonth/Months/February')) { + push(@months, 2); + } + if ($trigger->find('ScheduleByMonth/Months/March')) { + push(@months, 3); + } + if ($trigger->find('ScheduleByMonth/Months/April')) { + push(@months, 4); + } + if ($trigger->find('ScheduleByMonth/Months/May')) { + push(@months, 5); + } + if ($trigger->find('ScheduleByMonth/Months/June')) { + push(@months, 6); + } + if ($trigger->find('ScheduleByMonth/Months/July')) { + push(@months, 7); + } + if ($trigger->find('ScheduleByMonth/Months/August')) { + push(@months, 8); + } + if ($trigger->find('ScheduleByMonth/Months/September')) { + push(@months, 9); + } + if ($trigger->find('ScheduleByMonth/Months/October')) { + push(@months, 10); + } + if ($trigger->find('ScheduleByMonth/Months/November')) { + push(@months, 11); + } + if ($trigger->find('ScheduleByMonth/Months/December')) { + push(@months, 12); + } + $executionMonth = join(",", @months); + } + } + } + # Replace */1 in * (more simple to read) + if ($executionMonth eq '*/1') { + $executionMonth = '*'; + } + if ($executionDay eq '*/1') { + $executionDay = '*'; + } + if ($executionHour eq '*/1') { + $executionHour = '*'; + } + if ($executionMinute eq '*/1') { + $executionMinute = '*'; + } + if ($executionWeekday eq '*/1') { + $executionWeekday = '*'; + } + my $status = 1; + if ($xp->find('/Task/Settings/Enabled')->string_value ne 'true') { + $status = 0; + } + + + push @tasks, { + NAME => $path[-1], + DESCRIPTION => $xp->find('/Task/RegistrationInfo/Description')->string_value, + COMMAND => $completeCommand, + EXECUTION_MONTH => $executionMonth, + EXECUTION_DAY => $executionDay, + EXECUTION_HOUR => $executionHour, + EXECUTION_MINUTE => $executionMinute, + EXECUTION_WEEKDAY => $executionWeekday, + USER_EXECUTION => $xp->find('/Task/Principals/Principal/UserId')->string_value, + STORAGE => $filename, + USER_STORAGE => $xp->find('/Task/RegistrationInfo/Author')->string_value, + STATUS => $status + }; + } + + return @tasks; +} +1;