xref: /freebsd/sbin/camcontrol/timestamp.c (revision 28db0a5e740c9929ce560572efee40099428a246)
1*28db0a5eSKenneth D. Merry /*-
2*28db0a5eSKenneth D. Merry  * Copyright (c) 2016 Spectra Logic Corporation
3*28db0a5eSKenneth D. Merry  * All rights reserved.
4*28db0a5eSKenneth D. Merry  *
5*28db0a5eSKenneth D. Merry  * Redistribution and use in source and binary forms, with or without
6*28db0a5eSKenneth D. Merry  * modification, are permitted provided that the following conditions
7*28db0a5eSKenneth D. Merry  * are met:
8*28db0a5eSKenneth D. Merry  * 1. Redistributions of source code must retain the above copyright
9*28db0a5eSKenneth D. Merry  *    notice, this list of conditions, and the following disclaimer,
10*28db0a5eSKenneth D. Merry  *    without modification.
11*28db0a5eSKenneth D. Merry  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12*28db0a5eSKenneth D. Merry  *    substantially similar to the "NO WARRANTY" disclaimer below
13*28db0a5eSKenneth D. Merry  *    ("Disclaimer") and any redistribution must be conditioned upon
14*28db0a5eSKenneth D. Merry  *    including a substantially similar Disclaimer requirement for further
15*28db0a5eSKenneth D. Merry  *    binary redistribution.
16*28db0a5eSKenneth D. Merry  *
17*28db0a5eSKenneth D. Merry  * NO WARRANTY
18*28db0a5eSKenneth D. Merry  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19*28db0a5eSKenneth D. Merry  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20*28db0a5eSKenneth D. Merry  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
21*28db0a5eSKenneth D. Merry  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22*28db0a5eSKenneth D. Merry  * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23*28db0a5eSKenneth D. Merry  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24*28db0a5eSKenneth D. Merry  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25*28db0a5eSKenneth D. Merry  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26*28db0a5eSKenneth D. Merry  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
27*28db0a5eSKenneth D. Merry  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28*28db0a5eSKenneth D. Merry  * POSSIBILITY OF SUCH DAMAGES.
29*28db0a5eSKenneth D. Merry  *
30*28db0a5eSKenneth D. Merry  * Authors: Ken Merry           (Spectra Logic Corporation)
31*28db0a5eSKenneth D. Merry  *          Reid Linnemann      (Spectra Logic Corporation)
32*28db0a5eSKenneth D. Merry  *          Samuel Klopsch      (Spectra Logic Corporation)
33*28db0a5eSKenneth D. Merry  */
34*28db0a5eSKenneth D. Merry /*
35*28db0a5eSKenneth D. Merry  * SCSI tape drive timestamp support
36*28db0a5eSKenneth D. Merry  */
37*28db0a5eSKenneth D. Merry 
38*28db0a5eSKenneth D. Merry #include <sys/cdefs.h>
39*28db0a5eSKenneth D. Merry __FBSDID("$FreeBSD$");
40*28db0a5eSKenneth D. Merry 
41*28db0a5eSKenneth D. Merry #include <sys/types.h>
42*28db0a5eSKenneth D. Merry 
43*28db0a5eSKenneth D. Merry #include <stdio.h>
44*28db0a5eSKenneth D. Merry #include <stdlib.h>
45*28db0a5eSKenneth D. Merry #include <unistd.h>
46*28db0a5eSKenneth D. Merry #include <string.h>
47*28db0a5eSKenneth D. Merry #include <err.h>
48*28db0a5eSKenneth D. Merry #include <time.h>
49*28db0a5eSKenneth D. Merry #include <locale.h>
50*28db0a5eSKenneth D. Merry 
51*28db0a5eSKenneth D. Merry #include <cam/cam.h>
52*28db0a5eSKenneth D. Merry #include <cam/cam_debug.h>
53*28db0a5eSKenneth D. Merry #include <cam/cam_ccb.h>
54*28db0a5eSKenneth D. Merry #include <cam/scsi/scsi_all.h>
55*28db0a5eSKenneth D. Merry #include <cam/scsi/scsi_message.h>
56*28db0a5eSKenneth D. Merry #include <camlib.h>
57*28db0a5eSKenneth D. Merry #include "camcontrol.h"
58*28db0a5eSKenneth D. Merry 
59*28db0a5eSKenneth D. Merry #define TIMESTAMP_REPORT 0
60*28db0a5eSKenneth D. Merry #define TIMESTAMP_SET    1
61*28db0a5eSKenneth D. Merry #define MIL              "milliseconds"
62*28db0a5eSKenneth D. Merry #define UTC              "utc"
63*28db0a5eSKenneth D. Merry 
64*28db0a5eSKenneth D. Merry static int set_restore_flags(struct cam_device *device, uint8_t *flags,
65*28db0a5eSKenneth D. Merry 			     int set_flag, int retry_count, int timeout);
66*28db0a5eSKenneth D. Merry static int report_timestamp(struct cam_device *device, uint64_t *ts,
67*28db0a5eSKenneth D. Merry 			    int retry_count, int timeout);
68*28db0a5eSKenneth D. Merry static int set_timestamp(struct cam_device *device, char *format_string,
69*28db0a5eSKenneth D. Merry 			 char *timestamp_string,
70*28db0a5eSKenneth D. Merry 			 int retry_count, int timeout);
71*28db0a5eSKenneth D. Merry 
72*28db0a5eSKenneth D. Merry static int
73*28db0a5eSKenneth D. Merry set_restore_flags(struct cam_device *device, uint8_t *flags, int set_flag,
74*28db0a5eSKenneth D. Merry 		  int retry_count, int timeout)
75*28db0a5eSKenneth D. Merry {
76*28db0a5eSKenneth D. Merry 	unsigned long blk_desc_length, hdr_and_blk_length;
77*28db0a5eSKenneth D. Merry 	int error = 0;
78*28db0a5eSKenneth D. Merry 	struct scsi_control_ext_page *control_page = NULL;
79*28db0a5eSKenneth D. Merry 	struct scsi_mode_header_10 *mode_hdr = NULL;
80*28db0a5eSKenneth D. Merry 	struct scsi_mode_sense_10 *cdb = NULL;
81*28db0a5eSKenneth D. Merry 	union ccb *ccb = NULL;
82*28db0a5eSKenneth D. Merry 	unsigned long mode_buf_size = sizeof(struct scsi_mode_header_10) +
83*28db0a5eSKenneth D. Merry 	    sizeof(struct scsi_mode_blk_desc) +
84*28db0a5eSKenneth D. Merry 	    sizeof(struct scsi_control_ext_page);
85*28db0a5eSKenneth D. Merry 	uint8_t mode_buf[mode_buf_size];
86*28db0a5eSKenneth D. Merry 
87*28db0a5eSKenneth D. Merry 	ccb = cam_getccb(device);
88*28db0a5eSKenneth D. Merry 	if (ccb == NULL) {
89*28db0a5eSKenneth D. Merry 		warnx("%s: error allocating CCB", __func__);
90*28db0a5eSKenneth D. Merry 		error = 1;
91*28db0a5eSKenneth D. Merry 		goto bailout;
92*28db0a5eSKenneth D. Merry 	}
93*28db0a5eSKenneth D. Merry 	/*
94*28db0a5eSKenneth D. Merry 	 * Get the control extension subpage, we'll send it back modified to
95*28db0a5eSKenneth D. Merry 	 * enable SCSI control over the tape drive's timestamp
96*28db0a5eSKenneth D. Merry 	 */
97*28db0a5eSKenneth D. Merry 	scsi_mode_sense_len(&ccb->csio,
98*28db0a5eSKenneth D. Merry 	    /*retries*/ retry_count,
99*28db0a5eSKenneth D. Merry 	    /*cbfcnp*/ NULL,
100*28db0a5eSKenneth D. Merry 	    /*tag_action*/ MSG_SIMPLE_Q_TAG,
101*28db0a5eSKenneth D. Merry 	    /*dbd*/ 0,
102*28db0a5eSKenneth D. Merry 	    /*page_control*/ SMS_PAGE_CTRL_CURRENT,
103*28db0a5eSKenneth D. Merry 	    /*page*/ SCEP_PAGE_CODE,
104*28db0a5eSKenneth D. Merry 	    /*param_buf*/ &mode_buf[0],
105*28db0a5eSKenneth D. Merry 	    /*param_len*/ mode_buf_size,
106*28db0a5eSKenneth D. Merry 	    /*minimum_cmd_size*/ 10,
107*28db0a5eSKenneth D. Merry 	    /*sense_len*/ SSD_FULL_SIZE,
108*28db0a5eSKenneth D. Merry 	    /*timeout*/ timeout ? timeout : 5000);
109*28db0a5eSKenneth D. Merry 	/*
110*28db0a5eSKenneth D. Merry 	 * scsi_mode_sense_len does not have a subpage argument at the moment,
111*28db0a5eSKenneth D. Merry 	 * so we have to manually set the subpage code before calling
112*28db0a5eSKenneth D. Merry 	 * cam_send_ccb().
113*28db0a5eSKenneth D. Merry 	 */
114*28db0a5eSKenneth D. Merry 	cdb = (struct scsi_mode_sense_10 *)ccb->csio.cdb_io.cdb_bytes;
115*28db0a5eSKenneth D. Merry 	cdb->subpage = SCEP_SUBPAGE_CODE;
116*28db0a5eSKenneth D. Merry 
117*28db0a5eSKenneth D. Merry 	ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
118*28db0a5eSKenneth D. Merry 	if (retry_count > 0)
119*28db0a5eSKenneth D. Merry 		ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
120*28db0a5eSKenneth D. Merry 
121*28db0a5eSKenneth D. Merry 	error = cam_send_ccb(device, ccb);
122*28db0a5eSKenneth D. Merry 	if (error != 0) {
123*28db0a5eSKenneth D. Merry 		warn("error sending Mode Sense");
124*28db0a5eSKenneth D. Merry 		goto bailout;
125*28db0a5eSKenneth D. Merry 	}
126*28db0a5eSKenneth D. Merry 
127*28db0a5eSKenneth D. Merry 	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
128*28db0a5eSKenneth D. Merry 		cam_error_print(device, ccb, CAM_ESF_ALL,
129*28db0a5eSKenneth D. Merry 				CAM_EPF_ALL, stderr);
130*28db0a5eSKenneth D. Merry 		error = 1;
131*28db0a5eSKenneth D. Merry 		goto bailout;
132*28db0a5eSKenneth D. Merry 	}
133*28db0a5eSKenneth D. Merry 
134*28db0a5eSKenneth D. Merry 	mode_hdr = (struct scsi_mode_header_10 *)&mode_buf[0];
135*28db0a5eSKenneth D. Merry 	blk_desc_length = scsi_2btoul(mode_hdr->blk_desc_len);
136*28db0a5eSKenneth D. Merry 	hdr_and_blk_length = sizeof(struct scsi_mode_header_10)+blk_desc_length;
137*28db0a5eSKenneth D. Merry 	/*
138*28db0a5eSKenneth D. Merry 	 * Create the control page at the correct point in the mode_buf, it
139*28db0a5eSKenneth D. Merry 	 * starts after the header and the blk description.
140*28db0a5eSKenneth D. Merry 	 */
141*28db0a5eSKenneth D. Merry 	control_page = (struct scsi_control_ext_page *)&mode_buf
142*28db0a5eSKenneth D. Merry 	    [hdr_and_blk_length];
143*28db0a5eSKenneth D. Merry 	if (set_flag != 0) {
144*28db0a5eSKenneth D. Merry 		*flags = control_page->flags;
145*28db0a5eSKenneth D. Merry 		/*
146*28db0a5eSKenneth D. Merry 		 * Set the SCSIP flag to enable SCSI to change the
147*28db0a5eSKenneth D. Merry 		 * tape drive's timestamp.
148*28db0a5eSKenneth D. Merry 		 */
149*28db0a5eSKenneth D. Merry 		control_page->flags |= SCEP_SCSIP;
150*28db0a5eSKenneth D. Merry 	} else {
151*28db0a5eSKenneth D. Merry 		control_page->flags = *flags;
152*28db0a5eSKenneth D. Merry 	}
153*28db0a5eSKenneth D. Merry 
154*28db0a5eSKenneth D. Merry 	scsi_mode_select_len(&ccb->csio,
155*28db0a5eSKenneth D. Merry 	    /*retries*/ retry_count,
156*28db0a5eSKenneth D. Merry 	    /*cbfcnp*/ NULL,
157*28db0a5eSKenneth D. Merry 	    /*tag_action*/ MSG_SIMPLE_Q_TAG,
158*28db0a5eSKenneth D. Merry 	    /*scsi_page_fmt*/ 1,
159*28db0a5eSKenneth D. Merry 	    /*save_pages*/ 0,
160*28db0a5eSKenneth D. Merry 	    /*param_buf*/ &mode_buf[0],
161*28db0a5eSKenneth D. Merry 	    /*param_len*/ mode_buf_size,
162*28db0a5eSKenneth D. Merry 	    /*minimum_cmd_size*/ 10,
163*28db0a5eSKenneth D. Merry 	    /*sense_len*/ SSD_FULL_SIZE,
164*28db0a5eSKenneth D. Merry 	    /*timeout*/ timeout ? timeout : 5000);
165*28db0a5eSKenneth D. Merry 
166*28db0a5eSKenneth D. Merry 	ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
167*28db0a5eSKenneth D. Merry 	if (retry_count > 0)
168*28db0a5eSKenneth D. Merry 		ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
169*28db0a5eSKenneth D. Merry 
170*28db0a5eSKenneth D. Merry 	error = cam_send_ccb(device, ccb);
171*28db0a5eSKenneth D. Merry 	if (error != 0) {
172*28db0a5eSKenneth D. Merry 		warn("error sending Mode Select");
173*28db0a5eSKenneth D. Merry 		goto bailout;
174*28db0a5eSKenneth D. Merry 	}
175*28db0a5eSKenneth D. Merry 
176*28db0a5eSKenneth D. Merry 	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
177*28db0a5eSKenneth D. Merry 		cam_error_print(device, ccb, CAM_ESF_ALL,
178*28db0a5eSKenneth D. Merry 				CAM_EPF_ALL, stderr);
179*28db0a5eSKenneth D. Merry 		error = 1;
180*28db0a5eSKenneth D. Merry 		goto bailout;
181*28db0a5eSKenneth D. Merry 	}
182*28db0a5eSKenneth D. Merry 
183*28db0a5eSKenneth D. Merry bailout:
184*28db0a5eSKenneth D. Merry 	if (ccb != NULL)
185*28db0a5eSKenneth D. Merry 		cam_freeccb(ccb);
186*28db0a5eSKenneth D. Merry 
187*28db0a5eSKenneth D. Merry 	return error;
188*28db0a5eSKenneth D. Merry }
189*28db0a5eSKenneth D. Merry 
190*28db0a5eSKenneth D. Merry static int
191*28db0a5eSKenneth D. Merry report_timestamp(struct cam_device *device, uint64_t *ts,
192*28db0a5eSKenneth D. Merry 		 int retry_count, int timeout)
193*28db0a5eSKenneth D. Merry {
194*28db0a5eSKenneth D. Merry 	int error = 0;
195*28db0a5eSKenneth D. Merry 	struct scsi_report_timestamp_data *report_buf = malloc(
196*28db0a5eSKenneth D. Merry 		sizeof(struct scsi_report_timestamp_data));
197*28db0a5eSKenneth D. Merry 	uint8_t temp_timestamp[8];
198*28db0a5eSKenneth D. Merry 	uint32_t report_buf_size = sizeof(
199*28db0a5eSKenneth D. Merry 	    struct scsi_report_timestamp_data);
200*28db0a5eSKenneth D. Merry 	union ccb *ccb = NULL;
201*28db0a5eSKenneth D. Merry 
202*28db0a5eSKenneth D. Merry 	ccb = cam_getccb(device);
203*28db0a5eSKenneth D. Merry 	if (ccb == NULL) {
204*28db0a5eSKenneth D. Merry 		warnx("%s: error allocating CCB", __func__);
205*28db0a5eSKenneth D. Merry 		error = 1;
206*28db0a5eSKenneth D. Merry 		goto bailout;
207*28db0a5eSKenneth D. Merry 	}
208*28db0a5eSKenneth D. Merry 
209*28db0a5eSKenneth D. Merry 	scsi_report_timestamp(&ccb->csio,
210*28db0a5eSKenneth D. Merry 	    /*retries*/ retry_count,
211*28db0a5eSKenneth D. Merry 	    /*cbfcnp*/ NULL,
212*28db0a5eSKenneth D. Merry 	    /*tag_action*/ MSG_SIMPLE_Q_TAG,
213*28db0a5eSKenneth D. Merry 	    /*pdf*/ 0,
214*28db0a5eSKenneth D. Merry 	    /*buf*/ report_buf,
215*28db0a5eSKenneth D. Merry 	    /*buf_len*/ report_buf_size,
216*28db0a5eSKenneth D. Merry 	    /*sense_len*/ SSD_FULL_SIZE,
217*28db0a5eSKenneth D. Merry 	    /*timeout*/ timeout ? timeout : 5000);
218*28db0a5eSKenneth D. Merry 
219*28db0a5eSKenneth D. Merry 	ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
220*28db0a5eSKenneth D. Merry 	if (retry_count > 0)
221*28db0a5eSKenneth D. Merry 		ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
222*28db0a5eSKenneth D. Merry 
223*28db0a5eSKenneth D. Merry 	error = cam_send_ccb(device, ccb);
224*28db0a5eSKenneth D. Merry 	if (error != 0) {
225*28db0a5eSKenneth D. Merry 		warn("error sending Report Timestamp");
226*28db0a5eSKenneth D. Merry 		goto bailout;
227*28db0a5eSKenneth D. Merry 	}
228*28db0a5eSKenneth D. Merry 	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
229*28db0a5eSKenneth D. Merry 		cam_error_print(device, ccb, CAM_ESF_ALL,
230*28db0a5eSKenneth D. Merry 				CAM_EPF_ALL, stderr);
231*28db0a5eSKenneth D. Merry 		error = 1;
232*28db0a5eSKenneth D. Merry 		goto bailout;
233*28db0a5eSKenneth D. Merry 	}
234*28db0a5eSKenneth D. Merry 
235*28db0a5eSKenneth D. Merry 	bzero(temp_timestamp, sizeof(temp_timestamp));
236*28db0a5eSKenneth D. Merry 	memcpy(&temp_timestamp[2], &report_buf->timestamp, 6);
237*28db0a5eSKenneth D. Merry 
238*28db0a5eSKenneth D. Merry 	*ts = scsi_8btou64(temp_timestamp);
239*28db0a5eSKenneth D. Merry 
240*28db0a5eSKenneth D. Merry bailout:
241*28db0a5eSKenneth D. Merry 	if (ccb != NULL)
242*28db0a5eSKenneth D. Merry 		cam_freeccb(ccb);
243*28db0a5eSKenneth D. Merry 
244*28db0a5eSKenneth D. Merry 	return error;
245*28db0a5eSKenneth D. Merry }
246*28db0a5eSKenneth D. Merry 
247*28db0a5eSKenneth D. Merry static int
248*28db0a5eSKenneth D. Merry set_timestamp(struct cam_device *device, char *format_string,
249*28db0a5eSKenneth D. Merry 	      char *timestamp_string, int retry_count,
250*28db0a5eSKenneth D. Merry 	      int timeout)
251*28db0a5eSKenneth D. Merry {
252*28db0a5eSKenneth D. Merry 	struct scsi_set_timestamp_parameters ts_p;
253*28db0a5eSKenneth D. Merry 	time_t time_value;
254*28db0a5eSKenneth D. Merry 	struct tm time_struct;
255*28db0a5eSKenneth D. Merry 	uint8_t flags = 0;
256*28db0a5eSKenneth D. Merry 	int error = 0;
257*28db0a5eSKenneth D. Merry 	uint64_t ts = 0;
258*28db0a5eSKenneth D. Merry 	union ccb *ccb = NULL;
259*28db0a5eSKenneth D. Merry 	int do_restore_flags = 0;
260*28db0a5eSKenneth D. Merry 
261*28db0a5eSKenneth D. Merry 	error = set_restore_flags(device, &flags, /*set_flag*/ 1, retry_count,
262*28db0a5eSKenneth D. Merry 				  timeout);
263*28db0a5eSKenneth D. Merry 	if (error != 0)
264*28db0a5eSKenneth D. Merry 		goto bailout;
265*28db0a5eSKenneth D. Merry 
266*28db0a5eSKenneth D. Merry 	do_restore_flags = 1;
267*28db0a5eSKenneth D. Merry 
268*28db0a5eSKenneth D. Merry 	ccb = cam_getccb(device);
269*28db0a5eSKenneth D. Merry 	if (ccb == NULL) {
270*28db0a5eSKenneth D. Merry 		warnx("%s: error allocating CCB", __func__);
271*28db0a5eSKenneth D. Merry 		error = 1;
272*28db0a5eSKenneth D. Merry 		goto bailout;
273*28db0a5eSKenneth D. Merry 	}
274*28db0a5eSKenneth D. Merry 
275*28db0a5eSKenneth D. Merry 	if (strcmp(format_string, UTC) == 0) {
276*28db0a5eSKenneth D. Merry 		time(&time_value);
277*28db0a5eSKenneth D. Merry 		ts = (uint64_t) time_value;
278*28db0a5eSKenneth D. Merry 	} else {
279*28db0a5eSKenneth D. Merry 		bzero(&time_struct, sizeof(struct tm));
280*28db0a5eSKenneth D. Merry 		strptime(timestamp_string, format_string, &time_struct);
281*28db0a5eSKenneth D. Merry 		time_value = mktime(&time_struct);
282*28db0a5eSKenneth D. Merry 		ts = (uint64_t) time_value;
283*28db0a5eSKenneth D. Merry 	}
284*28db0a5eSKenneth D. Merry 	/* Convert time from seconds to milliseconds */
285*28db0a5eSKenneth D. Merry 	ts *= 1000;
286*28db0a5eSKenneth D. Merry 	scsi_create_timestamp(ts_p.timestamp, ts);
287*28db0a5eSKenneth D. Merry 
288*28db0a5eSKenneth D. Merry 	scsi_set_timestamp(&ccb->csio,
289*28db0a5eSKenneth D. Merry 	    /*retries*/ retry_count,
290*28db0a5eSKenneth D. Merry 	    /*cbfcnp*/ NULL,
291*28db0a5eSKenneth D. Merry 	    /*tag_action*/ MSG_SIMPLE_Q_TAG,
292*28db0a5eSKenneth D. Merry 	    /*buf*/ &ts_p,
293*28db0a5eSKenneth D. Merry 	    /*buf_len*/ sizeof(ts_p),
294*28db0a5eSKenneth D. Merry 	    /*sense_len*/ SSD_FULL_SIZE,
295*28db0a5eSKenneth D. Merry 	    /*timeout*/ timeout ? timeout : 5000);
296*28db0a5eSKenneth D. Merry 
297*28db0a5eSKenneth D. Merry 	ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
298*28db0a5eSKenneth D. Merry 	if (retry_count > 0)
299*28db0a5eSKenneth D. Merry 		ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
300*28db0a5eSKenneth D. Merry 
301*28db0a5eSKenneth D. Merry 	error = cam_send_ccb(device, ccb);
302*28db0a5eSKenneth D. Merry 	if (error != 0) {
303*28db0a5eSKenneth D. Merry 		warn("error sending Set Timestamp");
304*28db0a5eSKenneth D. Merry 		goto bailout;
305*28db0a5eSKenneth D. Merry 	}
306*28db0a5eSKenneth D. Merry 
307*28db0a5eSKenneth D. Merry 	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
308*28db0a5eSKenneth D. Merry 		cam_error_print(device, ccb, CAM_ESF_ALL,
309*28db0a5eSKenneth D. Merry 				CAM_EPF_ALL, stderr);
310*28db0a5eSKenneth D. Merry 		error = 1;
311*28db0a5eSKenneth D. Merry 		goto bailout;
312*28db0a5eSKenneth D. Merry 	}
313*28db0a5eSKenneth D. Merry 
314*28db0a5eSKenneth D. Merry 	printf("Timestamp set to %ju\n", (uintmax_t)ts);
315*28db0a5eSKenneth D. Merry 
316*28db0a5eSKenneth D. Merry bailout:
317*28db0a5eSKenneth D. Merry 	if (do_restore_flags != 0)
318*28db0a5eSKenneth D. Merry 		error = set_restore_flags(device, &flags, /*set_flag*/ 0,
319*28db0a5eSKenneth D. Merry 					  retry_count, timeout);
320*28db0a5eSKenneth D. Merry 	if (ccb != NULL)
321*28db0a5eSKenneth D. Merry 		cam_freeccb(ccb);
322*28db0a5eSKenneth D. Merry 
323*28db0a5eSKenneth D. Merry 	return error;
324*28db0a5eSKenneth D. Merry }
325*28db0a5eSKenneth D. Merry 
326*28db0a5eSKenneth D. Merry int
327*28db0a5eSKenneth D. Merry timestamp(struct cam_device *device, int argc, char **argv, char *combinedopt,
328*28db0a5eSKenneth D. Merry 	  int retry_count, int timeout, int verbosemode __unused)
329*28db0a5eSKenneth D. Merry {
330*28db0a5eSKenneth D. Merry 	int c;
331*28db0a5eSKenneth D. Merry 	uint64_t ts;
332*28db0a5eSKenneth D. Merry 	char *format_string = NULL;
333*28db0a5eSKenneth D. Merry 	char *timestamp_string = NULL;
334*28db0a5eSKenneth D. Merry 	int action = -1;
335*28db0a5eSKenneth D. Merry 	int error = 0;
336*28db0a5eSKenneth D. Merry 	int single_arg = 0;
337*28db0a5eSKenneth D. Merry 	int do_utc = 0;
338*28db0a5eSKenneth D. Merry 
339*28db0a5eSKenneth D. Merry 	while ((c = getopt(argc, argv, combinedopt)) != -1) {
340*28db0a5eSKenneth D. Merry 		switch (c) {
341*28db0a5eSKenneth D. Merry 		case 'r': {
342*28db0a5eSKenneth D. Merry 			if (action != -1) {
343*28db0a5eSKenneth D. Merry 				warnx("Use only one -r or only one -s");
344*28db0a5eSKenneth D. Merry 				error =1;
345*28db0a5eSKenneth D. Merry 				goto bailout;
346*28db0a5eSKenneth D. Merry 			}
347*28db0a5eSKenneth D. Merry 			action = TIMESTAMP_REPORT;
348*28db0a5eSKenneth D. Merry 			break;
349*28db0a5eSKenneth D. Merry 		}
350*28db0a5eSKenneth D. Merry 		case 's': {
351*28db0a5eSKenneth D. Merry 			if (action != -1) {
352*28db0a5eSKenneth D. Merry 				warnx("Use only one -r or only one -s");
353*28db0a5eSKenneth D. Merry 				error = 1;
354*28db0a5eSKenneth D. Merry 				goto bailout;
355*28db0a5eSKenneth D. Merry 			}
356*28db0a5eSKenneth D. Merry 			action = TIMESTAMP_SET;
357*28db0a5eSKenneth D. Merry 			break;
358*28db0a5eSKenneth D. Merry 		}
359*28db0a5eSKenneth D. Merry 		case 'f': {
360*28db0a5eSKenneth D. Merry 			single_arg++;
361*28db0a5eSKenneth D. Merry 			format_string = strdup(optarg);
362*28db0a5eSKenneth D. Merry 			if (format_string == NULL) {
363*28db0a5eSKenneth D. Merry 				warn("Error allocating memory for format "
364*28db0a5eSKenneth D. Merry 				   "argument");
365*28db0a5eSKenneth D. Merry 				error = 1;
366*28db0a5eSKenneth D. Merry 				goto bailout;
367*28db0a5eSKenneth D. Merry 			}
368*28db0a5eSKenneth D. Merry 			break;
369*28db0a5eSKenneth D. Merry 		}
370*28db0a5eSKenneth D. Merry 		case 'm': {
371*28db0a5eSKenneth D. Merry 			single_arg++;
372*28db0a5eSKenneth D. Merry 			format_string = strdup(MIL);
373*28db0a5eSKenneth D. Merry 			if (format_string == NULL) {
374*28db0a5eSKenneth D. Merry 				warn("Error allocating memory");
375*28db0a5eSKenneth D. Merry 				error = 1;
376*28db0a5eSKenneth D. Merry 				goto bailout;
377*28db0a5eSKenneth D. Merry 			}
378*28db0a5eSKenneth D. Merry 			break;
379*28db0a5eSKenneth D. Merry 		}
380*28db0a5eSKenneth D. Merry 		case 'U': {
381*28db0a5eSKenneth D. Merry 			do_utc = 1;
382*28db0a5eSKenneth D. Merry 			break;
383*28db0a5eSKenneth D. Merry 		}
384*28db0a5eSKenneth D. Merry 		case 'T':
385*28db0a5eSKenneth D. Merry 			timestamp_string = strdup(optarg);
386*28db0a5eSKenneth D. Merry 			if (timestamp_string == NULL) {
387*28db0a5eSKenneth D. Merry 				warn("Error allocating memory for format "
388*28db0a5eSKenneth D. Merry 				   "argument");
389*28db0a5eSKenneth D. Merry 				error = 1;
390*28db0a5eSKenneth D. Merry 				goto bailout;
391*28db0a5eSKenneth D. Merry 			}
392*28db0a5eSKenneth D. Merry 			break;
393*28db0a5eSKenneth D. Merry 		default:
394*28db0a5eSKenneth D. Merry 			break;
395*28db0a5eSKenneth D. Merry 		}
396*28db0a5eSKenneth D. Merry 	}
397*28db0a5eSKenneth D. Merry 
398*28db0a5eSKenneth D. Merry 	if (action == -1) {
399*28db0a5eSKenneth D. Merry 		warnx("Must specify an action, either -r or -s");
400*28db0a5eSKenneth D. Merry 		error = 1;
401*28db0a5eSKenneth D. Merry 		goto bailout;
402*28db0a5eSKenneth D. Merry 	}
403*28db0a5eSKenneth D. Merry 
404*28db0a5eSKenneth D. Merry 	if (single_arg > 1) {
405*28db0a5eSKenneth D. Merry 		warnx("Select only one: %s",
406*28db0a5eSKenneth D. Merry 		    (action == TIMESTAMP_REPORT) ?
407*28db0a5eSKenneth D. Merry 		    "-f format or -m for the -r flag" :
408*28db0a5eSKenneth D. Merry 		    "-f format -T time or -U for the -s flag");
409*28db0a5eSKenneth D. Merry 		error = 1;
410*28db0a5eSKenneth D. Merry 		goto bailout;
411*28db0a5eSKenneth D. Merry 	}
412*28db0a5eSKenneth D. Merry 
413*28db0a5eSKenneth D. Merry 	if (action == TIMESTAMP_SET) {
414*28db0a5eSKenneth D. Merry 		if ((format_string == NULL)
415*28db0a5eSKenneth D. Merry 		 && (do_utc == 0)) {
416*28db0a5eSKenneth D. Merry 			warnx("Must specify either -f format or -U for "
417*28db0a5eSKenneth D. Merry 			    "setting the timestamp");
418*28db0a5eSKenneth D. Merry 			error = 1;
419*28db0a5eSKenneth D. Merry 		} else if ((format_string != NULL)
420*28db0a5eSKenneth D. Merry 			&& (do_utc != 0)) {
421*28db0a5eSKenneth D. Merry 			warnx("Must specify only one of -f or -U to set "
422*28db0a5eSKenneth D. Merry 			    "the timestamp");
423*28db0a5eSKenneth D. Merry 			error = 1;
424*28db0a5eSKenneth D. Merry 		} else if ((format_string != NULL)
425*28db0a5eSKenneth D. Merry 			&& (strcmp(format_string, MIL) == 0)) {
426*28db0a5eSKenneth D. Merry 			warnx("-m is not allowed for setting the "
427*28db0a5eSKenneth D. Merry 			    "timestamp");
428*28db0a5eSKenneth D. Merry 			error = 1;
429*28db0a5eSKenneth D. Merry 		} else if ((do_utc == 0)
430*28db0a5eSKenneth D. Merry 			&& (timestamp_string == NULL)) {
431*28db0a5eSKenneth D. Merry 			warnx("Must specify the time (-T) to set as the "
432*28db0a5eSKenneth D. Merry 			    "timestamp");
433*28db0a5eSKenneth D. Merry 			error = 1;
434*28db0a5eSKenneth D. Merry 		}
435*28db0a5eSKenneth D. Merry 		if (error != 0)
436*28db0a5eSKenneth D. Merry 			goto bailout;
437*28db0a5eSKenneth D. Merry 	} else if (action == TIMESTAMP_REPORT) {
438*28db0a5eSKenneth D. Merry 		if (format_string == NULL) {
439*28db0a5eSKenneth D. Merry 			format_string = strdup("%c %Z");
440*28db0a5eSKenneth D. Merry 			if (format_string == NULL) {
441*28db0a5eSKenneth D. Merry 				warn("Error allocating memory for format "
442*28db0a5eSKenneth D. Merry 				    "string");
443*28db0a5eSKenneth D. Merry 				error = 1;
444*28db0a5eSKenneth D. Merry 				goto bailout;
445*28db0a5eSKenneth D. Merry 			}
446*28db0a5eSKenneth D. Merry 		}
447*28db0a5eSKenneth D. Merry 	}
448*28db0a5eSKenneth D. Merry 
449*28db0a5eSKenneth D. Merry 	if (action == TIMESTAMP_REPORT) {
450*28db0a5eSKenneth D. Merry 		error = report_timestamp(device, &ts, retry_count,
451*28db0a5eSKenneth D. Merry 		    timeout);
452*28db0a5eSKenneth D. Merry 		if (error != 0) {
453*28db0a5eSKenneth D. Merry 			goto bailout;
454*28db0a5eSKenneth D. Merry 		} else if (strcmp(format_string, MIL) == 0) {
455*28db0a5eSKenneth D. Merry 			printf("Timestamp in milliseconds: %ju\n",
456*28db0a5eSKenneth D. Merry 			    (uintmax_t)ts);
457*28db0a5eSKenneth D. Merry 		} else {
458*28db0a5eSKenneth D. Merry 			char temp_timestamp_string[100];
459*28db0a5eSKenneth D. Merry 			time_t time_var = ts / 1000;
460*28db0a5eSKenneth D. Merry 			const struct tm *restrict cur_time;
461*28db0a5eSKenneth D. Merry 
462*28db0a5eSKenneth D. Merry 			setlocale(LC_TIME, "");
463*28db0a5eSKenneth D. Merry 			if (do_utc != 0)
464*28db0a5eSKenneth D. Merry 				cur_time = gmtime(&time_var);
465*28db0a5eSKenneth D. Merry 			else
466*28db0a5eSKenneth D. Merry 				cur_time = localtime(&time_var);
467*28db0a5eSKenneth D. Merry 
468*28db0a5eSKenneth D. Merry 			strftime(temp_timestamp_string,
469*28db0a5eSKenneth D. Merry 			    sizeof(temp_timestamp_string), format_string,
470*28db0a5eSKenneth D. Merry 			    cur_time);
471*28db0a5eSKenneth D. Merry 			printf("Formatted timestamp: %s\n",
472*28db0a5eSKenneth D. Merry 			    temp_timestamp_string);
473*28db0a5eSKenneth D. Merry 		}
474*28db0a5eSKenneth D. Merry 	} else if (action == TIMESTAMP_SET) {
475*28db0a5eSKenneth D. Merry 		if (do_utc != 0) {
476*28db0a5eSKenneth D. Merry 			format_string = strdup(UTC);
477*28db0a5eSKenneth D. Merry 			if (format_string == NULL) {
478*28db0a5eSKenneth D. Merry 				warn("Error allocating memory for format "
479*28db0a5eSKenneth D. Merry 				    "string");
480*28db0a5eSKenneth D. Merry 				error = 1;
481*28db0a5eSKenneth D. Merry 				goto bailout;
482*28db0a5eSKenneth D. Merry 			}
483*28db0a5eSKenneth D. Merry 		}
484*28db0a5eSKenneth D. Merry 
485*28db0a5eSKenneth D. Merry 		error = set_timestamp(device, format_string, timestamp_string,
486*28db0a5eSKenneth D. Merry 		    retry_count, timeout);
487*28db0a5eSKenneth D. Merry 	}
488*28db0a5eSKenneth D. Merry 
489*28db0a5eSKenneth D. Merry bailout:
490*28db0a5eSKenneth D. Merry 	free(format_string);
491*28db0a5eSKenneth D. Merry 	free(timestamp_string);
492*28db0a5eSKenneth D. Merry 
493*28db0a5eSKenneth D. Merry 	return (error);
494*28db0a5eSKenneth D. Merry }
495