aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChuck Tuffli <chuck@FreeBSD.org>2026-04-28 18:49:22 +0000
committerChuck Tuffli <chuck@FreeBSD.org>2026-04-28 19:03:38 +0000
commite139a49b5d8e9385b4d42ea1ec3850bc80d7f989 (patch)
treed4e417358240c31d8bf31e0c5ebe33fefc63c9f4
parent146b30bad9f65a098e6c09ae93bb1da2ff59616d (diff)
Revert "Vendor import of smart at 1.0.2"
This reverts commit 68e5b71517e947b4e3f349c970af362b47b45f27.
-rw-r--r--contrib/smart/Changelog37
-rw-r--r--contrib/smart/LICENSE13
-rw-r--r--contrib/smart/Makefile26
-rw-r--r--contrib/smart/freebsd_dev.c828
-rw-r--r--contrib/smart/libsmart.c1359
-rw-r--r--contrib/smart/libsmart.h174
-rw-r--r--contrib/smart/libsmart_desc.c158
-rw-r--r--contrib/smart/libsmart_dev.h60
-rw-r--r--contrib/smart/libsmart_priv.h83
-rw-r--r--contrib/smart/smart.8245
-rw-r--r--contrib/smart/smart.c334
-rw-r--r--packages/Makefile1
-rw-r--r--packages/smart/Makefile4
-rw-r--r--packages/smart/smart.ucl30
-rw-r--r--usr.sbin/Makefile1
-rw-r--r--usr.sbin/smart/Makefile8
16 files changed, 0 insertions, 3361 deletions
diff --git a/contrib/smart/Changelog b/contrib/smart/Changelog
deleted file mode 100644
index 42b79bc34070..000000000000
--- a/contrib/smart/Changelog
+++ /dev/null
@@ -1,37 +0,0 @@
-This file documents changes for smart releases
-
-version 1.0.2
- - Bring man page up to snuff
- - Fix various complier warnings
-
-version 1.0.1
- - Fix don't print attribute ID with description
-
-version 1.0.0
- - Fix ATA threshold output (gh-10). This is a breaking change as it
- reduces the output from 4 fields to 3 (drops the "reserved" byte
- from threshold).
- - Fix the ATA raw output. This is a breaking change as it increase the
- output from 6 bytes to 7 (i.e., includes the "reserved" byte). Note
- that while some attributes use this byte, most do not.
- - Fix direct debug output (--debug) to standard error
- - Use POSIX memcpy and memset instead of older bXXX equivalents
-
-version 0.4.2
- - Update README contents
-
-version 0.4.1
- - Allow a comma-separated list of attributes
- - Code refactor + update code comments
-
-version 0.3.0
-
- - Reclaim the -d option from debug
- - Change field separator from spaces to tab
- - Add textual descriptions of attribute IDs for ATA, NVMe, and SCSI
- - Add a manual page
- - Fixes
- * libxo structure for attribute and attributes
- * simplify LIBXO ifdef sprawl
- * display of threshold values
- * display of long values
diff --git a/contrib/smart/LICENSE b/contrib/smart/LICENSE
deleted file mode 100644
index 8b0a0bf6a4a6..000000000000
--- a/contrib/smart/LICENSE
+++ /dev/null
@@ -1,13 +0,0 @@
-Copyright (c) 2016-2026 Chuck Tuffli <chuck@tuffli.net>
-
-Permission to use, copy, modify, and distribute this software for any
-purpose with or without fee is hereby granted, provided that the above
-copyright notice and this permission notice appear in all copies.
-
-THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/contrib/smart/Makefile b/contrib/smart/Makefile
deleted file mode 100644
index 64cab720e08f..000000000000
--- a/contrib/smart/Makefile
+++ /dev/null
@@ -1,26 +0,0 @@
-#
-# Copyright (c) 2016-2021 Chuck Tuffli <chuck@tuffli.net>
-#
-# Permission to use, copy, modify, and distribute this software for any
-# purpose with or without fee is hereby granted, provided that the above
-# copyright notice and this permission notice appear in all copies.
-#
-# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-#
-PROG= smart
-SRCS= smart.c libsmart.c libsmart_desc.c
-SRCS+= freebsd_dev.c
-LIBADD= cam xo
-MAN=smart.8
-MLINKS= smart.8 diskhealth.8
-#CFLAGS+= -ggdb -O0
-CFLAGS+= -DLIBXO
-LINKS= ${BINDIR}/smart ${BINDIR}/diskhealth
-
-.include <bsd.prog.mk>
diff --git a/contrib/smart/freebsd_dev.c b/contrib/smart/freebsd_dev.c
deleted file mode 100644
index d1dda2289742..000000000000
--- a/contrib/smart/freebsd_dev.c
+++ /dev/null
@@ -1,828 +0,0 @@
-/*
- * Copyright (c) 2016-2021 Chuck Tuffli <chuck@tuffli.net>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-#include <stdio.h>
-#include <stdlib.h>
-#include <fcntl.h>
-#include <string.h>
-#include <err.h>
-#include <errno.h>
-#include <camlib.h>
-#include <cam/scsi/scsi_message.h>
-
-#include "libsmart.h"
-#include "libsmart_priv.h"
-#include "libsmart_dev.h"
-
-/* Provide compatibility for FreeBSD 11.0 */
-#if (__FreeBSD_version < 1101000)
-
-struct scsi_log_informational_exceptions {
- struct scsi_log_param_header hdr;
-#define SLP_IE_GEN 0x0000
- uint8_t ie_asc;
- uint8_t ie_ascq;
- uint8_t temperature;
-};
-
-#endif
-
-struct fbsd_smart {
- smart_t common;
- struct cam_device *camdev;
-};
-
-static smart_protocol_e __device_get_proto(struct fbsd_smart *);
-static bool __device_proto_tunneled(struct fbsd_smart *);
-static int32_t __device_get_info(struct fbsd_smart *);
-
-smart_h
-device_open(smart_protocol_e protocol, char *devname)
-{
- struct fbsd_smart *h = NULL;
-
- h = malloc(sizeof(struct fbsd_smart));
- if (h == NULL)
- return NULL;
-
- memset(h, 0, sizeof(struct fbsd_smart));
-
- h->common.protocol = SMART_PROTO_MAX;
- h->camdev = cam_open_device(devname, O_RDWR);
- if (h->camdev == NULL) {
- printf("%s: error opening %s - %s\n",
- __func__, devname,
- cam_errbuf);
- free(h);
- h = NULL;
- } else {
- smart_protocol_e proto = __device_get_proto(h);
-
- if ((protocol == SMART_PROTO_AUTO) ||
- (protocol == proto)) {
- h->common.protocol = proto;
- } else {
- printf("%s: protocol mismatch %d vs %d\n",
- __func__, protocol, proto);
- }
-
- if (proto == SMART_PROTO_SCSI) {
- if (__device_proto_tunneled(h)) {
- h->common.protocol = SMART_PROTO_ATA;
- h->common.info.tunneled = 1;
- }
- }
-
- __device_get_info(h);
- }
-
- return h;
-}
-
-void
-device_close(smart_h h)
-{
- struct fbsd_smart *fsmart = h;
-
- if (fsmart != NULL) {
- if (fsmart->camdev != NULL) {
- cam_close_device(fsmart->camdev);
- }
-
- free(fsmart);
- }
-}
-
-static const uint8_t smart_read_data[] = {
- 0xb0, 0xd0, 0x00, 0x4f, 0xc2, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-};
-
-static const uint8_t smart_return_status[] = {
- 0xb0, 0xda, 0x00, 0x4f, 0xc2, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-};
-
-static int32_t
-__device_read_ata(smart_h h, uint32_t page, void *buf, size_t bsize, union ccb *ccb)
-{
- struct fbsd_smart *fsmart = h;
- const uint8_t *smart_fis;
- uint32_t smart_fis_size = 0;
- uint32_t cam_flags = 0;
- uint16_t sector_count = 0;
- uint8_t protocol = 0;
-
- switch (page) {
- case PAGE_ID_ATA_SMART_READ_DATA: /* Support SMART READ DATA */
- smart_fis = smart_read_data;
- smart_fis_size = sizeof(smart_read_data);
- cam_flags = CAM_DIR_IN;
- sector_count = 1;
- protocol = AP_PROTO_PIO_IN;
- break;
- case PAGE_ID_ATA_SMART_RET_STATUS: /* Support SMART RETURN STATUS */
- smart_fis = smart_return_status;
- smart_fis_size = sizeof(smart_return_status);
- /* Command has no data but uses the return status */
- cam_flags = CAM_DIR_NONE;
- protocol = AP_PROTO_NON_DATA;
- bsize = 0;
- break;
- default:
- return EINVAL;
- }
-
- if (fsmart->common.info.tunneled) {
- struct ata_pass_16 *cdb;
- uint8_t cdb_flags;
-
- if (bsize > 0) {
- cdb_flags = AP_FLAG_TDIR_FROM_DEV |
- AP_FLAG_BYT_BLOK_BLOCKS |
- AP_FLAG_TLEN_SECT_CNT;
- } else {
- cdb_flags = AP_FLAG_CHK_COND |
- AP_FLAG_TDIR_FROM_DEV |
- AP_FLAG_BYT_BLOK_BLOCKS;
- }
-
- cdb = (struct ata_pass_16 *)ccb->csio.cdb_io.cdb_bytes;
- memset(cdb, 0, sizeof(*cdb));
-
- scsi_ata_pass_16(&ccb->csio,
- /*retries*/ 1,
- /*cbfcnp*/ NULL,
- /*flags*/ cam_flags,
- /*tag_action*/ MSG_SIMPLE_Q_TAG,
- /*protocol*/ protocol,
- /*ata_flags*/ cdb_flags,
- /*features*/ page,
- /*sector_count*/sector_count,
- /*lba*/ 0,
- /*command*/ ATA_SMART_CMD,
- /*control*/ 0,
- /*data_ptr*/ buf,
- /*dxfer_len*/ bsize,
- /*sense_len*/ SSD_FULL_SIZE,
- /*timeout*/ 5000
- );
- cdb->lba_mid = 0x4f;
- cdb->lba_high = 0xc2;
- cdb->device = 0; /* scsi_ata_pass_16() sets this */
- } else {
- memcpy(&ccb->ataio.cmd.command, smart_fis, smart_fis_size);
-
- cam_fill_ataio(&ccb->ataio,
- /* retries */1,
- /* cbfcnp */NULL,
- /* flags */cam_flags,
- /* tag_action */0,
- /* data_ptr */buf,
- /* dxfer_len */bsize,
- /* timeout */5000);
- ccb->ataio.cmd.flags |= CAM_ATAIO_NEEDRESULT;
- ccb->ataio.cmd.control = 0;
- }
-
- return 0;
-}
-
-static int32_t
-__device_read_scsi(__attribute__((unused)) smart_h h, uint32_t page, void *buf, size_t bsize, union ccb *ccb)
-{
-
- scsi_log_sense(&ccb->csio,
- /* retries */1,
- /* cbfcnp */NULL,
- /* tag_action */0,
- /* page_code */SLS_PAGE_CTRL_CUMULATIVE,
- /* page */page,
- /* save_pages */0,
- /* ppc */0,
- /* paramptr */0,
- /* param_buf */buf,
- /* param_len */bsize,
- /* sense_len */0,
- /* timeout */5000);
-
- return 0;
-}
-
-static int32_t
-__device_read_nvme(__attribute__((unused)) smart_h h, uint32_t page, void *buf, size_t bsize, union ccb *ccb)
-{
- struct ccb_nvmeio *nvmeio = &ccb->nvmeio;
- uint32_t numd = 0; /* number of dwords */
-
- /*
- * NVME CAM passthru
- * 1200000 > version > 1101510 uses nvmeio->cmd.opc
- * 1200059 > version > 1200038 uses nvmeio->cmd.opc
- * 1200081 > version > 1200058 uses nvmeio->cmd.opc_fuse
- * > 1200080 uses nvmeio->cmd.opc
- * This code doesn't support the brief 'opc_fuse' period.
- */
-#if ((__FreeBSD_version > 1200038) || ((__FreeBSD_version > 1101510) && (__FreeBSD_version < 1200000)))
- switch (page) {
- case NVME_LOG_HEALTH_INFORMATION:
- numd = (sizeof(struct nvme_health_information_page) / sizeof(uint32_t));
- break;
- default:
- /* Unsupported log page */
- return EINVAL;
- }
-
- /* Subtract 1 because NUMD is a zero based value */
- numd--;
-
- nvmeio->cmd.opc = NVME_OPC_GET_LOG_PAGE;
- nvmeio->cmd.nsid = NVME_GLOBAL_NAMESPACE_TAG;
- nvmeio->cmd.cdw10 = page | (numd << 16);
-
- cam_fill_nvmeadmin(&ccb->nvmeio,
- /* retries */1,
- /* cbfcnp */NULL,
- /* flags */CAM_DIR_IN,
- /* data_ptr */buf,
- /* dxfer_len */bsize,
- /* timeout */5000);
-#endif
- return 0;
-}
-
-/*
- * Retrieve the SMART RETURN STATUS
- *
- * SMART RETURN STATUS provides the reliability status of the
- * device and can be used as a high-level indication of health.
- */
-static int32_t
-__device_status_ata(smart_h h, union ccb *ccb)
-{
- struct fbsd_smart *fsmart = h;
- uint8_t *buf = NULL;
- uint32_t page = 0;
- uint8_t lba_high = 0, lba_mid = 0, device = 0, status = 0;
-
- if (fsmart->common.info.tunneled) {
- struct ata_res_pass16 {
- u_int16_t reserved[5];
- u_int8_t flags;
- u_int8_t error;
- u_int8_t sector_count_exp;
- u_int8_t sector_count;
- u_int8_t lba_low_exp;
- u_int8_t lba_low;
- u_int8_t lba_mid_exp;
- u_int8_t lba_mid;
- u_int8_t lba_high_exp;
- u_int8_t lba_high;
- u_int8_t device;
- u_int8_t status;
- } *res_pass16 = (struct ata_res_pass16 *)(uintptr_t)
- &ccb->csio.sense_data;
-
- buf = ccb->csio.data_ptr;
- page = ((struct ata_pass_16 *)ccb->csio.cdb_io.cdb_bytes)->features;
- lba_high = res_pass16->lba_high;
- lba_mid = res_pass16->lba_mid;
- device = res_pass16->device;
- status = res_pass16->status;
-
- /*
- * Note that this generates an expected CHECK CONDITION.
- * Mask it so the outer function doesn't print an error
- * message.
- */
- ccb->ccb_h.status &= ~CAM_STATUS_MASK;
- ccb->ccb_h.status |= CAM_REQ_CMP;
- } else {
- struct ccb_ataio *ataio = (struct ccb_ataio *)&ccb->ataio;
-
- buf = ataio->data_ptr;
- page = ataio->cmd.features;
- lba_high = ataio->res.lba_high;
- lba_mid = ataio->res.lba_mid;
- device = ataio->res.device;
- status = ataio->res.status;
- }
-
- switch (page) {
- case PAGE_ID_ATA_SMART_RET_STATUS:
- /*
- * Typically, SMART related log pages return data, but this
- * command is different in that the data is encoded in the
- * result registers.
- *
- * Handle this in a UNIX-like way by writing a 0 (no errors)
- * or 1 (threshold exceeded condition) to the output buffer.
- */
- dprintf("SMART_RET_STATUS: lba mid=%#x high=%#x device=%#x status=%#x\n",
- lba_mid,
- lba_high,
- device,
- status);
- if ((lba_high == 0x2c) && (lba_mid == 0xf4)) {
- buf[0] = 1;
- } else if ((lba_high == 0xc2) && (lba_mid == 0x4f)) {
- buf[0] = 0;
- } else {
- /* Ruh-roh ... */
- buf[0] = 255;
- }
- break;
- default:
- ;
- }
-
- return 0;
-}
-
-int32_t
-device_read_log(smart_h h, uint32_t page, void *buf, size_t bsize)
-{
- struct fbsd_smart *fsmart = h;
- union ccb *ccb = NULL;
- int rc = 0;
-
- if (fsmart == NULL)
- return EINVAL;
-
- dprintf("read log page %#x\n", page);
-
- ccb = cam_getccb(fsmart->camdev);
- if (ccb == NULL)
- return ENOMEM;
-
- CCB_CLEAR_ALL_EXCEPT_HDR(ccb);
-
- switch (fsmart->common.protocol) {
- case SMART_PROTO_ATA:
- rc = __device_read_ata(h, page, buf, bsize, ccb);
- break;
- case SMART_PROTO_SCSI:
- rc = __device_read_scsi(h, page, buf, bsize, ccb);
- break;
- case SMART_PROTO_NVME:
- rc = __device_read_nvme(h, page, buf, bsize, ccb);
- break;
- default:
- warnx("unsupported protocol %d", fsmart->common.protocol);
- cam_freeccb(ccb);
- return ENODEV;
- }
-
- if (rc) {
- if (rc == EINVAL)
- warnx("unsupported page %#x", page);
-
- return rc;
- }
-
- if (((rc = cam_send_ccb(fsmart->camdev, ccb)) < 0)
- || ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)) {
- if (rc < 0)
- warn("error sending command");
- }
-
- /*
- * Most commands don't need any post-processing. But then there's
- * ATA. It's why we can't have nice things :(
- */
- switch (fsmart->common.protocol) {
- case SMART_PROTO_ATA:
- __device_status_ata(h, ccb);
- break;
- default:
- ;
- }
-
- if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
- cam_error_print(fsmart->camdev, ccb, CAM_ESF_ALL,
- CAM_EPF_ALL, stderr);
- }
-
- cam_freeccb(ccb);
-
- return 0;
-}
-
-/*
- * The SCSI / ATA Translation (SAT) requires devices to support the ATA
- * Information VPD Page (T10/2126-D Revision 04). Use the existence of
- * this page to identify tunneled devices.
- */
-static bool
-__device_proto_tunneled(struct fbsd_smart *fsmart)
-{
- union ccb *ccb = NULL;
- struct scsi_vpd_supported_page_list supportedp;
- uint32_t i;
- bool is_tunneled = false;
-
- if (fsmart->common.protocol != SMART_PROTO_SCSI) {
- return false;
- }
-
- ccb = cam_getccb(fsmart->camdev);
- if (!ccb) {
- warn("Allocation failure ccb=%p", ccb);
- goto __device_proto_tunneled_out;
- }
-
- scsi_inquiry(&ccb->csio,
- 3, // retries
- NULL, // callback function
- MSG_SIMPLE_Q_TAG, // tag action
- (uint8_t *)&supportedp,
- sizeof(struct scsi_vpd_supported_page_list),
- 1, // EVPD
- SVPD_SUPPORTED_PAGE_LIST, // page code
- SSD_FULL_SIZE, // sense length
- 5000); // timeout
-
- ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
-
- if ((cam_send_ccb(fsmart->camdev, ccb) >= 0) &&
- ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP)) {
- dprintf("Looking for page %#x (total = %u):\n", SVPD_ATA_INFORMATION,
- supportedp.length);
- for (i = 0; i < supportedp.length; i++) {
- dprintf("\t[%u] = %#x\n", i, supportedp.list[i]);
- if (supportedp.list[i] == SVPD_ATA_INFORMATION) {
- is_tunneled = true;
- break;
- }
- }
- }
-
- cam_freeccb(ccb);
-
-__device_proto_tunneled_out:
- return is_tunneled;
-}
-
-/**
- * Retrieve the device protocol type via the transport settings
- *
- * @return protocol type or SMART_PROTO_MAX on error
- */
-static smart_protocol_e
-__device_get_proto(struct fbsd_smart *fsmart)
-{
- smart_protocol_e proto = SMART_PROTO_MAX;
- union ccb *ccb;
-
- if (!fsmart || !fsmart->camdev) {
- warn("Bad handle %p", fsmart);
- return proto;
- }
-
- ccb = cam_getccb(fsmart->camdev);
- if (ccb != NULL) {
- CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->cts);
-
- ccb->ccb_h.func_code = XPT_GET_TRAN_SETTINGS;
- ccb->cts.type = CTS_TYPE_CURRENT_SETTINGS;
-
- if (cam_send_ccb(fsmart->camdev, ccb) >= 0) {
- if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) {
- struct ccb_trans_settings *cts = &ccb->cts;
-
- switch (cts->protocol) {
- case PROTO_ATA:
- proto = SMART_PROTO_ATA;
- break;
- case PROTO_SCSI:
- proto = SMART_PROTO_SCSI;
- break;
- case PROTO_NVME:
- proto = SMART_PROTO_NVME;
- break;
- default:
- printf("%s: unknown protocol %d\n",
- __func__,
- cts->protocol);
- }
- }
- }
-
- cam_freeccb(ccb);
- }
-
- return proto;
-}
-
-static int32_t
-__device_info_ata(struct fbsd_smart *fsmart, struct ccb_getdev *cgd)
-{
- smart_info_t *sinfo = NULL;
-
- if (!fsmart || !cgd) {
- return -1;
- }
-
- sinfo = &fsmart->common.info;
-
- sinfo->supported = cgd->ident_data.support.command1 &
- ATA_SUPPORT_SMART;
-
- dprintf("ATA command1 = %#x\n", cgd->ident_data.support.command1);
-
- cam_strvis((uint8_t *)sinfo->device, cgd->ident_data.model,
- sizeof(cgd->ident_data.model),
- sizeof(sinfo->device));
- cam_strvis((uint8_t *)sinfo->rev, cgd->ident_data.revision,
- sizeof(cgd->ident_data.revision),
- sizeof(sinfo->rev));
- cam_strvis((uint8_t *)sinfo->serial, cgd->ident_data.serial,
- sizeof(cgd->ident_data.serial),
- sizeof(sinfo->serial));
-
- return 0;
-}
-
-static int32_t
-__device_info_scsi(struct fbsd_smart *fsmart, struct ccb_getdev *cgd)
-{
- smart_info_t *sinfo = NULL;
- union ccb *ccb = NULL;
- struct scsi_vpd_unit_serial_number *snum = NULL;
- struct scsi_log_informational_exceptions ie = {0};
-
- if (!fsmart || !cgd) {
- return -1;
- }
-
- sinfo = &fsmart->common.info;
-
- cam_strvis((uint8_t *)sinfo->vendor, (uint8_t *)cgd->inq_data.vendor,
- sizeof(cgd->inq_data.vendor),
- sizeof(sinfo->vendor));
- cam_strvis((uint8_t *)sinfo->device, (uint8_t *)cgd->inq_data.product,
- sizeof(cgd->inq_data.product),
- sizeof(sinfo->device));
- cam_strvis((uint8_t *)sinfo->rev, (uint8_t *)cgd->inq_data.revision,
- sizeof(cgd->inq_data.revision),
- sizeof(sinfo->rev));
-
- ccb = cam_getccb(fsmart->camdev);
- snum = malloc(sizeof(struct scsi_vpd_unit_serial_number));
- if (!ccb || !snum) {
- warn("Allocation failure ccb=%p snum=%p", ccb, snum);
- goto __device_info_scsi_out;
- }
-
- /* Get the serial number */
- CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->csio);
-
- scsi_inquiry(&ccb->csio,
- 3, // retries
- NULL, // callback function
- MSG_SIMPLE_Q_TAG, // tag action
- (uint8_t *)snum,
- sizeof(struct scsi_vpd_unit_serial_number),
- 1, // EVPD
- SVPD_UNIT_SERIAL_NUMBER, // page code
- SSD_FULL_SIZE, // sense length
- 5000); // timeout
-
- ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
-
- if ((cam_send_ccb(fsmart->camdev, ccb) >= 0) &&
- ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP)) {
- cam_strvis((uint8_t *)sinfo->serial, snum->serial_num,
- snum->length,
- sizeof(sinfo->serial));
- sinfo->serial[sizeof(sinfo->serial) - 1] = '\0';
- }
-
- memset(ccb, 0, sizeof(*ccb));
-
- scsi_log_sense(&ccb->csio,
- /* retries */1,
- /* cbfcnp */NULL,
- /* tag_action */0,
- /* page_code */SLS_PAGE_CTRL_CUMULATIVE,
- /* page */SLS_IE_PAGE,
- /* save_pages */0,
- /* ppc */0,
- /* paramptr */0,
- /* param_buf */(uint8_t *)&ie,
- /* param_len */sizeof(ie),
- /* sense_len */0,
- /* timeout */5000);
-
- /*
- * Note: The existance of the Informational Exceptions (IE) log page
- * appears to be the litmus test for SMART support in SCSI
- * devices. Confusingly, smartctl will report SMART health
- * status as 'OK' if the device doesn't support the IE page.
- * For now, just report the facts.
- */
- if ((cam_send_ccb(fsmart->camdev, ccb) >= 0) &&
- ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP)) {
- if ((ie.hdr.param_len < 4) || ie.ie_asc || ie.ie_ascq) {
- printf("Log Sense, Informational Exceptions failed "
- "(length=%u asc=%#x ascq=%#x)\n",
- ie.hdr.param_len, ie.ie_asc, ie.ie_ascq);
- } else {
- sinfo->supported = true;
- }
- }
-
-__device_info_scsi_out:
- free(snum);
- if (ccb)
- cam_freeccb(ccb);
-
- return 0;
-}
-
-static int32_t
-__device_info_nvme(struct fbsd_smart *fsmart, struct ccb_getdev *cgd)
-{
- union ccb *ccb;
- smart_info_t *sinfo = NULL;
- struct nvme_controller_data cd;
-
- if (!fsmart || !cgd) {
- return -1;
- }
-
- sinfo = &fsmart->common.info;
-
- sinfo->supported = true;
-
- ccb = cam_getccb(fsmart->camdev);
- if (ccb != NULL) {
- struct ccb_dev_advinfo *cdai = &ccb->cdai;
-
- CCB_CLEAR_ALL_EXCEPT_HDR(cdai);
-
- cdai->ccb_h.func_code = XPT_DEV_ADVINFO;
- cdai->ccb_h.flags = CAM_DIR_IN;
- cdai->flags = CDAI_FLAG_NONE;
-#ifdef CDAI_TYPE_NVME_CNTRL
- cdai->buftype = CDAI_TYPE_NVME_CNTRL;
-#else
- cdai->buftype = 6;
-#endif
- cdai->bufsiz = sizeof(struct nvme_controller_data);
- cdai->buf = (uint8_t *)&cd;
-
- if (cam_send_ccb(fsmart->camdev, ccb) >= 0) {
- if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) {
- cam_strvis((uint8_t *)sinfo->device, cd.mn,
- sizeof(cd.mn),
- sizeof(sinfo->device));
- cam_strvis((uint8_t *)sinfo->rev, cd.fr,
- sizeof(cd.fr),
- sizeof(sinfo->rev));
- cam_strvis((uint8_t *)sinfo->serial, cd.sn,
- sizeof(cd.sn),
- sizeof(sinfo->serial));
- }
- }
-
- cam_freeccb(ccb);
- }
-
- return 0;
-}
-
-static int32_t
-__device_info_tunneled_ata(struct fbsd_smart *fsmart)
-{
- struct ata_params ident_data;
- union ccb *ccb = NULL;
- struct ata_pass_16 *ata_pass_16;
- struct ata_cmd ata_cmd;
- int32_t rc = -1;
-
- ccb = cam_getccb(fsmart->camdev);
- if (ccb == NULL) {
- goto __device_info_tunneled_ata_out;
- }
-
- memset(&ident_data, 0, sizeof(struct ata_params));
-
- CCB_CLEAR_ALL_EXCEPT_HDR(ccb);
-
- scsi_ata_pass_16(&ccb->csio,
- /*retries*/ 1,
- /*cbfcnp*/ NULL,
- /*flags*/ CAM_DIR_IN,
- /*tag_action*/ MSG_SIMPLE_Q_TAG,
- /*protocol*/ AP_PROTO_PIO_IN,
- /*ata_flags*/ AP_FLAG_TLEN_SECT_CNT |
- AP_FLAG_BYT_BLOK_BLOCKS |
- AP_FLAG_TDIR_FROM_DEV,
- /*features*/ 0,
- /*sector_count*/sizeof(struct ata_params),
- /*lba*/ 0,
- /*command*/ ATA_ATA_IDENTIFY,
- /*control*/ 0,
- /*data_ptr*/ (uint8_t *)&ident_data,
- /*dxfer_len*/ sizeof(struct ata_params),
- /*sense_len*/ SSD_FULL_SIZE,
- /*timeout*/ 5000
- );
-
- ata_pass_16 = (struct ata_pass_16 *)ccb->csio.cdb_io.cdb_bytes;
- ata_cmd.command = ata_pass_16->command;
- ata_cmd.control = ata_pass_16->control;
- ata_cmd.features = ata_pass_16->features;
-
- rc = cam_send_ccb(fsmart->camdev, ccb);
- if (rc != 0) {
- warnx("%s: scsi_ata_pass_16() failed (programmer error?)",
- __func__);
- goto __device_info_tunneled_ata_out;
- }
-
- fsmart->common.info.supported = ident_data.support.command1 &
- ATA_SUPPORT_SMART;
-
- dprintf("ATA command1 = %#x\n", ident_data.support.command1);
-
-__device_info_tunneled_ata_out:
- if (ccb) {
- cam_freeccb(ccb);
- }
-
- return rc;
-}
-
-/**
- * Retrieve the device information and use to populate the info structure
- */
-static int32_t
-__device_get_info(struct fbsd_smart *fsmart)
-{
- union ccb *ccb;
- int32_t rc = -1;
-
- if (!fsmart || !fsmart->camdev) {
- warn("Bad handle %p", fsmart);
- return -1;
- }
-
- ccb = cam_getccb(fsmart->camdev);
- if (ccb != NULL) {
- struct ccb_getdev *cgd = &ccb->cgd;
-
- CCB_CLEAR_ALL_EXCEPT_HDR(cgd);
-
- /*
- * GDEV_TYPE doesn't support NVMe. What we do get is:
- * - device (ata/model, scsi/product)
- * - revision (ata, scsi)
- * - serial (ata)
- * - vendor (scsi)
- * - supported (ata)
- *
- * Serial # for all proto via ccb_dev_advinfo (buftype CDAI_TYPE_SERIAL_NUM)
- */
- ccb->ccb_h.func_code = XPT_GDEV_TYPE;
-
- if (cam_send_ccb(fsmart->camdev, ccb) >= 0) {
- if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) {
- switch (cgd->protocol) {
- case PROTO_ATA:
- rc = __device_info_ata(fsmart, cgd);
- break;
- case PROTO_SCSI:
- rc = __device_info_scsi(fsmart, cgd);
- if (!rc && fsmart->common.protocol == SMART_PROTO_ATA) {
- rc = __device_info_tunneled_ata(fsmart);
- }
- break;
- case PROTO_NVME:
- rc = __device_info_nvme(fsmart, cgd);
- break;
- default:
- printf("%s: unsupported protocol %d\n",
- __func__, cgd->protocol);
- }
- }
- }
-
- cam_freeccb(ccb);
- }
-
- return rc;
-}
diff --git a/contrib/smart/libsmart.c b/contrib/smart/libsmart.c
deleted file mode 100644
index a1732de09ed9..000000000000
--- a/contrib/smart/libsmart.c
+++ /dev/null
@@ -1,1359 +0,0 @@
-/*
- * Copyright (c) 2016-2026 Chuck Tuffli <chuck@tuffli.net>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdbool.h>
-#include <stddef.h>
-#include <assert.h>
-#include <err.h>
-#include <string.h>
-#include <sys/endian.h>
-
-#ifdef LIBXO
-#include <libxo/xo.h>
-#endif
-
-#include "libsmart.h"
-#include "libsmart_priv.h"
-#include "libsmart_dev.h"
-
-/* Default page lists */
-
-static smart_page_list_t pg_list_ata = {
- .pg_count = 2,
- .pages = {
- { .id = PAGE_ID_ATA_SMART_READ_DATA, .bytes = 512 },
- { .id = PAGE_ID_ATA_SMART_RET_STATUS, .bytes = 4 }
- }
-};
-
-#define PAGE_ID_NVME_SMART_HEALTH 0x02
-
-static smart_page_list_t pg_list_nvme = {
- .pg_count = 1,
- .pages = {
- { .id = PAGE_ID_NVME_SMART_HEALTH, .bytes = 512 }
- }
-};
-
-static smart_page_list_t pg_list_scsi = {
- .pg_count = 8,
- .pages = {
- { .id = PAGE_ID_SCSI_WRITE_ERR, .bytes = 128 },
- { .id = PAGE_ID_SCSI_READ_ERR, .bytes = 128 },
- { .id = PAGE_ID_SCSI_VERIFY_ERR, .bytes = 128 },
- { .id = PAGE_ID_SCSI_NON_MEDIUM_ERR, .bytes = 128 },
- { .id = PAGE_ID_SCSI_LAST_N_ERR, .bytes = 128 },
- { .id = PAGE_ID_SCSI_TEMPERATURE, .bytes = 64 },
- { .id = PAGE_ID_SCSI_START_STOP_CYCLE, .bytes = 128 },
- { .id = PAGE_ID_SCSI_INFO_EXCEPTION, .bytes = 64 },
- }
-};
-
-static uint32_t __smart_attribute_max(smart_buf_t *sb);
-static uint32_t __smart_buffer_size(smart_h h);
-static smart_map_t *__smart_map(smart_h h, smart_buf_t *sb);
-static smart_page_list_t *__smart_page_list(smart_h h);
-static int32_t __smart_read_pages(smart_h h, smart_buf_t *sb);
-
-static const char *
-smart_proto_str(smart_protocol_e p)
-{
-
- switch (p) {
- case SMART_PROTO_AUTO:
- return "auto";
- case SMART_PROTO_ATA:
- return "ATA";
- case SMART_PROTO_SCSI:
- return "SCSI";
- case SMART_PROTO_NVME:
- return "NVME";
- default:
- return "Unknown";
- }
-}
-
-smart_h
-smart_open(smart_protocol_e protocol, char *devname)
-{
- smart_t *s;
-
- s = device_open(protocol, devname);
-
- if (s) {
- dprintf("protocol %s (specified %s%s)\n",
- smart_proto_str(s->protocol),
- smart_proto_str(protocol),
- s->info.tunneled ? ", tunneled ATA" : "");
- }
-
- return s;
-}
-
-void
-smart_close(smart_h h)
-{
-
- device_close(h);
-}
-
-bool
-smart_supported(smart_h h)
-{
- smart_t *s = h;
- bool supported = false;
-
- if (s) {
- supported = s->info.supported;
- dprintf("SMART is %ssupported\n", supported ? "" : "not ");
- }
-
- return supported;
-}
-
-smart_map_t *
-smart_read(smart_h h)
-{
- smart_t *s = h;
- smart_buf_t *sb = NULL;
- smart_map_t *sm = NULL;
-
- sb = calloc(1, sizeof(smart_buf_t));
- if (sb) {
- sb->protocol = s->protocol;
-
- /*
- * Need the page list to calculate the buffer size. If one
- * isn't specified, get the default based on the protocol.
- */
- if (s->pg_list == NULL) {
- s->pg_list = __smart_page_list(s);
- if (!s->pg_list) {
- goto smart_read_out;
- }
- }
-
- sb->b = NULL;
- sb->bsize = __smart_buffer_size(s);
-
- if (sb->bsize != 0) {
- sb->b = malloc(sb->bsize);
- }
-
- if (sb->b == NULL) {
- goto smart_read_out;
- }
-
- if (__smart_read_pages(s, sb) < 0) {
- goto smart_read_out;
- }
-
- sb->attr_count = __smart_attribute_max(sb);
-
- sm = __smart_map(h, sb);
- if (!sm) {
- free(sb->b);
- free(sb);
- sb = NULL;
- }
- }
-
-smart_read_out:
- if (!sm) {
- if (sb) {
- if (sb->b) {
- free(sb->b);
- }
-
- free(sb);
- }
- }
-
- return sm;
-}
-
-void
-smart_free(smart_map_t *sm)
-{
- smart_buf_t *sb = NULL;
- uint32_t i;
-
- if (sm == NULL)
- return;
-
- sb = sm->sb;
-
- if (sb) {
- if (sb->b) {
- free(sb->b);
- sb->b = NULL;
- }
-
- free(sb);
- }
-
- for (i = 0; i < sm->count; i++) {
- smart_map_t *tm = sm->attr[i].thresh;
-
- if (tm) {
- free(tm);
- }
-
- if (sm->attr[i].flags & SMART_ATTR_F_ALLOC) {
- free((void *)(uintptr_t)sm->attr[i].description);
- }
- }
-
- free(sm);
-}
-
-/*
- * Format specifier for the various output types
- * Provides versions to use with libxo and without
- * TODO some of this is ATA specific
- */
-#ifndef LIBXO
-# define __smart_print_val(fmt, ...) printf(fmt, ##__VA_ARGS__)
-# define VEND_STR "Vendor\t%s\n"
-# define DEV_STR "Device\t%s\n"
-# define REV_STR "Revision\t%s\n"
-# define SERIAL_STR "Serial\t%s\n"
-# define PAGE_HEX "%#01.1x\t"
-# define PAGE_DEC "%d\t"
-# define ID_HEX "%#01.1x\t"
-# define ID_DEC "%d\t"
-# define RAW_STR "%s"
-# define RAW_HEX "%#01.1x"
-# define RAW_DEC "%d"
-/* Long integer version of the format macro */
-# define RAW_LHEX "%#01.1" PRIx64
-# define RAW_LDEC "%" PRId64
-# define THRESH_HEX "\t%#02.2x\t%#01.1x\t%#01.1x"
-# define THRESH_DEC "\t%d\t%d\t%d"
-# define DESC_STR "%s"
-#else
-# define __smart_print_val(fmt, ...) xo_emit(fmt, ##__VA_ARGS__)
-# define VEND_STR "{L:Vendor}{P:\t}{:vendor/%s}\n"
-# define DEV_STR "{L:Device}{P:\t}{:device/%s}\n"
-# define REV_STR "{L:Revision}{P:\t}{:rev/%s}\n"
-# define SERIAL_STR "{L:Serial}{P:\t}{:serial/%s}\n"
-# define PAGE_HEX "{k:page/%#01.1x}{P:\t}"
-# define PAGE_DEC "{k:page/%d}{P:\t}"
-# define ID_HEX "{k:id/%#01.1x}{P:\t}"
-# define ID_DEC "{k:id/%d}{P:\t}"
-# define RAW_STR "{k:raw/%s}"
-# define RAW_HEX "{k:raw/%#01.1x}"
-# define RAW_DEC "{k:raw/%d}"
-/* Long integer version of the format macro */
-# define RAW_LHEX "{k:raw/%#01.1" PRIx64 "}"
-# define RAW_LDEC "{k:raw/%" PRId64 "}"
-# define THRESH_HEX "{P:\t}{k:flags/%#02.2x}{P:\t}{k:nominal/%#01.1x}{P:\t}{k:worst/%#01.1x}"
-# define THRESH_DEC "{P:\t}{k:flags/%d}{P:\t}{k:nominal/%d}{P:\t}{k:worst/%d}"
-# define DESC_STR "{:description}{P:\t}"
-#endif
-
-#define THRESH_COUNT 3
-
-
-/* Convert an 128-bit unsigned integer to a string */
-static char *
-__smart_u128_str(smart_attr_t *sa)
-{
- /* Max size is log10(x) = log2(x) / log2(10) ~= log2(x) / 3.322 */
-#define MAX_LEN (128 / 3 + 1 + 1)
- static char s[MAX_LEN];
- char *p = s + MAX_LEN - 1;
- uint32_t *a = (uint32_t *)sa->raw;
- uint64_t r, d;
-
- *p-- = '\0';
-
- do {
- r = a[3];
-
- d = r / 10;
- r = ((r - d * 10) << 32) + a[2];
- a[3] = d;
-
- d = r / 10;
- r = ((r - d * 10) << 32) + a[1];
- a[2] = d;
-
- d = r / 10;
- r = ((r - d * 10) << 32) + a[0];
- a[1] = d;
-
- d = r / 10;
- r = r - d * 10;
- a[0] = d;
-
- *p-- = '0' + r;
- } while (a[0] || a[1] || a[2] || a[3]);
-
- p++;
-
- while ((*p == '0') && (p < &s[sizeof(s) - 2]))
- p++;
-
- return p;
-}
-
-static void
-__smart_print_thresh(smart_map_t *tm, uint32_t flags)
-{
- bool do_hex = false;
-
- if (!tm) {
- return;
- }
-
- if (flags & SMART_OPEN_F_HEX)
- do_hex = true;
-
- __smart_print_val(do_hex ? THRESH_HEX : THRESH_DEC,
- *((uint16_t *)tm->attr[0].raw),
- *((uint8_t *)tm->attr[1].raw),
- *((uint8_t *)tm->attr[2].raw));
-}
-
-/* Does the attribute match one requested by the caller? */
-static bool
-__smart_attr_match(smart_matches_t *match, smart_attr_t *attr)
-{
- uint32_t i;
-
- assert((match != NULL) && (attr != NULL));
-
- for (i = 0; i < match->count; i++) {
- if ((match->m[i].page != -1) && ((uint32_t)match->m[i].page != attr->page))
- continue;
-
- if ((uint32_t)match->m[i].id == attr->id)
- return true;
- }
-
- return false;
-}
-
-void
-smart_print(__attribute__((unused)) smart_h h, smart_map_t *sm, smart_matches_t *which, uint32_t flags)
-{
- uint32_t i;
- bool do_hex = false, do_descr = false;
- uint32_t bytes = 0;
-
- if (!sm) {
- return;
- }
-
- if (flags & SMART_OPEN_F_HEX)
- do_hex = true;
- if (flags & SMART_OPEN_F_DESCR)
- do_descr = true;
-
-#ifdef LIBXO
- xo_open_container("attributes");
- xo_open_list("attribute");
-#endif
- for (i = 0; i < sm->count; i++) {
- /* If we're printing a specific attribute, is this it? */
- if ((which != NULL) && !__smart_attr_match(which, &sm->attr[i])) {
- continue;
- }
-
-#ifdef LIBXO
- xo_open_instance("attribute");
-#endif
- /* Print the page / attribute ID if selecting all attributes */
- if (which == NULL) {
- if (do_descr && (sm->attr[i].description != NULL))
- __smart_print_val(DESC_STR, sm->attr[i].description);
- else {
- __smart_print_val(do_hex ? PAGE_HEX : PAGE_DEC, sm->attr[i].page);
- __smart_print_val(do_hex ? ID_HEX : ID_DEC, sm->attr[i].id);
- }
- }
-
- bytes = sm->attr[i].bytes;
-
- /* Print the attribute based on its size */
- if (sm->attr[i].flags & SMART_ATTR_F_STR) {
- __smart_print_val(RAW_STR, (char *)sm->attr[i].raw);
- } else if (bytes > 8) {
- if (do_hex)
- ;
- else
- __smart_print_val(RAW_STR,
- __smart_u128_str(&sm->attr[i]));
-
- } else if (bytes > 4) {
- uint64_t v64 = 0;
- uint64_t mask = UINT64_MAX;
-
- memcpy(&v64, sm->attr[i].raw, bytes);
-
- if (sm->attr[i].flags & SMART_ATTR_F_BE) {
- v64 = be64toh(v64);
- } else {
- v64 = le64toh(v64);
- }
-
- mask >>= 8 * (sizeof(uint64_t) - bytes);
-
- v64 &= mask;
-
- __smart_print_val(do_hex ? RAW_LHEX : RAW_LDEC, v64);
-
- } else if (bytes > 2) {
- uint32_t v32 = 0;
- uint32_t mask = UINT32_MAX;
-
- memcpy(&v32, sm->attr[i].raw, bytes);
-
- if (sm->attr[i].flags & SMART_ATTR_F_BE) {
- v32 = be32toh(v32);
- } else {
- v32 = le32toh(v32);
- }
-
- mask >>= 8 * (sizeof(uint32_t) - bytes);
-
- v32 &= mask;
-
- __smart_print_val(do_hex ? RAW_HEX : RAW_DEC, v32);
-
- } else if (bytes > 1) {
- uint16_t v16 = 0;
- uint16_t mask = UINT16_MAX;
-
- memcpy(&v16, sm->attr[i].raw, bytes);
-
- if (sm->attr[i].flags & SMART_ATTR_F_BE) {
- v16 = be16toh(v16);
- } else {
- v16 = le16toh(v16);
- }
-
- mask >>= 8 * (sizeof(uint16_t) - bytes);
-
- v16 &= mask;
-
- __smart_print_val(do_hex ? RAW_HEX : RAW_DEC, v16);
-
- } else if (bytes > 0) {
- uint8_t v8 = *((uint8_t *)sm->attr[i].raw);
-
- __smart_print_val(do_hex ? RAW_HEX : RAW_DEC, v8);
- }
-
- if ((flags & SMART_OPEN_F_THRESH) && sm->attr[i].thresh) {
- xo_open_container("threshold");
- __smart_print_thresh(sm->attr[i].thresh, flags);
- xo_close_container("threshold");
- }
-
- __smart_print_val("\n");
-
-#ifdef LIBXO
- xo_close_instance("attribute");
-#endif
- }
-#ifdef LIBXO
- xo_close_list("attribute");
- xo_close_container("attributes");
-#endif
-}
-
-void
-smart_print_device_info(smart_h h)
-{
- smart_t *s = h;
-
- if (!s) {
- return;
- }
-
- if (*s->info.vendor != '\0')
- __smart_print_val(VEND_STR, s->info.vendor);
- if (*s->info.device != '\0')
- __smart_print_val(DEV_STR, s->info.device);
- if (*s->info.rev != '\0')
- __smart_print_val(REV_STR, s->info.device);
- if (*s->info.serial != '\0')
- __smart_print_val(SERIAL_STR, s->info.serial);
-}
-
-static uint32_t
-__smart_attr_max_ata(smart_buf_t *sb)
-{
- uint32_t max = 0;
-
- if (sb) {
- max = 30;
- }
-
- return max;
-}
-
-static uint32_t
-__smart_attr_max_nvme(smart_buf_t *sb)
-{
- uint32_t max = 0;
-
- if (sb) {
- max = 512;
- }
-
- return max;
-}
-
-static uint32_t
-__smart_attr_max_scsi(smart_buf_t *sb)
-{
- uint32_t max = 0;
-
- if (sb) {
- max = 512;
- }
-
- return max;
-}
-
-static uint32_t
-__smart_attribute_max(smart_buf_t *sb)
-{
- uint32_t count = 0;
-
- if (sb != NULL) {
- switch (sb->protocol) {
- case SMART_PROTO_ATA:
- count = __smart_attr_max_ata(sb);
- break;
- case SMART_PROTO_NVME:
- count = __smart_attr_max_nvme(sb);
- break;
- case SMART_PROTO_SCSI:
- count = __smart_attr_max_scsi(sb);
- break;
- default:
- ;
- }
- }
-
- return count;
-}
-
-/**
- * Return the total buffer size needed by the protocol's page list
- */
-static uint32_t
-__smart_buffer_size(smart_h h)
-{
- smart_t *s = h;
- uint32_t size = 0;
-
- if ((s != NULL) && (s->pg_list != NULL)) {
- smart_page_list_t *plist = s->pg_list;
- uint32_t p = 0;
-
- for (p = 0; p < plist->pg_count; p++) {
- size += plist->pages[p].bytes;
- }
- }
-
- return size;
-}
-
-/*
- * Map SMART READ DATA threshold attributes
- *
- * Read the 3 consecutive values (flags, nominal, and worst)
- */
-static smart_map_t *
-__smart_map_ata_thresh(uint8_t *b)
-{
- smart_map_t *sm = NULL;
-
- sm = malloc(sizeof(smart_map_t) + (THRESH_COUNT * sizeof(smart_attr_t)));
- if (sm) {
- uint32_t i;
-
- sm->count = THRESH_COUNT;
-
- sm->attr[0].page = 0;
- sm->attr[0].id = 0;
- sm->attr[0].bytes = 2;
- sm->attr[0].flags = 0;
- sm->attr[0].raw = b;
- sm->attr[0].thresh = NULL;
-
- b +=2;
-
- for (i = 1; i < sm->count; i++) {
- sm->attr[i].page = 0;
- sm->attr[i].id = i;
- sm->attr[i].bytes = 1;
- sm->attr[i].flags = 0;
- sm->attr[i].raw = b;
- sm->attr[i].thresh = NULL;
-
- b ++;
- }
- }
-
- return sm;
-}
-
-/*
- * Map SMART READ DATA attributes
- *
- * The format for the READ DATA buffer is:
- * 2 bytes Revision
- * 360 bytes Attributes (12 bytes each)
- *
- * Each attribute consists of:
- * 1 byte ID
- * 2 byte Status Flags
- * 1 byte Nominal value
- * 1 byte Worst value
- * 7 byte Raw value
- * Note that many attributes do not use the entire 7 bytes of the raw value.
- */
-static void
-__smart_map_ata_read_data(smart_map_t *sm, void *buf, size_t bsize)
-{
- uint8_t *b = NULL;
- uint8_t *b_end = NULL;
- uint32_t max_attr = 0;
- uint32_t a;
-
- max_attr = __smart_attr_max_ata(sm->sb);
- a = sm->count;
-
- b = buf;
-
- /* skip revision */
- b += 2;
-
- b_end = b + (max_attr * 12);
- if (b_end > (b + bsize)) {
- sm->count = 0;
- return;
- }
-
- while (b < b_end) {
- if (*b != 0) {
- if ((a - sm->count) >= max_attr) {
- warnx("More attributes (%d) than fit in map",
- a - sm->count);
- break;
- }
-
- sm->attr[a].page = PAGE_ID_ATA_SMART_READ_DATA;
- sm->attr[a].id = b[0];
- sm->attr[a].description = __smart_ata_desc(
- PAGE_ID_ATA_SMART_READ_DATA, sm->attr[a].id);
- sm->attr[a].bytes = 7;
- sm->attr[a].flags = 0;
- sm->attr[a].raw = b + 5;
- sm->attr[a].thresh = __smart_map_ata_thresh(b + 1);
-
- a++;
- }
-
- b += 12;
- }
-
- sm->count = a;
-}
-
-static void
-__smart_map_ata_return_status(smart_map_t *sm, void *buf)
-{
- uint8_t *b = NULL;
- uint32_t a;
-
- a = sm->count;
-
- b = buf;
-
- sm->attr[a].page = PAGE_ID_ATA_SMART_RET_STATUS;
- sm->attr[a].id = 0;
- sm->attr[a].description = __smart_ata_desc(PAGE_ID_ATA_SMART_RET_STATUS,
- sm->attr[a].id);
- sm->attr[a].bytes = 1;
- sm->attr[a].flags = 0;
- sm->attr[a].raw = b;
- sm->attr[a].thresh = NULL;
-
- a++;
-
- sm->count = a;
-}
-
-static void
-__smart_map_ata(smart_h h, smart_buf_t *sb, smart_map_t *sm)
-{
- smart_t *s = h;
- smart_page_list_t *pg_list = NULL;
- uint8_t *b = NULL;
- uint32_t p;
-
- pg_list = s->pg_list;
- b = sb->b;
-
- sm->count = 0;
-
- for (p = 0; p < pg_list->pg_count; p++) {
- switch (pg_list->pages[p].id) {
- case PAGE_ID_ATA_SMART_READ_DATA:
- __smart_map_ata_read_data(sm, b, pg_list->pages[p].bytes);
- break;
- case PAGE_ID_ATA_SMART_RET_STATUS:
- __smart_map_ata_return_status(sm, b);
- break;
- }
-
- b += pg_list->pages[p].bytes;
- }
-}
-
-#ifndef ARRAYLEN
-#define ARRAYLEN(p) sizeof(p)/sizeof(p[0])
-#endif
-
-#define NVME_VS(mjr,mnr,ter) (((mjr) << 16) | ((mnr) << 8) | (ter))
-#define NVME_VS_1_0 NVME_VS(1,0,0)
-#define NVME_VS_1_1 NVME_VS(1,1,0)
-#define NVME_VS_1_2 NVME_VS(1,2,0)
-#define NVME_VS_1_2_1 NVME_VS(1,2,1)
-#define NVME_VS_1_3 NVME_VS(1,3,0)
-#define NVME_VS_1_4 NVME_VS(1,4,0)
-static struct {
- uint32_t off; /* buffer offset */
- uint32_t bytes; /* size in bytes */
- uint32_t ver; /* first version available */
- const char *description;
-} __smart_nvme_values[] = {
- { 0, 1, NVME_VS_1_0, "Critical Warning" },
- { 1, 2, NVME_VS_1_0, "Composite Temperature" },
- { 3, 1, NVME_VS_1_0, "Available Spare" },
- { 4, 1, NVME_VS_1_0, "Available Spare Threshold" },
- { 5, 1, NVME_VS_1_0, "Percentage Used" },
- { 6, 1, NVME_VS_1_4, "Endurance Group Critical Warning Summary" },
- { 32, 16, NVME_VS_1_0, "Data Units Read" },
- { 48, 16, NVME_VS_1_0, "Data Units Written" },
- { 64, 16, NVME_VS_1_0, "Host Read Commands" },
- { 80, 16, NVME_VS_1_0, "Host Write Commands" },
- { 96, 16, NVME_VS_1_0, "Controller Busy Time" },
- { 112, 16, NVME_VS_1_0, "Power Cycles" },
- { 128, 16, NVME_VS_1_0, "Power On Hours" },
- { 144, 16, NVME_VS_1_0, "Unsafe Shutdowns" },
- { 160, 16, NVME_VS_1_0, "Media and Data Integrity Errors" },
- { 176, 16, NVME_VS_1_0, "Number of Error Information Log Entries" },
- { 192, 4, NVME_VS_1_2, "Warning Composite Temperature Time" },
- { 196, 4, NVME_VS_1_2, "Critical Composite Temperature Time" },
- { 200, 2, NVME_VS_1_2, "Temperature Sensor 1" },
- { 202, 2, NVME_VS_1_2, "Temperature Sensor 2" },
- { 204, 2, NVME_VS_1_2, "Temperature Sensor 3" },
- { 206, 2, NVME_VS_1_2, "Temperature Sensor 4" },
- { 208, 2, NVME_VS_1_2, "Temperature Sensor 5" },
- { 210, 2, NVME_VS_1_2, "Temperature Sensor 6" },
- { 212, 2, NVME_VS_1_2, "Temperature Sensor 7" },
- { 214, 2, NVME_VS_1_2, "Temperature Sensor 8" },
- { 216, 4, NVME_VS_1_3, "Thermal Management Temperature 1 Transition Count" },
- { 220, 4, NVME_VS_1_3, "Thermal Management Temperature 2 Transition Count" },
- { 224, 4, NVME_VS_1_3, "Total Time For Thermal Management Temperature 1" },
- { 228, 4, NVME_VS_1_3, "Total Time For Thermal Management Temperature 2" },
-};
-
-/**
- * NVMe doesn't define attribute IDs like ATA does, but we can
- * approximate this behavior by treating the byte offset as the
- * attribute ID.
- */
-static void
-__smart_map_nvme(smart_buf_t *sb, smart_map_t *sm)
-{
- uint8_t *b = NULL;
- uint32_t vs = NVME_VS_1_0; // XXX assume device is 1.0
- uint32_t i, a;
-
- sm->count = 0;
- b = sb->b;
-
- for (i = 0, a = 0; i < ARRAYLEN(__smart_nvme_values); i++) {
- if (vs >= __smart_nvme_values[i].ver) {
- sm->attr[a].page = 0x2;
- sm->attr[a].id = __smart_nvme_values[i].off;
- sm->attr[a].description = __smart_nvme_values[i].description;
- sm->attr[a].bytes = __smart_nvme_values[i].bytes;
- sm->attr[a].flags = 0;
- sm->attr[a].raw = b + __smart_nvme_values[i].off;
- sm->attr[a].thresh = NULL;
-
- a++;
- }
- }
-
- sm->count = a;
-}
-
-/*
- * Create a SMART map for SCSI error counter pages
- *
- * Several SCSI log pages have a similar format for the error counter log
- * pages
- */
-static void
-__smart_map_scsi_err_page(smart_map_t *sm, void *b)
-{
- struct scsi_err_page {
- uint8_t page_code;
- uint8_t subpage_code;
- uint16_t page_length;
- uint8_t param[];
- } __attribute__((packed)) *err = b;
- struct scsi_err_counter_param {
- uint16_t code;
- uint8_t format:2,
- tmc:2,
- etc:1,
- tsd:1,
- :1,
- du:1;
- uint8_t length;
- uint8_t counter[];
- } __attribute__((packed)) *param = NULL;
- uint32_t a, p, page_length;
- const char *cmd = NULL, *desc = NULL;
-
- switch (err->page_code) {
- case PAGE_ID_SCSI_WRITE_ERR:
- cmd = "Write";
- break;
- case PAGE_ID_SCSI_READ_ERR:
- cmd = "Read";
- break;
- case PAGE_ID_SCSI_VERIFY_ERR:
- cmd = "Verify";
- break;
- case PAGE_ID_SCSI_NON_MEDIUM_ERR:
- cmd = "Non-Medium";
- break;
- default:
- fprintf(stderr, "Unknown command %#x\n", err->page_code);
- cmd = "Unknown";
- break;
- }
-
- a = sm->count;
-
- p = 0;
- page_length = be16toh(err->page_length);
-
- while (p < page_length) {
- param = (struct scsi_err_counter_param *) (err->param + p);
-
- sm->attr[a].page = err->page_code;
- sm->attr[a].id = be16toh(param->code);
- desc = __smart_scsi_err_desc(sm->attr[a].id);
- if (desc != NULL) {
- size_t bytes;
- char *str;
-
- bytes = snprintf(NULL, 0, "%s %s", cmd, desc);
- str = malloc(bytes + 1);
- if (str != NULL) {
- snprintf(str, bytes + 1, "%s %s", cmd, desc);
- sm->attr[a].description = str;
- sm->attr[a].flags |= SMART_ATTR_F_ALLOC;
- }
- }
- sm->attr[a].bytes = param->length;
- sm->attr[a].flags = SMART_ATTR_F_BE;
- sm->attr[a].raw = param->counter;
- sm->attr[a].thresh = NULL;
-
- p += 4 + param->length;
-
- a++;
- }
-
- sm->count = a;
-}
-
-static void
-__smart_map_scsi_last_err(smart_map_t *sm, void *b)
-{
- struct scsi_last_n_error_event_page {
- uint8_t page_code:6,
- spf:1,
- ds:1;
- uint8_t subpage_code;
- uint16_t page_length;
- uint8_t event[];
- } __attribute__((packed)) *lastn = b;
- struct scsi_last_n_error_event {
- uint16_t code;
- uint8_t format:2,
- tmc:2,
- etc:1,
- tsd:1,
- :1,
- du:1;
- uint8_t length;
- uint8_t data[];
- } __attribute__((packed)) *event = NULL;
- uint32_t a, p, page_length;
-
- a = sm->count;
-
- p = 0;
- page_length = be16toh(lastn->page_length);
-
- while (p < page_length) {
- event = (struct scsi_last_n_error_event *) (lastn->event + p);
-
- sm->attr[a].page = lastn->page_code;
- sm->attr[a].id = be16toh(event->code);
- sm->attr[a].bytes = event->length;
- sm->attr[a].flags = SMART_ATTR_F_BE;
- sm->attr[a].raw = event->data;
- sm->attr[a].thresh = NULL;
-
- p += 4 + event->length;
-
- a++;
- }
-
- sm->count = a;
-}
-
-static void
-__smart_map_scsi_temp(smart_map_t *sm, void *b)
-{
- struct scsi_temperature_log_page {
- uint8_t page_code;
- uint8_t subpage_code;
- uint16_t page_length;
- struct scsi_temperature_log_entry {
- uint16_t code;
- uint8_t control;
- uint8_t length;
- uint8_t rsvd;
- uint8_t temperature;
- } param[];
- } __attribute__((packed)) *temp = b;
- uint32_t a, p, count;
-
- count = be16toh(temp->page_length) / sizeof(struct scsi_temperature_log_entry);
-
- a = sm->count;
-
- for (p = 0; p < count; p++) {
- uint16_t code = be16toh(temp->param[p].code);
- switch (code) {
- case 0:
- case 1:
- sm->attr[a].page = temp->page_code;
- sm->attr[a].id = be16toh(temp->param[p].code);
- sm->attr[a].description = code == 0 ? "Temperature" : "Reference Temperature";
- sm->attr[a].bytes = 1;
- sm->attr[a].flags = 0;
- sm->attr[a].raw = &(temp->param[p].temperature);
- sm->attr[a].thresh = NULL;
- a++;
- break;
- default:
- break;
- }
- }
-
- sm->count = a;
-}
-
-static void
-__smart_map_scsi_start_stop(smart_map_t *sm, void *b)
-{
- struct scsi_start_stop_page {
- uint8_t page_code;
-#define START_STOP_CODE_DATE_MFG 0x0001
-#define START_STOP_CODE_DATE_ACCTN 0x0002
-#define START_STOP_CODE_CYCLES_LIFE 0x0003
-#define START_STOP_CODE_CYCLES_ACCUM 0x0004
-#define START_STOP_CODE_LOAD_LIFE 0x0005
-#define START_STOP_CODE_LOAD_ACCUM 0x0006
- uint8_t subpage_code;
- uint16_t page_length;
- uint8_t param[];
- } __attribute__((packed)) *sstop = b;
- struct scsi_start_stop_param {
- uint16_t code;
- uint8_t format:2,
- tmc:2,
- etc:1,
- tsd:1,
- :1,
- du:1;
- uint8_t length;
- uint8_t data[];
- } __attribute__((packed)) *param;
- uint32_t a, p, page_length;
-
- a = sm->count;
-
- p = 0;
- page_length = be16toh(sstop->page_length);
-
- while (p < page_length) {
- param = (struct scsi_start_stop_param *) (sstop->param + p);
-
- sm->attr[a].page = sstop->page_code;
- sm->attr[a].id = be16toh(param->code);
- sm->attr[a].bytes = param->length;
-
- switch (sm->attr[a].id) {
- case START_STOP_CODE_DATE_MFG:
- sm->attr[a].description = "Date of Manufacture";
- sm->attr[a].flags = SMART_ATTR_F_STR;
- break;
- case START_STOP_CODE_DATE_ACCTN:
- sm->attr[a].description = "Accounting Date";
- sm->attr[a].flags = SMART_ATTR_F_STR;
- break;
- case START_STOP_CODE_CYCLES_LIFE:
- sm->attr[a].description = "Specified Cycle Count Over Device Lifetime";
- sm->attr[a].flags = SMART_ATTR_F_BE;
- break;
- case START_STOP_CODE_CYCLES_ACCUM:
- sm->attr[a].description = "Accumulated Start-Stop Cycles";
- sm->attr[a].flags = SMART_ATTR_F_BE;
- break;
- case START_STOP_CODE_LOAD_LIFE:
- sm->attr[a].description = "Specified Load-Unload Count Over Device Lifetime";
- sm->attr[a].flags = SMART_ATTR_F_BE;
- break;
- case START_STOP_CODE_LOAD_ACCUM:
- sm->attr[a].description = "Accumulated Load-Unload Cycles";
- sm->attr[a].flags = SMART_ATTR_F_BE;
- break;
- }
-
- sm->attr[a].raw = param->data;
- sm->attr[a].thresh = NULL;
-
- p += 4 + param->length;
-
- a++;
- }
-
- sm->count = a;
-}
-
-static void
-__smart_map_scsi_info_exception(smart_map_t *sm, void *b)
-{
- struct scsi_info_exception_log_page {
- uint8_t page_code;
- uint8_t subpage_code;
- uint16_t page_length;
- uint8_t param[];
- } __attribute__((packed)) *ie = b;
- struct scsi_ie_param {
- uint16_t code;
- uint8_t control;
- uint8_t length;
- uint8_t asc; /* IE Additional Sense Code */
- uint8_t ascq; /* IE Additional Sense Code Qualifier */
- uint8_t temp_recent;
- uint8_t temp_trip_point;
- uint8_t temp_max;
- } __attribute__((packed)) *param;
- uint32_t a, p, page_length;
-
- a = sm->count;
-
- p = 0;
- page_length = be16toh(ie->page_length);
-
- while (p < page_length) {
- param = (struct scsi_ie_param *)(ie->param + p);
-
- p += 4 + param->length;
-
- sm->attr[a].page = ie->page_code;
- sm->attr[a].id = offsetof(struct scsi_ie_param, asc);
- sm->attr[a].description = "Informational Exception ASC";
- sm->attr[a].bytes = 1;
- sm->attr[a].flags = 0;
- sm->attr[a].raw = &param->asc;
- sm->attr[a].thresh = NULL;
- a++;
-
- sm->attr[a].page = ie->page_code;
- sm->attr[a].id = offsetof(struct scsi_ie_param, ascq);
- sm->attr[a].description = "Informational Exception ASCQ";
- sm->attr[a].bytes = 1;
- sm->attr[a].flags = 0;
- sm->attr[a].raw = &param->ascq;
- sm->attr[a].thresh = NULL;
- a++;
-
- sm->attr[a].page = ie->page_code;
- sm->attr[a].id = offsetof(struct scsi_ie_param, temp_recent);
- sm->attr[a].description = "Informational Exception Most recent temperature";
- sm->attr[a].bytes = 1;
- sm->attr[a].flags = 0;
- sm->attr[a].raw = &param->temp_recent;
- sm->attr[a].thresh = NULL;
- a++;
-
- sm->attr[a].page = ie->page_code;
- sm->attr[a].id = offsetof(struct scsi_ie_param, temp_trip_point);
- sm->attr[a].description = "Informational Exception Vendor HDA temperature trip point";
- sm->attr[a].bytes = 1;
- sm->attr[a].flags = 0;
- sm->attr[a].raw = &param->temp_trip_point;
- sm->attr[a].thresh = NULL;
- a++;
-
- sm->attr[a].page = ie->page_code;
- sm->attr[a].id = offsetof(struct scsi_ie_param, temp_max);
- sm->attr[a].description = "Informational Exception Maximum temperature";
- sm->attr[a].bytes = 1;
- sm->attr[a].flags = 0;
- sm->attr[a].raw = &param->temp_max;
- sm->attr[a].thresh = NULL;
- a++;
- }
-
- sm->count = a;
-}
-
-/*
- * Create a map based on the page list
- */
-static void
-__smart_map_scsi(smart_h h, smart_buf_t *sb, smart_map_t *sm)
-{
- smart_t *s = h;
- smart_page_list_t *pg_list = NULL;
- uint8_t *b = NULL;
- uint32_t p;
-
- pg_list = s->pg_list;
- b = sb->b;
-
- sm->count = 0;
-
- for (p = 0; p < pg_list->pg_count; p++) {
- switch (pg_list->pages[p].id) {
- case PAGE_ID_SCSI_WRITE_ERR:
- case PAGE_ID_SCSI_READ_ERR:
- case PAGE_ID_SCSI_VERIFY_ERR:
- case PAGE_ID_SCSI_NON_MEDIUM_ERR:
- __smart_map_scsi_err_page(sm, b);
- break;
- case PAGE_ID_SCSI_LAST_N_ERR:
- __smart_map_scsi_last_err(sm, b);
- break;
- case PAGE_ID_SCSI_TEMPERATURE:
- __smart_map_scsi_temp(sm, b);
- break;
- case PAGE_ID_SCSI_START_STOP_CYCLE:
- __smart_map_scsi_start_stop(sm, b);
- break;
- case PAGE_ID_SCSI_INFO_EXCEPTION:
- __smart_map_scsi_info_exception(sm, b);
- break;
- }
-
- b += pg_list->pages[p].bytes;
- }
-}
-
-/**
- * Create a map of SMART values
- */
-static void
-__smart_attribute_map(smart_h h, smart_buf_t *sb, smart_map_t *sm)
-{
-
- if (!sb || !sm) {
- return;
- }
-
- switch (sb->protocol) {
- case SMART_PROTO_ATA:
- __smart_map_ata(h, sb, sm);
- break;
- case SMART_PROTO_NVME:
- __smart_map_nvme(sb, sm);
- break;
- case SMART_PROTO_SCSI:
- __smart_map_scsi(h, sb, sm);
- break;
- default:
- sm->count = 0;
- }
-}
-
-static smart_map_t *
-__smart_map(smart_h h, smart_buf_t *sb)
-{
- smart_map_t *sm = NULL;
- uint32_t max = 0;
-
- max = sb->attr_count;
- if (max == 0) {
- warnx("Attribute count is zero?!?");
- return NULL;
- }
-
- sm = malloc(sizeof(smart_map_t) + (max * sizeof(smart_attr_t)));
- if (sm) {
- memset(sm, 0, sizeof(smart_map_t) + (max * sizeof(smart_attr_t)));
- sm->sb = sb;
-
- /* count starts as the max but is adjusted to reflect the actual number */
- sm->count = max;
-
- __smart_attribute_map(h, sb, sm);
- }
-
- return sm;
-}
-
-typedef struct {
- uint8_t page_code;
- uint8_t subpage_code;
- uint16_t page_length;
- uint8_t supported_pages[];
-} __attribute__((packed)) scsi_supported_log_pages;
-
-static smart_page_list_t *
-__smart_page_list_scsi(smart_t *s)
-{
- smart_page_list_t *pg_list = NULL;
- scsi_supported_log_pages *b = NULL;
- uint32_t bsize = 68; /* 4 byte header + 63 entries + 1 just cuz */
- int32_t rc;
-
- b = malloc(bsize);
- if (!b) {
- return NULL;
- }
-
- /* Supported Pages page ID is 0 */
- rc = device_read_log(s, PAGE_ID_SCSI_SUPPORTED_PAGES, (uint8_t *)b,
- bsize);
- if (rc < 0) {
- fprintf(stderr, "Read Supported Log Pages failed\n");
- } else {
- uint8_t *supported_page = b->supported_pages;
- uint32_t n_supported = be16toh(b->page_length);
- uint32_t pg, p, pmax = pg_list_scsi.pg_count;
-
- /* Build a page list using only pages the device supports */
- pg_list = malloc(sizeof(pg_list_scsi));
- if (pg_list == NULL) {
- n_supported = 0;
- } else {
- pg_list->pg_count = 0;
- }
-
- /*
- * Loop through all supported pages looking for those related
- * to SMART. The below assumes the supported page list from the
- * device and in pg_lsit_scsi are sorted in increasing order.
- */
- dprintf("Supported SCSI pages:\n");
- for (pg = 0, p = 0; (pg < n_supported) && (p < pmax); pg++) {
- dprintf("\t[%u] = %#x\n", pg, supported_page[pg]);
- while ((supported_page[pg] > pg_list_scsi.pages[p].id) &&
- (p < pmax)) {
- p++;
- }
-
- if (supported_page[pg] == pg_list_scsi.pages[p].id) {
- pg_list->pages[pg_list->pg_count] = pg_list_scsi.pages[p];
- pg_list->pg_count++;
- p++;
- }
- }
- }
-
- free(b);
-
- return pg_list;
-}
-
-static smart_page_list_t *
-__smart_page_list(smart_h h)
-{
- smart_t *s = h;
- smart_page_list_t *pg_list = NULL;
-
- if (!s) {
- return NULL;
- }
-
- switch (s->protocol) {
- case SMART_PROTO_ATA:
- pg_list = &pg_list_ata;
- break;
- case SMART_PROTO_NVME:
- pg_list = &pg_list_nvme;
- break;
- case SMART_PROTO_SCSI:
- pg_list = __smart_page_list_scsi(s);
- break;
- default:
- pg_list = NULL;
- }
-
- return pg_list;
-}
-
-static int32_t
-__smart_read_pages(smart_h h, smart_buf_t *sb)
-{
- smart_t *s = h;
- smart_page_list_t *plist = NULL;
- uint8_t *buf = NULL;
- int32_t rc = 0;
- uint32_t p = 0;
-
- plist = s->pg_list;
-
- buf = sb->b;
-
- for (p = 0; p < s->pg_list->pg_count; p++) {
- memset(buf, 0, plist->pages[p].bytes);
- rc = device_read_log(h, plist->pages[p].id, buf, plist->pages[p].bytes);
- if (rc) {
- dprintf("bad read (%d) from page %#x (bytes=%lu)\n", rc,
- plist->pages[p].id, plist->pages[p].bytes);
- break;
- }
-
- buf += plist->pages[p].bytes;
- }
-
- return rc;
-}
diff --git a/contrib/smart/libsmart.h b/contrib/smart/libsmart.h
deleted file mode 100644
index 60346f3b3a70..000000000000
--- a/contrib/smart/libsmart.h
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * Copyright (c) 2016-2021 Chuck Tuffli <chuck@tuffli.net>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-#ifndef _LIBSMART_H
-#define _LIBSMART_H
-
-#include <inttypes.h>
-#include <stdbool.h>
-
-/*
- * libsmart uses a common model for SMART data (a.k.a. "attributes") across
- * storage protocols. Each health value consists of:
- * - The identifier of the log page containing this attribute
- * - The attribute's identifier
- * - A description of the attribute
- * - A pointer to the raw data
- * - The attribute's size in bytes
- *
- * This model most closely resembles SCSI's native representation, but it
- * can represent ATA and NVMe with the following substitutions:
- * - ATA : use the Command Feature field value for the log page ID
- * - NVMe : use the field's starting byte offset for the attribute ID
- *
- * libsmart returns a "map" to the SMART/health data read from a device
- * in the smart_map_t structure. The map consists of:
- * - A variable-length array of attributes
- * - The length of the array
- * - The raw data read from the device
- *
- * Consumers of the map will typically iterate through the array of attributes
- * to print or otherwise process the health data.
- */
-
-/*
- * A smart handle is an opaque reference to the device
- */
-typedef void * smart_h;
-
-typedef enum {
- SMART_PROTO_AUTO,
- SMART_PROTO_ATA,
- SMART_PROTO_SCSI,
- SMART_PROTO_NVME,
- SMART_PROTO_MAX
-} smart_protocol_e;
-
-/*
- * A smart buffer contains the raw data returned from the protocol-specific
- * health command.
- */
-typedef struct {
- smart_protocol_e protocol;
- void *b; // buffer of raw data
- size_t bsize; // buffer size
- uint32_t attr_count; // number of SMART attributes
-} smart_buf_t;
-
-struct smart_map_s;
-
-/*
- * A smart attribute is an individual health data element
- */
-typedef struct smart_attr_s {
- uint32_t page;
- uint32_t id;
- const char *description; /* human readable description */
- uint32_t bytes;
- uint32_t flags;
-#define SMART_ATTR_F_BE 0x00000001 /* Attribute is big-endian */
-#define SMART_ATTR_F_STR 0x00000002 /* Attribute is a string */
-#define SMART_ATTR_F_ALLOC 0x00000004 /* Attribute description dynamically allocated */
- void *raw;
- struct smart_map_s *thresh; /* Threshold values (if any) */
-} smart_attr_t;
-
-/*
- * A smart map is the collection of health data elements from the device
- */
-typedef struct smart_map_s {
- smart_buf_t *sb;
- uint32_t count; /* Number of attributes */
- smart_attr_t attr[]; /* Array of attributes */
-} smart_map_t;
-
-#define SMART_OPEN_F_HEX 0x1 /* Print values in hexadecimal */
-#define SMART_OPEN_F_THRESH 0x2 /* Print threshold values */
-#define SMART_OPEN_F_DESCR 0x4 /* Print textual description */
-
-/* SMART attribute to match */
-typedef struct smart_match_s {
- int32_t page;
- int32_t id;
-} smart_match_t;
-
-/* List of SMART attribute(s) to match */
-typedef struct smart_matches_s {
- uint32_t count;
- smart_match_t m[];
-} smart_matches_t;
-
-/**
- * Connect to a device to read SMART data
- *
- * @param p The desired protocol or "auto" to automatically detect it
- * @param devname The device name to open
- *
- * @return An opaque handle or NULL on failure
- */
-smart_h smart_open(smart_protocol_e p, char *devname);
-/**
- * Close device connection
- *
- * @param handle The handle returned from smart_open()
- *
- * @return None
- */
-void smart_close(smart_h);
-/**
- * Does the device support SMART/health data?
- *
- * @param handle The handle returned from smart_open()
- *
- * @return true / false
- */
-bool smart_supported(smart_h);
-/**
- * Read SMART/health data from the device
- *
- * @param handle The handle returned from smart_open()
- *
- * @return a pointer to the SMART map or NULL on failure
- */
-smart_map_t *smart_read(smart_h);
-/**
- * Free memory associated with the health data read from the device
- *
- * @param map Pointer returned from smart_read()
- *
- * @return None
- */
-void smart_free(smart_map_t *);
-/**
- * Print health data matching the desired attributes
- *
- * @param handle The handle returned from smart_open()
- * @param map Pointer returned from smart_read()
- * @param which Pointer to attributes to match or NULL to match all
- * @param flags Control display of attributes (hexadecimal, description, ...
- *
- * @return None
- */
-void smart_print(smart_h, smart_map_t *, smart_matches_t *, uint32_t);
-/**
- * Print high-level device information
- *
- * @param handle The handle returned from smart_open()
- *
- * @return None
- */
-void smart_print_device_info(smart_h);
-
-#endif
diff --git a/contrib/smart/libsmart_desc.c b/contrib/smart/libsmart_desc.c
deleted file mode 100644
index bcfb31d66922..000000000000
--- a/contrib/smart/libsmart_desc.c
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright (c) 2021 Chuck Tuffli <chuck@tuffli.net>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-#include <stddef.h>
-
-#include "libsmart.h"
-#include "libsmart_priv.h"
-
-/* Strings from "SMART Attribute Descriptions" (SAD) */
-static const char *
-desc_ata_data[] = {
- [1] = "Read Error Rate",
- [2] = "Throughput Performance",
- [3] = "Spin-Up Time",
- [4] = "Start/Stop Count",
- [5] = "Reallocated Sectors Count",
- [6] = "Read Channel Margin",
- [7] = "Seek Error Rate",
- [8] = "Seek Time Performance",
- [9] = "Power-On Hours",
- [10] = "Spin Retry Count",
- [11] = "Calibration Retry Count",
- [12] = "Power Cycle Count",
- [13] = "Soft Read Error Rate",
- [22] = "Current Helium Level", /* HGST */
- [170] = "Available Reserved Space", /* Intel */
- [171] = "SSD Program Fail", /* Kingston? */
- [172] = "SSD Erase Fail Count", /* Kingston? */
- [173] = "SSD Wear Leveling Count", /* HPE SSD Endurance Limit */
- [174] = "Unexpected Power Loss Count", /* Intel */
- [175] = "Power Loss Protection Failure", /* Intel */
- [176] = "Erase Fail Count (chip)",
- [177] = "Wear Range Delta",
- [179] = "Used Reserved Block Count Total",
-/* [180] = HPE, Seagate, Intel differences */
- [181] = "Non-4K Aligned Access Count", /* Micron. Conflict Kingston */
- [182] = "Erase Fail Count",
- [183] = "Runtime Bad Block",
- [184] = "End-to-End Error",
- [185] = "Head Stability", /* WD */
- [186] = "Induced Op-Vibration Detection", /* WD */
- [187] = "Reported Uncorrectable Errors",
- [188] = "Command Timeout",
- [189] = "High Fly Writes",
- [190] = "Airflow Temperature", /* WDC, HPE conflict */
- [191] = "G-Sense Error Rate",
- [192] = "Power-Off Count", /* HPE, Seagate */
- [193] = "Load/Unload Cycle Count",
- [194] = "Temperature Celsius",
- [195] = "Hardware ECC Recovered",
- [196] = "Reallocation Event Count",
- [197] = "Current Pending Sector Count",
- [198] = "Uncorrectable Sector Count", /* Fujitsu */
- [199] = "UltraDMA CRC Error Count",
- [200] = "Write Error Rate",
- [201] = "Soft Read Error Rate",
- [202] = "Data Address Mark Errors",
- [203] = "Run Out Cancel",
- [204] = "Soft ECC Correction",
- [205] = "Thermal Asperity Rate",
- [206] = "Flying Height",
- [207] = "Spin High Current",
- [208] = "Spin Buzz",
- [209] = "Offline Seek Performnce",
- [210] = "Vibration, During Write", /* Maxtor */
- [211] = "Vibration During Write", /* Acronis */
- [212] = "Shock During Write", /* Acronis */
- [220] = "Disk Shift",
- [221] = "G-Sense Error Rate",
- [222] = "Loaded Hours",
- [223] = "Load/Unload Retry Count",
- [224] = "Load Friction",
- [225] = "Load/Unload Cycle Count",
- [226] = "Load-in Time",
- [227] = "Torque Amplification Count",
- [228] = "Power-off Retract Cycle",
- [230] = "GMR Head Amplitude Drive Life Protection Status",
- [231] = "Temperature SSD Life Left", /* Kingston */
- [232] = "Endurance Remaining", /* Multiple conflict */
- [233] = "Power-On Hours", /* Multiple conflict */
- [234] = "Average Erase Count", /* Multiple conflict */
- [235] = "Good Block Count", /* Multiple conflict */
- [240] = "Head Flying Hours",
- [241] = "Total LBAs Written",
- [242] = "Total LBAs Read",
- [243] = "Total LBAs Written Expanded", /* Multiple conflict */
- [244] = "Total LBAs Read Expanded", /* Multiple conflict */
- [250] = "Read Error Rate",
- [251] = "Minimum Spares Remaining",
- [252] = "Newly Added Bad Flash Block",
- [254] = "Free Fall Protection"
-};
-
-const char *
-__smart_ata_desc(uint32_t page, uint32_t id)
-{
- const char *desc = NULL;
-
- switch (page) {
- case PAGE_ID_ATA_SMART_READ_DATA:
- if (desc_ata_data[id] != NULL)
- desc = desc_ata_data[id];
- break;
- case PAGE_ID_ATA_SMART_RET_STATUS:
- desc = "SMART Status";
- break;
- default:
- ;
- }
-
- return (desc);
-}
-
-const char *
-__smart_scsi_err_desc(uint32_t id)
-{
- const char *param = NULL;
-
- switch (id) {
- case 0:
- param = "Errors corrected without substantial delay";
- break;
- case 1:
- param = "Errors corrected with possible delays";
- break;
- case 2:
- param = "Total retries";
- break;
- case 3:
- param = "Total errors corrected";
- break;
- case 4:
- param = "Total times correction algorithm processed";
- break;
- case 5:
- param = "Total bytes processed";
- break;
- case 6:
- param = "Total uncorrected errors";
- break;
- default:
- return (NULL);
- }
-
- return (param);
-}
diff --git a/contrib/smart/libsmart_dev.h b/contrib/smart/libsmart_dev.h
deleted file mode 100644
index bbf028e73712..000000000000
--- a/contrib/smart/libsmart_dev.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (c) 2017-2021 Chuck Tuffli <chuck@tuffli.net>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-#ifndef _LIBSMART_DEV_H
-#define _LIBSMART_DEV_H
-
-/**
- * Open a device to gather SMART information
- *
- * The call performs OS specific functions necessary to prepare the device
- * to receive read log requests.
- *
- * Although opaque to the user, the handle must be a pointer to a structure
- * with the first member being struct smart_s. The remaining members are OS
- * specific and are not used by the library.
- *
- * @param protocol The desired protocol or "auto" to automatically detect it
- * @param devname The device name to open
- *
- * @return An opaque handle to the device or NULL on failure
- */
-extern smart_h device_open(smart_protocol_e, char *);
-
-/**
- * Close a device and release the associated resources
- *
- * @param handle The handle returned from device_open()
- *
- * @return None
- */
-extern void device_close(smart_h);
-
-/**
- * Read the log page
- *
- * This call reads the specified log page in the protocol specific manner
- * needed by the device. The results are placed in the provided buffer.
- *
- * @param h SMART handle returned from device_open()
- * @param page The log page ID
- * @param buf Pointer to buffer containing the results of the read
- * @param bsize Size of the buffer in bytes
- *
- * @return Zero on success, errno on failure
- */
-extern int32_t device_read_log(smart_h, uint32_t, void *, size_t);
-
-#endif /* !_LIBSMART_DEV_H */
diff --git a/contrib/smart/libsmart_priv.h b/contrib/smart/libsmart_priv.h
deleted file mode 100644
index f29fc1e1e033..000000000000
--- a/contrib/smart/libsmart_priv.h
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (c) 2016-2021 Chuck Tuffli <chuck@tuffli.net>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-#ifndef _LIBSMART_PRIV_H
-#define _LIBSMART_PRIV_H
-
-/* OS-independent structures and definitions internal to libsmart */
-
-#define PAGE_ID_ATA_SMART_READ_DATA 0xd0 /* SMART Read Data */
-#define PAGE_ID_ATA_SMART_RET_STATUS 0xda /* SMART Return Status */
-
-#define PAGE_ID_SCSI_SUPPORTED_PAGES 0x00
-#define PAGE_ID_SCSI_WRITE_ERR 0x02 /* Write Error counter */
-#define PAGE_ID_SCSI_READ_ERR 0x03 /* Read Error counter */
-#define PAGE_ID_SCSI_VERIFY_ERR 0x05 /* Verify Error counter */
-#define PAGE_ID_SCSI_NON_MEDIUM_ERR 0x06 /* Non-Medium Error */
-#define PAGE_ID_SCSI_LAST_N_ERR 0x07 /* Last n Error events */
-#define PAGE_ID_SCSI_TEMPERATURE 0x0d /* Temperature */
-#define PAGE_ID_SCSI_START_STOP_CYCLE 0x0e /* Start-Stop Cycle counter */
-#define PAGE_ID_SCSI_INFO_EXCEPTION 0x2f /* Informational Exceptions */
-
-extern bool do_debug;
-
-#define dprintf(f, ...) if (do_debug) fprintf(stderr, "dbg: " f, ## __VA_ARGS__)
-
-/* General information about the device */
-typedef struct smart_info_s {
- /* device supports SMART */
- uint32_t supported:1,
- /* storage protocol is tunneled (e.g. ATA inside SCSI) */
- tunneled:1,
- :30;
- /*
- * Device-provided information, including
- * - vendor name
- * - device / model
- * - firmware revision
- * - serial number
- * Protocols may provide a subset of this information
- */
- char vendor[16], device[48], rev[16], serial[32];
-} smart_info_t;
-
-/* List of pages providing SMART/health data */
-typedef struct smart_page_list_s {
- uint32_t pg_count;
- struct {
- uint32_t id;
- size_t bytes;
- } pages[];
-} smart_page_list_t;
-
-/*
- * The device handle (i.e. smart_h) is an opaque pointer to memory containing
- * device/OS independent and dependent data. The library uses type punning to
- * isolate the OS-independent portion (struct smart_s) from the OS-dependent
- * details. Because of this, the device layer allocates and frees this memory.
- */
-typedef struct smart_s {
- smart_protocol_e protocol;
- smart_info_t info;
- smart_page_list_t *pg_list;
- /* Device / OS specific follows this structure */
-} smart_t;
-
-/* Return a textual description of the ATA attribute */
-const char * __smart_ata_desc(uint32_t page, uint32_t id);
-/* Return a textual description of the SCSI error attribute */
-const char * __smart_scsi_err_desc(uint32_t id);
-
-#endif
diff --git a/contrib/smart/smart.8 b/contrib/smart/smart.8
deleted file mode 100644
index 3215ff890001..000000000000
--- a/contrib/smart/smart.8
+++ /dev/null
@@ -1,245 +0,0 @@
-.\"
-.\" SPDX-License-Identifier: BSD-2-Clause
-.\"
-.\" Copyright (c) 2021-2026 Chuck Tuffli
-.\"
-.\" Redistribution and use in source and binary forms, with or without
-.\" modification, are permitted provided that the following conditions
-.\" are met:
-.\" 1. Redistributions of source code must retain the above copyright
-.\" notice, this list of conditions and the following disclaimer.
-.\" 2. Redistributions in binary form must reproduce the above copyright
-.\" notice, this list of conditions and the following disclaimer in the
-.\" documentation and/or other materials provided with the distribution.
-.\"
-.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-.\" SUCH DAMAGE.
-.\"
-.\" Note: The date here should be updated whenever a non-trivial
-.\" change is made to the manual page.
-.Dd February 14, 2026
-.Dt SMART 8
-.Os
-.Sh NAME
-.Nm smart ,
-.Nm diskhealth
-.Nd monitor disk health from a storage device via SMART
-.Sh SYNOPSIS
-.Nm
-.Op Fl dhitvx
-.Oo Fl a Ar page:attribute Ns Oo , Ns Ar page:attribute Oc Ns ... Oc
-.Op Fl Fl debug
-.Ar device
-.Nm diskhealth
-.Op Fl Dhitvx
-.Oo Fl a Ar page:attribute Ns Oo , Ns Ar page:attribute Oc Ns ... Oc
-.Op Fl Fl debug
-.Ar device
-.Sh DESCRIPTION
-The
-.Nm
-command allows the user to monitor the various information reported
-by Self-Monitoring, Analysis and Reporting Technology (SMART) present
-on most ATA, SCSI, and NVMe storage media.
-Because the structure of this information varies across protocols,
-.Nm
-normalizes entries using the format:
-.Bd -literal
- <Page ID> <Attribute ID> <Value> <Thresholds>
-.Ed
-.Pp
-Fields are tab-delimited by default, but the command can output
-data in any format supported by
-.Xr libxo 3 .
-.Pp
-Because ATA does not have log pages,
-.Nm
-uses the Command Feature field value in place of the log page ID.
-For SMART READ DATA, this value is 208 / 0xd0.
-Note that devices choose which attribute ID values they support, their
-descriptions, and the format of the data.
-The three values displayed with the
-.Fl Fl threshold
-option are:
-.Pp
-.Bl -dash -compact -offset indent
-.It
-Status flags (byte offset 1, 2 bytes)
-.It
-Nominal attribute value (byte offset 3, 1 byte)
-.It
-Worst Ever attribute value (byte offset 4, 1 byte)
-.El
-.Pp
-Additionally,
-.Nm
-reports the value of the SMART STATUS command (Command Feature field
-value 218 / 0xda).
-As this command does not return any data,
-the command represents this entry with a synthetic attribute
-ID of 0, and it uses the command status (0 or 1) as the attribute
-value.
-.Pp
-NVMe devices support the SMART/Health log page (Page ID 2 / 0x2).
-The data returned in this log page is not structured as attribute IDs.
-Instead,
-.Nm
-uses the byte offset of each field as the attribute ID.
-For example,
-byte 3 is the Available Spare.
-Thus, for NVMe, attribute ID 3 is
-Available Spare.
-Note that NVMe health data does not include threshold
-values, and as a result, the command will ignore the
-.Fl Fl threshold
-option.
-.Pp
-SCSI devices can support a number of log pages which report drive
-health.
-The command will report the following pages:
-.Pp
-.Bl -dash -compact -offset indent
-.It
-Write Errors (Page ID 2 / 0x2)
-.It
-Read Errors (Page ID 3 / 0x3)
-.It
-Verify Errors (Page ID 5 / 0x5)
-.It
-Non-medium Errors (Page ID 6 / 0x6)
-.It
-Last N Errors (Page ID 7 / 0x7)
-.It
-Temperature (Page ID 13 / 0xd)
-.It
-Start-stop Cycles (Page ID 14 / 0xe)
-.It
-Informational Exceptions (Page ID 47 / 0x2f)
-.El
-.Pp
-Note that all log pages are optional, and a particular drive
-may not support all these pages.
-For SCSI devices, the Attribute ID
-maps to the SCSI parameter code defined by the command.
-Parameter
-codes are integer values from 0 to N, and, by themselves, are ambiguous
-outside the context of a particular log page.
-Note that SCSI health data
-does not include threshold values, and as a result, the command will
-ignore the
-.Fl Fl threshold
-option.
-.Pp
-The following options are available:
-.Bl -tag -width "-d argument"
-.It Fl a Ar page:attribute , Fl Fl attribute= Ns Ar page:attribute
-A comma-separated list of attributes to display.
-If page is missing, display the matching attribute from any page.
-.It Fl d , Fl Fl decode
-Decode the attribute ID values.
-This is the default when invoked as
-.Nm diskhealth .
-.It Fl D , Fl Fl no-decode
-Do not decode the attribute ID values.
-This is the default when invoked as
-.Nm .
-.It Fl h , Fl Fl help
-Prints a usage message and exits.
-.It Fl i , Fl Fl info
-Print general device information.
-.It Fl t , Fl Fl threshold
-Also print the threshold values.
-.It Fl v , Fl Fl version
-Print the version and copyright.
-.It Fl x , Fl Fl hex
-Print the values in hexadecimal.
-.It Ar device
-An explicit device path
-.Pq Pa /dev/ada0
-or GEOM provider
-.Pq Pa ada0
-.
-.El
-.El
-.Sh EXIT STATUS
-.Ex -std
-.Sh EXAMPLES
-Print all SMART READ DATA and SMART STATUS including the
-threshold values for ATA drive ada0.
-.Pp
-.Dl # smart -t ada0
-.Pp
-Print only attribute ID 5 ("Reallocated Sectors Count") for
-ATA drive ada0.
-.Pp
-.Dl # smart -a 5 ada0
-.Pp
-Print attribute IDs 5 ("Reallocated Sectors Count") and 171
-("SSD Program Fail") for ATA drive ada0.
-.Pp
-.Dl # smart -a 5,171 ada0
-.Pp
-Print all health pages supported by SCSI device da0 including:
-.Bl -dash -compact -offset indent
-.It
-Write Errors
-.It
-Read Errors
-.It
-Verify Errors
-.It
-Non-medium Errors
-.It
-Last N Errors
-.It
-Temperature
-.It
-Start-stop Cycles
-.It
-Informational Exceptions
-.El
-.Pp
-.Dl # smart da0
-.Pp
-Print all Health log page entries in hexadecimal for NVMe
-device nda0.
-.Pp
-.Dl # smart -x nda0
-.Sh DIAGNOSTICS
-The command may fail for one of the following reasons:
-.Bl -diag
-.It "No output displayed"
-The device does not support health data.
-.It "CAMGETPASSTHRU ioctl failed"
-.Nm
-relies on
-.Xr cam 4
-to retrieve data from devices and will display this message if the
-device does not have a passthrough driver.
-This can happen, for
-example, if the system uses the
-.Xr nvd 4
-NVMe driver instead of the
-.Xr nda 4
-driver.
-.El
-.Sh SEE ALSO
-.Xr libxo 3 ,
-.Xr cam 4 ,
-.Xr nda 4 ,
-.Xr nvd 4
-.Sh AUTHORS
-This
-utility was written by
-.An Chuck Tuffli Aq Mt chuck@FreeBSD.org .
-.Sh BUGS
-Probably.
diff --git a/contrib/smart/smart.c b/contrib/smart/smart.c
deleted file mode 100644
index 8b543fea1819..000000000000
--- a/contrib/smart/smart.c
+++ /dev/null
@@ -1,334 +0,0 @@
-/*
- * Copyright (c) 2016-2026 Chuck Tuffli <chuck@tuffli.net>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <getopt.h>
-#include <stdbool.h>
-#include <string.h>
-#ifdef LIBXO
-#include <libxo/xo.h>
-#endif
-
-#include "libsmart.h"
-
-#define SMART_NAME "smart"
-#define SMART_VERSION "1.0.2"
-
-extern bool do_debug;
-
-static const char *pn;
-bool do_debug = false;
-static int debugset = 0;
-
-static struct option opts[] = {
- { "help", no_argument, NULL, 'h' },
- { "threshold", no_argument, NULL, 't' },
- { "hex", no_argument, NULL, 'x' },
- { "attribute", required_argument, NULL, 'a' },
- { "info", no_argument, NULL, 'i' },
- { "version", no_argument, NULL, 'v' },
- { "decode", no_argument, NULL, 'd' },
- { "no-decode", no_argument, NULL, 'D' },
- { "debug", no_argument, &debugset, 1 },
- { NULL, 0, NULL, 0 }
-};
-
-static void
-usage(const char *name)
-{
- printf("Usage: %s [-htxi] [-a attribute[,attribute]...] <device name>\n", name);
- printf("\t-h, --help\n");
- printf("\t-t, --threshold : also print out the threshold values\n");
- printf("\t-x, --hex : print the values out in hexadecimal\n");
- printf("\t-a, --attribute : print specified attribute(s)\n");
- printf("\t-i, --info : print general device information\n");
- printf("\t-d, --decode: decode the attribute IDs\n");
- printf("\t-D, --no-decode: don't decode the attribute IDs\n");
- printf("\t-v, --version : print the version and copyright\n");
- printf("\t --debug : output diagnostic information\n");
-}
-
-/*
- * Convert string to an integer
- *
- * Returns -1 on error, converted value otherwise
- */
-static int32_t
-get_val(char *attr, char **next)
-{
- char *sep = NULL;
- long val;
-
- *next = NULL;
-
- val = strtol(attr, &sep, 0);
- if ((val == 0) && (errno != 0)) {
- printf("Error parsing attribute %s", attr);
- switch (errno) {
- case EINVAL:
- printf(" (not a number?)\n");
- break;
- case ERANGE:
- printf(" (value out of range)\n");
- break;
- default:
- printf("\n");
- }
- return -1;
- }
-
- if (val > INT32_MAX) {
- printf("Attribute value %ld too big\n", val);
- return -1;
- }
-
- *next = sep;
- return ((int32_t)val);
-}
-
-/*
- * Create a match specification from the given attribute
- *
- * Attribute format is
- * <Page ID>:<Attribute ID>
- * where page and attribute IDs are integers. If the page ID is missing,
- * match the specified attribute ID on any page (i.e. -1). Valid forms are
- * <int>:<int>
- * :<int>
- * <int>
- *
- * Returns 0 on success
- */
-static int
-add_match(smart_matches_t **matches, char *attr)
-{
- char *next;
- int32_t page = -1, id;
- uint32_t count = 0;
-
- id = get_val(attr, &next);
- if (id < 0)
- return id;
-
- if (*next == ':') {
- page = id;
- id = get_val(next + 1, &next);
- if (id < 0)
- return id;
- }
-
- if (*matches == NULL) {
- *matches = calloc(1, sizeof(smart_matches_t) + sizeof(smart_match_t));
- if (*matches == NULL)
- return ENOMEM;
- } else {
- void *tmp;
-
- count = (*matches)->count;
- tmp = realloc(*matches, sizeof(smart_matches_t) + ((count + 1) * sizeof(smart_match_t)));
- if (tmp == NULL)
- return ENOMEM;
- *matches = tmp;
- }
-
- (*matches)->m[count].page = page;
- (*matches)->m[count].id = id;
- (*matches)->count++;
- return 0;
-}
-
-/*
- * Parse the comma separated list of attributes to match
- *
- * Caller frees memory allocated for the smart_matches_t pointer.
- *
- * Returns 0 on success
- */
-static int
-parse_matches(smart_matches_t **matches, char *attr)
-{
- int res;
-
- if (attr[0] == '\0')
- return -1;
-
- while (*attr != '\0') {
- char *next;
- size_t len;
-
- if ((next = strchr(attr, ',')) == NULL) {
- len = strlen(attr);
- next = attr + len;
- } else {
- len = next - attr;
- next++;
- }
-
- if (len == 0) {
- printf("Malformed attribute %s\n", attr);
- return -1;
- }
-
- res = add_match(matches, attr);
- if (res)
- return res;
-
- attr = next;
- }
-
- return 0;
-}
-
-int
-main(int argc, char *argv[])
-{
- smart_h h;
- smart_map_t *sm = NULL;
- char *devname = NULL;
- int ch;
- bool do_thresh = false, do_hex = false, do_info = false, do_version = false,
- do_descr;
- smart_matches_t *matches = NULL;
- int rc = EXIT_SUCCESS;
-
- /*
- * By default, keep the original behavior (output numbers only) if
- * invoked as smart. Otherwise, default to printing the human-friendly
- * text descriptions.
- */
- pn = getprogname();
- if (strcmp(pn, SMART_NAME) == 0)
- do_descr = false;
- else
- do_descr = true;
-
-#ifdef LIBXO
- argc = xo_parse_args(argc, argv);
-#endif
-
- while ((ch = getopt_long(argc, argv, "htxa:idDv", opts, NULL)) != -1) {
- switch (ch) {
- case 'h':
- usage(pn);
-#ifdef LIBXO
- xo_finish();
-#endif
- return EXIT_SUCCESS;
- break;
- case 't':
- do_thresh = true;
- break;
- case 'x':
- do_hex = true;
- break;
- case 'a':
- if (parse_matches(&matches, optarg)) {
- usage(pn);
- return EXIT_FAILURE;
- }
- break;
- case 'i':
- do_info = true;
- break;
- case 'd':
- do_descr = true;
- break;
- case 'D':
- do_descr = false;
- break;
- case 'v':
- do_version = true;
- break;
- case 0:
- if (debugset)
- do_debug = true;
- break;
- default:
- usage(pn);
-#ifdef LIBXO
- xo_finish();
-#endif
- return EXIT_FAILURE;
- }
- }
-
- if (do_version) {
- printf("%s, version %s\n", pn, SMART_VERSION);
- printf("Copyright (c) 2016-2026 Chuck Tuffli\n"
- "This is free software; see the source for copying conditions.\n");
- return EXIT_SUCCESS;
- }
-
- argc -= optind;
- argv += optind;
-
- devname = argv[0];
-
- if (!devname) {
- printf("no device specified\n");
- usage(pn);
-#ifdef LIBXO
- xo_finish();
-#endif
- return EXIT_FAILURE;
- }
-
- h = smart_open(SMART_PROTO_AUTO, argv[0]);
-
- if (h == NULL) {
- printf("device open failed %s\n", argv[0]);
-#ifdef LIBXO
- xo_finish();
-#endif
- return EXIT_FAILURE;
- }
-
-#ifdef LIBXO
- xo_open_container("drive");
-#endif
-
- if (do_info) {
- smart_print_device_info(h);
- }
-
- if (smart_supported(h)) {
- sm = smart_read(h);
-
- if (sm) {
- uint32_t flags = 0;
-
- if (do_hex)
- flags |= SMART_OPEN_F_HEX;
- if (do_thresh)
- flags |= SMART_OPEN_F_THRESH;
- if (do_descr)
- flags |= SMART_OPEN_F_DESCR;
-
- smart_print(h, sm, matches, flags);
-
- smart_free(sm);
- }
- } else {
- rc = EXIT_FAILURE;
- }
-#ifdef LIBXO
- xo_finish();
-#endif
- smart_close(h);
-
- return rc;
-}
diff --git a/packages/Makefile b/packages/Makefile
index 71a49d6d06ea..79bdab9f1abd 100644
--- a/packages/Makefile
+++ b/packages/Makefile
@@ -66,7 +66,6 @@ SUBDIR= blocklist \
resolvconf \
rip \
runtime \
- smart \
smbutils \
syslogd \
tcpd \
diff --git a/packages/smart/Makefile b/packages/smart/Makefile
deleted file mode 100644
index 2cc04493da57..000000000000
--- a/packages/smart/Makefile
+++ /dev/null
@@ -1,4 +0,0 @@
-WORLDPACKAGE= smart
-
-.include <bsd.pkg.mk>
-
diff --git a/packages/smart/smart.ucl b/packages/smart/smart.ucl
deleted file mode 100644
index e861f61a08ab..000000000000
--- a/packages/smart/smart.ucl
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * SPDX-License-Identifier: ISC
- *
- * Copyright (c) 2026 Chuck Tuffli <chuck@FreeBSD.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-comment = "SMART monitoring"
-
-desc = <<EOD
-smart(8) allows the user to monitor the various information reported
-by Self-Monitoring, Analysis and Reporting Technology (SMART) present
-on most ATA, SCSI, and NVMe storage media.
-EOD
-
-annotations {
- set = "optional,optional-jail"
-}
-
diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile
index 9e885d5d45c5..3d3a8443a7cf 100644
--- a/usr.sbin/Makefile
+++ b/usr.sbin/Makefile
@@ -81,7 +81,6 @@ SUBDIR= adduser \
setfib \
setfmac \
setpmac \
- smart \
smbmsg \
snapinfo \
spi \
diff --git a/usr.sbin/smart/Makefile b/usr.sbin/smart/Makefile
deleted file mode 100644
index 924b06d3ebcb..000000000000
--- a/usr.sbin/smart/Makefile
+++ /dev/null
@@ -1,8 +0,0 @@
-.include <src.opts.mk>
-
-SMARTDIR=${SRCTOP}/contrib/smart
-.PATH: ${SMARTDIR}
-
-PACKAGE= smart
-
-.include "${SMARTDIR}/Makefile"