目前SATA硬盘温度读取都是通过SMART协议来读取,当前可利用的开源工具有两个:
1、hddtemp工具:
hddtemp是专门读取sata硬盘温度的工具,如果只需要知道硬盘的温度,此工具足以。
该工具小巧玲珑,而且不需要依赖第三方库,可以直接使用,缺点:如果是自己的的代码需要知道硬盘温度,不能直接调用hddtemp接口。
使用方法: hddtemp /dev/sdx
Usage: hddtemp [OPTIONS] [TYPE:]DISK1 [[TYPE:]DISK2]...
hddtemp displays the temperature of drives supplied in argument.
Drives must support S.M.A.R.T.
TYPE could be SATA, PATA or SCSI. If omitted hddtemp will try to guess.
-b --drivebase : display database file content that allow hddtemp to
recognize supported drives.
-D --debug : display various S.M.A.R.T. fields and their values.
Useful to find a value that seems to match the
temperature and/or to send me a report.
(done for every drive supplied).
-d --daemon : run hddtemp in TCP/IP daemon mode (port 7634 by default.)
-f --file=FILE : specify database file to use.
-F --foreground : don't daemonize, stay in foreground.
-l --listen=addr : listen on a specific interface (in TCP/IP daemon mode).
-n --numeric : print only the temperature.
-p --port=# : port to listen to (in TCP/IP daemon mode).
-s --separator=C : separator to use between fields (in TCP/IP daemon mode).
-S --syslog=s : log temperature to syslog every s seconds.
-u --unit=[C|F] : force output temperature either in Celsius or Fahrenheit.
-q --quiet : do not check if the drive is supported.
-v --version : display hddtemp version number.
-w --wake-up : wake-up the drive if need.
-4 : listen on IPv4 sockets only.
-6 : listen on IPv6 sockets only.
Report bugs or new drives to <hddtemp@guzu.net>.
hddtemp version 0.3-beta15
2、smartmontools工具:
此工具功能强大,可以读取你所了解的所有硬盘信息,包括硬盘读写时间,读写数据了、温度等等其他一切硬盘信息。
优点:你所有想了解的硬盘信息,此工具皆能获取到,缺点:smartmontools依赖其他第三方库,比如mailx等等。
使用方法: smartctl -a /dev/sdx smartctl -x /dev/sdx
=== START OF INFORMATION SECTION ===
Device Model: LITEON V5G+ 128
Serial Number: P02743137697
LU WWN Device Id: 5 002303 10103438e
Firmware Version: 2870306
User Capacity: 128,035,676,160 bytes [128 GB]
Sector Size: 512 bytes logical/physical
Rotation Rate: Solid State Device
Form Factor: M.2
Device is: Not in smartctl database [for details use: -P showall]
ATA Version is: ACS-2 (minor revision not indicated)
SATA Version is: SATA 3.1, 6.0 Gb/s (current: 6.0 Gb/s)
Local Time is: Mon Jul 16 15:41:47 2018 CST
SMART support is: Available - device has SMART capability.
SMART support is: Enabled
Device Statistics (GP Log 0x04)
Page Offset Size Value Flags Description
0x01 ===== = = === == General Statistics (rev 1) ==
0x01 0x008 4 121 --- Lifetime Power-On Resets
0x01 0x010 4 1512 --- Power-on Hours
0x01 0x018 6 2287532613 --- Logical Sectors Written
0x01 0x020 6 38912385 --- Number of Write Commands
0x01 0x028 6 78111346 --- Logical Sectors Read
0x01 0x030 6 2053933 --- Number of Read Commands
0x04 ===== = = === == General Errors Statistics (rev 1) ==
0x04 0x008 4 0 --- Number of Reported Uncorrectable Errors
0x04 0x010 4 8241353 --- Resets Between Cmd Acceptance and Completion
0x06 ===== = = === == Transport Statistics (rev 1) ==
0x06 0x008 4 465 --- Number of Hardware Resets
0x06 0x018 4 0 --- Number of Interface CRC Errors
0x07 ===== = = === == Solid State Device Statistics (rev 1) ==
0x07 0x008 1 0 --- Percentage Used Endurance Indicator
|||_ C monitored condition met
||__ D supports DSN
|___ N normalized value
SATA Phy Event Counters (GP Log 0x11)
ID Size Value Description
0x0001 2 0 Command failed due to ICRC error
0x0002 2 0 R_ERR response for data FIS
0x0003 2 0 R_ERR response for device-to-host data FIS
0x0004 2 0 R_ERR response for host-to-device data FIS
0x0005 2 0 R_ERR response for non-data FIS
0x0006 2 0 R_ERR response for device-to-host non-data FIS
0x0007 2 0 R_ERR response for host-to-device non-data FIS
0x0008 2 0 Device-to-host non-data FIS retries
0x0009 2 0 Transition from drive PhyRdy to drive PhyNRdy
0x000a 2 49 Device-to-host register FISes sent due to a COMRESET
0x000b 2 0 CRC errors within host-to-device FIS
0x000d 2 0 Non-CRC errors within host-to-device FIS
0x000f 2 0 R_ERR response for host-to-device data FIS, CRC
0x0010 2 0 R_ERR response for host-to-device data FIS, non-CRC
0x0012 2 0 R_ERR response for host-to-device non-data FIS, CRC
0x0013 2 0 R_ERR response for host-to-device non-data FIS, non-CRC
SMART Attributes Data Structure revision number: 1
Vendor Specific SMART Attributes with Thresholds:
ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE UPDATED WHEN_FAILED RAW_VALUE
1 Raw_Read_Error_Rate 0x002f 100 100 000 Pre-fail Always - 0
5 Reallocated_Sector_Ct 0x0003 100 100 000 Pre-fail Always - 0
9 Power_On_Hours 0x0002 100 100 000 Old_age Always - 1512
12 Power_Cycle_Count 0x0003 100 100 000 Pre-fail Always - 121
170 Unknown_Attribute 0x0032 100 100 000 Old_age Always - 0
171 Unknown_Attribute 0x0003 100 100 000 Pre-fail Always - 0
172 Unknown_Attribute 0x0003 100 100 000 Pre-fail Always - 0
173 Unknown_Attribute 0x0003 100 100 000 Pre-fail Always - 8
174 Unknown_Attribute 0x0003 100 100 000 Pre-fail Always - 23
175 Program_Fail_Count_Chip 0x0003 100 100 000 Pre-fail Always - 0
176 Erase_Fail_Count_Chip 0x0003 100 100 000 Pre-fail Always - 0
178 Used_Rsvd_Blk_Cnt_Chip 0x0003 100 100 000 Pre-fail Always - 0
179 Used_Rsvd_Blk_Cnt_Tot 0x0003 100 100 000 Pre-fail Always - 0
180 Unused_Rsvd_Blk_Cnt_Tot 0x0033 100 100 005 Pre-fail Always - 61
181 Program_Fail_Cnt_Total 0x0003 100 100 000 Pre-fail Always - 0
182 Erase_Fail_Count_Total 0x0003 100 100 000 Pre-fail Always - 0
183 Runtime_Bad_Block 0x0032 100 100 000 Old_age Always - 0
195 Hardware_ECC_Recovered 0x0003 100 100 000 Pre-fail Always - 0
199 UDMA_CRC_Error_Count 0x0003 100 100 000 Pre-fail Always - 0
232 Available_Reservd_Space 0x0003 100 100 010 Pre-fail Always - 100
233 Media_Wearout_Indicator 0x0003 100 100 000 Pre-fail Always - 1139
241 Total_LBAs_Written 0x0003 100 100 000 Pre-fail Always - 34904
242 Total_LBAs_Read 0x0003 100 100 000 Pre-fail Always - 1191
3、再有就是自己提取smart接口来自定义读取温度了。以下是提取hddtemp代码中的接口自己弄得读硬盘温度的代码,好处是可以放入自己的代码当中,直接调用接口就可以读取硬盘温度。aarch64和x64环境通过验证。
使用方法:
Usage:
satatemp [-d] /dev/sda
-d print debug info
源代码如下:
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <getopt.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <linux/hdreg.h>
#include <scsi/scsi.h>
#include <scsi/sg.h>
#include <scsi/scsi_ioctl.h>
#include <fcntl.h>
#define DEF(X) 1
int debug = 0;
#if DEF(ATA)
typedef unsigned short u16;
#define swapb(x) \
({ \
u16 __x = (x); \
(x) = ((u16)( \
(((u16)(__x) & (u16)0x00ffU) << 8) | \
(((u16)(__x) & (u16)0xff00U) >> 8) )); \
})
#define GBUF_SIZE 65535
#define DEFAULT_ATTRIBUTE_ID 194
#define DEFAULT_ATTRIBUTE_ID2 190
#define SBUFF_SIZE 512
static char sbuff[SBUFF_SIZE];
static int ata_probe(int device) {
if(device == -1 || ioctl(device, HDIO_GET_IDENTITY, sbuff))
return 0;
else
return 1;
}
int ata_enable_smart(int device) {
unsigned char cmd[4] = { WIN_SMART, 0, SMART_ENABLE, 0 };
return ioctl(device, HDIO_DRIVE_CMD, cmd);
}
int ata_get_smart_values(int device, unsigned char* buff) {
unsigned char cmd[516] = { WIN_SMART, 0, SMART_READ_VALUES, 1 };
int ret;
ret = ioctl(device, HDIO_DRIVE_CMD, cmd);
if(ret)
return ret;
memcpy(buff, cmd+4, 512);
return 0;
}
static char *ata_model (int device) {
if(device == -1 || ioctl(device, HDIO_GET_IDENTITY, sbuff))
return strdup("unknown");
else
return strdup((char*) ((u16*)sbuff + 27));
}
unsigned char* ata_search_temperature(const unsigned char* smart_data, int attribute_id) {
int i, n;
n = 3;
i = 0;
if(debug)
printf("============ ata ============\n");
while((debug || *(smart_data + n) != attribute_id) && i < 30) {
if(debug && *(smart_data + n))
printf("field(%d)\t = %d\t(0x%02x)\n",
(int)*(smart_data + n),
(int)*(smart_data + n + 3),
*(smart_data + n + 3));
n += 12;
i++;
}
if(i >= 30)
return NULL;
else
return (unsigned char*)(smart_data + n);
}
int ata_get_temperature(int fd)
{
unsigned char values[512]/*, thresholds[512]*/;
unsigned char *field;
int i;
unsigned short *p;
if(ata_enable_smart(fd) != 0)
{
printf("ATA S.M.A.R.T. not available!\n");
return -1;
}
if(ata_get_smart_values(fd, values))
{
printf("ATA Enable S.M.A.R.T. err!\n");
return -1;
}
p = (u16*)values;
for(i = 0; i < 256; i++) {
swapb(*(p+i));
}
/* get SMART threshold values */
/*
if(get_smart_threshold_values(fd, thresholds)) {
perror("ioctl");
exit(3);
}
p = (u16*)thresholds;
for(i = 0; i < 256; i++) {
swapb(*(p+i));
}
*/
/* temperature */
field = ata_search_temperature(values, DEFAULT_ATTRIBUTE_ID);
if(!field)
field = ata_search_temperature(values, DEFAULT_ATTRIBUTE_ID2);
if(field)
return *(field+3);
else
return -1;
}
#endif
#if DEF(SCSI)
#define TEMPERATURE_PAGE 0x0d
#define CDB_12_HDR_SIZE 14
#define CDB_12_MAX_DATA_SIZE 0xffffffff
#define CDB_6_HDR_SIZE 14
#define CDB_6_MAX_DATA_SIZE 0xff
#define DEXCPT_DISABLE 0xf7
#define DEXCPT_ENABLE 0x08
#define EWASC_ENABLE 0x10
#define EWASC_DISABLE 0xef
#define GBUF_SIZE 65535
#define MODE_DATA_HDR_SIZE 12
#define SMART_SUPPORT 0x00
struct cdb10hdr {
unsigned int inbufsize;
unsigned int outbufsize;
unsigned int cdb [10];
} ;
struct cdb6hdr{
unsigned int inbufsize;
unsigned int outbufsize;
unsigned char cdb [6];
};
static void scsi_fixstring(unsigned char *s, int bytecount)
{
unsigned char *p;
unsigned char *end;
p = s;
end = s + bytecount;
/* strip leading blanks */
while (s != end && *s == ' ')
++s;
/* compress internal blanks and strip trailing blanks */
while (s != end && *s) {
if (*s++ != ' ' || (s != end && *s && *s != ' '))
*p++ = *(s-1);
}
/* wipe out trailing garbage */
while (p != end)
*p++ = '\0';
}
int scsi_SG_IO(int device, unsigned char *cdb, int cdb_len, unsigned char *buffer, int buffer_len, unsigned char *sense, unsigned char sense_len, int dxfer_direction) {
struct sg_io_hdr io_hdr;
memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
io_hdr.interface_id = 'S';
io_hdr.cmdp = cdb;
io_hdr.cmd_len = cdb_len;
io_hdr.dxfer_len = buffer_len;
io_hdr.dxferp = buffer;
io_hdr.mx_sb_len = sense_len;
io_hdr.sbp = sense;
io_hdr.dxfer_direction = dxfer_direction;
io_hdr.timeout = 3000; /* 3 seconds should be ample */
return ioctl(device, SG_IO, &io_hdr);
}
int scsi_SEND_COMMAND(int device, unsigned char *cdb, int cdb_len, unsigned char *buffer, int buffer_len, int dxfer_direction)
{
unsigned char buf[2048];
unsigned int inbufsize, outbufsize, ret;
switch(dxfer_direction) {
case SG_DXFER_FROM_DEV:
inbufsize = 0;
outbufsize = buffer_len;
break;
case SG_DXFER_TO_DEV:
inbufsize = buffer_len;
outbufsize = 0;
break;
default:
inbufsize = 0;
outbufsize = 0;
break;
}
memcpy(buf, &inbufsize , sizeof(inbufsize));
memcpy(buf + sizeof(inbufsize), &outbufsize , sizeof(outbufsize));
memcpy(buf + sizeof(inbufsize) + sizeof(outbufsize), cdb, cdb_len);
memcpy(buf + sizeof(inbufsize) + sizeof(outbufsize) + cdb_len, buffer, buffer_len);
ret = ioctl(device, SCSI_IOCTL_SEND_COMMAND, buf);
memcpy(buffer, buf + sizeof(inbufsize) + sizeof(outbufsize), buffer_len);
return ret;
}
int scsi_command(int device, unsigned char *cdb, int cdb_len, unsigned char *buffer, int buffer_len, int dxfer_direction)
{
static int SG_IO_supported = -1;
int ret;
if (SG_IO_supported == 1)
return scsi_SG_IO(device, cdb, cdb_len, buffer, buffer_len, NULL, 0, dxfer_direction);
else if (SG_IO_supported == 0)
return scsi_SEND_COMMAND(device, cdb, cdb_len, buffer, buffer_len, dxfer_direction);
else {
ret = scsi_SG_IO(device, cdb, cdb_len, buffer, buffer_len, NULL, 0, dxfer_direction);
if (ret == 0) {
SG_IO_supported = 1;
return ret;
} else {
SG_IO_supported = 0;
return scsi_SEND_COMMAND(device, cdb, cdb_len, buffer, buffer_len, dxfer_direction);
}
}
}
int scsi_inquiry(int device, unsigned char *buffer)
{
unsigned char cdb[6];
memset(cdb, 0, sizeof(cdb));
cdb[0] = INQUIRY;
cdb[4] = 36; /* should be 36 for unsafe devices (like USB mass storage stuff)
* otherwise they can lock up! SPC sections 7.4 and 8.6 */
if (scsi_command(device, cdb, sizeof(cdb), buffer, cdb[4], SG_DXFER_FROM_DEV) != 0)
return 1;
else {
scsi_fixstring(buffer + 8, 24);
return 0;
}
}
unsigned char modesense (int device, unsigned char pagenum, unsigned char *pBuf)
{
unsigned char tBuf[CDB_6_MAX_DATA_SIZE + CDB_6_HDR_SIZE ];
struct cdb6hdr *ioctlhdr;
unsigned char status;
memset ( &tBuf, 0, CDB_6_MAX_DATA_SIZE + CDB_6_HDR_SIZE );
ioctlhdr = (struct cdb6hdr *) &tBuf;
ioctlhdr->inbufsize = 0;
ioctlhdr->outbufsize = 0xff;
ioctlhdr->cdb[0] = MODE_SENSE;
ioctlhdr->cdb[1] = 0x00;
ioctlhdr->cdb[2] = pagenum;
ioctlhdr->cdb[3] = 0x00;
ioctlhdr->cdb[4] = CDB_6_MAX_DATA_SIZE;
ioctlhdr->cdb[5] = 0x00;
status = ioctl( device, 1 , &tBuf);
memcpy ( pBuf, &tBuf[8], 256);
return status;
}
unsigned char modeselect (int device, unsigned char pagenum, unsigned char *pBuf)
{
struct cdb6hdr *ioctlhdr;
unsigned char tBuf[CDB_6_MAX_DATA_SIZE + CDB_6_HDR_SIZE ];
unsigned char status;
memset ( &tBuf, 0, CDB_6_MAX_DATA_SIZE + CDB_6_HDR_SIZE );
ioctlhdr = (struct cdb6hdr *) &tBuf;
ioctlhdr->inbufsize = pBuf[0] + 1;
ioctlhdr->outbufsize = 0;
ioctlhdr->cdb[0] = MODE_SELECT;
ioctlhdr->cdb[1] = 0x11;
ioctlhdr->cdb[2] = 0x00;
ioctlhdr->cdb[3] = 0x00;
ioctlhdr->cdb[4] = pBuf[0] + 1;
ioctlhdr->cdb[5] = 0x00;
tBuf[CDB_6_HDR_SIZE + 3] = 0x08;
tBuf[CDB_6_HDR_SIZE + 10] = 0x02;
memcpy ( &tBuf[ CDB_6_HDR_SIZE + MODE_DATA_HDR_SIZE],
pBuf + MODE_DATA_HDR_SIZE,
pBuf[0] - MODE_DATA_HDR_SIZE + 1);
tBuf[26] &= 0x3f;
status = ioctl( device, 1 , &tBuf);
return status;
}
unsigned char scsi_smart_mode_page1c_handler(int device, unsigned char setting, unsigned char *retval)
{
char tBuf[CDB_6_MAX_DATA_SIZE];
if (modesense ( device, 0x1c, (unsigned char *) &tBuf) != 0)
{
return 1;
}
switch (setting)
{
case DEXCPT_DISABLE:
tBuf[14] &= 0xf7;
tBuf[15] = 0x04;
break;
case DEXCPT_ENABLE:
tBuf[14] |= 0x08;
break;
case EWASC_ENABLE:
tBuf[14] |= 0x10;
break;
case EWASC_DISABLE:
tBuf[14] &= 0xef;
break;
case SMART_SUPPORT:
*retval = tBuf[14] & 0x08;
return 0;
break;
default:
return 1;
}
if (modeselect ( device, 0x1c, (unsigned char *) &tBuf ) != 0)
{
return 1;
}
return 0;
}
unsigned char log_sense (int device, unsigned char pagenum, unsigned char *pBuf)
{
struct cdb10hdr *ioctlhdr;
unsigned char tBuf[1024 + CDB_12_HDR_SIZE];
unsigned char status;
memset ( &tBuf, 0, 255);
ioctlhdr = (struct cdb10hdr *) tBuf;
ioctlhdr->inbufsize = 0;
ioctlhdr->outbufsize = 1024;
ioctlhdr->cdb[0] = LOG_SENSE;
ioctlhdr->cdb[1] = 0x00;
ioctlhdr->cdb[2] = 0x40 | pagenum;
ioctlhdr->cdb[3] = 0x00;
ioctlhdr->cdb[4] = 0x00;
ioctlhdr->cdb[5] = 0x00;
ioctlhdr->cdb[6] = 0x00;
ioctlhdr->cdb[7] = 0x04;
ioctlhdr->cdb[8] = 0x00;
ioctlhdr->cdb[9] = 0x00;
status = ioctl( device, 1 , &tBuf);
memcpy ( pBuf, &tBuf[8], 1024);
return status;
}
static int scsi_probe(int device) {
int bus_num;
if(ioctl(device, SCSI_IOCTL_GET_BUS_NUMBER, &bus_num))
return 0;
else
return 1;
}
static char *scsi_model (int device) {
unsigned char buf[36];
if (scsi_inquiry(device, buf) != 0)
return strdup("unknown");
else {
return strdup(buf + 8);
}
}
int scsi_get_temperature(int fd)
{
unsigned char buf[1024];
unsigned char smartsupport;
char gBuf[GBUF_SIZE];
if(0 != scsi_smart_mode_page1c_handler( fd, SMART_SUPPORT, &smartsupport))
{
printf("SCSI S.M.A.R.T. not available!\n");
return -1;
}
if(0 != scsi_smart_mode_page1c_handler(fd, DEXCPT_DISABLE, NULL))
{
printf("SCSI Enable S.M.A.R.T. err!\n");
return -1;
}
if (log_sense(fd , TEMPERATURE_PAGE, buf) != 0)
{
printf("SCSI read err!\n");
return -1;
}
return buf[9];
}
#endif
#if DEF(SATA)
#ifndef ATA_16
/* Values for T10/04-262r7 */
#define ATA_16 0x85 /* 16-byte pass-thru */
#endif
int sata_pass_thru(int device, unsigned char *cmd, unsigned char *buffer) {
unsigned char cdb[16];
unsigned char sense[32];
int dxfer_direction;
int ret;
memset(cdb, 0, sizeof(cdb));
cdb[0] = ATA_16;
if (cmd[3]) {
cdb[1] = (4 << 1); /* PIO Data-in */
cdb[2] = 0x2e; /* no off.line, cc, read from dev, lock count in sector count field */
dxfer_direction = SG_DXFER_FROM_DEV;
} else {
cdb[1] = (3 << 1); /* Non-data */
cdb[2] = 0x20; /* cc */
dxfer_direction = SG_DXFER_NONE;
}
cdb[4] = cmd[2];
if (cmd[0] == WIN_SMART) {
cdb[6] = cmd[3];
cdb[8] = cmd[1];
cdb[10] = 0x4f;
cdb[12] = 0xc2;
}
else
cdb[6] = cmd[1];
cdb[14] = cmd[0];
ret = scsi_SG_IO(device, cdb, sizeof(cdb), buffer, cmd[3] * 512, sense, sizeof(sense), dxfer_direction);
/* Verify SATA magics */
if (sense[0] != 0x72)
return 1;
else
return ret;
}
void sata_fixstring(unsigned char *s, int bytecount)
{
unsigned char *p;
unsigned char *end;
p = s;
end = &s[bytecount & ~1]; /* bytecount must be even */
/* convert from big-endian to host byte order */
for (p = end ; p != s;) {
unsigned short *pp = (unsigned short *) (p -= 2);
*pp = ntohs(*pp);
}
/* strip leading blanks */
while (s != end && *s == ' ')
++s;
/* compress internal blanks and strip trailing blanks */
while (s != end && *s) {
if (*s++ != ' ' || (s != end && *s && *s != ' '))
*p++ = *(s-1);
}
/* wipe out trailing garbage */
while (p != end)
*p++ = '\0';
}
static int sata_probe(int device) {
int bus_num;
unsigned char cmd[4] = { WIN_IDENTIFY, 0, 0, 1 };
unsigned char identify[512];
char buf[36]; /* should be 36 for unsafe devices (like USB mass storage stuff)
otherwise they can lock up! SPC sections 7.4 and 8.6 */
/* SATA disks are difficult to detect as they answer to both ATA and SCSI
commands */
/* First check that the device is accessible through SCSI */
if(ioctl(device, SCSI_IOCTL_GET_BUS_NUMBER, &bus_num))
return 0;
/* Get SCSI name and verify it starts with "ATA " */
if (scsi_inquiry(device, buf))
return 0;
else if (strncmp(buf + 8, "ATA ", 4))
return 0;
/* Verify that it supports ATA pass thru */
if (sata_pass_thru(device, cmd, identify) != 0)
return 0;
else
return 1;
}
int sata_enable_smart(int device) {
unsigned char cmd[4] = { WIN_SMART, 0, SMART_ENABLE, 0 };
return sata_pass_thru(device, cmd, NULL);
}
int sata_get_smart_values(int device, unsigned char* buff) {
unsigned char cmd[4] = { WIN_SMART, 0, SMART_READ_VALUES, 1 };
return sata_pass_thru(device, cmd, buff);
}
static char *sata_model (int device) {
unsigned char cmd[4] = { WIN_IDENTIFY, 0, 0, 1 };
unsigned char identify[512];
if(device == -1 || sata_pass_thru(device, cmd, identify))
return strdup("unknown");
else
{
sata_fixstring(identify + 54, 40);
return strdup(identify + 54);
}
}
static unsigned char* sata_search_temperature(const unsigned char* smart_data, int attribute_id) {
int i, n;
n = 3;
i = 0;
if(debug)
printf("============ sata ============\n");
while((debug || *(smart_data + n) != attribute_id) && i < 30) {
if(debug && *(smart_data + n))
printf("field(%d)\t = %d\t(0x%02x)\n", *(smart_data + n), *(smart_data + n + 3), *(smart_data + n + 3));
n += 12;
i++;
}
if(i >= 30)
return NULL;
else
return (unsigned char*)(smart_data + n);
}
int sata_get_temperature(int fd)
{
unsigned char values[512];
unsigned char *field;
int i;
u16 * p;
/* get SMART values */
if(sata_enable_smart(fd) != 0)
{
printf("SATA S.M.A.R.T. not available!\n");
return -1;
}
if(sata_get_smart_values(fd, values)) {
printf("SATA Enable S.M.A.R.T. err!\n");
return -1;
}
p = (u16*)values;
for(i = 0; i < 256; i++) {
swapb(*(p+i));
}
/* temperature */
field = sata_search_temperature(values, DEFAULT_ATTRIBUTE_ID);
if(!field)
field = sata_search_temperature(values, DEFAULT_ATTRIBUTE_ID2);
if(field)
return *(field+3);
else
return -1;
}
#endif
int print_usage()
{
printf("Usage:\n");
printf(" satatemp [-d] /dev/sda\n");
printf(" -d print debug info\n");
return 0;
}
int main(int argc, char* argv[])
{
int fd = 0;
int value = -1;
char type[16] = "";
char *mode = NULL;
char *device = NULL;
if(3 == argc)
{
if(0 != strncmp(argv[1], "-d", strlen("-d")))
{
print_usage();
return 0;
}
device = argv[2];
debug = 1;
}
else if(argc == 2)
{
if(0 != strncmp(argv[1], "/dev/", strlen("/dev/")))
{
print_usage();
return 0;
}
device = argv[1];
debug = 0;
}
else
{
print_usage();
return 0;
}
fd = open(device, O_RDONLY | O_NONBLOCK);
if (fd < 0)
{
printf("open err!\n");
return (-1);
}
if(sata_probe(fd))
{
value = sata_get_temperature(fd);
memset(type, 0 ,sizeof(type));
strcpy(type,"SATA mode");
mode = sata_model(fd);
}
else if(ata_probe(fd))
{
value = ata_get_temperature(fd);
memset(type, 0 ,sizeof(type));
strcpy(type,"ATA mode");
mode = ata_model(fd);
}
else if(scsi_probe(fd))
{
value = scsi_get_temperature(fd);
memset(type, 0 ,sizeof(type));
strcpy(type,"SCSI mode");
mode = scsi_model(fd);
}
if(value > 0)
printf("%s: %s, temperature: %d C\n", type, mode, value);
else if(mode)
printf("%s: %s: no sensor\n", device, mode);
else
printf("get temperature failed!\n");
close(fd);
free(mode);
return 0;
}