lib/core/jea/Read-IcingaPowerShellModuleFile.psm1
function Read-IcingaPowerShellModuleFile() { param ( [string]$File, [string]$FileContent = '' ); if (([string]::IsNullOrEmpty($File) -Or (Test-Path -Path $File) -eq $FALSE) -And [string]::IsNullOrEmpty($FileContent)) { return ''; } if ([string]::IsNullOrEmpty($FileContent)) { $FileContent = Read-IcingaFileSecure -File $File; } $PSParser = [System.Management.Automation.PSParser]::Tokenize($FileContent, [ref]$null); [array]$Comments = @(); [array]$RegexFilter = @(); [string]$RegexPattern = ''; [array]$CommandList = @(); [array]$FunctionList = @(); [hashtable]$CmdCache = @{ }; [hashtable]$FncCache = @{ }; [int]$Index = 0; [bool]$ThreadCommand = $FALSE; [bool]$ThreadFetchNext = $FALSE; [bool]$ShellCommand = $FALSE; [bool]$ShellGroupStart = $FALSE; foreach ($entry in $PSParser) { if ($entry.Type -eq 'Comment') { $Comments += Select-Object -InputObject $entry -ExpandProperty 'Content'; } elseif ($entry.Type -eq 'Command') { if ($CmdCache.ContainsKey($entry.Content) -eq $FALSE) { $CommandList += [string]$entry.Content; $CmdCache.Add($entry.Content, 0); } # We need to include commands we call with New-IcingaThreadInstance e.g. # => New-IcingaThreadInstance -Name "Main" -ThreadPool (Get-IcingaThreadPool -Name 'MainPool') -Command 'Add-IcingaForWindowsDaemon' -Start; if ($entry.Content.ToLower() -eq 'new-icingathreadinstance') { $ThreadCommand = $TRUE; } } elseif ($entry.Type -eq 'CommandArgument') { if ($PSParser[$index - 1].Type -eq 'Keyword' -And $PSParser[$index - 1].Content.ToLower() -eq 'function') { if ($FncCache.ContainsKey($entry.Content) -eq $FALSE) { $FunctionList += [string]$entry.Content; $FncCache.Add($entry.Content, 0); } } } elseif ($entry.Type -eq 'Member' -And $entry.Content.ToLower() -eq 'addcommand') { # In case we have objects that use .AddCommand() we should add these to our function list e.g. # => [void]$Shell.AddCommand('Set-IcingaEnvironmentGlobal'); $ShellCommand = $TRUE; } # If we reached -Command for New-IcingaThreadInstance, check for the String element and add its value to our function list e.g. # => Add-IcingaForWindowsDaemon if ($ThreadFetchNext) { if ($entry.Type -eq 'String') { if (Test-IcingaFunction $entry.Content) { if ($FncCache.ContainsKey($entry.Content) -eq $FALSE) { $FunctionList += [string]$entry.Content; $FncCache.Add($entry.Content, 0); } } } $ThreadFetchNext = $FALSE; } # If we found the command New-IcingaThreadInstance inside ths script, loop until we reach -Command if ($ThreadCommand) { if ($entry.Type -eq 'CommandParameter' -And $entry.Content.ToLower() -eq '-command') { $ThreadFetchNext = $TRUE; $ThreadCommand = $FALSE; } } # If we reached the string content of our .AddCommand() object. add its value to our function list e.g. # => Set-IcingaEnvironmentGlobal if ($ShellGroupStart) { if ($entry.Type -eq 'String') { if (Test-IcingaFunction $entry.Content) { if ($FncCache.ContainsKey($entry.Content) -eq $FALSE) { $FunctionList += [string]$entry.Content; $FncCache.Add($entry.Content, 0); } } $ShellGroupStart = $FALSE; } } # If we found an .AddArgument() member, continue until our group starts with ( if ($ShellCommand) { if ($entry.Type -eq 'GroupStart' -And $entry.Content.ToLower() -eq '(') { $ShellCommand = $FALSE; $ShellGroupStart = $TRUE; } } $Index += 1; } foreach ($entry in $Comments) { $RegexFilter += [regex]::Escape($entry); } $RegexPattern = [string]::Join('|', $RegexFilter); return @{ 'NormalisedContent' = ($FileContent -Replace $RegexPattern -Split '\r?\n' -NotMatch '^\s*$'); 'RawContent' = $FileContent; 'CommandList' = $CommandList; 'FunctionList' = $FunctionList; }; } |