xref: /freebsd/sbin/camcontrol/timestamp.c (revision 32e86a82f54826f14ea381affa6674db3aa3b5ae)
128db0a5eSKenneth D. Merry /*-
228db0a5eSKenneth D. Merry  * Copyright (c) 2016 Spectra Logic Corporation
328db0a5eSKenneth D. Merry  * All rights reserved.
428db0a5eSKenneth D. Merry  *
528db0a5eSKenneth D. Merry  * Redistribution and use in source and binary forms, with or without
628db0a5eSKenneth D. Merry  * modification, are permitted provided that the following conditions
728db0a5eSKenneth D. Merry  * are met:
828db0a5eSKenneth D. Merry  * 1. Redistributions of source code must retain the above copyright
928db0a5eSKenneth D. Merry  *    notice, this list of conditions, and the following disclaimer,
1028db0a5eSKenneth D. Merry  *    without modification.
1128db0a5eSKenneth D. Merry  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
1228db0a5eSKenneth D. Merry  *    substantially similar to the "NO WARRANTY" disclaimer below
1328db0a5eSKenneth D. Merry  *    ("Disclaimer") and any redistribution must be conditioned upon
1428db0a5eSKenneth D. Merry  *    including a substantially similar Disclaimer requirement for further
1528db0a5eSKenneth D. Merry  *    binary redistribution.
1628db0a5eSKenneth D. Merry  *
1728db0a5eSKenneth D. Merry  * NO WARRANTY
1828db0a5eSKenneth D. Merry  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1928db0a5eSKenneth D. Merry  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2028db0a5eSKenneth D. Merry  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
2128db0a5eSKenneth D. Merry  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
2228db0a5eSKenneth D. Merry  * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2328db0a5eSKenneth D. Merry  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2428db0a5eSKenneth D. Merry  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2528db0a5eSKenneth D. Merry  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
2628db0a5eSKenneth D. Merry  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
2728db0a5eSKenneth D. Merry  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2828db0a5eSKenneth D. Merry  * POSSIBILITY OF SUCH DAMAGES.
2928db0a5eSKenneth D. Merry  *
3028db0a5eSKenneth D. Merry  * Authors: Ken Merry           (Spectra Logic Corporation)
3128db0a5eSKenneth D. Merry  *          Reid Linnemann      (Spectra Logic Corporation)
3228db0a5eSKenneth D. Merry  *          Samuel Klopsch      (Spectra Logic Corporation)
3328db0a5eSKenneth D. Merry  */
3428db0a5eSKenneth D. Merry /*
3528db0a5eSKenneth D. Merry  * SCSI tape drive timestamp support
3628db0a5eSKenneth D. Merry  */
3728db0a5eSKenneth D. Merry 
3828db0a5eSKenneth D. Merry #include <sys/types.h>
3928db0a5eSKenneth D. Merry 
4077d9b0efSAlan Somers #include <assert.h>
4128db0a5eSKenneth D. Merry #include <stdio.h>
4228db0a5eSKenneth D. Merry #include <stdlib.h>
4328db0a5eSKenneth D. Merry #include <unistd.h>
4428db0a5eSKenneth D. Merry #include <string.h>
4528db0a5eSKenneth D. Merry #include <err.h>
4628db0a5eSKenneth D. Merry #include <time.h>
4728db0a5eSKenneth D. Merry #include <locale.h>
4828db0a5eSKenneth D. Merry 
4928db0a5eSKenneth D. Merry #include <cam/cam.h>
5028db0a5eSKenneth D. Merry #include <cam/cam_debug.h>
5128db0a5eSKenneth D. Merry #include <cam/cam_ccb.h>
5228db0a5eSKenneth D. Merry #include <cam/scsi/scsi_all.h>
5328db0a5eSKenneth D. Merry #include <cam/scsi/scsi_message.h>
5428db0a5eSKenneth D. Merry #include <camlib.h>
5528db0a5eSKenneth D. Merry #include "camcontrol.h"
5628db0a5eSKenneth D. Merry 
5728db0a5eSKenneth D. Merry #define TIMESTAMP_REPORT 0
5828db0a5eSKenneth D. Merry #define TIMESTAMP_SET    1
5928db0a5eSKenneth D. Merry #define MIL              "milliseconds"
6028db0a5eSKenneth D. Merry #define UTC              "utc"
6128db0a5eSKenneth D. Merry 
6228db0a5eSKenneth D. Merry static int set_restore_flags(struct cam_device *device, uint8_t *flags,
63492a2ef5SKenneth D. Merry 			     int set_flag, int task_attr, int retry_count,
64492a2ef5SKenneth D. Merry 			     int timeout);
6528db0a5eSKenneth D. Merry static int report_timestamp(struct cam_device *device, uint64_t *ts,
66492a2ef5SKenneth D. Merry 			    int task_attr, int retry_count, int timeout);
6728db0a5eSKenneth D. Merry static int set_timestamp(struct cam_device *device, char *format_string,
68492a2ef5SKenneth D. Merry 			 char *timestamp_string, int task_attr, int retry_count,
69492a2ef5SKenneth D. Merry 			 int timeout);
7028db0a5eSKenneth D. Merry 
7128db0a5eSKenneth D. Merry static int
set_restore_flags(struct cam_device * device,uint8_t * flags,int set_flag,int task_attr,int retry_count,int timeout)7228db0a5eSKenneth D. Merry set_restore_flags(struct cam_device *device, uint8_t *flags, int set_flag,
73492a2ef5SKenneth D. Merry 		  int task_attr, int retry_count, int timeout)
7428db0a5eSKenneth D. Merry {
7528db0a5eSKenneth D. Merry 	unsigned long blk_desc_length, hdr_and_blk_length;
7628db0a5eSKenneth D. Merry 	int error = 0;
7728db0a5eSKenneth D. Merry 	struct scsi_control_ext_page *control_page = NULL;
7828db0a5eSKenneth D. Merry 	struct scsi_mode_header_10 *mode_hdr = NULL;
7928db0a5eSKenneth D. Merry 	union ccb *ccb = NULL;
8028db0a5eSKenneth D. Merry 	unsigned long mode_buf_size = sizeof(struct scsi_mode_header_10) +
8128db0a5eSKenneth D. Merry 	    sizeof(struct scsi_mode_blk_desc) +
8228db0a5eSKenneth D. Merry 	    sizeof(struct scsi_control_ext_page);
8328db0a5eSKenneth D. Merry 	uint8_t mode_buf[mode_buf_size];
8428db0a5eSKenneth D. Merry 
8528db0a5eSKenneth D. Merry 	ccb = cam_getccb(device);
8628db0a5eSKenneth D. Merry 	if (ccb == NULL) {
8728db0a5eSKenneth D. Merry 		warnx("%s: error allocating CCB", __func__);
8828db0a5eSKenneth D. Merry 		error = 1;
8928db0a5eSKenneth D. Merry 		goto bailout;
9028db0a5eSKenneth D. Merry 	}
9128db0a5eSKenneth D. Merry 	/*
9228db0a5eSKenneth D. Merry 	 * Get the control extension subpage, we'll send it back modified to
9328db0a5eSKenneth D. Merry 	 * enable SCSI control over the tape drive's timestamp
9428db0a5eSKenneth D. Merry 	 */
95*df8aa95bSAlexander Motin 	scsi_mode_sense_subpage(&ccb->csio,
9628db0a5eSKenneth D. Merry 	    /*retries*/ retry_count,
9728db0a5eSKenneth D. Merry 	    /*cbfcnp*/ NULL,
98492a2ef5SKenneth D. Merry 	    /*tag_action*/ task_attr,
9928db0a5eSKenneth D. Merry 	    /*dbd*/ 0,
10028db0a5eSKenneth D. Merry 	    /*page_control*/ SMS_PAGE_CTRL_CURRENT,
10128db0a5eSKenneth D. Merry 	    /*page*/ SCEP_PAGE_CODE,
102*df8aa95bSAlexander Motin 	    /*subpage*/ SCEP_SUBPAGE_CODE,
10328db0a5eSKenneth D. Merry 	    /*param_buf*/ &mode_buf[0],
10428db0a5eSKenneth D. Merry 	    /*param_len*/ mode_buf_size,
10528db0a5eSKenneth D. Merry 	    /*minimum_cmd_size*/ 10,
10628db0a5eSKenneth D. Merry 	    /*sense_len*/ SSD_FULL_SIZE,
10728db0a5eSKenneth D. Merry 	    /*timeout*/ timeout ? timeout : 5000);
10828db0a5eSKenneth D. Merry 
10928db0a5eSKenneth D. Merry 	ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
11028db0a5eSKenneth D. Merry 	if (retry_count > 0)
11128db0a5eSKenneth D. Merry 		ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
11228db0a5eSKenneth D. Merry 
11328db0a5eSKenneth D. Merry 	error = cam_send_ccb(device, ccb);
11428db0a5eSKenneth D. Merry 	if (error != 0) {
11528db0a5eSKenneth D. Merry 		warn("error sending Mode Sense");
11628db0a5eSKenneth D. Merry 		goto bailout;
11728db0a5eSKenneth D. Merry 	}
11828db0a5eSKenneth D. Merry 
11928db0a5eSKenneth D. Merry 	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
12028db0a5eSKenneth D. Merry 		cam_error_print(device, ccb, CAM_ESF_ALL,
12128db0a5eSKenneth D. Merry 				CAM_EPF_ALL, stderr);
12228db0a5eSKenneth D. Merry 		error = 1;
12328db0a5eSKenneth D. Merry 		goto bailout;
12428db0a5eSKenneth D. Merry 	}
12528db0a5eSKenneth D. Merry 
12628db0a5eSKenneth D. Merry 	mode_hdr = (struct scsi_mode_header_10 *)&mode_buf[0];
12728db0a5eSKenneth D. Merry 	blk_desc_length = scsi_2btoul(mode_hdr->blk_desc_len);
12828db0a5eSKenneth D. Merry 	hdr_and_blk_length = sizeof(struct scsi_mode_header_10)+blk_desc_length;
12928db0a5eSKenneth D. Merry 	/*
13028db0a5eSKenneth D. Merry 	 * Create the control page at the correct point in the mode_buf, it
13128db0a5eSKenneth D. Merry 	 * starts after the header and the blk description.
13228db0a5eSKenneth D. Merry 	 */
13377d9b0efSAlan Somers 	assert(hdr_and_blk_length <=
13477d9b0efSAlan Somers 	    sizeof(mode_buf) - sizeof(struct scsi_control_ext_page));
13528db0a5eSKenneth D. Merry 	control_page = (struct scsi_control_ext_page *)&mode_buf
13628db0a5eSKenneth D. Merry 	    [hdr_and_blk_length];
13728db0a5eSKenneth D. Merry 	if (set_flag != 0) {
13828db0a5eSKenneth D. Merry 		*flags = control_page->flags;
13928db0a5eSKenneth D. Merry 		/*
14028db0a5eSKenneth D. Merry 		 * Set the SCSIP flag to enable SCSI to change the
14128db0a5eSKenneth D. Merry 		 * tape drive's timestamp.
14228db0a5eSKenneth D. Merry 		 */
14328db0a5eSKenneth D. Merry 		control_page->flags |= SCEP_SCSIP;
14428db0a5eSKenneth D. Merry 	} else {
14528db0a5eSKenneth D. Merry 		control_page->flags = *flags;
14628db0a5eSKenneth D. Merry 	}
14728db0a5eSKenneth D. Merry 
14828db0a5eSKenneth D. Merry 	scsi_mode_select_len(&ccb->csio,
14928db0a5eSKenneth D. Merry 	    /*retries*/ retry_count,
15028db0a5eSKenneth D. Merry 	    /*cbfcnp*/ NULL,
151492a2ef5SKenneth D. Merry 	    /*tag_action*/ task_attr,
15228db0a5eSKenneth D. Merry 	    /*scsi_page_fmt*/ 1,
15328db0a5eSKenneth D. Merry 	    /*save_pages*/ 0,
15428db0a5eSKenneth D. Merry 	    /*param_buf*/ &mode_buf[0],
15528db0a5eSKenneth D. Merry 	    /*param_len*/ mode_buf_size,
15628db0a5eSKenneth D. Merry 	    /*minimum_cmd_size*/ 10,
15728db0a5eSKenneth D. Merry 	    /*sense_len*/ SSD_FULL_SIZE,
15828db0a5eSKenneth D. Merry 	    /*timeout*/ timeout ? timeout : 5000);
15928db0a5eSKenneth D. Merry 
16028db0a5eSKenneth D. Merry 	ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
16128db0a5eSKenneth D. Merry 	if (retry_count > 0)
16228db0a5eSKenneth D. Merry 		ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
16328db0a5eSKenneth D. Merry 
16428db0a5eSKenneth D. Merry 	error = cam_send_ccb(device, ccb);
16528db0a5eSKenneth D. Merry 	if (error != 0) {
16628db0a5eSKenneth D. Merry 		warn("error sending Mode Select");
16728db0a5eSKenneth D. Merry 		goto bailout;
16828db0a5eSKenneth D. Merry 	}
16928db0a5eSKenneth D. Merry 
17028db0a5eSKenneth D. Merry 	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
17128db0a5eSKenneth D. Merry 		cam_error_print(device, ccb, CAM_ESF_ALL,
17228db0a5eSKenneth D. Merry 				CAM_EPF_ALL, stderr);
17328db0a5eSKenneth D. Merry 		error = 1;
17428db0a5eSKenneth D. Merry 		goto bailout;
17528db0a5eSKenneth D. Merry 	}
17628db0a5eSKenneth D. Merry 
17728db0a5eSKenneth D. Merry bailout:
17828db0a5eSKenneth D. Merry 	if (ccb != NULL)
17928db0a5eSKenneth D. Merry 		cam_freeccb(ccb);
18028db0a5eSKenneth D. Merry 
18128db0a5eSKenneth D. Merry 	return error;
18228db0a5eSKenneth D. Merry }
18328db0a5eSKenneth D. Merry 
18428db0a5eSKenneth D. Merry static int
report_timestamp(struct cam_device * device,uint64_t * ts,int task_attr,int retry_count,int timeout)185492a2ef5SKenneth D. Merry report_timestamp(struct cam_device *device, uint64_t *ts, int task_attr,
18628db0a5eSKenneth D. Merry 		 int retry_count, int timeout)
18728db0a5eSKenneth D. Merry {
18828db0a5eSKenneth D. Merry 	int error = 0;
18928db0a5eSKenneth D. Merry 	struct scsi_report_timestamp_data *report_buf = malloc(
19028db0a5eSKenneth D. Merry 		sizeof(struct scsi_report_timestamp_data));
19128db0a5eSKenneth D. Merry 	uint8_t temp_timestamp[8];
19228db0a5eSKenneth D. Merry 	uint32_t report_buf_size = sizeof(
19328db0a5eSKenneth D. Merry 	    struct scsi_report_timestamp_data);
19428db0a5eSKenneth D. Merry 	union ccb *ccb = NULL;
19528db0a5eSKenneth D. Merry 
19628db0a5eSKenneth D. Merry 	ccb = cam_getccb(device);
19728db0a5eSKenneth D. Merry 	if (ccb == NULL) {
19828db0a5eSKenneth D. Merry 		warnx("%s: error allocating CCB", __func__);
19928db0a5eSKenneth D. Merry 		error = 1;
20028db0a5eSKenneth D. Merry 		goto bailout;
20128db0a5eSKenneth D. Merry 	}
20228db0a5eSKenneth D. Merry 
20328db0a5eSKenneth D. Merry 	scsi_report_timestamp(&ccb->csio,
20428db0a5eSKenneth D. Merry 	    /*retries*/ retry_count,
20528db0a5eSKenneth D. Merry 	    /*cbfcnp*/ NULL,
206492a2ef5SKenneth D. Merry 	    /*tag_action*/ task_attr,
20728db0a5eSKenneth D. Merry 	    /*pdf*/ 0,
20828db0a5eSKenneth D. Merry 	    /*buf*/ report_buf,
20928db0a5eSKenneth D. Merry 	    /*buf_len*/ report_buf_size,
21028db0a5eSKenneth D. Merry 	    /*sense_len*/ SSD_FULL_SIZE,
21128db0a5eSKenneth D. Merry 	    /*timeout*/ timeout ? timeout : 5000);
21228db0a5eSKenneth D. Merry 
21328db0a5eSKenneth D. Merry 	ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
21428db0a5eSKenneth D. Merry 	if (retry_count > 0)
21528db0a5eSKenneth D. Merry 		ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
21628db0a5eSKenneth D. Merry 
21728db0a5eSKenneth D. Merry 	error = cam_send_ccb(device, ccb);
21828db0a5eSKenneth D. Merry 	if (error != 0) {
21928db0a5eSKenneth D. Merry 		warn("error sending Report Timestamp");
22028db0a5eSKenneth D. Merry 		goto bailout;
22128db0a5eSKenneth D. Merry 	}
22228db0a5eSKenneth D. Merry 	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
22328db0a5eSKenneth D. Merry 		cam_error_print(device, ccb, CAM_ESF_ALL,
22428db0a5eSKenneth D. Merry 				CAM_EPF_ALL, stderr);
22528db0a5eSKenneth D. Merry 		error = 1;
22628db0a5eSKenneth D. Merry 		goto bailout;
22728db0a5eSKenneth D. Merry 	}
22828db0a5eSKenneth D. Merry 
22928db0a5eSKenneth D. Merry 	bzero(temp_timestamp, sizeof(temp_timestamp));
23028db0a5eSKenneth D. Merry 	memcpy(&temp_timestamp[2], &report_buf->timestamp, 6);
23128db0a5eSKenneth D. Merry 
23228db0a5eSKenneth D. Merry 	*ts = scsi_8btou64(temp_timestamp);
23328db0a5eSKenneth D. Merry 
23428db0a5eSKenneth D. Merry bailout:
23528db0a5eSKenneth D. Merry 	if (ccb != NULL)
23628db0a5eSKenneth D. Merry 		cam_freeccb(ccb);
23777d9b0efSAlan Somers 	free(report_buf);
23828db0a5eSKenneth D. Merry 
23928db0a5eSKenneth D. Merry 	return error;
24028db0a5eSKenneth D. Merry }
24128db0a5eSKenneth D. Merry 
24228db0a5eSKenneth D. Merry static int
set_timestamp(struct cam_device * device,char * format_string,char * timestamp_string,int task_attr,int retry_count,int timeout)24328db0a5eSKenneth D. Merry set_timestamp(struct cam_device *device, char *format_string,
244492a2ef5SKenneth D. Merry 	      char *timestamp_string, int task_attr, int retry_count,
24528db0a5eSKenneth D. Merry 	      int timeout)
24628db0a5eSKenneth D. Merry {
24728db0a5eSKenneth D. Merry 	struct scsi_set_timestamp_parameters ts_p;
24828db0a5eSKenneth D. Merry 	time_t time_value;
24928db0a5eSKenneth D. Merry 	struct tm time_struct;
25028db0a5eSKenneth D. Merry 	uint8_t flags = 0;
25128db0a5eSKenneth D. Merry 	int error = 0;
25228db0a5eSKenneth D. Merry 	uint64_t ts = 0;
25328db0a5eSKenneth D. Merry 	union ccb *ccb = NULL;
25428db0a5eSKenneth D. Merry 	int do_restore_flags = 0;
25528db0a5eSKenneth D. Merry 
256492a2ef5SKenneth D. Merry 	error = set_restore_flags(device, &flags, /*set_flag*/ 1, task_attr,
257492a2ef5SKenneth D. Merry 				  retry_count, timeout);
25828db0a5eSKenneth D. Merry 	if (error != 0)
25928db0a5eSKenneth D. Merry 		goto bailout;
26028db0a5eSKenneth D. Merry 
26128db0a5eSKenneth D. Merry 	do_restore_flags = 1;
26228db0a5eSKenneth D. Merry 
26328db0a5eSKenneth D. Merry 	ccb = cam_getccb(device);
26428db0a5eSKenneth D. Merry 	if (ccb == NULL) {
26528db0a5eSKenneth D. Merry 		warnx("%s: error allocating CCB", __func__);
26628db0a5eSKenneth D. Merry 		error = 1;
26728db0a5eSKenneth D. Merry 		goto bailout;
26828db0a5eSKenneth D. Merry 	}
26928db0a5eSKenneth D. Merry 
27028db0a5eSKenneth D. Merry 	if (strcmp(format_string, UTC) == 0) {
27128db0a5eSKenneth D. Merry 		time(&time_value);
27228db0a5eSKenneth D. Merry 		ts = (uint64_t) time_value;
27328db0a5eSKenneth D. Merry 	} else {
27428db0a5eSKenneth D. Merry 		bzero(&time_struct, sizeof(struct tm));
275d618624cSKenneth D. Merry 		if (strptime(timestamp_string, format_string,
276d618624cSKenneth D. Merry 		    &time_struct) == NULL) {
277d618624cSKenneth D. Merry 			warnx("%s: strptime(3) failed", __func__);
278d618624cSKenneth D. Merry 			error = 1;
279d618624cSKenneth D. Merry 			goto bailout;
280d618624cSKenneth D. Merry 		}
28128db0a5eSKenneth D. Merry 		time_value = mktime(&time_struct);
28228db0a5eSKenneth D. Merry 		ts = (uint64_t) time_value;
28328db0a5eSKenneth D. Merry 	}
28428db0a5eSKenneth D. Merry 	/* Convert time from seconds to milliseconds */
28528db0a5eSKenneth D. Merry 	ts *= 1000;
286d618624cSKenneth D. Merry 	bzero(&ts_p, sizeof(ts_p));
28728db0a5eSKenneth D. Merry 	scsi_create_timestamp(ts_p.timestamp, ts);
28828db0a5eSKenneth D. Merry 
28928db0a5eSKenneth D. Merry 	scsi_set_timestamp(&ccb->csio,
29028db0a5eSKenneth D. Merry 	    /*retries*/ retry_count,
29128db0a5eSKenneth D. Merry 	    /*cbfcnp*/ NULL,
292492a2ef5SKenneth D. Merry 	    /*tag_action*/ task_attr,
29328db0a5eSKenneth D. Merry 	    /*buf*/ &ts_p,
29428db0a5eSKenneth D. Merry 	    /*buf_len*/ sizeof(ts_p),
29528db0a5eSKenneth D. Merry 	    /*sense_len*/ SSD_FULL_SIZE,
29628db0a5eSKenneth D. Merry 	    /*timeout*/ timeout ? timeout : 5000);
29728db0a5eSKenneth D. Merry 
29828db0a5eSKenneth D. Merry 	ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
29928db0a5eSKenneth D. Merry 	if (retry_count > 0)
30028db0a5eSKenneth D. Merry 		ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
30128db0a5eSKenneth D. Merry 
30228db0a5eSKenneth D. Merry 	error = cam_send_ccb(device, ccb);
30328db0a5eSKenneth D. Merry 	if (error != 0) {
30428db0a5eSKenneth D. Merry 		warn("error sending Set Timestamp");
30528db0a5eSKenneth D. Merry 		goto bailout;
30628db0a5eSKenneth D. Merry 	}
30728db0a5eSKenneth D. Merry 
30828db0a5eSKenneth D. Merry 	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
30928db0a5eSKenneth D. Merry 		cam_error_print(device, ccb, CAM_ESF_ALL,
31028db0a5eSKenneth D. Merry 				CAM_EPF_ALL, stderr);
31128db0a5eSKenneth D. Merry 		error = 1;
31228db0a5eSKenneth D. Merry 		goto bailout;
31328db0a5eSKenneth D. Merry 	}
31428db0a5eSKenneth D. Merry 
31528db0a5eSKenneth D. Merry 	printf("Timestamp set to %ju\n", (uintmax_t)ts);
31628db0a5eSKenneth D. Merry 
31728db0a5eSKenneth D. Merry bailout:
31828db0a5eSKenneth D. Merry 	if (do_restore_flags != 0)
31928db0a5eSKenneth D. Merry 		error = set_restore_flags(device, &flags, /*set_flag*/ 0,
320492a2ef5SKenneth D. Merry 					  task_attr, retry_count, timeout);
32128db0a5eSKenneth D. Merry 	if (ccb != NULL)
32228db0a5eSKenneth D. Merry 		cam_freeccb(ccb);
32328db0a5eSKenneth D. Merry 
32428db0a5eSKenneth D. Merry 	return error;
32528db0a5eSKenneth D. Merry }
32628db0a5eSKenneth D. Merry 
32728db0a5eSKenneth D. Merry int
timestamp(struct cam_device * device,int argc,char ** argv,char * combinedopt,int task_attr,int retry_count,int timeout,int verbosemode __unused)32828db0a5eSKenneth D. Merry timestamp(struct cam_device *device, int argc, char **argv, char *combinedopt,
329492a2ef5SKenneth D. Merry 	  int task_attr, int retry_count, int timeout, int verbosemode __unused)
33028db0a5eSKenneth D. Merry {
33128db0a5eSKenneth D. Merry 	int c;
332a1fa2391SAdrian Chadd 	uint64_t ts = 0;
33328db0a5eSKenneth D. Merry 	char *format_string = NULL;
33428db0a5eSKenneth D. Merry 	char *timestamp_string = NULL;
33528db0a5eSKenneth D. Merry 	int action = -1;
33628db0a5eSKenneth D. Merry 	int error = 0;
33728db0a5eSKenneth D. Merry 	int single_arg = 0;
33828db0a5eSKenneth D. Merry 	int do_utc = 0;
33928db0a5eSKenneth D. Merry 
34028db0a5eSKenneth D. Merry 	while ((c = getopt(argc, argv, combinedopt)) != -1) {
34128db0a5eSKenneth D. Merry 		switch (c) {
34228db0a5eSKenneth D. Merry 		case 'r': {
34328db0a5eSKenneth D. Merry 			if (action != -1) {
34428db0a5eSKenneth D. Merry 				warnx("Use only one -r or only one -s");
34528db0a5eSKenneth D. Merry 				error =1;
34628db0a5eSKenneth D. Merry 				goto bailout;
34728db0a5eSKenneth D. Merry 			}
34828db0a5eSKenneth D. Merry 			action = TIMESTAMP_REPORT;
34928db0a5eSKenneth D. Merry 			break;
35028db0a5eSKenneth D. Merry 		}
35128db0a5eSKenneth D. Merry 		case 's': {
35228db0a5eSKenneth D. Merry 			if (action != -1) {
35328db0a5eSKenneth D. Merry 				warnx("Use only one -r or only one -s");
35428db0a5eSKenneth D. Merry 				error = 1;
35528db0a5eSKenneth D. Merry 				goto bailout;
35628db0a5eSKenneth D. Merry 			}
35728db0a5eSKenneth D. Merry 			action = TIMESTAMP_SET;
35828db0a5eSKenneth D. Merry 			break;
35928db0a5eSKenneth D. Merry 		}
36028db0a5eSKenneth D. Merry 		case 'f': {
36128db0a5eSKenneth D. Merry 			single_arg++;
362f2a12bceSEnji Cooper 			free(format_string);
36328db0a5eSKenneth D. Merry 			format_string = strdup(optarg);
36428db0a5eSKenneth D. Merry 			if (format_string == NULL) {
36528db0a5eSKenneth D. Merry 				warn("Error allocating memory for format "
36628db0a5eSKenneth D. Merry 				   "argument");
36728db0a5eSKenneth D. Merry 				error = 1;
36828db0a5eSKenneth D. Merry 				goto bailout;
36928db0a5eSKenneth D. Merry 			}
37028db0a5eSKenneth D. Merry 			break;
37128db0a5eSKenneth D. Merry 		}
37228db0a5eSKenneth D. Merry 		case 'm': {
37328db0a5eSKenneth D. Merry 			single_arg++;
374f2a12bceSEnji Cooper 			free(format_string);
37528db0a5eSKenneth D. Merry 			format_string = strdup(MIL);
37628db0a5eSKenneth D. Merry 			if (format_string == NULL) {
37728db0a5eSKenneth D. Merry 				warn("Error allocating memory");
37828db0a5eSKenneth D. Merry 				error = 1;
37928db0a5eSKenneth D. Merry 				goto bailout;
38028db0a5eSKenneth D. Merry 			}
38128db0a5eSKenneth D. Merry 			break;
38228db0a5eSKenneth D. Merry 		}
38328db0a5eSKenneth D. Merry 		case 'U': {
38428db0a5eSKenneth D. Merry 			do_utc = 1;
38528db0a5eSKenneth D. Merry 			break;
38628db0a5eSKenneth D. Merry 		}
38728db0a5eSKenneth D. Merry 		case 'T':
388f2a12bceSEnji Cooper 			free(timestamp_string);
38928db0a5eSKenneth D. Merry 			timestamp_string = strdup(optarg);
39028db0a5eSKenneth D. Merry 			if (timestamp_string == NULL) {
39128db0a5eSKenneth D. Merry 				warn("Error allocating memory for format "
39228db0a5eSKenneth D. Merry 				   "argument");
39328db0a5eSKenneth D. Merry 				error = 1;
39428db0a5eSKenneth D. Merry 				goto bailout;
39528db0a5eSKenneth D. Merry 			}
39628db0a5eSKenneth D. Merry 			break;
39728db0a5eSKenneth D. Merry 		default:
39828db0a5eSKenneth D. Merry 			break;
39928db0a5eSKenneth D. Merry 		}
40028db0a5eSKenneth D. Merry 	}
40128db0a5eSKenneth D. Merry 
40228db0a5eSKenneth D. Merry 	if (action == -1) {
40328db0a5eSKenneth D. Merry 		warnx("Must specify an action, either -r or -s");
40428db0a5eSKenneth D. Merry 		error = 1;
40528db0a5eSKenneth D. Merry 		goto bailout;
40628db0a5eSKenneth D. Merry 	}
40728db0a5eSKenneth D. Merry 
40828db0a5eSKenneth D. Merry 	if (single_arg > 1) {
40928db0a5eSKenneth D. Merry 		warnx("Select only one: %s",
41028db0a5eSKenneth D. Merry 		    (action == TIMESTAMP_REPORT) ?
41128db0a5eSKenneth D. Merry 		    "-f format or -m for the -r flag" :
41228db0a5eSKenneth D. Merry 		    "-f format -T time or -U for the -s flag");
41328db0a5eSKenneth D. Merry 		error = 1;
41428db0a5eSKenneth D. Merry 		goto bailout;
41528db0a5eSKenneth D. Merry 	}
41628db0a5eSKenneth D. Merry 
41728db0a5eSKenneth D. Merry 	if (action == TIMESTAMP_SET) {
41828db0a5eSKenneth D. Merry 		if ((format_string == NULL)
41928db0a5eSKenneth D. Merry 		 && (do_utc == 0)) {
42028db0a5eSKenneth D. Merry 			warnx("Must specify either -f format or -U for "
42128db0a5eSKenneth D. Merry 			    "setting the timestamp");
42228db0a5eSKenneth D. Merry 			error = 1;
42328db0a5eSKenneth D. Merry 		} else if ((format_string != NULL)
42428db0a5eSKenneth D. Merry 			&& (do_utc != 0)) {
42528db0a5eSKenneth D. Merry 			warnx("Must specify only one of -f or -U to set "
42628db0a5eSKenneth D. Merry 			    "the timestamp");
42728db0a5eSKenneth D. Merry 			error = 1;
42828db0a5eSKenneth D. Merry 		} else if ((format_string != NULL)
42928db0a5eSKenneth D. Merry 			&& (strcmp(format_string, MIL) == 0)) {
43028db0a5eSKenneth D. Merry 			warnx("-m is not allowed for setting the "
43128db0a5eSKenneth D. Merry 			    "timestamp");
43228db0a5eSKenneth D. Merry 			error = 1;
43328db0a5eSKenneth D. Merry 		} else if ((do_utc == 0)
43428db0a5eSKenneth D. Merry 			&& (timestamp_string == NULL)) {
43528db0a5eSKenneth D. Merry 			warnx("Must specify the time (-T) to set as the "
43628db0a5eSKenneth D. Merry 			    "timestamp");
43728db0a5eSKenneth D. Merry 			error = 1;
43828db0a5eSKenneth D. Merry 		}
43928db0a5eSKenneth D. Merry 		if (error != 0)
44028db0a5eSKenneth D. Merry 			goto bailout;
44128db0a5eSKenneth D. Merry 	} else if (action == TIMESTAMP_REPORT) {
44228db0a5eSKenneth D. Merry 		if (format_string == NULL) {
44328db0a5eSKenneth D. Merry 			format_string = strdup("%c %Z");
44428db0a5eSKenneth D. Merry 			if (format_string == NULL) {
44528db0a5eSKenneth D. Merry 				warn("Error allocating memory for format "
44628db0a5eSKenneth D. Merry 				    "string");
44728db0a5eSKenneth D. Merry 				error = 1;
44828db0a5eSKenneth D. Merry 				goto bailout;
44928db0a5eSKenneth D. Merry 			}
45028db0a5eSKenneth D. Merry 		}
45128db0a5eSKenneth D. Merry 	}
45228db0a5eSKenneth D. Merry 
45328db0a5eSKenneth D. Merry 	if (action == TIMESTAMP_REPORT) {
454492a2ef5SKenneth D. Merry 		error = report_timestamp(device, &ts, task_attr, retry_count,
45528db0a5eSKenneth D. Merry 		    timeout);
45628db0a5eSKenneth D. Merry 		if (error != 0) {
45728db0a5eSKenneth D. Merry 			goto bailout;
45828db0a5eSKenneth D. Merry 		} else if (strcmp(format_string, MIL) == 0) {
45928db0a5eSKenneth D. Merry 			printf("Timestamp in milliseconds: %ju\n",
46028db0a5eSKenneth D. Merry 			    (uintmax_t)ts);
46128db0a5eSKenneth D. Merry 		} else {
46228db0a5eSKenneth D. Merry 			char temp_timestamp_string[100];
46328db0a5eSKenneth D. Merry 			time_t time_var = ts / 1000;
46428db0a5eSKenneth D. Merry 			const struct tm *restrict cur_time;
46528db0a5eSKenneth D. Merry 
46628db0a5eSKenneth D. Merry 			setlocale(LC_TIME, "");
46728db0a5eSKenneth D. Merry 			if (do_utc != 0)
46828db0a5eSKenneth D. Merry 				cur_time = gmtime(&time_var);
46928db0a5eSKenneth D. Merry 			else
47028db0a5eSKenneth D. Merry 				cur_time = localtime(&time_var);
47128db0a5eSKenneth D. Merry 
47228db0a5eSKenneth D. Merry 			strftime(temp_timestamp_string,
47328db0a5eSKenneth D. Merry 			    sizeof(temp_timestamp_string), format_string,
47428db0a5eSKenneth D. Merry 			    cur_time);
47528db0a5eSKenneth D. Merry 			printf("Formatted timestamp: %s\n",
47628db0a5eSKenneth D. Merry 			    temp_timestamp_string);
47728db0a5eSKenneth D. Merry 		}
47828db0a5eSKenneth D. Merry 	} else if (action == TIMESTAMP_SET) {
47928db0a5eSKenneth D. Merry 		if (do_utc != 0) {
48028db0a5eSKenneth D. Merry 			format_string = strdup(UTC);
48128db0a5eSKenneth D. Merry 			if (format_string == NULL) {
48228db0a5eSKenneth D. Merry 				warn("Error allocating memory for format "
48328db0a5eSKenneth D. Merry 				    "string");
48428db0a5eSKenneth D. Merry 				error = 1;
48528db0a5eSKenneth D. Merry 				goto bailout;
48628db0a5eSKenneth D. Merry 			}
48728db0a5eSKenneth D. Merry 		}
48828db0a5eSKenneth D. Merry 
48928db0a5eSKenneth D. Merry 		error = set_timestamp(device, format_string, timestamp_string,
490492a2ef5SKenneth D. Merry 		    task_attr, retry_count, timeout);
49128db0a5eSKenneth D. Merry 	}
49228db0a5eSKenneth D. Merry 
49328db0a5eSKenneth D. Merry bailout:
49428db0a5eSKenneth D. Merry 	free(format_string);
49528db0a5eSKenneth D. Merry 	free(timestamp_string);
49628db0a5eSKenneth D. Merry 
49728db0a5eSKenneth D. Merry 	return (error);
49828db0a5eSKenneth D. Merry }
499