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

154 lines
5.6 KiB

/*
* smartdump.c - FreeBSD CLI utility for querying and decoding
* disk hardware S.M.A.R.T. records directly via the CAM subsystem.
*
* Can output raw binary stream for piping or human-readable decoded matrix.
*
* cc -O2 -Wall smartdump.c -lcam -o smartdump
*/
#include <sys/param.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <ctype.h>
#include <stdint.h>
/* FreeBSD CAM Library Interface Headers */
#include <camlib.h>
#include <cam/scsi/scsi_all.h>
/* Import local shared SMART layout matrix definitions */
#include "../src/smart.h"
/* Cross-vendor SMART buffer decoder and normalizer */
static void decode_smart(struct smart_values *smart_buf) {
printf("ID# ATTRIBUTE_NAME VALUE WORST RAW_VALUE\n");
printf("==================================================================\n");
for (int i = 0; i < 30; i++) {
struct smart_attribute *a = &smart_buf->attr[i];
if (a->id == 0) continue;
uint64_t raw_val = 0;
/* Enforce custom cross-vendor temperature extraction rule */
if (a->id == 194 || a->id == 190) {
raw_val = parse_smart_temperature(a);
} else {
/* Assemble clean 48-bit metric integer for standard counters */
for (int b = 0; b < 6; b++) {
raw_val |= ((uint64_t)a->raw[b]) << (b * 8);
}
}
printf("%3d %-23s %3d %3d %llu\n",
a->id, get_attr_name(a->id), a->current, a->worst, (unsigned long long)raw_val);
}
}
int main(int argc, char **argv) {
int ch;
int mode_decode = 0;
/* Parse configuration operational runtime flag args */
while ((ch = getopt(argc, argv, "dr")) != -1) {
switch (ch) {
case 'd': mode_decode = 1; break;
case 'r': mode_decode = 0; break;
default:
fprintf(stderr, "Usage: %s [-d|-r] <device_name>\n", argv[0]);
return 1;
}
}
argc -= optind;
argv += optind;
if (argc < 1) {
fprintf(stderr, "Usage: %s [-d|-r] <device_name> (e.g. ada1 or da0)\n", argv[-optind]);
return 1;
}
const char *disk = argv[0];
struct cam_device *cam_dev = NULL;
union ccb *ccb = NULL;
struct smart_values *smart_buf = NULL;
/* Open device via O_RDWR to unlock transport-layer CCB execution paths */
cam_dev = cam_open_device(disk, O_RDWR);
if (cam_dev == NULL) {
fprintf(stderr, "Error: cam_open_device failed for '%s'. Check permissions.\n", disk);
return 1;
}
ccb = cam_getccb(cam_dev);
if (ccb == NULL) {
fprintf(stderr, "Error: Failed to allocate CCB block\n");
cam_close_device(cam_dev);
return 1;
}
smart_buf = calloc(1, sizeof(struct smart_values));
if (smart_buf == NULL) {
fprintf(stderr, "Error: Memory allocation failed\n");
cam_freeccb(ccb);
cam_close_device(cam_dev);
return 1;
}
/* --- PROTOCOL BRANCH 1: Native SATA/ATA Devices (ada) --- */
if (strncmp(disk, "ada", 3) == 0) {
cam_fill_ataio(&ccb->ataio, 2, NULL, CAM_DIR_IN, CAM_TAG_ACTION_NONE,
(uint8_t *)smart_buf, sizeof(struct smart_values), 5000);
ccb->ataio.cmd.command = 0xB0; /* ATA SMART Command Opcode */
ccb->ataio.cmd.features = 0xD0; /* SMART READ DATA Command Feature Code */
ccb->ataio.cmd.lba_mid = 0x4F; /* Subsystem Spec Signature Marker */
ccb->ataio.cmd.lba_high = 0xC2; /* Subsystem Spec Signature Marker */
if (cam_send_ccb(cam_dev, ccb) != 0 || (ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
fprintf(stderr, "Error: ATA SMART command failed (CAM Status: 0x%X)\n", ccb->ccb_h.status);
free(smart_buf); cam_freeccb(ccb); cam_close_device(cam_dev); return 1;
}
}
/* --- PROTOCOL BRANCH 2: USB / SCSI Bridges (da) --- */
else if (strncmp(disk, "da", 2) == 0) {
memset(&ccb->csio, 0, sizeof(struct ccb_scsiio));
cam_fill_csio(&ccb->csio, 2, NULL, CAM_DIR_IN, CAM_TAG_ACTION_NONE,
(uint8_t *)smart_buf, sizeof(struct smart_values), SSD_FULL_SIZE, 16, 5000);
uint8_t *cdb = ccb->csio.cdb_io.cdb_bytes;
cdb[0] = 0x85; /* SCSI Opcode: ATA PASS-THROUGH (16) */
cdb[1] = 4 << 1; /* Protocol: PIO Data-In */
cdb[2] = 0x08 | 0x02; /* T_DIR = 1 (Data-In), ByteBlock = 1 (Sectors mode) */
cdb[4] = 0xD0; /* Features (lower) -> SMART READ DATA */
cdb[6] = 1; /* Sector Count = 1 sector (512 bytes) */
cdb[8] = 0x4F; /* LBA Mid -> SMART Signature Mid */
cdb[10] = 0xC2; /* LBA High -> SMART Signature High */
cdb[14] = 0xB0; /* ATA Command -> SMART Command Opcode */
if (cam_send_ccb(cam_dev, ccb) != 0 || (ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
fprintf(stderr, "Error: SCSI SAT SMART command failed (CAM Status: 0x%X)\n", ccb->ccb_h.status);
free(smart_buf); cam_freeccb(ccb); cam_close_device(cam_dev); return 1;
}
} else {
fprintf(stderr, "Error: Unsupported device driver type target routing path.\n");
free(smart_buf); cam_freeccb(ccb); cam_close_device(cam_dev); return 1;
}
/* Route output generation maps according to active selection flag switches */
if (mode_decode) {
decode_smart(smart_buf);
} else {
fwrite(smart_buf, 1, 512, stdout);
}
free(smart_buf);
cam_freeccb(ccb);
cam_close_device(cam_dev);
return 0;
}