diff options
Diffstat (limited to 'src/sg_safte.c')
-rw-r--r-- | src/sg_safte.c | 776 |
1 files changed, 776 insertions, 0 deletions
diff --git a/src/sg_safte.c b/src/sg_safte.c new file mode 100644 index 00000000..588d47f8 --- /dev/null +++ b/src/sg_safte.c @@ -0,0 +1,776 @@ +/* + * Copyright (c) 2004-2018 Hannes Reinecke and Douglas Gilbert. + * All rights reserved. + * Use of this source code is governed by a BSD-style + * license that can be found in the BSD_LICENSE file. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <unistd.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdint.h> +#include <string.h> +#include <ctype.h> +#include <getopt.h> + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "sg_lib.h" +#include "sg_cmds_basic.h" +#include "sg_cmds_extra.h" +#include "sg_unaligned.h" +#include "sg_pr2serr.h" + +/* A utility program for the Linux OS SCSI subsystem. + * + * This program accesses a processor device which operates according + * to the 'SCSI Accessed Fault-Tolerant Enclosures' (SAF-TE) spec. + */ + +static const char * version_str = "0.33 20180628"; + + +#define SENSE_BUFF_LEN 64 /* Arbitrary, could be larger */ +#define DEF_TIMEOUT 60000 /* 60,000 millisecs == 60 seconds */ +#define EBUFF_SZ 256 + +#define RB_MODE_DESC 3 +#define RWB_MODE_DATA 2 +#define RWB_MODE_VENDOR 1 +#define RB_DESC_LEN 4 + +#define SAFTE_CFG_FLAG_DOORLOCK 1 +#define SAFTE_CFG_FLAG_ALARM 2 +#define SAFTE_CFG_FLAG_CELSIUS 3 + +struct safte_cfg_t { + int fans; + int psupplies; + int slots; + int temps; + int thermostats; + int vendor_specific; + int flags; +}; + +struct safte_cfg_t safte_cfg; + +static unsigned int buf_capacity = 64; + +static void +dStrRaw(const uint8_t * str, int len) +{ + int k; + + for (k = 0; k < len; ++k) + printf("%c", str[k]); +} + +/* Buffer ID 0x0: Read Enclosure Configuration (mandatory) */ +static int +read_safte_configuration(int sg_fd, uint8_t *rb_buff, + unsigned int rb_len, int verbose) +{ + int res; + + if (rb_len < buf_capacity) { + pr2serr("SCSI BUFFER size too small (%d/%d bytes)\n", rb_len, + buf_capacity); + return SG_LIB_CAT_ILLEGAL_REQ; + } + + if (verbose > 1) + pr2serr("Use READ BUFFER,mode=vendor_specific,buff_id=0 to fetch " + "configuration\n"); + res = sg_ll_read_buffer(sg_fd, RWB_MODE_VENDOR, 0, 0, + rb_buff, rb_len, true, verbose); + if (res && res != SG_LIB_CAT_RECOVERED) + return res; + + safte_cfg.fans = rb_buff[0]; + safte_cfg.psupplies = rb_buff[1]; + safte_cfg.slots = rb_buff[2]; + safte_cfg.temps = rb_buff[4]; + if (rb_buff[3]) + safte_cfg.flags |= SAFTE_CFG_FLAG_DOORLOCK; + if (rb_buff[5]) + safte_cfg.flags |= SAFTE_CFG_FLAG_ALARM; + if (rb_buff[6] & 0x80) + safte_cfg.flags |= SAFTE_CFG_FLAG_CELSIUS; + + safte_cfg.thermostats = rb_buff[6] & 0x0f; + safte_cfg.vendor_specific = rb_buff[63]; + + return 0; +} + +static int +print_safte_configuration(void) +{ + printf("Enclosure Configuration:\n"); + printf("\tNumber of Fans: %d\n", safte_cfg.fans); + printf("\tNumber of Power Supplies: %d\n", safte_cfg.psupplies); + printf("\tNumber of Device Slots: %d\n", safte_cfg.slots); + printf("\tNumber of Temperature Sensors: %d\n", safte_cfg.temps); + printf("\tNumber of Thermostats: %d\n", safte_cfg.thermostats); + printf("\tVendor unique bytes: %d\n", safte_cfg.vendor_specific); + + return 0; +} + +/* Buffer ID 0x01: Read Enclosure Status (mandatory) */ +static int +do_safte_encl_status(int sg_fd, int do_hex, int do_raw, int verbose) +{ + int res, i, offset; + unsigned int rb_len; + uint8_t *rb_buff; + + rb_len = safte_cfg.fans + safte_cfg.psupplies + safte_cfg.slots + + safte_cfg.temps + 5 + safte_cfg.vendor_specific; + rb_buff = (uint8_t *)malloc(rb_len); + + + if (verbose > 1) + pr2serr("Use READ BUFFER,mode=vendor_specific,buff_id=1 to read " + "enclosure status\n"); + res = sg_ll_read_buffer(sg_fd, RWB_MODE_VENDOR, 1, 0, + rb_buff, rb_len, false, verbose); + if (res && res != SG_LIB_CAT_RECOVERED) + return res; + + if (do_raw > 1) { + dStrRaw(rb_buff, buf_capacity); + return 0; + } + if (do_hex > 1) { + hex2stdout(rb_buff, buf_capacity, 1); + return 0; + } + printf("Enclosure Status:\n"); + offset = 0; + for (i = 0; i < safte_cfg.fans; i++) { + printf("\tFan %d status: ", i); + switch(rb_buff[i]) { + case 0: + printf("operational\n"); + break; + case 1: + printf("malfunctioning\n"); + break; + case 2: + printf("not installed\n"); + break; + case 80: + printf("not reportable\n"); + break; + default: + printf("unknown\n"); + break; + } + } + + offset += safte_cfg.fans; + for (i = 0; i < safte_cfg.psupplies; i++) { + printf("\tPower supply %d status: ", i); + switch(rb_buff[i + offset]) { + case 0: + printf("operational / on\n"); + break; + case 1: + printf("operational / off\n"); + break; + case 0x10: + printf("malfunctioning / on\n"); + break; + case 0x11: + printf("malfunctioning / off\n"); + break; + case 0x20: + printf("not present\n"); + break; + case 0x21: + printf("present\n"); + break; + case 0x80: + printf("not reportable\n"); + break; + default: + printf("unknown\n"); + break; + } + } + + offset += safte_cfg.psupplies; + for (i = 0; i < safte_cfg.slots; i++) { + printf("\tDevice Slot %d: SCSI ID %d\n", i, rb_buff[i + offset]); + } + + offset += safte_cfg.slots; + if (safte_cfg.flags & SAFTE_CFG_FLAG_DOORLOCK) { + switch(rb_buff[offset]) { + case 0x0: + printf("\tDoor lock status: locked\n"); + break; + case 0x01: + printf("\tDoor lock status: unlocked\n"); + break; + case 0x80: + printf("\tDoor lock status: not reportable\n"); + break; + } + } else { + printf("\tDoor lock status: not installed\n"); + } + + offset++; + if (!(safte_cfg.flags & SAFTE_CFG_FLAG_ALARM)) { + printf("\tSpeaker status: not installed\n"); + } else { + switch(rb_buff[offset]) { + case 0x0: + printf("\tSpeaker status: off\n"); + break; + case 0x01: + printf("\tSpeaker status: on\n"); + break; + } + } + + offset++; + for (i = 0; i < safte_cfg.temps; i++) { + int temp = rb_buff[i + offset]; + int is_celsius = !!(safte_cfg.flags & SAFTE_CFG_FLAG_CELSIUS); + + if (! is_celsius) + temp -= 10; + + printf("\tTemperature sensor %d: %d deg %c\n", i, temp, + is_celsius ? 'C' : 'F'); + } + + offset += safte_cfg.temps; + if (safte_cfg.thermostats) { + if (rb_buff[offset] & 0x80) { + printf("\tEnclosure Temperature alert status: abnormal\n"); + } else { + printf("\tEnclosure Temperature alert status: normal\n"); + } + } + return 0; +} + +/* Buffer ID 0x02: Read Usage Statistics (optional) */ +static int +do_safte_usage_statistics(int sg_fd, int do_hex, int do_raw, int verbose) +{ + int res; + unsigned int rb_len; + uint8_t *rb_buff; + unsigned int minutes; + + rb_len = 16 + safte_cfg.vendor_specific; + rb_buff = (uint8_t *)malloc(rb_len); + + if (verbose > 1) + pr2serr("Use READ BUFFER,mode=vendor_specific,buff_id=2 to read " + "usage statistics\n"); + res = sg_ll_read_buffer(sg_fd, RWB_MODE_VENDOR, 2, 0, + rb_buff, rb_len, false, verbose); + if (res) { + if (res == SG_LIB_CAT_ILLEGAL_REQ) { + printf("Usage Statistics:\n\tNot implemented\n"); + return 0; + } + if (res != SG_LIB_CAT_RECOVERED) { + free(rb_buff); + return res; + } + } + + if (do_raw > 1) { + dStrRaw(rb_buff, buf_capacity); + return 0; + } + if (do_hex > 1) { + hex2stdout(rb_buff, buf_capacity, 1); + return 0; + } + printf("Usage Statistics:\n"); + minutes = sg_get_unaligned_be32(rb_buff + 0); + printf("\tPower on Minutes: %u\n", minutes); + minutes = sg_get_unaligned_be32(rb_buff + 4); + printf("\tPower on Cycles: %u\n", minutes); + + free(rb_buff); + return 0; +} + +/* Buffer ID 0x03: Read Device Insertions (optional) */ +static int +do_safte_slot_insertions(int sg_fd, int do_hex, int do_raw, int verbose) +{ + int res, i; + unsigned int rb_len; + uint8_t *rb_buff, slot_status; + + rb_len = safte_cfg.slots * 2; + rb_buff = (uint8_t *)malloc(rb_len); + + if (verbose > 1) + pr2serr("Use READ BUFFER,mode=vendor_specific,buff_id=3 to read " + "device insertions\n"); + res = sg_ll_read_buffer(sg_fd, RWB_MODE_VENDOR, 3, 0, + rb_buff, rb_len, false, verbose); + if (res ) { + if (res == SG_LIB_CAT_ILLEGAL_REQ) { + printf("Slot insertions:\n\tNot implemented\n"); + return 0; + } + if (res != SG_LIB_CAT_RECOVERED) { + free(rb_buff); + return res; + } + } + + if (do_raw > 1) { + dStrRaw(rb_buff, buf_capacity); + return 0; + } + if (do_hex > 1) { + hex2stdout(rb_buff, buf_capacity, 1); + return 0; + } + printf("Slot insertions:\n"); + for (i = 0; i < safte_cfg.slots; i++) { + slot_status = sg_get_unaligned_be16(rb_buff + (i * 2)); + printf("\tSlot %d: %d insertions", i, slot_status); + } + free(rb_buff); + return 0; +} + +/* Buffer ID 0x04: Read Device Slot Status (mandatory) */ +static int +do_safte_slot_status(int sg_fd, int do_hex, int do_raw, int verbose) +{ + int res, i; + unsigned int rb_len; + uint8_t *rb_buff, slot_status; + + rb_len = safte_cfg.slots * 4; + rb_buff = (uint8_t *)malloc(rb_len); + + if (verbose > 1) + pr2serr("Use READ BUFFER,mode=vendor_specific,buff_id=4 to read " + "device slot status\n"); + res = sg_ll_read_buffer(sg_fd, RWB_MODE_VENDOR, 4, 0, + rb_buff, rb_len, false, verbose); + if (res && res != SG_LIB_CAT_RECOVERED) { + free(rb_buff); + return res; + } + + if (do_raw > 1) { + dStrRaw(rb_buff, buf_capacity); + return 0; + } + if (do_hex > 1) { + hex2stdout(rb_buff, buf_capacity, 1); + return 0; + } + printf("Slot status:\n"); + for (i = 0; i < safte_cfg.slots; i++) { + slot_status = rb_buff[i * 4 + 3]; + printf("\tSlot %d: ", i); + if (slot_status & 0x7) { + if (slot_status & 0x1) + printf("inserted "); + if (slot_status & 0x2) + printf("ready "); + if (slot_status & 0x4) + printf("activated "); + printf("\n"); + } else { + printf("empty\n"); + } + } + free(rb_buff); + return 0; +} + +/* Buffer ID 0x05: Read Global Flags (optional) */ +static int +do_safte_global_flags(int sg_fd, int do_hex, int do_raw, int verbose) +{ + int res; + unsigned int rb_len; + uint8_t *rb_buff; + + rb_len = 16; + rb_buff = (uint8_t *)malloc(rb_len); + + if (verbose > 1) + pr2serr("Use READ BUFFER,mode=vendor_specific,buff_id=5 to read " + "global flags\n"); + res = sg_ll_read_buffer(sg_fd, RWB_MODE_VENDOR, 5, 0, + rb_buff, rb_len, false, verbose); + if (res ) { + if (res == SG_LIB_CAT_ILLEGAL_REQ) { + printf("Global Flags:\n\tNot implemented\n"); + return 0; + } + if (res != SG_LIB_CAT_RECOVERED) { + free(rb_buff); + return res; + } + } + + if (do_raw > 1) { + dStrRaw(rb_buff, buf_capacity); + return 0; + } + if (do_hex > 1) { + hex2stdout(rb_buff, buf_capacity, 1); + return 0; + } + printf("Global Flags:\n"); + printf("\tAudible Alarm Control: %s\n", + rb_buff[0] & 0x1?"on":"off"); + printf("\tGlobal Failure Indicator: %s\n", + rb_buff[0] & 0x2?"on":"off"); + printf("\tGlobal Warning Indicator: %s\n", + rb_buff[0] & 0x4?"on":"off"); + printf("\tEnclosure Power: %s\n", + rb_buff[0] & 0x8?"on":"off"); + printf("\tCooling Failure: %s\n", + rb_buff[0] & 0x10?"yes":"no"); + printf("\tPower Failure: %s\n", + rb_buff[0] & 0x20?"yes":"no"); + printf("\tDrive Failure: %s\n", + rb_buff[0] & 0x40?"yes":"no"); + printf("\tDrive Warning: %s\n", + rb_buff[0] & 0x80?"yes":"no"); + printf("\tArray Failure: %s\n", + rb_buff[1] & 0x1?"yes":"no"); + printf("\tArray Warning: %s\n", + rb_buff[0] & 0x2?"yes":"no"); + printf("\tEnclosure Lock: %s\n", + rb_buff[0] & 0x4?"on":"off"); + printf("\tEnclosure Identify: %s\n", + rb_buff[0] & 0x8?"on":"off"); + + free(rb_buff); + return 0; +} + +static void +usage() +{ + pr2serr("Usage: sg_safte [--config] [--devstatus] [--encstatus] " + "[--flags] [--help]\n" + " [--hex] [--insertions] [--raw] [--usage] " + "[--verbose]\n" + " [--version] DEVICE\n" + " where:\n" + " --config|-c output enclosure configuration\n" + " --devstatus|-d output device slot status\n" + " --encstatus|-s output enclosure status\n" + " --flags|-f output global flags\n" + " --help|-h output command usage message then " + "exit\n" + " --hex|-H output enclosure config in hex\n" + " --insertions|-i output insertion statistics\n" + " --raw|-r output enclosure config in binary " + "to stdout\n" + " --usage|-u output usage statistics\n" + " --verbose|-v increase verbosity\n" + " --version|-v output version then exit\n\n" + "Queries a SAF-TE processor device\n"); +} + +static struct option long_options[] = { + {"config", 0, 0, 'c'}, + {"devstatus", 0, 0, 'd'}, + {"encstatus", 0, 0, 's'}, + {"flags", 0, 0, 'f'}, + {"help", 0, 0, 'h'}, + {"hex", 0, 0, 'H'}, + {"insertions", 0, 0, 'i'}, + {"raw", 0, 0, 'r'}, + {"usage", 0, 0, 'u'}, + {"verbose", 0, 0, 'v'}, + {"version", 0, 0, 'V'}, + {0, 0, 0, 0}, +}; + +int +main(int argc, char * argv[]) +{ + bool do_insertions = false; + bool no_hex_raw; + bool verbose_given = false; + bool version_given = false; + int c, ret, peri_type; + int sg_fd = -1; + int res = SG_LIB_CAT_OTHER; + const char * device_name = NULL; + char ebuff[EBUFF_SZ]; + uint8_t *rb_buff; + bool do_config = false; + bool do_status = false; + bool do_slots = false; + bool do_flags = false; + bool do_usage = false; + int do_hex = 0; + int do_raw = 0; + int verbose = 0; + const char * cp; + char buff[48]; + char b[80]; + struct sg_simple_inquiry_resp inq_resp; + const char op_name[] = "READ BUFFER"; + + while (1) { + int option_index = 0; + + c = getopt_long(argc, argv, "cdfhHirsuvV?", long_options, + &option_index); + + if (c == -1) + break; + + switch (c) { + case 'c': + do_config = true; + break; + case 'd': + do_slots = true; + break; + case 'f': + do_flags = true; + break; + case 'h': + case '?': + usage(); + return 0; + case 'H': + ++do_hex; + break; + case 'i': + do_insertions = true; + break; + case 'r': + ++do_raw; + break; + case 's': + do_status = true; + break; + case 'u': + do_usage = true; + break; + case 'v': + verbose_given = true; + ++verbose; + break; + case 'V': + version_given = true; + break; + default: + pr2serr("unrecognised option code 0x%x ??\n", c); + usage(); + return SG_LIB_SYNTAX_ERROR; + } + } + if (optind < argc) { + if (NULL == device_name) { + device_name = argv[optind]; + ++optind; + } + if (optind < argc) { + for (; optind < argc; ++optind) + pr2serr("Unexpected extra argument: %s\n", argv[optind]); + usage(); + return SG_LIB_SYNTAX_ERROR; + } + } + +#ifdef DEBUG + pr2serr("In DEBUG mode, "); + if (verbose_given && version_given) { + pr2serr("but override: '-vV' given, zero verbose and continue\n"); + verbose_given = false; + version_given = false; + verbose = 0; + } else if (! verbose_given) { + pr2serr("set '-vv'\n"); + verbose = 2; + } else + pr2serr("keep verbose=%d\n", verbose); +#else + if (verbose_given && version_given) + pr2serr("Not in DEBUG mode, so '-vV' has no special action\n"); +#endif + if (version_given) { + pr2serr("Version string: %s\n", version_str); + return 0; + } + + if (NULL == device_name) { + pr2serr("Missing device name!\n\n"); + usage(); + return SG_LIB_SYNTAX_ERROR; + } + if (do_raw) { + if (sg_set_binary_mode(STDOUT_FILENO) < 0) { + perror("sg_set_binary_mode"); + return SG_LIB_FILE_ERROR; + } + } + + if ((sg_fd = sg_cmds_open_device(device_name, false /* rw */, verbose)) + < 0) { + if (verbose) { + snprintf(ebuff, EBUFF_SZ, "sg_safte: error opening file: %s (rw)", + device_name); + perror(ebuff); + } + ret = sg_convert_errno(-sg_fd); + goto fini; + } + no_hex_raw = ((0 == do_hex) && (0 == do_raw)); + + if (no_hex_raw) { + if (0 == sg_simple_inquiry(sg_fd, &inq_resp, true, verbose)) { + printf(" %.8s %.16s %.4s\n", inq_resp.vendor, + inq_resp.product, inq_resp.revision); + peri_type = inq_resp.peripheral_type; + cp = sg_get_pdt_str(peri_type, sizeof(buff), buff); + if (strlen(cp) > 0) + printf(" Peripheral device type: %s\n", cp); + else + printf(" Peripheral device type: 0x%x\n", peri_type); + } else { + pr2serr("sg_safte: %s doesn't respond to a SCSI INQUIRY\n", + device_name); + return SG_LIB_CAT_OTHER; + } + } + + rb_buff = (uint8_t *)malloc(buf_capacity); + if (!rb_buff) + goto err_out; + + memset(rb_buff, 0, buf_capacity); + + res = read_safte_configuration(sg_fd, rb_buff, buf_capacity, verbose); + switch (res) { + case 0: + case SG_LIB_CAT_RECOVERED: + break; + default: + goto err_out; + } + if (1 == do_raw) { + dStrRaw(rb_buff, buf_capacity); + goto finish; + } + if (1 == do_hex) { + hex2stdout(rb_buff, buf_capacity, 1); + goto finish; + } + + if (do_config && no_hex_raw) + print_safte_configuration(); + + if (do_status) { + res = do_safte_encl_status(sg_fd, do_hex, do_raw, verbose); + switch (res) { + case 0: + case SG_LIB_CAT_RECOVERED: + break; + default: + goto err_out; + } + } + + if (do_usage) { + res = do_safte_usage_statistics(sg_fd, do_hex, do_raw, verbose); + switch (res) { + case 0: + case SG_LIB_CAT_RECOVERED: + break; + default: + goto err_out; + } + } + + if (do_insertions) { + res = do_safte_slot_insertions(sg_fd, do_hex, do_raw, verbose); + switch (res) { + case 0: + case SG_LIB_CAT_RECOVERED: + break; + default: + goto err_out; + } + } + + if (do_slots) { + res = do_safte_slot_status(sg_fd, do_hex, do_raw, verbose); + switch (res) { + case 0: + case SG_LIB_CAT_RECOVERED: + break; + default: + goto err_out; + } + } + + if (do_flags) { + res = do_safte_global_flags(sg_fd, do_hex, do_raw, verbose); + switch (res) { + case 0: + case SG_LIB_CAT_RECOVERED: + break; + default: + goto err_out; + } + } +finish: + res = 0; + +err_out: + switch (res) { + case 0: + case SG_LIB_CAT_RECOVERED: + break; + default: + sg_get_category_sense_str(res, sizeof(b), b, verbose); + pr2serr("%s failed: %s\n", op_name, b); + break; + } + ret = res; +fini: + if (sg_fd >= 0) { + res = sg_cmds_close_device(sg_fd); + if (res < 0) { + pr2serr("close error: %s\n", safe_strerror(-res)); + if (0 == ret) + ret = sg_convert_errno(-res); + } + } + if (0 == verbose) { + if (! sg_if_can2stderr("sg_safte failed: ", ret)) + pr2serr("Some error occurred, try again with '-v' " + "or '-vv' for more information\n"); + } + return (ret >= 0) ? ret : SG_LIB_CAT_OTHER; +} |