system.disk.smart.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. package main
  2. import (
  3. "encoding/json"
  4. "log"
  5. "net/http"
  6. "os/exec"
  7. "runtime"
  8. "time"
  9. )
  10. //SystemSmartExecutable xxx
  11. var SystemSmartExecutable = ""
  12. //SMARTInformation xxx
  13. var SMARTInformation = []SMART{}
  14. var lastScanTime int64 = 0
  15. // DevicesList was used for storing the disk scanning result
  16. type DevicesList struct {
  17. JSONFormatVersion []int `json:"json_format_version"`
  18. Smartctl struct {
  19. Version []int `json:"version"`
  20. SvnRevision string `json:"svn_revision"`
  21. PlatformInfo string `json:"platform_info"`
  22. BuildInfo string `json:"build_info"`
  23. Argv []string `json:"argv"`
  24. Messages []struct {
  25. String string `json:"string"`
  26. Severity string `json:"severity"`
  27. } `json:"messages"`
  28. ExitStatus int `json:"exit_status"`
  29. } `json:"smartctl"`
  30. Devices []struct {
  31. Name string `json:"name"`
  32. InfoName string `json:"info_name"`
  33. Type string `json:"type"`
  34. Protocol string `json:"protocol"`
  35. } `json:"devices"`
  36. }
  37. // DeviceSMART was used for storing each disk smart information
  38. type DeviceSMART struct {
  39. JSONFormatVersion []int `json:"json_format_version"`
  40. Smartctl struct {
  41. Version []int `json:"version"`
  42. SvnRevision string `json:"svn_revision"`
  43. PlatformInfo string `json:"platform_info"`
  44. BuildInfo string `json:"build_info"`
  45. Argv []string `json:"argv"`
  46. Messages []struct {
  47. String string `json:"string"`
  48. Severity string `json:"severity"`
  49. } `json:"messages"`
  50. ExitStatus int `json:"exit_status"`
  51. } `json:"smartctl"`
  52. Device struct {
  53. Name string `json:"name"`
  54. InfoName string `json:"info_name"`
  55. Type string `json:"type"`
  56. Protocol string `json:"protocol"`
  57. } `json:"device"`
  58. ModelFamily string `json:"model_family"`
  59. ModelName string `json:"model_name"`
  60. SerialNumber string `json:"serial_number"`
  61. Wwn struct {
  62. Naa int `json:"naa"`
  63. Oui int `json:"oui"`
  64. ID int64 `json:"id"`
  65. } `json:"wwn"`
  66. FirmwareVersion string `json:"firmware_version"`
  67. UserCapacity struct {
  68. Blocks int `json:"blocks"`
  69. Bytes int64 `json:"bytes"`
  70. } `json:"user_capacity"`
  71. LogicalBlockSize int `json:"logical_block_size"`
  72. PhysicalBlockSize int `json:"physical_block_size"`
  73. RotationRate int `json:"rotation_rate"`
  74. InSmartctlDatabase bool `json:"in_smartctl_database"`
  75. AtaVersion struct {
  76. String string `json:"string"`
  77. MajorValue int `json:"major_value"`
  78. MinorValue int `json:"minor_value"`
  79. } `json:"ata_version"`
  80. SataVersion struct {
  81. String string `json:"string"`
  82. Value int `json:"value"`
  83. } `json:"sata_version"`
  84. InterfaceSpeed struct {
  85. Max struct {
  86. SataValue int `json:"sata_value"`
  87. String string `json:"string"`
  88. UnitsPerSecond int `json:"units_per_second"`
  89. BitsPerUnit int `json:"bits_per_unit"`
  90. } `json:"max"`
  91. Current struct {
  92. SataValue int `json:"sata_value"`
  93. String string `json:"string"`
  94. UnitsPerSecond int `json:"units_per_second"`
  95. BitsPerUnit int `json:"bits_per_unit"`
  96. } `json:"current"`
  97. } `json:"interface_speed"`
  98. LocalTime struct {
  99. TimeT int `json:"time_t"`
  100. Asctime string `json:"asctime"`
  101. } `json:"local_time"`
  102. SmartStatus struct {
  103. Passed bool `json:"passed"`
  104. } `json:"smart_status"`
  105. AtaSmartData struct {
  106. OfflineDataCollection struct {
  107. Status struct {
  108. Value int `json:"value"`
  109. String string `json:"string"`
  110. } `json:"status"`
  111. CompletionSeconds int `json:"completion_seconds"`
  112. } `json:"offline_data_collection"`
  113. SelfTest struct {
  114. Status struct {
  115. Value int `json:"value"`
  116. String string `json:"string"`
  117. Passed bool `json:"passed"`
  118. } `json:"status"`
  119. PollingMinutes struct {
  120. Short int `json:"short"`
  121. Extended int `json:"extended"`
  122. Conveyance int `json:"conveyance"`
  123. } `json:"polling_minutes"`
  124. } `json:"self_test"`
  125. Capabilities struct {
  126. Values []int `json:"values"`
  127. ExecOfflineImmediateSupported bool `json:"exec_offline_immediate_supported"`
  128. OfflineIsAbortedUponNewCmd bool `json:"offline_is_aborted_upon_new_cmd"`
  129. OfflineSurfaceScanSupported bool `json:"offline_surface_scan_supported"`
  130. SelfTestsSupported bool `json:"self_tests_supported"`
  131. ConveyanceSelfTestSupported bool `json:"conveyance_self_test_supported"`
  132. SelectiveSelfTestSupported bool `json:"selective_self_test_supported"`
  133. AttributeAutosaveEnabled bool `json:"attribute_autosave_enabled"`
  134. ErrorLoggingSupported bool `json:"error_logging_supported"`
  135. GpLoggingSupported bool `json:"gp_logging_supported"`
  136. } `json:"capabilities"`
  137. } `json:"ata_smart_data"`
  138. AtaSctCapabilities struct {
  139. Value int `json:"value"`
  140. ErrorRecoveryControlSupported bool `json:"error_recovery_control_supported"`
  141. FeatureControlSupported bool `json:"feature_control_supported"`
  142. DataTableSupported bool `json:"data_table_supported"`
  143. } `json:"ata_sct_capabilities"`
  144. AtaSmartAttributes struct {
  145. Revision int `json:"revision"`
  146. Table []struct {
  147. ID int `json:"id"`
  148. Name string `json:"name"`
  149. Value int `json:"value"`
  150. Worst int `json:"worst"`
  151. Thresh int `json:"thresh"`
  152. WhenFailed string `json:"when_failed"`
  153. Flags struct {
  154. Value int `json:"value"`
  155. String string `json:"string"`
  156. Prefailure bool `json:"prefailure"`
  157. UpdatedOnline bool `json:"updated_online"`
  158. Performance bool `json:"performance"`
  159. ErrorRate bool `json:"error_rate"`
  160. EventCount bool `json:"event_count"`
  161. AutoKeep bool `json:"auto_keep"`
  162. } `json:"flags"`
  163. Raw struct {
  164. Value int `json:"value"`
  165. String string `json:"string"`
  166. } `json:"raw"`
  167. } `json:"table"`
  168. } `json:"ata_smart_attributes"`
  169. PowerOnTime struct {
  170. Hours int `json:"hours"`
  171. Minutes int `json:"minutes"`
  172. } `json:"power_on_time"`
  173. PowerCycleCount int `json:"power_cycle_count"`
  174. Temperature struct {
  175. Current int `json:"current"`
  176. } `json:"temperature"`
  177. AtaSmartSelfTestLog struct {
  178. Standard struct {
  179. Revision int `json:"revision"`
  180. Table []struct {
  181. Type struct {
  182. Value int `json:"value"`
  183. String string `json:"string"`
  184. } `json:"type"`
  185. Status struct {
  186. Value int `json:"value"`
  187. String string `json:"string"`
  188. Passed bool `json:"passed"`
  189. } `json:"status,omitempty"`
  190. LifetimeHours int `json:"lifetime_hours"`
  191. } `json:"table"`
  192. Count int `json:"count"`
  193. ErrorCountTotal int `json:"error_count_total"`
  194. ErrorCountOutdated int `json:"error_count_outdated"`
  195. } `json:"standard"`
  196. } `json:"ata_smart_self_test_log"`
  197. AtaSmartSelectiveSelfTestLog struct {
  198. Revision int `json:"revision"`
  199. Table []struct {
  200. LbaMin int `json:"lba_min"`
  201. LbaMax int `json:"lba_max"`
  202. Status struct {
  203. Value int `json:"value"`
  204. String string `json:"string"`
  205. } `json:"status"`
  206. } `json:"table"`
  207. Flags struct {
  208. Value int `json:"value"`
  209. RemainderScanEnabled bool `json:"remainder_scan_enabled"`
  210. } `json:"flags"`
  211. PowerUpScanResumeMinutes int `json:"power_up_scan_resume_minutes"`
  212. } `json:"ata_smart_selective_self_test_log"`
  213. }
  214. // SMART was used for storing all Devices data
  215. type SMART struct {
  216. Port string `json:"Port"`
  217. DriveSmart *DeviceSMART `json:"SMART"`
  218. }
  219. // DiskSmartInit Desktop script initiation
  220. func system_disk_smart_init() {
  221. log.Println("Starting SMART mointoring")
  222. if !(fileExists("system/disk/smart/win/smartctl.exe") || fileExists("system/disk/smart/linux/smartctl_arm") || fileExists("system/disk/smart/linux/smartctl_arm64") || fileExists("system/disk/smart/linux/smartctl_i386")) {
  223. if build_version == "development" {
  224. log.Fatal("[SMART Mointoring] One or more binary not found.")
  225. } else {
  226. panic("[SMART Mointoring] One or more binary not found.")
  227. }
  228. }
  229. if runtime.GOOS == "windows" {
  230. SystemSmartExecutable = "./system/disk/smart/win/smartctl.exe"
  231. } else if runtime.GOOS == "linux" {
  232. if runtime.GOARCH == "arm" {
  233. SystemSmartExecutable = "./system/disk/smart/linux/smartctl_armv6"
  234. }
  235. if runtime.GOARCH == "arm64" {
  236. SystemSmartExecutable = "./system/disk/smart/linux/smartctl_armv6"
  237. }
  238. if runtime.GOARCH == "386" {
  239. SystemSmartExecutable = "./system/disk/smart/linux/smartctl_i386"
  240. }
  241. if runtime.GOARCH == "amd64" {
  242. SystemSmartExecutable = "./system/disk/smart/linux/smartctl_i386"
  243. }
  244. } else {
  245. if build_version == "development" {
  246. //log.Fatal("[SMART Mointoring] This webApp can't run on imcompitiable environment")
  247. } else {
  248. panic("[SMART Mointoring] This webApp can't run on imcompitiable environment")
  249. }
  250. }
  251. //Register all the required API
  252. http.HandleFunc("/system/disk/smart/getSMART", GetSMART)
  253. http.HandleFunc("/system/disk/smart/getSMARTTable", checkDiskTable)
  254. http.HandleFunc("/system/disk/smart/getLogInfo", checkDiskTestStatus)
  255. //Only allow SMART under sudo moude
  256. if sudo_mode {
  257. //Register as a system setting
  258. registerSetting(settingModule{
  259. Name: "Disk SMART",
  260. Desc: "HardDisk Health Checking",
  261. IconPath: "SystemAO/disk/smart/img/small_icon.png",
  262. Group: "Disk",
  263. StartDir: "SystemAO/disk/smart/smart.html",
  264. RequireAdmin: true,
  265. })
  266. registerSetting(settingModule{
  267. Name: "SMART Log",
  268. Desc: "HardDisk Health Log",
  269. IconPath: "SystemAO/disk/smart/img/small_icon.png",
  270. Group: "Disk",
  271. StartDir: "SystemAO/disk/smart/log.html",
  272. RequireAdmin: true,
  273. })
  274. }
  275. }
  276. // ReadSMART xxx
  277. func ReadSMART() []SMART {
  278. if time.Now().Unix()-lastScanTime > 30 {
  279. SMARTInformation = []SMART{}
  280. //Scan disk
  281. cmd := exec.Command(SystemSmartExecutable, "--scan", "--json=c")
  282. out, _ := cmd.CombinedOutput()
  283. Devices := new(DevicesList)
  284. DevicesOutput := string(out)
  285. json.Unmarshal([]byte(DevicesOutput), &Devices)
  286. for _, element := range Devices.Devices {
  287. //Load SMART for each drive
  288. cmd := exec.Command(SystemSmartExecutable, "-i", element.Name, "-a", "--json=c")
  289. out, _ = cmd.CombinedOutput()
  290. InvSMARTInformation := new(DeviceSMART)
  291. SMARTOutput := string(out)
  292. json.Unmarshal([]byte(SMARTOutput), &InvSMARTInformation)
  293. if len(InvSMARTInformation.Smartctl.Messages) > 0 {
  294. if InvSMARTInformation.Smartctl.Messages[0].Severity == "error" {
  295. log.Println("[SMART Mointoring] Disk " + element.Name + " cannot be readed")
  296. } else {
  297. //putting everything into that struct array
  298. n := SMART{Port: element.Name, DriveSmart: InvSMARTInformation}
  299. SMARTInformation = append(SMARTInformation, n)
  300. }
  301. } else {
  302. //putting everything into that struct array
  303. n := SMART{Port: element.Name, DriveSmart: InvSMARTInformation}
  304. SMARTInformation = append(SMARTInformation, n)
  305. }
  306. }
  307. lastScanTime = time.Now().Unix()
  308. }
  309. return SMARTInformation
  310. }
  311. func GetSMART(w http.ResponseWriter, r *http.Request) {
  312. //Check if user has logged in
  313. if authAgent.CheckAuth(r) == false {
  314. redirectToLoginPage(w, r)
  315. return
  316. }
  317. jsonText, _ := json.Marshal(ReadSMART())
  318. //send!
  319. sendJSONResponse(w, string(jsonText))
  320. }
  321. func checkDiskTable(w http.ResponseWriter, r *http.Request) {
  322. //Check if user has logged in
  323. if authAgent.CheckAuth(r) == false {
  324. redirectToLoginPage(w, r)
  325. return
  326. }
  327. disks, ok := r.URL.Query()["disk"]
  328. if !ok || len(disks[0]) < 1 {
  329. log.Println("Parameter DISK not found.")
  330. return
  331. }
  332. DiskStatus := new(DeviceSMART)
  333. for _, info := range ReadSMART() {
  334. if info.Port == disks[0] {
  335. DiskStatus = info.DriveSmart
  336. }
  337. }
  338. JSONStr, _ := json.Marshal(DiskStatus.AtaSmartAttributes.Table)
  339. //send!
  340. sendJSONResponse(w, string(JSONStr))
  341. }
  342. func checkDiskTestStatus(w http.ResponseWriter, r *http.Request) {
  343. //Check if user has logged in
  344. if authAgent.CheckAuth(r) == false {
  345. redirectToLoginPage(w, r)
  346. return
  347. }
  348. disks, ok := r.URL.Query()["disk"]
  349. if !ok || len(disks[0]) < 1 {
  350. log.Println("Parameter DISK not found.")
  351. return
  352. }
  353. DiskTestStatus := new(DeviceSMART)
  354. for _, info := range ReadSMART() {
  355. if info.Port == disks[0] {
  356. DiskTestStatus = info.DriveSmart
  357. }
  358. }
  359. JSONStr, _ := json.Marshal(DiskTestStatus.AtaSmartData.SelfTest.Status)
  360. //send!
  361. sendJSONResponse(w, string(JSONStr))
  362. }