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