private/support.ps1
|
# add helper functions here function getGridSelect { <# .SYNOPSIS Display a grid view of the specified data set and return the selected item(s) .DESCRIPTION Display a grid view of the specified data set and return the selected item(s) .PARAMETER DataSet Specify the data set (array, arraylist, collection) to display in the grid view .PARAMETER Title Specify the title of the grid view window .PARAMETER OutputMode Specify the output mode: Single or Multiple. Default is Multiple .EXAMPLE getGridSelect -DataSet $users -Title "Select a User Account" -OutputMode Single Display a grid view of the specified data set and return the selected item .EXAMPLE getGridSelect -DataSet $users -Title "Select User Accounts" Display a grid view of the specified data set and return the selected item(s) #> param ( [parameter(Mandatory=$True)]$DataSet, [parameter(Mandatory=$True)][string]$Title, [parameter(Mandatory=$False)][string][ValidateSet('Single','Multiple')]$OutputMode = 'Multiple' ) if (Get-Module Microsoft.PowerShell.ConsoleGuiTools -ListAvailable) { @($DataSet | Out-ConsoleGridView -Title $Title -OutputMode $OutputMode) } else { Write-Warning "Linux platforms require module: microsoft.powershell.consoleguitools" } } function IsCinnamonInstalled { <# .SYNOPSIS Determine if Cinnamon desktop is installed .DESCRIPTION Determine if Cinnamon desktop is installed #> param() if (-not (Test-Path -Path "/usr/bin/cinnamon")) { Write-Output $false $global:IsCinnamon = $false return } $desktop = Get-ChildItem -Path "/usr/share/xsessions" -Filter "cinnamon.desktop" -ErrorAction SilentlyContinue if ($desktop) { Write-Output $true $global:IsCinnamon = $true } else { Write-Output $false $global:IsCinnamon = $false } } function Get-LogFile { param() Get-Content -Path $global:LogFilePath -ErrorAction SilentlyContinue } function get-NullString { param( [parameter(Mandatory=$false)][string]$String ) if ([string]::IsNullOrEmpty($String)) { return "" } else { return $String.Trim() } } function TestCinnamonComponentPath { param( [parameter(Mandatory=$true)][ValidateSet('applets','extensions','spices')][string]$Type ) $path = switch ($Type) { 'applets' { '~/.local/share/cinnamon/applets' } 'extensions' { '~/.local/share/cinnamon/extensions' } 'spices' { '~/.config/cinnamon/spices' } } Test-Path -Path $path } function ReadCinnamonComponentData { param( [parameter(Mandatory=$true)][ValidateSet('applets','extensions','spices')][string]$Type, [parameter(Mandatory=$false)][string]$Name ) $path = switch ($Type) { 'applets' { '~/.local/share/cinnamon/applets' } 'extensions' { '~/.local/share/cinnamon/extensions' } 'spices' { '~/.config/cinnamon/spices' } } if (-not (Test-Path -Path $path)) { throw "Required path not found: $path" } if ($Type -eq 'spices') { $spiceDirs = Get-ChildItem -Path $path -Directory -ErrorAction Stop if ([string]::IsNullOrWhiteSpace($Name)) { $spiceDirs | Select-Object -Property @{n='ComponentType';e={'spices'}}, Name, @{n='ShortName';e={$_.Name.Split('@')[0]}}, FullName return } $spiceDirs | Where-Object { $_.Name -like "*$Name*" -or $_.BaseName -like "*$Name*" } | ForEach-Object { $spiceName = $_.BaseName Get-ChildItem -Path $_.FullName -File -Filter '*.json' -ErrorAction SilentlyContinue | ForEach-Object { $baseName = $_.BaseName Get-Content -Path $_.FullName -Raw | ConvertFrom-Json | Select-Object -Property *, @{n='ComponentType';e={'spices'}}, @{n='Name';e={$spiceName}}, @{n='ShortName';e={$baseName}}, @{n='SourcePath';e={$_.PSPath}} } } return } Get-ChildItem -Path $path -File -Filter 'metadata.json' -Recurse -ErrorAction Stop | ForEach-Object { $componentFile = $_ $metadata = Get-Content -Path $componentFile.FullName -Raw | ConvertFrom-Json if (-not [string]::IsNullOrWhiteSpace($Name)) { $matchValues = @( $componentFile.Directory.Name, $metadata.uuid, $metadata.name, $metadata.description, $metadata.'short-description' ) | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } if (-not ($matchValues | Where-Object { $_ -like "*$Name*" })) { return } } $metadata | Select-Object -Property *, @{n='ComponentType';e={$Type}}, @{n='SourcePath';e={$componentFile.Directory.FullName}} } } function ReadAllCinnamonComponents { param( [parameter(Mandatory=$false)][ValidateSet('applets','extensions','spices','all')][string]$Type = 'spices', [parameter(Mandatory=$false)][string]$Name ) $result = @() $targets = if ($Type -eq 'all') { 'applets','extensions','spices' } else { @($Type) } foreach ($target in $targets) { if ($Type -ne 'all' -or (TestCinnamonComponentPath -Type $target)) { $result += @(ReadCinnamonComponentData -Type $target -Name $Name) } } $result } function TestLinuxPackageManager { param( [parameter(Mandatory=$true)][ValidateSet('apt','flatpak','snap','dnf')][string]$Type ) # To do: use Get-Command to validate each option switch ($Type) { 'apt' { Test-Path -Path '/usr/bin/apt' } 'flatpak' { Test-Path -Path '/usr/bin/flatpak' } 'snap' { Test-Path -Path '/usr/bin/snap' } 'dnf' { Test-Path -Path '/usr/bin/dnf' } } } function ReadAptPackageData { param( [parameter(Mandatory=$false)][switch]$Upgradable ) if (-not (Test-Path -Path '/usr/bin/apt')) { throw "Required file not found: apt" } Write-Host "Updating apt cache..." sudo apt update | Out-Null $apps = if ($Upgradable) { sudo apt list --upgradable 2>$null } else { sudo apt list 2>$null } foreach ($line in $apps) { if ([string]::IsNullOrWhiteSpace($line) -or $line -match '^(Listing|WARNING:)') { continue } if ($line -match '^(?<Name>[^/]+)/\S+\s+(?<Current>\S+)\s+(?<Rev>\S+)(?:\s+\[(?<Status>[^\]]+)\])?') { $available = $null if ($matches['Status'] -like 'upgradable from:*') { $available = $matches['Status'].Replace('upgradable from: ', '') } [pscustomobject]@{ SourceType = 'apt' Name = $matches['Name'] Current = $matches['Current'] Rev = $matches['Rev'] Available = $available } } } } function ReadDnfPackageData { param() if (-not (Test-Path -Path '/usr/bin/dnf')) { throw "Required file not found: dnf" } $pkgs = dnf list --installed 2>$null foreach ($row in $pkgs) { if ([string]::IsNullOrWhiteSpace($row) -or $row -match '^(Installed Packages|Last metadata expiration check:)') { continue } $items = $row.Split(' ') | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } if ($items.Count -ge 3) { [pscustomobject]@{ SourceType = 'dnf' Name = $items[0] Version = $items[1] Source = $items[2] } } } } function ReadSnapPackageData { param() Write-Host "Listing snaps..." $snaps = snap list --all 2>$null foreach ($line in $snaps) { if ([string]::IsNullOrWhiteSpace($line) -or $line -match '^Name\s+Version\s+Rev\s+Tracking\s+Publisher\s+Notes$') { continue } if ($line -match '^(?<Name>\S+)\s+(?<Version>\S+)\s+(?<Rev>\S+)\s+(?<Tracking>\S+)\s+(?<Publisher>\S+)\s*(?<Notes>.*)$') { [pscustomobject]@{ SourceType = 'snap' Name = $matches['Name'] Version = $matches['Version'] Rev = $matches['Rev'] Tracking = $matches['Tracking'] Publisher = $matches['Publisher'] Notes = $matches['Notes'] } } } } function ReadFlatpakPackageData { param() $flatpaks = flatpak list --columns=name,version,application,description,installation,size 2>$null foreach ($line in $flatpaks) { if ([string]::IsNullOrWhiteSpace($line) -or $line -match '^Name\tVersion\tApplication\tDescription\tInstallation\tSize$') { continue } $pkg = $line.Split("`t") | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } if ($pkg.Count -ge 6) { [pscustomobject]@{ SourceType = 'flatpak' Name = $pkg[0] Version = $pkg[1] AppId = $pkg[2] Description = $pkg[3] InstallLocation = $pkg[4] Size = $pkg[5] } } } } function ReadLinuxPackageData { param( [parameter(Mandatory=$false)][ValidateSet('apt','flatpak','snap','dnf','all')][string]$Type = 'all', [parameter(Mandatory=$false)][switch]$Upgradable ) $result = @() if ($Type -in ('apt', 'all')) { if ($Type -eq 'apt' -or (TestLinuxPackageManager -Type 'apt')) { $result += @(ReadAptPackageData -Upgradable:$Upgradable) } } if ($Type -in ('flatpak', 'all')) { if ($Type -eq 'flatpak' -or (TestLinuxPackageManager -Type 'flatpak')) { $result += @(ReadFlatpakPackageData) } } if ($Type -in ('snap', 'all')) { if ($Type -eq 'snap' -or (TestLinuxPackageManager -Type 'snap')) { $result += @(ReadSnapPackageData) } } if ($Type -in ('dnf', 'all')) { if ($Type -eq 'dnf' -or (TestLinuxPackageManager -Type 'dnf')) { $result += @(ReadDnfPackageData) } } $result } function ReadAptPackageUpdates { param() if (-not (TestLinuxPackageManager -Type 'apt')) { throw "Required file not found: apt" } $apps = apt list --upgradable 2>$null foreach ($line in $apps) { if ([string]::IsNullOrWhiteSpace($line) -or $line -match '^(Listing|WARNING:)') { continue } if ($line -match '^(?<Name>[^/]+)/\S+\s+(?<Version>\S+)\s+(?<Channel>\S+)(?:\s+\[upgradable from: (?<Installed>[^\]]+)\])?') { [pscustomobject]@{ SourceType = 'apt' Name = $matches['Name'] InstalledVersion = $matches['Installed'] AvailableVersion = $matches['Version'] Channel = $matches['Channel'] } } } } function ReadSnapPackageUpdates { param() if (-not (TestLinuxPackageManager -Type 'snap')) { throw "Required file not found: snap" } $snaps = snap refresh --list 2>$null foreach ($line in $snaps) { if ([string]::IsNullOrWhiteSpace($line) -or $line -match '^All snaps up to date\.$' -or $line -match '^Name\s+Version\s+Rev\s+Size\s+Publisher\s+Notes$') { continue } if ($line -match '^(?<Name>\S+)\s+(?<AvailableVersion>\S+)\s+(?<Rev>\S+)\s+(?<Size>\S+)\s+(?<Publisher>\S+)\s*(?<Notes>.*)$') { [pscustomobject]@{ SourceType = 'snap' Name = $matches['Name'] InstalledVersion = $null AvailableVersion = $matches['AvailableVersion'] Rev = $matches['Rev'] Size = $matches['Size'] Publisher = $matches['Publisher'] Notes = $matches['Notes'] } } } } function ReadFlatpakPackageUpdates { param() if (-not (TestLinuxPackageManager -Type 'flatpak')) { throw "Required file not found: flatpak" } $flatpaks = flatpak remote-ls --updates --columns=name,application,version,branch,origin,size 2>$null foreach ($line in $flatpaks) { if ([string]::IsNullOrWhiteSpace($line) -or $line -match '^Name\tApplication\tVersion\tBranch\tOrigin\tSize$') { continue } $pkg = $line.Split("`t") | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } if ($pkg.Count -ge 6) { [pscustomobject]@{ SourceType = 'flatpak' Name = $pkg[0] AppId = $pkg[1] AvailableVersion = $pkg[2] Branch = $pkg[3] Origin = $pkg[4] Size = $pkg[5] } } } } function ReadDnfPackageUpdates { param() if (-not (TestLinuxPackageManager -Type 'dnf')) { throw "Required file not found: dnf" } $pkgs = dnf check-update 2>$null foreach ($row in $pkgs) { if ([string]::IsNullOrWhiteSpace($row) -or $row -match '^(Last metadata expiration check:|Obsoleting Packages|Security:|Available Upgrades)$') { continue } $items = $row.Split(' ') | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } if ($items.Count -ge 3) { [pscustomobject]@{ SourceType = 'dnf' Name = $items[0] AvailableVersion = $items[1] Source = $items[2] } } } } function ReadLinuxPackageUpdates { param( [parameter(Mandatory=$false)][ValidateSet('apt','flatpak','snap','dnf','all')][string]$Type = 'all' ) $result = @() if ($Type -in ('apt', 'all')) { if ($Type -eq 'apt' -or (TestLinuxPackageManager -Type 'apt')) { $result += @(ReadAptPackageUpdates) } } if ($Type -in ('flatpak', 'all')) { if ($Type -eq 'flatpak' -or (TestLinuxPackageManager -Type 'flatpak')) { $result += @(ReadFlatpakPackageUpdates) } } if ($Type -in ('snap', 'all')) { if ($Type -eq 'snap' -or (TestLinuxPackageManager -Type 'snap')) { $result += @(ReadSnapPackageUpdates) } } if ($Type -in ('dnf', 'all')) { if ($Type -eq 'dnf' -or (TestLinuxPackageManager -Type 'dnf')) { $result += @(ReadDnfPackageUpdates) } } $result } function InvokeLinuxPackageUpdate { param( [parameter(Mandatory=$true)][ValidateSet('apt','flatpak','snap','dnf')][string]$Type ) switch ($Type) { 'apt' { if (Test-Path -Path '/usr/bin/nala') { Write-Host "Using Nala for package management..." sudo nala update sudo nala upgrade -y } else { Write-Host "Using APT for package management..." sudo apt update sudo apt upgrade -y } } 'dnf' { if (-not (Test-Path -Path '/usr/bin/dnf')) { throw "File not found: dnf" } Write-Host "Updating DNF packages..." sudo dnf upgrade -y } 'flatpak' { Write-Host "Updating flatpak packages..." flatpak update -y } 'snap' { Write-Host "Updating snap packages..." sudo snap refresh } } } function ReadLinuxSystemInfo { param( [parameter(Mandatory=$false)][ValidateSet('basic','detailed')][string]$Detail = 'basic' ) $hostname = (hostname 2>$null) $osInfoRaw = (lsb_release -a 2>$null | Out-String).Trim() $cpuModel = ((Get-Content /proc/cpuinfo 2>$null | Select-String 'model name' | Select-Object -First 1) -split ':')[-1].Trim() $memoryRaw = (free -h 2>$null | Out-String).Trim() $diskRaw = (df -h 2>$null | Out-String).Trim() $kernel = (uname -r 2>$null) $architecture = (uname -p 2>$null) $isLinuxMint = $osInfoRaw -match 'Linux Mint' $ubuntuBaseVersion = $null if ($isLinuxMint -and (Test-Path '/etc/upstream-release/lsb-release')) { $ubuntuBaseVersion = Get-Content '/etc/upstream-release/lsb-release' | Select-String 'DISTRIB_DESCRIPTION' | ForEach-Object { $_ -replace 'DISTRIB_DESCRIPTION="|"', '' } } if ($Detail -eq 'basic') { return [PSCustomObject]@{ ComputerName = $hostname OperatingSystem = $osInfoRaw Kernel = $kernel Architecture = $architecture CPU = $cpuModel Memory = $memoryRaw DiskSpace = $diskRaw UbuntuBaseVersion = if ($isLinuxMint) { $ubuntuBaseVersion } else { $null } } } $results = [ordered]@{ ComputerName = $hostname OperatingSystem = $osInfoRaw Kernel = $kernel Architecture = $architecture CPU = $cpuModel Memory = $memoryRaw DiskSpace = $diskRaw UbuntuBaseVersion = if ($isLinuxMint) { $ubuntuBaseVersion } else { $null } } if (Get-Command -Name dmidecode -ErrorAction SilentlyContinue) { $canReadDmi = $false if ((id -u 2>$null) -eq '0') { $canReadDmi = $true } else { sudo -n true 2>$null | Out-Null $canReadDmi = $LASTEXITCODE -eq 0 } if ($canReadDmi) { $hw = (sudo -n dmidecode 2>$null | grep -A3 '^System Information') | ForEach-Object { $_.Trim() } foreach ($item in $hw) { if ($item.Contains(':')) { $parts = $item.Split(':') | ForEach-Object { $_.Trim() } if ($parts.Count -ge 2) { $results[$parts[0].Replace(' ','_').Replace('(','').Replace(')','')] = $parts[1] } } } $sn = (sudo -n dmidecode -t system 2>$null | grep Serial) $results['SerialNumber'] = if (![string]::IsNullOrEmpty($sn)) { $sn.Split(':')[1].Trim() } else { 'N/A' } } else { $results['SerialNumber'] = 'N/A' $results['DmiAccess'] = 'Requires sudo privileges' } } $lscpu = lscpu 2>$null foreach ($item in $lscpu) { if ($item.Contains(':')) { $parts = $item.Split(':') | ForEach-Object { $_.Trim() } if ($parts.Count -ge 2) { $results[$parts[0].Replace(' ','_').Replace('(','').Replace(')','')] = $parts[1] } } } $bootRaw = (uptime -s 2>$null) if (![string]::IsNullOrWhiteSpace($bootRaw)) { try { $results['LastBoot'] = Get-Date $bootRaw } catch { $results['LastBoot'] = $bootRaw } } $uptimeRaw = (uptime -p 2>$null) if (![string]::IsNullOrWhiteSpace($uptimeRaw)) { $results['Uptime'] = if ($uptimeRaw.StartsWith('up ')) { $uptimeRaw.Substring(3) } else { $uptimeRaw } } [PSCustomObject]$results } function ReadMimeAppsDefaults { param( [parameter(Mandatory=$false)][string]$Path = "$env:HOME/.config/mimeapps.list" ) if (-not (Test-Path -Path $Path)) { throw "File not found: $Path" } $content = Get-Content -Path $Path $defaults = @{} $inDefaultSection = $false foreach ($line in $content) { $trimmed = $line.Trim() if ([string]::IsNullOrWhiteSpace($trimmed) -or $trimmed.StartsWith('#') -or $trimmed.StartsWith(';')) { continue } if ($trimmed -eq '[Default Applications]') { $inDefaultSection = $true continue } if ($trimmed.StartsWith('[')) { $inDefaultSection = $false continue } if (-not $inDefaultSection) { continue } if ($trimmed -match '^(?<MimeType>[^=]+)=(?<DesktopEntry>.+)$') { $mimeType = $matches['MimeType'].Trim() $desktopEntry = ($matches['DesktopEntry'].Split(';') | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | Select-Object -First 1) if (-not [string]::IsNullOrWhiteSpace($desktopEntry)) { $defaults[$mimeType] = $desktopEntry } } } $defaults } function ReadDefaultApplications { param( [parameter(Mandatory=$false)][ValidateSet('browser','audio','video','image','text','all')][string]$Category = 'all', [parameter(Mandatory=$false)][string]$MimeType, [parameter(Mandatory=$false)][string]$Path = "$env:HOME/.config/mimeapps.list" ) $defaults = ReadMimeAppsDefaults -Path $Path $rows = foreach ($key in $defaults.Keys) { [pscustomobject]@{ MimeType = $key DesktopEntry = $defaults[$key] Category = if ($key -like 'x-scheme-handler/http*' -or $key -like 'x-scheme-handler/https*') { 'browser' } elseif ($key -like 'audio/*') { 'audio' } elseif ($key -like 'video/*') { 'video' } elseif ($key -like 'image/*') { 'image' } elseif ($key -like 'text/*') { 'text' } else { 'other' } } } if (-not [string]::IsNullOrWhiteSpace($MimeType)) { return @($rows | Where-Object { $_.MimeType -like $MimeType }) } if ($Category -eq 'all') { return @($rows) } @($rows | Where-Object { $_.Category -eq $Category }) } function ReadDesktopEntries { param( [parameter(Mandatory=$false)][ValidateSet('user','system','autostart','all')][string]$Location = 'user', [parameter(Mandatory=$false)][string]$Name, [parameter(Mandatory=$false)][switch]$Contents, [parameter(Mandatory=$false)][string]$Path, [parameter(Mandatory=$false)][string]$Filter = '*.desktop' ) $paths = switch ($Location) { 'user' { @('~/.local/share/applications') } 'system' { @('/usr/share/applications') } 'autostart' { if ([string]::IsNullOrWhiteSpace($Path)) { @("$env:HOME/.config/autostart") } else { @($Path) } } 'all' { @('~/.local/share/applications','/usr/share/applications',"$env:HOME/.config/autostart") } } $result = @() foreach ($entryPath in $paths) { if (-not (Test-Path -Path $entryPath)) { continue } $items = Get-ChildItem -Path $entryPath -Filter $Filter -File -ErrorAction SilentlyContinue foreach ($item in $items) { if (-not [string]::IsNullOrWhiteSpace($Name)) { if ($item.BaseName -notlike "*$Name*" -and $item.Name -notlike "*$Name*") { continue } } if ($Contents) { $result += [pscustomobject]@{ Name = $item.Name BaseName = $item.BaseName FullName = $item.FullName Location = $Location SourcePath = $entryPath Content = (Get-Content -Path $item.FullName -Raw) } } else { $result += [pscustomobject]@{ Name = $item.Name BaseName = $item.BaseName FullName = $item.FullName Location = $Location SourcePath = $entryPath } } } } $result } |