aboutsummaryrefslogtreecommitdiff
path: root/src/sg_timestamp.c
diff options
context:
space:
mode:
authorDouglas Gilbert <dgilbert@interlog.com>2018-05-02 22:02:05 +0000
committerDouglas Gilbert <dgilbert@interlog.com>2018-05-02 22:02:05 +0000
commita59b767b38f08c0dafc011a58e8791aa06d4feb0 (patch)
tree8be0500cec5edb562779bc41f0ef7ad46b8c86fc /src/sg_timestamp.c
parentf65566c3934a9e22335dee20401cc1cc50865d56 (diff)
downloadsg3_utils-a59b767b38f08c0dafc011a58e8791aa06d4feb0.tar.gz
sg_lib: add sg_ll_inquiry_pt(), sg_ll_test_unit_ready_progress_pt(), sg_ll_request_sense_pt(), sg_ll_send_diag_pt(), sg_ll_receive_diag_pt(); sg_timestamp: add --elapsed, --hex and --no-timestamp options; sg_ses: check for NVMe enclosure bits
git-svn-id: https://svn.bingwo.ca/repos/sg3_utils/trunk@769 6180dd3e-e324-4e3e-922d-17de1ae2f315
Diffstat (limited to 'src/sg_timestamp.c')
-rw-r--r--src/sg_timestamp.c165
1 files changed, 137 insertions, 28 deletions
diff --git a/src/sg_timestamp.c b/src/sg_timestamp.c
index 17105976..976bca56 100644
--- a/src/sg_timestamp.c
+++ b/src/sg_timestamp.c
@@ -35,7 +35,7 @@
* to the given SCSI device. Based on spc5r07.pdf .
*/
-static const char * version_str = "1.07 20180219";
+static const char * version_str = "1.08 20180502";
#define REP_TIMESTAMP_CMDLEN 12
#define SET_TIMESTAMP_CMDLEN 12
@@ -53,8 +53,12 @@ uint8_t d_buff[256];
static struct option long_options[] = {
+ {"elapsed", no_argument, 0, 'e'},
{"help", no_argument, 0, 'h'},
+ {"hex", no_argument, 0, 'H'},
{"milliseconds", required_argument, 0, 'm'},
+ {"no_timestamp", no_argument, 0, 'N'},
+ {"no-timestamp", no_argument, 0, 'N'},
{"origin", no_argument, 0, 'o'},
{"raw", no_argument, 0, 'r'},
{"readonly", no_argument, 0, 'R'},
@@ -79,20 +83,38 @@ static const char * ts_origin_arr[] = {
static void
-usage()
+usage(int num)
{
+ if (num > 1)
+ goto page2;
+
pr2serr("Usage: "
- "sg_timestamp [--help] [--milliseconds=MS] [--origin] [--raw]\n"
- " [--readonly] [--seconds=SEC] [--srep] "
- "[--verbose]\n"
- " [--version] DEVICE\n");
+ "sg_timestamp [--elapsed] [--help] [--hex] [--milliseconds=MS]\n"
+ " [--no-timestamp] [--origin] [--raw] "
+ "[--readonly]\n"
+ " [--seconds=SEC] [--srep] [--verbose] "
+ "[--version]\n"
+ " DEVICE\n"
+ );
pr2serr(" where:\n"
- " --help|-h print out usage message\n"
+ " --elapsed|-e show time as '<n> days hh.mm.ss.xxx' "
+ "where\n"
+ " '.xxx' is the remainder milliseconds. "
+ "Don't show\n"
+ " '<n> days' if <n> is 0 (unless '-e' "
+ "given twice)\n"
+ " --help|-h print out usage message, use twice for "
+ "examples\n"
+ " --hex|-H output response in ASCII hexadecimal\n"
" --milliseconds=MS|-m MS set timestamp to MS "
"milliseconds since\n"
" 1970-01-01 00:00:00 UTC\n"
+ " --no-timestamp|-N suppress output of timestamp\n"
" --origin|-o show Report timestamp origin "
"(def: don't)\n"
+ " used twice outputs value of field\n"
+ " 0: power up or hard reset; 2: SET "
+ "TIMESTAMP\n"
" --raw|-r output Report timestamp response to "
"stdout in\n"
" binary\n"
@@ -106,17 +128,62 @@ usage()
" milliseconds)\n"
" --verbose|-v increase verbosity\n"
" --version|-V print version string and exit\n\n"
- "Performs a SCSI REPORT TIMESTAMP or SET TIMESTAMP command. "
+ );
+ pr2serr("Performs a SCSI REPORT TIMESTAMP or SET TIMESTAMP command. "
"The timestamp\nis SET if either the --milliseconds=MS or "
"--seconds=SEC option is given,\notherwise the existing "
- "timestamp is reported. The DEVICE stores the\ntimestamp as "
- "the number of milliseconds since power up (or reset) or\n"
- "since 1970-01-01 00:00:00 UTC which also happens to be the "
- "time 'epoch'\nof Unix machines. The 'date +%%s' command in "
+ "timestamp is reported in milliseconds. The\nDEVICE stores "
+ "the timestamp as the number of milliseconds since power up\n"
+ "(or reset) or since 1970-01-01 00:00:00 UTC which also "
+ "happens to\nbe the time 'epoch'of Unix machines.\n\n"
+ "Use '-hh' (the '-h' option twice) for examples.\n"
+#if 0
+ "The 'date +%%s' command in "
"Unix returns the number of\nseconds since the epoch. To "
"convert a reported timestamp (in seconds since\nthe epoch) "
"to a more readable form use "
- "'date --date='@<secs_since_epoch>' .\n");
+ "'date --date=@<secs_since_epoch>' .\n"
+#endif
+ );
+ return;
+page2:
+ pr2serr("sg_timestamp examples:\n"
+ "It is possible that the target device containing a SCSI "
+ "Logical Unit (LU)\nhas a battery (or supercapacitor) to "
+ "keep its RTC (real time clock)\nticking during a power "
+ "outage. More likely it doesn't and its RTC is\ncleared to "
+ "zero after a power cycle or hard reset.\n\n"
+ "Either way REPORT TIMESTAMP returns a 48 bit counter value "
+ "whose unit is\na millisecond. A heuristic to determine if a "
+ "date or elapsed time is\nbeing returned is to choose a date "
+ "like 1 January 2000 which is 30 years\nafter the Unix epoch "
+ "(946,684,800,000 milliseconds) and values less than\nthat are "
+ "elapsed times and greater are timestamps. Observing the "
+ "TIMESTAMP\nORIGIN field of REPORT TIMESTAMP is a better "
+ "method:\n\n"
+ );
+ pr2serr(" $ sg_timestamp -o -N /dev/sg1\n"
+ "Device clock initialized to zero at power on or by hard "
+ "reset\n"
+ " $ sg_timestamp -oo -N /dev/sg1\n"
+ "0\n\n"
+ " $ sg_timestamp /dev/sg1\n"
+ "3984499\n"
+ " $ sg_timestamp --elapsed /dev/sg1\n"
+ "01:06:28.802\n\n"
+ "The last output indicates an elapsed time of 1 hour, 6 minutes "
+ "and 28.802\nseconds. Next set the clock to the current time:\n\n"
+ " $ sg_timestamp --seconds=`date +%%s` /dev/sg1\n\n"
+ " $ sg_timestamp -o -N /dev/sg1\n"
+ "Device clock initialized by SET TIMESTAMP command\n\n"
+ "Now show that as an elapsed time:\n\n"
+ " $ sg_timestamp -e /dev/sg1\n"
+ "17652 days 20:53:22.545\n\n"
+ "That is over 48 years worth of days. Lets try again as a "
+ "data-time\nstamp in UTC:\n\n"
+ " $ date -u -R --date=@`sg_timestamp -S /dev/sg1`\n"
+ "Tue, 01 May 2018 20:56:38 +0000\n"
+ );
}
/* Invokes a SCSI REPORT TIMESTAMP command. Return of 0 -> success,
@@ -244,12 +311,16 @@ dStrRaw(const uint8_t * str, int len)
int
main(int argc, char * argv[])
{
- bool do_origin = false;
bool do_srep = false;
bool do_raw = false;
+ bool no_timestamp = false;
bool readonly = false;
bool secs_given = false;
int sg_fd, res, c;
+ int elapsed = 0;
+ int do_origin = 0;
+ int do_help = 0;
+ int do_hex = 0;
int do_set = 0;
int ret = 0;
int verbose = 0;
@@ -262,16 +333,22 @@ main(int argc, char * argv[])
while (1) {
int option_index = 0;
- c = getopt_long(argc, argv, "hm:orRs:SvV", long_options,
+ c = getopt_long(argc, argv, "ehHm:NorRs:SvV", long_options,
&option_index);
if (c == -1)
break;
switch (c) {
+ case 'e':
+ ++elapsed;
+ break;
case 'h':
case '?':
- usage();
- return 0;
+ ++do_help;
+ break;
+ case 'H':
+ ++do_hex;
+ break;
case 'm':
ll = sg_get_llnum(optarg);
if (-1 == ll) {
@@ -281,8 +358,11 @@ main(int argc, char * argv[])
msecs = (uint64_t)ll;
++do_set;
break;
+ case 'N':
+ no_timestamp = true;
+ break;
case 'o':
- do_origin = true;
+ ++do_origin;
break;
case 'r':
do_raw = true;
@@ -311,7 +391,7 @@ main(int argc, char * argv[])
return 0;
default:
pr2serr("unrecognised option code 0x%x ??\n", c);
- usage();
+ usage(1);
return SG_LIB_SYNTAX_ERROR;
}
}
@@ -323,21 +403,25 @@ main(int argc, char * argv[])
if (optind < argc) {
for (; optind < argc; ++optind)
pr2serr("Unexpected extra argument: %s\n", argv[optind]);
- usage();
+ usage(1);
return SG_LIB_SYNTAX_ERROR;
}
}
+ if (do_help) {
+ usage(do_help);
+ return 0;
+ }
if (do_set > 1) {
pr2serr("either --milliseconds=MS or --seconds=SEC may be given, "
"not both\n");
- usage();
+ usage(1);
return SG_LIB_SYNTAX_ERROR;
}
if (NULL == device_name) {
- pr2serr("missing device name!\n");
- usage();
+ pr2serr("missing device name!\n\n");
+ usage(1);
return SG_LIB_SYNTAX_ERROR;
}
@@ -359,17 +443,42 @@ main(int argc, char * argv[])
if (0 == res) {
if (do_raw)
dStrRaw(d_buff, 12);
+ else if (do_hex)
+ hex2stderr(d_buff, 12, 1);
else {
int len = sg_get_unaligned_be16(d_buff + 0);
+
if (len < 8)
pr2serr("timestamp parameter data length too short, "
"expect >= 10, got %d\n", len + 2);
else {
- if (do_origin)
- printf("Device clock %s\n",
- ts_origin_arr[0x7 & d_buff[2]]);
- msecs = sg_get_unaligned_be48(d_buff + 4);
- printf("%" PRIu64 "\n", do_srep ? (msecs / 1000) : msecs);
+ if (do_origin) {
+ if (1 == do_origin)
+ printf("Device clock %s\n",
+ ts_origin_arr[0x7 & d_buff[2]]);
+ else if (2 == do_origin)
+ printf("%d\n", 0x7 & d_buff[2]);
+ else
+ printf("TIMESTAMP_ORIGIN=%d\n", 0x7 & d_buff[2]);
+ }
+ if (! no_timestamp) {
+ msecs = sg_get_unaligned_be48(d_buff + 4);
+ if (elapsed) {
+ int days = (int)(msecs / 1000 / 60 / 60 / 24);
+ int hours = (int)(msecs / 1000 / 60 / 60 % 24);
+ int mins = (int)(msecs / 1000 / 60 % 60);
+ int secs_in_min =(int)( msecs / 1000 % 60);
+ int rem_msecs = (int)(msecs % 1000);
+
+ if ((elapsed > 1) || (days > 0))
+ printf("%d day%s ", days,
+ ((1 == days) ? "" : "s"));
+ printf("%02d:%02d:%02d.%03d\n", hours, mins,
+ secs_in_min, rem_msecs);
+ } else
+ printf("%" PRIu64 "\n", do_srep ?
+ (msecs / 1000) : msecs);
+ }
}
}
}