(view source code of gethddstatus.ps as plain text)
param(
[parameter( ValueFromRemainingArguments = $true )]
[string[]]$Args # Leave all argument validation to the script, not to PowerShell
)
if ( $args.Length -gt 0 ) {
Clear-Host
Write-Host
Write-Host "GetHDDStatus.ps1, Version 2.03"
Write-Host "Get the SMART status for all local harddisks"
Write-Host
Write-Host "Usage: " -NoNewline
if ( $HOME[0] -eq '/' ) {
# Linux
Write-Host "pwsh GetHDDStatus.ps1" -ForegroundColor White
} else {
# Windows
Write-Host "powershell . GetHDDStatus.ps1" -ForegroundColor White
}
Write-Host
Write-Host "Notes: Disks' status is shown in the console and as desktop notification."
Write-Host " This script requires elevated privileges (a.k.a. `'root access`')."
Write-Host " In Linux, however, it should " -NoNewline
Write-Host "NOT" -ForegroundColor Red -NoNewline
Write-Host " be started as root if you want"
Write-Host " a desktop notification; the script will restart itself as root to"
Write-Host " check the disks, and then send the desktop notification with the"
Write-Host " initial non-root user account."
Write-Host " In Windows the output includes disk indexes, in Linux it does not."
Write-Host " In Linux this script requires smartmontools:"
Write-Host " https://www.smartmontools.org/" -ForegroundColor DarkGray
Write-Host
Write-Host "Credits: Windows disk check based on code by Geoff @ UVM:"
Write-Host " www.uvm.edu/~gcd/2013/01/which-disk-is-that-volume-on" -ForegroundColor DarkGray
Write-Host " System Tray ToolTip Balloon code by Don Jones:"
Write-Host " http://blog.sapien.com/current/2007/4/27" -ForegroundColor DarkGray
Write-Host " /creating-a-balloon-tip-notification-in-powershell.html" -ForegroundColor DarkGray
Write-Host " Code to extract icons from Shell32.dll by Thomas Levesque:"
Write-Host " http://stackoverflow.com/questions/6873026" -ForegroundColor DarkGray
Write-Host
Write-Host "Written by Rob van der Woude"
Write-Host "http://www.robvanderwoude.com"
Write-Host
exit 1
}
$rc = 0
if ( $HOME[0] -eq '/' ) {
# Linux: lshw/smartctl/df/notify-send commands
# disk test requires root access, notification should NOT be run as root
if ( ( . whoami ) -match "root" ) {
Clear-Host
Write-Host
. smartctl -V > $null 2>&1
if ( -not $? ) {
Write-Host "This script requires smartmontools, available at"
Write-Host "https://www.smartmontools.org/"
exit 1
}
$notify = @( )
Write-Host "Volume `tStatus`tCapacity`tModel" -ForegroundColor White
Write-Host "====== `t======`t========`t=====`n" -ForegroundColor White
( . lshw -short -class disk ) -match "/dev/" | ForEach-Object {
$disk = ( $_.trim( ) -split '\s+', 3 )[1]
$name = ( $_.trim( ) -split 'disk', 2 )[1].trim( )
if ( $disk -notmatch "(/cd|/dvd|/loop|/sr)" ) {
try {
$size = 0
$size = ( ( ( . df -l --output=source,size ) -match $disk ) -split '\s+', 2 )[1] / 1MB
if ( [int]$size -gt 0 ) {
$test = ( ( ( . smartctl -H $disk ) -match "SMART [^\n\r]+: ([A-Z]+)" ) -split ":" )[1].trim( )
if ( $test -eq "PASSED" ) {
$fgc = "Green"
$test = "OK"
} else {
$fgc = "Red"
$rc = 1
}
$notify += "{0}\t{1}" -f $disk, $test
Write-Host "$disk`t" -NoNewline
Write-Host "$test`t" -ForegroundColor $fgc -NoNewline
Write-Host ( "{0,5:N0} GB`t$name" -f $size ) -ForegroundColor White
}
}
catch {
# ignore errors from USB sticks etc.
}
}
}
# prepare desktop notification:
# a temporary shell script is used because notify-send
# cannot send desktop notifications to other users
if ( $rc -eq 1 ) {
$icon = "error"
$category = "device.error"
$urgency = "critical"
$title = "HDD Warnings"
} else {
$icon = "info"
$category = "device"
$urgency = "normal"
$title = "HDD Status OK"
}
$message = $notify -join "\r"
$notifycommand = "notify-send -i $icon -c $category -u $urgency `"$title`" `"$message`""
# write notify-send command to temporary shell script
Out-File -FilePath "$PSScriptRoot/GetHDDStatus.sh" -InputObject $notifycommand -Force
Start-Sleep -s 1
. chmod +x "$PSScriptRoot/GetHDDStatus.sh"
Start-Sleep -s 1
} else {
if ( [System.IO.File]::Exists( "$PSScriptRoot/GetHDDStatus.sh" ) ) {
Remove-Item -Path "$PSScriptRoot/GetHDDStatus.sh" -Force
}
# restart this PowerShell script as root, required for smartctl and lshw
Start-Process -FilePath "sudo" -ArgumentList "pwsh `"$PSScriptRoot/GetHDDStatus.ps1`"" -NoNewWindow -Wait
# run the root generated temporary shell script to show a desktop notification
Start-Process -FilePath "bash" -ArgumentList "-c $PSScriptRoot/GetHDDStatus.sh"
Start-Sleep -s 1
Remove-Item -Path "$PSScriptRoot/GetHDDStatus.sh" -Force
}
} else {
# Windows: WMI and System.windows.Forms.NotifyIcon
# based on code by Geoff @ UVM
# https://www.uvm.edu/~gcd/2013/01/which-disk-is-that-volume-on/
Clear-Host
Write-Host
[Reflection.Assembly]::LoadWithPartialName( "System.Windows.Forms" ) > $null 2>&1
[Reflection.Assembly]::LoadWithPartialName( "System.Drawing" ) > $null 2>&1
Add-Type -AssemblyName System.Windows.Forms
[System.Collections.SortedList]$volumedetails = New-Object System.Collections.SortedList
[System.Collections.SortedList]$volumestatus = New-Object System.Collections.SortedList
# query status for all local disk drives
$diskdrives = Get-WmiObject -Namespace "root/CIMV2" -Class Win32_DiskDrive
foreach ( $disk in $diskdrives ) {
$diskindex = $disk.Index
$diskmodel = $disk.Model
$disksize = "{0,5:F0} GB" -f ( $disk.Size / 1GB )
$diskstatus = $disk.Status
$part_query = 'ASSOCIATORS OF {Win32_DiskDrive.DeviceID="' + $disk.DeviceID.replace('\','\\') + '"} WHERE AssocClass=Win32_DiskDriveToDiskPartition'
$partitions = @( Get-WmiObject -Query $part_query | Sort-Object StartingOffset )
foreach ( $partition in $partitions ) {
$vol_query = 'ASSOCIATORS OF {Win32_DiskPartition.DeviceID="' + $partition.DeviceID + '"} WHERE AssocClass=Win32_LogicalDiskToPartition'
$volumes = @( Get-WmiObject -Query $vol_query )
foreach ( $volume in $volumes ) {
# DriveType 3 means harddisks only
# 0 = Unknown; 1 = No Root Directory; 2 = Removable Disk; 3 = Local Disk; 4 = Network Drive; 5 = Compact Disc; 6 = RAM Disk
if ( $volume.DriveType -eq 3 ) {
if ( -not $volumedetails.Contains( $volume.Name ) ) {
$volumedetails.Add( $volume.Name, "[Disk $diskindex] $disksize $diskmodel" )
$volumestatus.Add( $volume.Name, $diskstatus )
}
}
}
}
}
# console output table header
Write-Host "Volume Status Disk# Capacity Model" -ForegroundColor White
Write-Host "====== ====== ===== ======== =====`n" -ForegroundColor White
# write table console output
$volumedetails.Keys | ForEach-Object {
$fgc = "Green"
$status = ( $volumestatus[$_] )
if ( $status -ne "OK" ) {
$fgc = "Red"
$rc = 1
}
Write-Host ( "$_ " ) -ForegroundColor White -NoNewline
Write-Host ( "$status ".Substring( 0, 6 ) + " " ) -ForegroundColor $fgc -NoNewline
Write-Host ( $volumedetails[$_] ) -ForegroundColor White
}
# system tray balloon tip output
[System.Windows.Forms.ToolTipIcon]$icon = [System.Windows.Forms.ToolTipIcon]::Info
$title = "HDD Status OK"
$systraymessage = ""
$volumedetails.Keys | ForEach-Object {
$status = ( $volumestatus[$_] )
if ( $status -ne "OK" ) {
$icon = [System.Windows.Forms.ToolTipIcon]::Error
$title = "Warning: HDD Errors"
}
$systraymessage = $systraymessage + "$_`t$status`n"
}
# Extract system tray icon from Shell32.dll
# C# code to extract icons from Shell32.dll by Thomas Levesque
# http://stackoverflow.com/questions/6873026
$signature = @'
[DllImport( "Shell32.dll", EntryPoint = "ExtractIconExW", CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall )]
private static extern int ExtractIconEx( string sFile, int iIndex, out IntPtr piLargeVersion, out IntPtr piSmallVersion, int amountIcons );
public static Icon Extract( string file, int number, bool largeIcon )
{
IntPtr large;
IntPtr small;
ExtractIconEx( file, number, out large, out small, 1 );
try
{
return Icon.FromHandle( largeIcon ? large : small );
}
catch
{
return null;
}
}
'@
$iconextractor = Add-Type -MemberDefinition $signature -Name IconExtract -Namespace IconExtractor -ReferencedAssemblies System.Windows.Forms,System.Drawing -UsingNamespace System.Windows.Forms,System.Drawing -PassThru
# icon depends on status
if( $title -eq "HDD Status OK" ) {
$systrayicon = $iconextractor::Extract( "C:\Windows\System32\shell32.dll", 223, $true )
} else {
$systrayicon = $iconextractor::Extract( "C:\Windows\System32\shell32.dll", 53, $true )
}
# show system tray icon and balloon tip
$notify = New-Object System.windows.Forms.NotifyIcon
$notify.BalloonTipText = $systraymessage
$notify.BalloonTipTitle = $title
$notify.BalloonTipIcon = [System.Windows.Forms.ToolTipIcon]$icon
$notify.Icon = $systrayicon
$notify.Visible = $true
$notify.ShowBalloonTip( 30000 )
}
Write-Host
exit $rc
page last modified: 2024-04-16; loaded in 0.0134 seconds