diff options
| author | Chuck Tuffli <chuck@FreeBSD.org> | 2026-04-28 18:49:22 +0000 |
|---|---|---|
| committer | Chuck Tuffli <chuck@FreeBSD.org> | 2026-04-28 19:03:38 +0000 |
| commit | e139a49b5d8e9385b4d42ea1ec3850bc80d7f989 (patch) | |
| tree | d4e417358240c31d8bf31e0c5ebe33fefc63c9f4 | |
| parent | 146b30bad9f65a098e6c09ae93bb1da2ff59616d (diff) | |
Revert "Vendor import of smart at 1.0.2"
This reverts commit 68e5b71517e947b4e3f349c970af362b47b45f27.
| -rw-r--r-- | contrib/smart/Changelog | 37 | ||||
| -rw-r--r-- | contrib/smart/LICENSE | 13 | ||||
| -rw-r--r-- | contrib/smart/Makefile | 26 | ||||
| -rw-r--r-- | contrib/smart/freebsd_dev.c | 828 | ||||
| -rw-r--r-- | contrib/smart/libsmart.c | 1359 | ||||
| -rw-r--r-- | contrib/smart/libsmart.h | 174 | ||||
| -rw-r--r-- | contrib/smart/libsmart_desc.c | 158 | ||||
| -rw-r--r-- | contrib/smart/libsmart_dev.h | 60 | ||||
| -rw-r--r-- | contrib/smart/libsmart_priv.h | 83 | ||||
| -rw-r--r-- | contrib/smart/smart.8 | 245 | ||||
| -rw-r--r-- | contrib/smart/smart.c | 334 | ||||
| -rw-r--r-- | packages/Makefile | 1 | ||||
| -rw-r--r-- | packages/smart/Makefile | 4 | ||||
| -rw-r--r-- | packages/smart/smart.ucl | 30 | ||||
| -rw-r--r-- | usr.sbin/Makefile | 1 | ||||
| -rw-r--r-- | usr.sbin/smart/Makefile | 8 |
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 = ¶m->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 = ¶m->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 = ¶m->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 = ¶m->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 = ¶m->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" |
