diff options
author | Douglas Gilbert <dgilbert@interlog.com> | 2018-05-02 22:02:05 +0000 |
---|---|---|
committer | Douglas Gilbert <dgilbert@interlog.com> | 2018-05-02 22:02:05 +0000 |
commit | a59b767b38f08c0dafc011a58e8791aa06d4feb0 (patch) | |
tree | 8be0500cec5edb562779bc41f0ef7ad46b8c86fc /src/sg_timestamp.c | |
parent | f65566c3934a9e22335dee20401cc1cc50865d56 (diff) | |
download | sg3_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.c | 165 |
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); + } } } } |