You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

250 lines
5.4 KiB
JavaScript

{
const matchValue = require("match-value");
const syncpipe = require("syncpipe");
const {B} = require("../unit-bytes-iec");
const mapAttributeFlags = require("./map-attribute-flags");
}
RootInfo
= header:Header infoSection:InfoSection Newline* {
return { ...header, fields: infoSection }
};
RootScan
= devices:ScanDevice* {
return { devices: devices };
}
RootAttributes
= header:Header attributesSection:AttributesSection Newline* {
return { ...header, attributes: attributesSection }
};
_
= (" " / "\t")*
RestOfLine
= content:$[^\n]+ Newline {
return content;
}
Newline
= "\n"
/ "\r\n"
Header 'header'
= "smartctl " versionString:RestOfLine "Copyright" copyrightStatement:RestOfLine Newline {
return { versionString, copyrightStatement };
}
BytesValue
= value:SeparatedNumberValue {
return B(value);
}
NumberValue
= value:$[0-9]+ {
return parseInt(value);
}
SeparatedNumberValue
= value:$[0-9,]+ {
return syncpipe(value, [
(_) => _.replace(/,/g, ""),
(_) => parseInt(_)
]);
}
HexNumberValue
= value:$[0-9A-Fa-f]+ {
return parseInt(value, 16);
}
IdentifierValue
= value:$[a-zA-Z_-]+ {
return value;
}
// smartctl --scan
ScanDevice 'scanned device'
= path:$[^ ]+ _ "-d" _ interface_:$[^ ]+ _ RestOfLine {
return { path: path, interface: interface_ };
}
// smartctl --info
InfoSection 'information section'
= "=== START OF INFORMATION SECTION ===" Newline fields:(InfoField+) {
return fields.filter((field) => field != null);
}
InfoField 'information field'
= InfoFieldSimple
/ InfoFieldIgnored
/ InfoFieldSize
/ InfoFieldRPM
/ InfoFieldSectorSizes
/ InfoFieldBoolean
/ InfoFieldUnknown
InfoFieldSimpleKey
= "Device Model" { return "model"; }
/ "Model Number" { return "model"; }
/ "Model Family" { return "modelFamily"; }
/ "Serial Number" { return "serialNumber"; }
/ "LU WWN Device Id" { return "wwn"; }
/ "Firmware Version" { return "firmwareVersion"; }
/ "Form Factor" { return "formFactor"; }
/ "ATA Version is" { return "ataVersion"; }
/ "SATA Version is" { return "sataVersion"; }
InfoFieldSimple
= key:InfoFieldSimpleKey ":" _ value:RestOfLine {
return { key: key, value: value };
}
InfoFieldUnknown
= key:$[^:]+ ":" _ RestOfLine {
console.warn(`Encountered unrecognized SMART info key: ${key}`);
return null;
}
InfoFieldIgnoredKey
= "Device is"
/ "Local Time is"
InfoFieldIgnored
= key:InfoFieldIgnoredKey ":" _ RestOfLine {
return null;
}
/ "SMART support is:" _ ("Available" / "Unavailable") RestOfLine {
// We don't actually care about this entry, but have to specify its possible values explicitly, to distinguish it from the entry we *do* care about that (annoyingly) uses the same key; see InfoFieldBoolean
return null;
}
InfoFieldSize
// NOTE: We don't actually care about the human-friendly display size after the 'bytes' specifier, hence the RestOfLine
= InfoFieldSizeKey _ value:SeparatedNumberValue _ "bytes"? _ RestOfLine {
return {
key: "size",
value: B(value)
};
}
InfoFieldSizeKey
= "User Capacity:"
/ "Total NVM Capacity:"
InfoFieldRPM
= "Rotation Rate:" _ value:NumberValue _ "rpm" Newline {
return {
key: "rpm",
value: value
};
}
InfoFieldSectorSizes
= "Sector Sizes:" _ logicalSize:BytesValue _ "bytes logical," _ physicalSize:BytesValue _ "bytes physical" Newline {
return {
key: "sectorSizes",
value: {
logical: logicalSize,
physical: physicalSize
}
};
}
InfoFieldBooleanKey
= "SMART support is" { return "smartEnabled"; }
InfoFieldBoolean
= key:InfoFieldBooleanKey ":" _ value:RestOfLine {
return {
key: key,
value: matchValue(value, {
Enabled: true,
Disabled: false
})
};
}
// smartctl --attributes
AttributesSection
= AttributesSectionSATA
/ AttributesSectionNVMe
AttributesSectionSATA
= "=== START OF READ SMART DATA SECTION ===" Newline
"SMART Attributes Data Structure revision number:" _ NumberValue Newline
"Vendor Specific SMART Attributes with Thresholds:" Newline
"ID#" _ "ATTRIBUTE_NAME" _ "FLAG" _ "VALUE" _ "WORST" _ "THRESH" _ "TYPE" _ "UPDATED" _ "WHEN_FAILED" _ "RAW_VALUE" Newline
attributes:AttributeFieldSATA+ {
return attributes;
}
AttributesSectionNVMe
= "=== START OF SMART DATA SECTION ===" Newline
"SMART/Health Information (NVMe Log 0x02)" Newline
attributes:AttributeFieldNVMe+ {
return attributes;
}
AttributeFlags
= "0x" number:HexNumberValue {
return mapAttributeFlags(number);
}
AttributeUpdatedWhen
= "Always"
/ "Offline"
AttributeFailedWhen
= "FAILING_NOW"
/ "In_the_past"
/ "-"
AttributeFieldType
= "Pre-fail"
/ "Old_age"
AttributeFieldSATA
= _ id:NumberValue
_ attributeName:IdentifierValue
_ flags:AttributeFlags
_ value:NumberValue
_ worstValue:NumberValue
_ threshold:NumberValue
_ type:AttributeFieldType
_ updatedWhen:AttributeUpdatedWhen
_ failedWhen:AttributeFailedWhen
_ rawValue:RestOfLine {
return {
id,
attributeName,
flags,
value,
worstValue,
threshold,
rawValue,
updatedWhen: matchValue(updatedWhen, {
"Always": "always",
"Offline": "offline"
}),
type: matchValue(type, {
"Pre-fail": "preFail",
"Old_age": "oldAge"
}),
failingNow: (failedWhen === "FAILING_NOW"),
/* TODO: Should the below include the FAILING_NOW state? */
failedBefore: (failedWhen === "In_the_past")
};
}
AttributeFieldNVMe
= label:$[^:]+ ":" _ value:RestOfLine {
return { label: label, value };
}