Add SCSI REPORT TIMESTAMP and SET TIMESTAMP support.
This adds support to camcontrol(8) and libcam(3) for getting and setting the time on SCSI protocol drives. This is more commonly found on tape drives, but is a SPC (SCSI Primary Commands) command, and may be found on any device that speaks SCSI. The new camcontrol timestamp subcommand allows getting the current device time or setting the time to the current system time or any arbitrary time. sbin/camcontrol/Makefile: Add timestamp.c. sbin/camcontrol/camcontrol.8: Document the new timestamp subcommand. sbin/camcontrol/camcontrol.c: Add the timestamp subcommand to camcontrol. sbin/camcontrol/camcontrol.h: Add the timestamp() function prototype. sbin/camcontrol/timestamp.c: Timestamp setting and reporting functionality. sys/cam/scsi/scsi_all.c: Add two new CCB building functions, scsi_set_timestamp() and scsi_report_timestamp(). Also, add a new helper function, scsi_create_timestamp(). sys/cam/scsi/scsi_all.h: Add CDB and parameter data for the the set and report timestamp commands. Add function declarations for the new CCB building and helper functions. Submitted by: Sam Klopsch Sponsored by: Spectra Logic MFC After: 2 weeks
This commit is contained in:
parent
710542df20
commit
28db0a5e74
@ -4,7 +4,7 @@ PACKAGE=runtime
|
||||
PROG= camcontrol
|
||||
SRCS= camcontrol.c util.c
|
||||
.if !defined(RELEASE_CRUNCH)
|
||||
SRCS+= attrib.c epc.c fwdownload.c modeedit.c persist.c progress.c zone.c
|
||||
SRCS+= attrib.c epc.c fwdownload.c modeedit.c persist.c progress.c timestamp.c zone.c
|
||||
.else
|
||||
CFLAGS+= -DMINIMALISTIC
|
||||
.endif
|
||||
|
@ -27,7 +27,7 @@
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd May 16, 2016
|
||||
.Dd November 30, 2016
|
||||
.Dt CAMCONTROL 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
@ -343,6 +343,11 @@
|
||||
.Op Fl S Ar power_src
|
||||
.Op Fl T Ar timer
|
||||
.Nm
|
||||
.Ic timestamp
|
||||
.Op device id
|
||||
.Op generic args
|
||||
.Ao Fl r Oo Ns Fl f Ar format | Fl m | Fl U Oc | Fl s Ao Fl f Ar format Fl T Ar time | Fl U Ac Ac
|
||||
.Nm
|
||||
.Ic help
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
@ -2417,6 +2422,54 @@ supports, and a number of parameters about each condition, including
|
||||
whether it is enabled and what the timer value is.
|
||||
.El
|
||||
.El
|
||||
.It Ic timestamp
|
||||
Issue REPORT TIMESTAMP or SET TIMESTAMP
|
||||
.Tn SCSI
|
||||
commands. Either the
|
||||
.Fl r
|
||||
option or the
|
||||
.Fl s
|
||||
option must be specified.
|
||||
.Bl -tag -width 6n
|
||||
.It Fl r
|
||||
Report the device's timestamp.
|
||||
If no more arguments are specified, the timestamp will be reported using
|
||||
the national representation of the date and time, followed by the time
|
||||
zone.
|
||||
.Bl -tag -width 9n
|
||||
.It Fl f Ar format
|
||||
Specify the strftime format string, as documented in strftime(3), to be used
|
||||
to format the reported timestamp.
|
||||
.It Fl m
|
||||
Report the timestamp as milliseconds since the epoch.
|
||||
.It Fl U
|
||||
Report the timestamp using the national representation of the date and
|
||||
time, but override the system time zone and use UTC instead.
|
||||
.El
|
||||
.El
|
||||
.Bl -tag -width 6n
|
||||
.It Fl s
|
||||
Set the device's timestamp. Either the
|
||||
.Fl f
|
||||
and
|
||||
.Fl T
|
||||
options or the
|
||||
.Fl U
|
||||
option must be specified.
|
||||
.Bl -tag -width 9n
|
||||
.It Fl f Ar format
|
||||
Specify the strptime format string, as documented in strptime(3).
|
||||
The time must also be specified with the
|
||||
.Fl T
|
||||
option.
|
||||
.It Fl T
|
||||
Provide the time in the format specified with the
|
||||
.Fl f
|
||||
option.
|
||||
.It Fl U
|
||||
Set the timestamp to the host system's time in UTC.
|
||||
.El
|
||||
.El
|
||||
.It Ic help
|
||||
Print out verbose usage information.
|
||||
.El
|
||||
@ -2730,6 +2783,18 @@ camcontrol epc ada0 -c list
|
||||
Display the ATA Power Conditions log (Log Address 0x08) for
|
||||
drive
|
||||
.Pa ada0 .
|
||||
.Pp
|
||||
.Bd -literal -offset indent
|
||||
camcontrol timestamp sa0 -s -f "%A %c" \e
|
||||
-T "Wednesday Wed Oct 26 21:43:57 2016"
|
||||
.Ed
|
||||
.Pp
|
||||
Set the timestamp of drive
|
||||
.Pa sa0
|
||||
using a
|
||||
.Xr strptime 3
|
||||
format string followed by a time string
|
||||
that was created using this format string.
|
||||
.Sh SEE ALSO
|
||||
.Xr cam 3 ,
|
||||
.Xr cam_cdbparse 3 ,
|
||||
|
@ -103,7 +103,8 @@ typedef enum {
|
||||
CAM_CMD_OPCODES = 0x00000024,
|
||||
CAM_CMD_REPROBE = 0x00000025,
|
||||
CAM_CMD_ZONE = 0x00000026,
|
||||
CAM_CMD_EPC = 0x00000027
|
||||
CAM_CMD_EPC = 0x00000027,
|
||||
CAM_CMD_TIMESTAMP = 0x00000028
|
||||
} cam_cmdmask;
|
||||
|
||||
typedef enum {
|
||||
@ -234,6 +235,7 @@ static struct camcontrol_opts option_table[] = {
|
||||
{"opcodes", CAM_CMD_OPCODES, CAM_ARG_NONE, "No:s:T"},
|
||||
{"zone", CAM_CMD_ZONE, CAM_ARG_NONE, "ac:l:No:P:"},
|
||||
{"epc", CAM_CMD_EPC, CAM_ARG_NONE, "c:dDeHp:Pr:sS:T:"},
|
||||
{"timestamp", CAM_CMD_TIMESTAMP, CAM_ARG_NONE, "f:mrsUT:"},
|
||||
#endif /* MINIMALISTIC */
|
||||
{"help", CAM_CMD_USAGE, CAM_ARG_NONE, NULL},
|
||||
{"-?", CAM_CMD_USAGE, CAM_ARG_NONE, NULL},
|
||||
@ -8922,6 +8924,9 @@ usage(int printlong)
|
||||
" camcontrol epc [dev_id][generic_args]<-c cmd> [-d] [-D] [-e]\n"
|
||||
" [-H] [-p power_cond] [-P] [-r rst_src] [-s]\n"
|
||||
" [-S power_src] [-T timer]\n"
|
||||
" camcontrol timestamp [dev_id][generic_args] <-r [-f format|-m|-U]>|\n"
|
||||
" <-s <-f format -T time | -U >>\n"
|
||||
" \n"
|
||||
#endif /* MINIMALISTIC */
|
||||
" camcontrol help\n");
|
||||
if (!printlong)
|
||||
@ -8966,6 +8971,7 @@ usage(int printlong)
|
||||
"opcodes send the SCSI REPORT SUPPORTED OPCODES command\n"
|
||||
"zone manage Zoned Block (Shingled) devices\n"
|
||||
"epc send ATA Extended Power Conditions commands\n"
|
||||
"timestamp report or set the device's timestamp\n"
|
||||
"help this message\n"
|
||||
"Device Identifiers:\n"
|
||||
"bus:target specify the bus and target, lun defaults to 0\n"
|
||||
@ -9157,6 +9163,17 @@ usage(int printlong)
|
||||
"-s save mode (timer, state, restore)\n"
|
||||
"-S power_src set power source: battery, nonbattery (source)\n"
|
||||
"-T timer set timer, seconds, .1 sec resolution (timer)\n"
|
||||
"timestamp arguments:\n"
|
||||
"-r report the timestamp of the device\n"
|
||||
"-f format report the timestamp of the device with the given\n"
|
||||
" strftime(3) format string\n"
|
||||
"-m report the timestamp of the device as milliseconds since\n"
|
||||
" January 1st, 1970\n"
|
||||
"-U report the time with UTC instead of the local time zone\n"
|
||||
"-s set the timestamp of the device\n"
|
||||
"-f format the format of the time string passed into strptime(3)\n"
|
||||
"-T time the time value passed into strptime(3)\n"
|
||||
"-U set the timestamp of the device to UTC time\n"
|
||||
);
|
||||
#endif /* MINIMALISTIC */
|
||||
}
|
||||
@ -9520,6 +9537,10 @@ main(int argc, char **argv)
|
||||
error = epc(cam_dev, argc, argv, combinedopt,
|
||||
retry_count, timeout, arglist & CAM_ARG_VERBOSE);
|
||||
break;
|
||||
case CAM_CMD_TIMESTAMP:
|
||||
error = timestamp(cam_dev, argc, argv, combinedopt,
|
||||
retry_count, timeout, arglist & CAM_ARG_VERBOSE);
|
||||
break;
|
||||
#endif /* MINIMALISTIC */
|
||||
case CAM_CMD_USAGE:
|
||||
usage(1);
|
||||
|
@ -81,6 +81,9 @@ int zone(struct cam_device *device, int argc, char **argv, char *combinedopt,
|
||||
int retry_count, int timeout, int verbosemode);
|
||||
int epc(struct cam_device *device, int argc, char **argv, char *combinedopt,
|
||||
int retry_count, int timeout, int verbosemode);
|
||||
int timestamp(struct cam_device *device, int argc, char **argv,
|
||||
char *combinedopt, int retry_count, int timeout,
|
||||
int verbosemode);
|
||||
void mode_sense(struct cam_device *device, int mode_page, int page_control,
|
||||
int dbd, int retry_count, int timeout, u_int8_t *data,
|
||||
int datalen);
|
||||
|
494
sbin/camcontrol/timestamp.c
Normal file
494
sbin/camcontrol/timestamp.c
Normal file
@ -0,0 +1,494 @@
|
||||
/*-
|
||||
* Copyright (c) 2016 Spectra Logic Corporation
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
|
||||
* substantially similar to the "NO WARRANTY" disclaimer below
|
||||
* ("Disclaimer") and any redistribution must be conditioned upon
|
||||
* including a substantially similar Disclaimer requirement for further
|
||||
* binary redistribution.
|
||||
*
|
||||
* NO WARRANTY
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGES.
|
||||
*
|
||||
* Authors: Ken Merry (Spectra Logic Corporation)
|
||||
* Reid Linnemann (Spectra Logic Corporation)
|
||||
* Samuel Klopsch (Spectra Logic Corporation)
|
||||
*/
|
||||
/*
|
||||
* SCSI tape drive timestamp support
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <err.h>
|
||||
#include <time.h>
|
||||
#include <locale.h>
|
||||
|
||||
#include <cam/cam.h>
|
||||
#include <cam/cam_debug.h>
|
||||
#include <cam/cam_ccb.h>
|
||||
#include <cam/scsi/scsi_all.h>
|
||||
#include <cam/scsi/scsi_message.h>
|
||||
#include <camlib.h>
|
||||
#include "camcontrol.h"
|
||||
|
||||
#define TIMESTAMP_REPORT 0
|
||||
#define TIMESTAMP_SET 1
|
||||
#define MIL "milliseconds"
|
||||
#define UTC "utc"
|
||||
|
||||
static int set_restore_flags(struct cam_device *device, uint8_t *flags,
|
||||
int set_flag, int retry_count, int timeout);
|
||||
static int report_timestamp(struct cam_device *device, uint64_t *ts,
|
||||
int retry_count, int timeout);
|
||||
static int set_timestamp(struct cam_device *device, char *format_string,
|
||||
char *timestamp_string,
|
||||
int retry_count, int timeout);
|
||||
|
||||
static int
|
||||
set_restore_flags(struct cam_device *device, uint8_t *flags, int set_flag,
|
||||
int retry_count, int timeout)
|
||||
{
|
||||
unsigned long blk_desc_length, hdr_and_blk_length;
|
||||
int error = 0;
|
||||
struct scsi_control_ext_page *control_page = NULL;
|
||||
struct scsi_mode_header_10 *mode_hdr = NULL;
|
||||
struct scsi_mode_sense_10 *cdb = NULL;
|
||||
union ccb *ccb = NULL;
|
||||
unsigned long mode_buf_size = sizeof(struct scsi_mode_header_10) +
|
||||
sizeof(struct scsi_mode_blk_desc) +
|
||||
sizeof(struct scsi_control_ext_page);
|
||||
uint8_t mode_buf[mode_buf_size];
|
||||
|
||||
ccb = cam_getccb(device);
|
||||
if (ccb == NULL) {
|
||||
warnx("%s: error allocating CCB", __func__);
|
||||
error = 1;
|
||||
goto bailout;
|
||||
}
|
||||
/*
|
||||
* Get the control extension subpage, we'll send it back modified to
|
||||
* enable SCSI control over the tape drive's timestamp
|
||||
*/
|
||||
scsi_mode_sense_len(&ccb->csio,
|
||||
/*retries*/ retry_count,
|
||||
/*cbfcnp*/ NULL,
|
||||
/*tag_action*/ MSG_SIMPLE_Q_TAG,
|
||||
/*dbd*/ 0,
|
||||
/*page_control*/ SMS_PAGE_CTRL_CURRENT,
|
||||
/*page*/ SCEP_PAGE_CODE,
|
||||
/*param_buf*/ &mode_buf[0],
|
||||
/*param_len*/ mode_buf_size,
|
||||
/*minimum_cmd_size*/ 10,
|
||||
/*sense_len*/ SSD_FULL_SIZE,
|
||||
/*timeout*/ timeout ? timeout : 5000);
|
||||
/*
|
||||
* scsi_mode_sense_len does not have a subpage argument at the moment,
|
||||
* so we have to manually set the subpage code before calling
|
||||
* cam_send_ccb().
|
||||
*/
|
||||
cdb = (struct scsi_mode_sense_10 *)ccb->csio.cdb_io.cdb_bytes;
|
||||
cdb->subpage = SCEP_SUBPAGE_CODE;
|
||||
|
||||
ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
|
||||
if (retry_count > 0)
|
||||
ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
|
||||
|
||||
error = cam_send_ccb(device, ccb);
|
||||
if (error != 0) {
|
||||
warn("error sending Mode Sense");
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
|
||||
cam_error_print(device, ccb, CAM_ESF_ALL,
|
||||
CAM_EPF_ALL, stderr);
|
||||
error = 1;
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
mode_hdr = (struct scsi_mode_header_10 *)&mode_buf[0];
|
||||
blk_desc_length = scsi_2btoul(mode_hdr->blk_desc_len);
|
||||
hdr_and_blk_length = sizeof(struct scsi_mode_header_10)+blk_desc_length;
|
||||
/*
|
||||
* Create the control page at the correct point in the mode_buf, it
|
||||
* starts after the header and the blk description.
|
||||
*/
|
||||
control_page = (struct scsi_control_ext_page *)&mode_buf
|
||||
[hdr_and_blk_length];
|
||||
if (set_flag != 0) {
|
||||
*flags = control_page->flags;
|
||||
/*
|
||||
* Set the SCSIP flag to enable SCSI to change the
|
||||
* tape drive's timestamp.
|
||||
*/
|
||||
control_page->flags |= SCEP_SCSIP;
|
||||
} else {
|
||||
control_page->flags = *flags;
|
||||
}
|
||||
|
||||
scsi_mode_select_len(&ccb->csio,
|
||||
/*retries*/ retry_count,
|
||||
/*cbfcnp*/ NULL,
|
||||
/*tag_action*/ MSG_SIMPLE_Q_TAG,
|
||||
/*scsi_page_fmt*/ 1,
|
||||
/*save_pages*/ 0,
|
||||
/*param_buf*/ &mode_buf[0],
|
||||
/*param_len*/ mode_buf_size,
|
||||
/*minimum_cmd_size*/ 10,
|
||||
/*sense_len*/ SSD_FULL_SIZE,
|
||||
/*timeout*/ timeout ? timeout : 5000);
|
||||
|
||||
ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
|
||||
if (retry_count > 0)
|
||||
ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
|
||||
|
||||
error = cam_send_ccb(device, ccb);
|
||||
if (error != 0) {
|
||||
warn("error sending Mode Select");
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
|
||||
cam_error_print(device, ccb, CAM_ESF_ALL,
|
||||
CAM_EPF_ALL, stderr);
|
||||
error = 1;
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
bailout:
|
||||
if (ccb != NULL)
|
||||
cam_freeccb(ccb);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int
|
||||
report_timestamp(struct cam_device *device, uint64_t *ts,
|
||||
int retry_count, int timeout)
|
||||
{
|
||||
int error = 0;
|
||||
struct scsi_report_timestamp_data *report_buf = malloc(
|
||||
sizeof(struct scsi_report_timestamp_data));
|
||||
uint8_t temp_timestamp[8];
|
||||
uint32_t report_buf_size = sizeof(
|
||||
struct scsi_report_timestamp_data);
|
||||
union ccb *ccb = NULL;
|
||||
|
||||
ccb = cam_getccb(device);
|
||||
if (ccb == NULL) {
|
||||
warnx("%s: error allocating CCB", __func__);
|
||||
error = 1;
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
scsi_report_timestamp(&ccb->csio,
|
||||
/*retries*/ retry_count,
|
||||
/*cbfcnp*/ NULL,
|
||||
/*tag_action*/ MSG_SIMPLE_Q_TAG,
|
||||
/*pdf*/ 0,
|
||||
/*buf*/ report_buf,
|
||||
/*buf_len*/ report_buf_size,
|
||||
/*sense_len*/ SSD_FULL_SIZE,
|
||||
/*timeout*/ timeout ? timeout : 5000);
|
||||
|
||||
ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
|
||||
if (retry_count > 0)
|
||||
ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
|
||||
|
||||
error = cam_send_ccb(device, ccb);
|
||||
if (error != 0) {
|
||||
warn("error sending Report Timestamp");
|
||||
goto bailout;
|
||||
}
|
||||
if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
|
||||
cam_error_print(device, ccb, CAM_ESF_ALL,
|
||||
CAM_EPF_ALL, stderr);
|
||||
error = 1;
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
bzero(temp_timestamp, sizeof(temp_timestamp));
|
||||
memcpy(&temp_timestamp[2], &report_buf->timestamp, 6);
|
||||
|
||||
*ts = scsi_8btou64(temp_timestamp);
|
||||
|
||||
bailout:
|
||||
if (ccb != NULL)
|
||||
cam_freeccb(ccb);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int
|
||||
set_timestamp(struct cam_device *device, char *format_string,
|
||||
char *timestamp_string, int retry_count,
|
||||
int timeout)
|
||||
{
|
||||
struct scsi_set_timestamp_parameters ts_p;
|
||||
time_t time_value;
|
||||
struct tm time_struct;
|
||||
uint8_t flags = 0;
|
||||
int error = 0;
|
||||
uint64_t ts = 0;
|
||||
union ccb *ccb = NULL;
|
||||
int do_restore_flags = 0;
|
||||
|
||||
error = set_restore_flags(device, &flags, /*set_flag*/ 1, retry_count,
|
||||
timeout);
|
||||
if (error != 0)
|
||||
goto bailout;
|
||||
|
||||
do_restore_flags = 1;
|
||||
|
||||
ccb = cam_getccb(device);
|
||||
if (ccb == NULL) {
|
||||
warnx("%s: error allocating CCB", __func__);
|
||||
error = 1;
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
if (strcmp(format_string, UTC) == 0) {
|
||||
time(&time_value);
|
||||
ts = (uint64_t) time_value;
|
||||
} else {
|
||||
bzero(&time_struct, sizeof(struct tm));
|
||||
strptime(timestamp_string, format_string, &time_struct);
|
||||
time_value = mktime(&time_struct);
|
||||
ts = (uint64_t) time_value;
|
||||
}
|
||||
/* Convert time from seconds to milliseconds */
|
||||
ts *= 1000;
|
||||
scsi_create_timestamp(ts_p.timestamp, ts);
|
||||
|
||||
scsi_set_timestamp(&ccb->csio,
|
||||
/*retries*/ retry_count,
|
||||
/*cbfcnp*/ NULL,
|
||||
/*tag_action*/ MSG_SIMPLE_Q_TAG,
|
||||
/*buf*/ &ts_p,
|
||||
/*buf_len*/ sizeof(ts_p),
|
||||
/*sense_len*/ SSD_FULL_SIZE,
|
||||
/*timeout*/ timeout ? timeout : 5000);
|
||||
|
||||
ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
|
||||
if (retry_count > 0)
|
||||
ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
|
||||
|
||||
error = cam_send_ccb(device, ccb);
|
||||
if (error != 0) {
|
||||
warn("error sending Set Timestamp");
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
|
||||
cam_error_print(device, ccb, CAM_ESF_ALL,
|
||||
CAM_EPF_ALL, stderr);
|
||||
error = 1;
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
printf("Timestamp set to %ju\n", (uintmax_t)ts);
|
||||
|
||||
bailout:
|
||||
if (do_restore_flags != 0)
|
||||
error = set_restore_flags(device, &flags, /*set_flag*/ 0,
|
||||
retry_count, timeout);
|
||||
if (ccb != NULL)
|
||||
cam_freeccb(ccb);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int
|
||||
timestamp(struct cam_device *device, int argc, char **argv, char *combinedopt,
|
||||
int retry_count, int timeout, int verbosemode __unused)
|
||||
{
|
||||
int c;
|
||||
uint64_t ts;
|
||||
char *format_string = NULL;
|
||||
char *timestamp_string = NULL;
|
||||
int action = -1;
|
||||
int error = 0;
|
||||
int single_arg = 0;
|
||||
int do_utc = 0;
|
||||
|
||||
while ((c = getopt(argc, argv, combinedopt)) != -1) {
|
||||
switch (c) {
|
||||
case 'r': {
|
||||
if (action != -1) {
|
||||
warnx("Use only one -r or only one -s");
|
||||
error =1;
|
||||
goto bailout;
|
||||
}
|
||||
action = TIMESTAMP_REPORT;
|
||||
break;
|
||||
}
|
||||
case 's': {
|
||||
if (action != -1) {
|
||||
warnx("Use only one -r or only one -s");
|
||||
error = 1;
|
||||
goto bailout;
|
||||
}
|
||||
action = TIMESTAMP_SET;
|
||||
break;
|
||||
}
|
||||
case 'f': {
|
||||
single_arg++;
|
||||
format_string = strdup(optarg);
|
||||
if (format_string == NULL) {
|
||||
warn("Error allocating memory for format "
|
||||
"argument");
|
||||
error = 1;
|
||||
goto bailout;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'm': {
|
||||
single_arg++;
|
||||
format_string = strdup(MIL);
|
||||
if (format_string == NULL) {
|
||||
warn("Error allocating memory");
|
||||
error = 1;
|
||||
goto bailout;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'U': {
|
||||
do_utc = 1;
|
||||
break;
|
||||
}
|
||||
case 'T':
|
||||
timestamp_string = strdup(optarg);
|
||||
if (timestamp_string == NULL) {
|
||||
warn("Error allocating memory for format "
|
||||
"argument");
|
||||
error = 1;
|
||||
goto bailout;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (action == -1) {
|
||||
warnx("Must specify an action, either -r or -s");
|
||||
error = 1;
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
if (single_arg > 1) {
|
||||
warnx("Select only one: %s",
|
||||
(action == TIMESTAMP_REPORT) ?
|
||||
"-f format or -m for the -r flag" :
|
||||
"-f format -T time or -U for the -s flag");
|
||||
error = 1;
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
if (action == TIMESTAMP_SET) {
|
||||
if ((format_string == NULL)
|
||||
&& (do_utc == 0)) {
|
||||
warnx("Must specify either -f format or -U for "
|
||||
"setting the timestamp");
|
||||
error = 1;
|
||||
} else if ((format_string != NULL)
|
||||
&& (do_utc != 0)) {
|
||||
warnx("Must specify only one of -f or -U to set "
|
||||
"the timestamp");
|
||||
error = 1;
|
||||
} else if ((format_string != NULL)
|
||||
&& (strcmp(format_string, MIL) == 0)) {
|
||||
warnx("-m is not allowed for setting the "
|
||||
"timestamp");
|
||||
error = 1;
|
||||
} else if ((do_utc == 0)
|
||||
&& (timestamp_string == NULL)) {
|
||||
warnx("Must specify the time (-T) to set as the "
|
||||
"timestamp");
|
||||
error = 1;
|
||||
}
|
||||
if (error != 0)
|
||||
goto bailout;
|
||||
} else if (action == TIMESTAMP_REPORT) {
|
||||
if (format_string == NULL) {
|
||||
format_string = strdup("%c %Z");
|
||||
if (format_string == NULL) {
|
||||
warn("Error allocating memory for format "
|
||||
"string");
|
||||
error = 1;
|
||||
goto bailout;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (action == TIMESTAMP_REPORT) {
|
||||
error = report_timestamp(device, &ts, retry_count,
|
||||
timeout);
|
||||
if (error != 0) {
|
||||
goto bailout;
|
||||
} else if (strcmp(format_string, MIL) == 0) {
|
||||
printf("Timestamp in milliseconds: %ju\n",
|
||||
(uintmax_t)ts);
|
||||
} else {
|
||||
char temp_timestamp_string[100];
|
||||
time_t time_var = ts / 1000;
|
||||
const struct tm *restrict cur_time;
|
||||
|
||||
setlocale(LC_TIME, "");
|
||||
if (do_utc != 0)
|
||||
cur_time = gmtime(&time_var);
|
||||
else
|
||||
cur_time = localtime(&time_var);
|
||||
|
||||
strftime(temp_timestamp_string,
|
||||
sizeof(temp_timestamp_string), format_string,
|
||||
cur_time);
|
||||
printf("Formatted timestamp: %s\n",
|
||||
temp_timestamp_string);
|
||||
}
|
||||
} else if (action == TIMESTAMP_SET) {
|
||||
if (do_utc != 0) {
|
||||
format_string = strdup(UTC);
|
||||
if (format_string == NULL) {
|
||||
warn("Error allocating memory for format "
|
||||
"string");
|
||||
error = 1;
|
||||
goto bailout;
|
||||
}
|
||||
}
|
||||
|
||||
error = set_timestamp(device, format_string, timestamp_string,
|
||||
retry_count, timeout);
|
||||
}
|
||||
|
||||
bailout:
|
||||
free(format_string);
|
||||
free(timestamp_string);
|
||||
|
||||
return (error);
|
||||
}
|
@ -7946,6 +7946,32 @@ scsi_report_target_group(struct ccb_scsiio *csio, u_int32_t retries,
|
||||
scsi_ulto4b(alloc_len, scsi_cmd->length);
|
||||
}
|
||||
|
||||
void
|
||||
scsi_report_timestamp(struct ccb_scsiio *csio, u_int32_t retries,
|
||||
void (*cbfcnp)(struct cam_periph *, union ccb *),
|
||||
u_int8_t tag_action, u_int8_t pdf,
|
||||
void *buf, u_int32_t alloc_len,
|
||||
u_int8_t sense_len, u_int32_t timeout)
|
||||
{
|
||||
struct scsi_timestamp *scsi_cmd;
|
||||
|
||||
cam_fill_csio(csio,
|
||||
retries,
|
||||
cbfcnp,
|
||||
/*flags*/CAM_DIR_IN,
|
||||
tag_action,
|
||||
/*data_ptr*/(u_int8_t *)buf,
|
||||
/*dxfer_len*/alloc_len,
|
||||
sense_len,
|
||||
sizeof(*scsi_cmd),
|
||||
timeout);
|
||||
scsi_cmd = (struct scsi_timestamp *)&csio->cdb_io.cdb_bytes;
|
||||
bzero(scsi_cmd, sizeof(*scsi_cmd));
|
||||
scsi_cmd->opcode = MAINTENANCE_IN;
|
||||
scsi_cmd->service_action = REPORT_TIMESTAMP | pdf;
|
||||
scsi_ulto4b(alloc_len, scsi_cmd->length);
|
||||
}
|
||||
|
||||
void
|
||||
scsi_set_target_group(struct ccb_scsiio *csio, u_int32_t retries,
|
||||
void (*cbfcnp)(struct cam_periph *, union ccb *),
|
||||
@ -7971,6 +7997,45 @@ scsi_set_target_group(struct ccb_scsiio *csio, u_int32_t retries,
|
||||
scsi_ulto4b(alloc_len, scsi_cmd->length);
|
||||
}
|
||||
|
||||
void
|
||||
scsi_create_timestamp(uint8_t *timestamp_6b_buf,
|
||||
uint64_t timestamp)
|
||||
{
|
||||
uint8_t buf[8];
|
||||
scsi_u64to8b(timestamp, buf);
|
||||
/*
|
||||
* Using memcopy starting at buf[2] because the set timestamp parameters
|
||||
* only has six bytes for the timestamp to fit into, and we don't have a
|
||||
* scsi_u64to6b function.
|
||||
*/
|
||||
memcpy(timestamp_6b_buf, &buf[2], 6);
|
||||
}
|
||||
|
||||
void
|
||||
scsi_set_timestamp(struct ccb_scsiio *csio, u_int32_t retries,
|
||||
void (*cbfcnp)(struct cam_periph *, union ccb *),
|
||||
u_int8_t tag_action, void *buf, u_int32_t alloc_len,
|
||||
u_int8_t sense_len, u_int32_t timeout)
|
||||
{
|
||||
struct scsi_timestamp *scsi_cmd;
|
||||
|
||||
cam_fill_csio(csio,
|
||||
retries,
|
||||
cbfcnp,
|
||||
/*flags*/CAM_DIR_OUT,
|
||||
tag_action,
|
||||
/*data_ptr*/(u_int8_t *) buf,
|
||||
/*dxfer_len*/alloc_len,
|
||||
sense_len,
|
||||
sizeof(*scsi_cmd),
|
||||
timeout);
|
||||
scsi_cmd = (struct scsi_timestamp *)&csio->cdb_io.cdb_bytes;
|
||||
bzero(scsi_cmd, sizeof(*scsi_cmd));
|
||||
scsi_cmd->opcode = MAINTENANCE_OUT;
|
||||
scsi_cmd->service_action = SET_TIMESTAMP;
|
||||
scsi_ulto4b(alloc_len, scsi_cmd->length);
|
||||
}
|
||||
|
||||
/*
|
||||
* Syncronize the media to the contents of the cache for
|
||||
* the given lba/count pair. Specifying 0/0 means sync
|
||||
|
@ -702,7 +702,9 @@ struct scsi_control_page {
|
||||
|
||||
struct scsi_control_ext_page {
|
||||
uint8_t page_code;
|
||||
#define SCEP_PAGE_CODE 0x0a
|
||||
uint8_t subpage_code;
|
||||
#define SCEP_SUBPAGE_CODE 0x01
|
||||
uint8_t page_length[2];
|
||||
uint8_t flags;
|
||||
#define SCEP_TCMOS 0x04 /* Timestamp Changeable by */
|
||||
@ -2971,6 +2973,31 @@ struct scsi_target_group
|
||||
uint8_t control;
|
||||
};
|
||||
|
||||
struct scsi_timestamp
|
||||
{
|
||||
uint8_t opcode;
|
||||
uint8_t service_action;
|
||||
uint8_t reserved1[4];
|
||||
uint8_t length[4];
|
||||
uint8_t reserved2;
|
||||
uint8_t control;
|
||||
};
|
||||
|
||||
struct scsi_set_timestamp_parameters
|
||||
{
|
||||
uint8_t reserved1[4];
|
||||
uint8_t timestamp[6];
|
||||
uint8_t reserved2[4];
|
||||
};
|
||||
|
||||
struct scsi_report_timestamp_parameter_data
|
||||
{
|
||||
uint8_t length[2];
|
||||
uint8_t reserved1[2];
|
||||
uint8_t timestamp[6];
|
||||
uint8_t reserved2[2];
|
||||
};
|
||||
|
||||
struct scsi_target_port_descriptor {
|
||||
uint8_t reserved[2];
|
||||
uint8_t relative_target_port_identifier[2];
|
||||
@ -3966,12 +3993,29 @@ void scsi_report_target_group(struct ccb_scsiio *csio, u_int32_t retries,
|
||||
u_int32_t alloc_len, u_int8_t sense_len,
|
||||
u_int32_t timeout);
|
||||
|
||||
void scsi_report_timestamp(struct ccb_scsiio *csio, u_int32_t retries,
|
||||
void (*cbfcnp)(struct cam_periph *,
|
||||
union ccb *), u_int8_t tag_action,
|
||||
u_int8_t pdf,
|
||||
void *buf,
|
||||
u_int32_t alloc_len, u_int8_t sense_len,
|
||||
u_int32_t timeout);
|
||||
|
||||
void scsi_set_target_group(struct ccb_scsiio *csio, u_int32_t retries,
|
||||
void (*cbfcnp)(struct cam_periph *,
|
||||
union ccb *), u_int8_t tag_action, void *buf,
|
||||
u_int32_t alloc_len, u_int8_t sense_len,
|
||||
u_int32_t timeout);
|
||||
|
||||
void scsi_create_timestamp(uint8_t *timestamp_6b_buf,
|
||||
uint64_t timestamp);
|
||||
|
||||
void scsi_set_timestamp(struct ccb_scsiio *csio, u_int32_t retries,
|
||||
void (*cbfcnp)(struct cam_periph *,
|
||||
union ccb *), u_int8_t tag_action,
|
||||
void *buf, u_int32_t alloc_len,
|
||||
u_int8_t sense_len, u_int32_t timeout);
|
||||
|
||||
void scsi_synchronize_cache(struct ccb_scsiio *csio,
|
||||
u_int32_t retries,
|
||||
void (*cbfcnp)(struct cam_periph *,
|
||||
|
Loading…
Reference in New Issue
Block a user