Rob van der Woude's Scripting Pages
Powered by GeSHi

Source code for gethddstatus.ps

(view source code of gethddstatus.ps as plain text)

  1. param(
  2. 	[parameter( ValueFromRemainingArguments = $true )]
  3. 	[string[]]$Args # Leave all argument validation to the script, not to PowerShell
  4. )
  5.  
  6. if ( $args.Length -gt 0 ) {
  7. 	Clear-Host
  8. 	Write-Host
  9. 	Write-Host "GetHDDStatus.ps1,  Version 2.03"
  10. 	Write-Host "Get the SMART status for all local harddisks"
  11. 	Write-Host
  12. 	Write-Host "Usage:    " -NoNewline
  13. 	if ( $HOME[0] -eq '/' ) {
  14. 		# Linux
  15. 		Write-Host "pwsh  GetHDDStatus.ps1" -ForegroundColor White
  16. 	} else {
  17. 		# Windows
  18. 		Write-Host "powershell  . GetHDDStatus.ps1" -ForegroundColor White
  19. 	}
  20. 	Write-Host
  21. 	Write-Host "Notes:    Disks' status is shown in the console and as desktop notification."
  22. 	Write-Host "          This script requires elevated privileges (a.k.a. `'root access`')."
  23. 	Write-Host "          In Linux, however, it should " -NoNewline
  24. 	Write-Host "NOT" -ForegroundColor Red -NoNewline
  25. 	Write-Host " be started as root if you want"
  26. 	Write-Host "          a desktop notification; the script will restart itself as root to"
  27. 	Write-Host "          check the disks, and then send the desktop notification with the"
  28. 	Write-Host "          initial non-root user account."
  29. 	Write-Host "          In Windows the output includes disk indexes, in Linux it does not."
  30. 	Write-Host "          In Linux this script requires smartmontools:"
  31. 	Write-Host "          https://www.smartmontools.org/" -ForegroundColor DarkGray
  32. 	Write-Host
  33. 	Write-Host "Credits:  Windows disk check based on code by Geoff @ UVM:"
  34. 	Write-Host "          www.uvm.edu/~gcd/2013/01/which-disk-is-that-volume-on" -ForegroundColor DarkGray
  35. 	Write-Host "          System Tray ToolTip Balloon code by Don Jones:"
  36. 	Write-Host "          http://blog.sapien.com/current/2007/4/27" -ForegroundColor DarkGray
  37. 	Write-Host "          /creating-a-balloon-tip-notification-in-powershell.html" -ForegroundColor DarkGray
  38. 	Write-Host "          Code to extract icons from Shell32.dll by Thomas Levesque:"
  39. 	Write-Host "          http://stackoverflow.com/questions/6873026" -ForegroundColor DarkGray
  40. 	Write-Host
  41. 	Write-Host "Written by Rob van der Woude"
  42. 	Write-Host "http://www.robvanderwoude.com"
  43. 	Write-Host
  44.  
  45. 	exit 1
  46. }
  47.  
  48. $rc = 0
  49.  
  50. if ( $HOME[0] -eq '/' ) {
  51. 	# Linux: lshw/smartctl/df/notify-send commands
  52.  
  53. 	# disk test requires root access, notification should NOT be run as root
  54. 	if ( ( . whoami ) -match "root" ) {
  55. 		Clear-Host
  56. 		Write-Host
  57.  
  58. 		. smartctl -V > $null 2>&1
  59. 		if ( -not $? ) {
  60. 			Write-Host "This script requires smartmontools, available at"
  61. 			Write-Host "https://www.smartmontools.org/"
  62. 			exit 1
  63. 		}
  64.  
  65. 		$notify = @( )
  66.  
  67. 		Write-Host "Volume   `tStatus`tCapacity`tModel" -ForegroundColor White
  68. 		Write-Host "======   `t======`t========`t=====`n" -ForegroundColor White
  69.  
  70. 		( . lshw -short -class disk ) -match "/dev/" | ForEach-Object {
  71. 			$disk = ( $_.trim( ) -split '\s+',  3 )[1]
  72. 			$name = ( $_.trim( ) -split 'disk', 2 )[1].trim( )
  73. 			if ( $disk -notmatch "(/cd|/dvd|/loop|/sr)" ) {
  74. 				try {
  75. 					$size = 0
  76. 					$size = ( ( ( . df -l --output=source,size ) -match $disk ) -split '\s+', 2 )[1] / 1MB
  77. 					if ( [int]$size -gt 0 ) {
  78. 						$test = ( ( ( . smartctl -H $disk ) -match "SMART [^\n\r]+: ([A-Z]+)" ) -split ":" )[1].trim( )
  79. 						if ( $test -eq "PASSED" ) {
  80. 								$fgc = "Green"
  81. 								$test = "OK"
  82. 						} else {
  83. 							$fgc = "Red"
  84. 							$rc = 1
  85. 						}
  86. 						$notify += "{0}\t{1}" -f $disk, $test
  87. 						Write-Host "$disk`t" -NoNewline
  88. 						Write-Host "$test`t" -ForegroundColor $fgc -NoNewline
  89. 						Write-Host ( "{0,5:N0} GB`t$name" -f $size ) -ForegroundColor White
  90. 					}
  91. 				}
  92. 				catch {
  93. 					# ignore errors from USB sticks etc.
  94. 				}
  95. 			}
  96. 		} 
  97.  
  98. 		# prepare desktop notification:
  99. 		# a temporary shell script is used because notify-send
  100. 		# cannot send desktop notifications to other users
  101. 		if ( $rc -eq 1 ) {
  102. 			$icon = "error"
  103. 			$category = "device.error"
  104. 			$urgency = "critical"
  105. 			$title = "HDD Warnings"
  106. 		} else {
  107. 			$icon = "info"
  108. 			$category = "device"
  109. 			$urgency = "normal"
  110. 			$title = "HDD Status OK"
  111. 		}
  112. 		$message = $notify -join "\r"
  113. 		$notifycommand = "notify-send -i $icon -c $category -u $urgency `"$title`" `"$message`""
  114. 		# write notify-send command to temporary shell script
  115. 		Out-File -FilePath "$PSScriptRoot/GetHDDStatus.sh" -InputObject $notifycommand -Force
  116. 		Start-Sleep -s 1
  117. 		. chmod +x "$PSScriptRoot/GetHDDStatus.sh"
  118. 		Start-Sleep -s 1
  119. 	} else {
  120. 		if ( [System.IO.File]::Exists( "$PSScriptRoot/GetHDDStatus.sh" ) ) {
  121. 			Remove-Item -Path "$PSScriptRoot/GetHDDStatus.sh" -Force
  122. 		}
  123. 		# restart this PowerShell script as root, required for smartctl and lshw
  124. 		Start-Process -FilePath "sudo" -ArgumentList "pwsh `"$PSScriptRoot/GetHDDStatus.ps1`"" -NoNewWindow -Wait
  125. 		# run the root generated temporary shell script to show a desktop notification
  126. 		Start-Process -FilePath "bash" -ArgumentList "-c $PSScriptRoot/GetHDDStatus.sh"
  127. 		Start-Sleep -s 1
  128. 		Remove-Item -Path "$PSScriptRoot/GetHDDStatus.sh" -Force
  129. 	}
  130. } else {
  131. 	# Windows: WMI and System.windows.Forms.NotifyIcon
  132.  
  133. 	# based on code by Geoff @ UVM
  134. 	# https://www.uvm.edu/~gcd/2013/01/which-disk-is-that-volume-on/
  135.  
  136. 	Clear-Host
  137. 	Write-Host
  138.  
  139. 	[Reflection.Assembly]::LoadWithPartialName( "System.Windows.Forms" ) > $null 2>&1
  140. 	[Reflection.Assembly]::LoadWithPartialName( "System.Drawing" ) > $null 2>&1
  141. 	Add-Type -AssemblyName System.Windows.Forms
  142.  
  143. 	[System.Collections.SortedList]$volumedetails = New-Object System.Collections.SortedList
  144. 	[System.Collections.SortedList]$volumestatus  = New-Object System.Collections.SortedList
  145.  
  146. 	# query status for all local disk drives
  147. 	$diskdrives = Get-WmiObject -Namespace "root/CIMV2" -Class Win32_DiskDrive
  148. 	foreach ( $disk in $diskdrives ) {
  149. 		$diskindex  = $disk.Index
  150. 		$diskmodel  = $disk.Model
  151. 		$disksize   = "{0,5:F0} GB" -f ( $disk.Size / 1GB )
  152. 		$diskstatus = $disk.Status
  153. 		$part_query = 'ASSOCIATORS OF {Win32_DiskDrive.DeviceID="' + $disk.DeviceID.replace('\','\\') + '"} WHERE AssocClass=Win32_DiskDriveToDiskPartition'
  154. 		$partitions = @( Get-WmiObject -Query $part_query | Sort-Object StartingOffset )
  155. 		foreach ( $partition in $partitions ) {
  156. 			$vol_query = 'ASSOCIATORS OF {Win32_DiskPartition.DeviceID="' + $partition.DeviceID + '"} WHERE AssocClass=Win32_LogicalDiskToPartition'
  157. 			$volumes   = @( Get-WmiObject -Query $vol_query )
  158. 			foreach ( $volume in $volumes ) {
  159. 				# DriveType 3 means harddisks only
  160. 				# 0 = Unknown; 1 = No Root Directory; 2 = Removable Disk; 3 = Local Disk; 4 = Network Drive; 5 = Compact Disc; 6 = RAM Disk
  161. 				if ( $volume.DriveType -eq 3 ) {
  162. 					if ( -not $volumedetails.Contains( $volume.Name ) ) {
  163. 						$volumedetails.Add( $volume.Name, "[Disk $diskindex]  $disksize  $diskmodel" )
  164. 						$volumestatus.Add( $volume.Name, $diskstatus )
  165. 					}
  166. 				}
  167. 			}
  168. 		}
  169. 	}
  170.  
  171. 	# console output table header
  172. 	Write-Host "Volume  Status  Disk#     Capacity  Model" -ForegroundColor White
  173. 	Write-Host "======  ======  =====     ========  =====`n" -ForegroundColor White
  174.  
  175. 	# write table console output
  176. 	$volumedetails.Keys | ForEach-Object {
  177. 		$fgc = "Green"
  178. 		$status = ( $volumestatus[$_] )
  179. 		if ( $status -ne "OK" ) {
  180. 			$fgc = "Red"
  181. 			$rc  = 1
  182. 		}
  183. 		Write-Host ( "$_      " ) -ForegroundColor White -NoNewline
  184. 		Write-Host ( "$status      ".Substring( 0, 6 ) + "  " ) -ForegroundColor $fgc -NoNewline
  185. 		Write-Host ( $volumedetails[$_] ) -ForegroundColor White
  186. 	}
  187.  
  188. 	# system tray balloon tip output
  189. 	[System.Windows.Forms.ToolTipIcon]$icon = [System.Windows.Forms.ToolTipIcon]::Info
  190. 	$title = "HDD Status OK"
  191. 	$systraymessage = ""
  192. 	$volumedetails.Keys | ForEach-Object {
  193. 		$status = ( $volumestatus[$_] )
  194. 		if ( $status -ne "OK" ) {
  195. 			$icon = [System.Windows.Forms.ToolTipIcon]::Error
  196. 			$title = "Warning: HDD Errors"
  197. 		}
  198. 		$systraymessage = $systraymessage + "$_`t$status`n"
  199. 	}
  200.  
  201. 	# Extract system tray icon from Shell32.dll
  202. 	# C# code to extract icons from Shell32.dll by Thomas Levesque
  203. 	# http://stackoverflow.com/questions/6873026
  204. 	$signature = @'
  205. 	[DllImport( "Shell32.dll", EntryPoint = "ExtractIconExW", CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall )]
  206. 	private static extern int ExtractIconEx( string sFile, int iIndex, out IntPtr piLargeVersion, out IntPtr piSmallVersion, int amountIcons );
  207.  
  208. 	public static Icon Extract( string file, int number, bool largeIcon )
  209. 	{
  210. 		IntPtr large;
  211. 		IntPtr small;
  212. 		ExtractIconEx( file, number, out large, out small, 1 );
  213. 		try
  214. 		{
  215. 			return Icon.FromHandle( largeIcon ? large : small );
  216. 		}
  217. 		catch
  218. 		{
  219. 			return null;
  220. 		}
  221. 	}
  222. '@
  223.  
  224. 	$iconextractor = Add-Type -MemberDefinition $signature -Name IconExtract -Namespace IconExtractor -ReferencedAssemblies System.Windows.Forms,System.Drawing -UsingNamespace System.Windows.Forms,System.Drawing -PassThru
  225.  
  226. 	# icon depends on status
  227. 	if( $title -eq "HDD Status OK" ) {
  228. 		$systrayicon = $iconextractor::Extract( "C:\Windows\System32\shell32.dll", 223, $true )
  229. 	} else {
  230. 		$systrayicon = $iconextractor::Extract( "C:\Windows\System32\shell32.dll", 53, $true )
  231. 	}
  232.  
  233. 	# show system tray icon and balloon tip
  234. 	$notify = New-Object System.windows.Forms.NotifyIcon
  235. 	$notify.BalloonTipText = $systraymessage
  236. 	$notify.BalloonTipTitle = $title
  237. 	$notify.BalloonTipIcon = [System.Windows.Forms.ToolTipIcon]$icon
  238. 	$notify.Icon = $systrayicon
  239. 	$notify.Visible = $true
  240. 	$notify.ShowBalloonTip( 30000 )
  241. }
  242.  
  243. Write-Host
  244.  
  245. exit $rc
  246.  

page last modified: 2024-04-16; loaded in 0.0134 seconds