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/cdefs.h> 3928db0a5eSKenneth D. Merry __FBSDID("$FreeBSD$"); 4028db0a5eSKenneth D. Merry 4128db0a5eSKenneth D. Merry #include <sys/types.h> 4228db0a5eSKenneth D. Merry 4377d9b0efSAlan Somers #include <assert.h> 4428db0a5eSKenneth D. Merry #include <stdio.h> 4528db0a5eSKenneth D. Merry #include <stdlib.h> 4628db0a5eSKenneth D. Merry #include <unistd.h> 4728db0a5eSKenneth D. Merry #include <string.h> 4828db0a5eSKenneth D. Merry #include <err.h> 4928db0a5eSKenneth D. Merry #include <time.h> 5028db0a5eSKenneth D. Merry #include <locale.h> 5128db0a5eSKenneth D. Merry 5228db0a5eSKenneth D. Merry #include <cam/cam.h> 5328db0a5eSKenneth D. Merry #include <cam/cam_debug.h> 5428db0a5eSKenneth D. Merry #include <cam/cam_ccb.h> 5528db0a5eSKenneth D. Merry #include <cam/scsi/scsi_all.h> 5628db0a5eSKenneth D. Merry #include <cam/scsi/scsi_message.h> 5728db0a5eSKenneth D. Merry #include <camlib.h> 5828db0a5eSKenneth D. Merry #include "camcontrol.h" 5928db0a5eSKenneth D. Merry 6028db0a5eSKenneth D. Merry #define TIMESTAMP_REPORT 0 6128db0a5eSKenneth D. Merry #define TIMESTAMP_SET 1 6228db0a5eSKenneth D. Merry #define MIL "milliseconds" 6328db0a5eSKenneth D. Merry #define UTC "utc" 6428db0a5eSKenneth D. Merry 6528db0a5eSKenneth D. Merry static int set_restore_flags(struct cam_device *device, uint8_t *flags, 66492a2ef5SKenneth D. Merry int set_flag, int task_attr, int retry_count, 67492a2ef5SKenneth D. Merry int timeout); 6828db0a5eSKenneth D. Merry static int report_timestamp(struct cam_device *device, uint64_t *ts, 69492a2ef5SKenneth D. Merry int task_attr, int retry_count, int timeout); 7028db0a5eSKenneth D. Merry static int set_timestamp(struct cam_device *device, char *format_string, 71492a2ef5SKenneth D. Merry char *timestamp_string, int task_attr, int retry_count, 72492a2ef5SKenneth D. Merry int timeout); 7328db0a5eSKenneth D. Merry 7428db0a5eSKenneth D. Merry static int 7528db0a5eSKenneth D. Merry set_restore_flags(struct cam_device *device, uint8_t *flags, int set_flag, 76492a2ef5SKenneth D. Merry int task_attr, int retry_count, int timeout) 7728db0a5eSKenneth D. Merry { 7828db0a5eSKenneth D. Merry unsigned long blk_desc_length, hdr_and_blk_length; 7928db0a5eSKenneth D. Merry int error = 0; 8028db0a5eSKenneth D. Merry struct scsi_control_ext_page *control_page = NULL; 8128db0a5eSKenneth D. Merry struct scsi_mode_header_10 *mode_hdr = NULL; 8228db0a5eSKenneth D. Merry struct scsi_mode_sense_10 *cdb = NULL; 8328db0a5eSKenneth D. Merry union ccb *ccb = NULL; 8428db0a5eSKenneth D. Merry unsigned long mode_buf_size = sizeof(struct scsi_mode_header_10) + 8528db0a5eSKenneth D. Merry sizeof(struct scsi_mode_blk_desc) + 8628db0a5eSKenneth D. Merry sizeof(struct scsi_control_ext_page); 8728db0a5eSKenneth D. Merry uint8_t mode_buf[mode_buf_size]; 8828db0a5eSKenneth D. Merry 8928db0a5eSKenneth D. Merry ccb = cam_getccb(device); 9028db0a5eSKenneth D. Merry if (ccb == NULL) { 9128db0a5eSKenneth D. Merry warnx("%s: error allocating CCB", __func__); 9228db0a5eSKenneth D. Merry error = 1; 9328db0a5eSKenneth D. Merry goto bailout; 9428db0a5eSKenneth D. Merry } 9528db0a5eSKenneth D. Merry /* 9628db0a5eSKenneth D. Merry * Get the control extension subpage, we'll send it back modified to 9728db0a5eSKenneth D. Merry * enable SCSI control over the tape drive's timestamp 9828db0a5eSKenneth D. Merry */ 9928db0a5eSKenneth D. Merry scsi_mode_sense_len(&ccb->csio, 10028db0a5eSKenneth D. Merry /*retries*/ retry_count, 10128db0a5eSKenneth D. Merry /*cbfcnp*/ NULL, 102492a2ef5SKenneth D. Merry /*tag_action*/ task_attr, 10328db0a5eSKenneth D. Merry /*dbd*/ 0, 10428db0a5eSKenneth D. Merry /*page_control*/ SMS_PAGE_CTRL_CURRENT, 10528db0a5eSKenneth D. Merry /*page*/ SCEP_PAGE_CODE, 10628db0a5eSKenneth D. Merry /*param_buf*/ &mode_buf[0], 10728db0a5eSKenneth D. Merry /*param_len*/ mode_buf_size, 10828db0a5eSKenneth D. Merry /*minimum_cmd_size*/ 10, 10928db0a5eSKenneth D. Merry /*sense_len*/ SSD_FULL_SIZE, 11028db0a5eSKenneth D. Merry /*timeout*/ timeout ? timeout : 5000); 11128db0a5eSKenneth D. Merry /* 11228db0a5eSKenneth D. Merry * scsi_mode_sense_len does not have a subpage argument at the moment, 11328db0a5eSKenneth D. Merry * so we have to manually set the subpage code before calling 11428db0a5eSKenneth D. Merry * cam_send_ccb(). 11528db0a5eSKenneth D. Merry */ 11628db0a5eSKenneth D. Merry cdb = (struct scsi_mode_sense_10 *)ccb->csio.cdb_io.cdb_bytes; 11728db0a5eSKenneth D. Merry cdb->subpage = SCEP_SUBPAGE_CODE; 11828db0a5eSKenneth D. Merry 11928db0a5eSKenneth D. Merry ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; 12028db0a5eSKenneth D. Merry if (retry_count > 0) 12128db0a5eSKenneth D. Merry ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER; 12228db0a5eSKenneth D. Merry 12328db0a5eSKenneth D. Merry error = cam_send_ccb(device, ccb); 12428db0a5eSKenneth D. Merry if (error != 0) { 12528db0a5eSKenneth D. Merry warn("error sending Mode Sense"); 12628db0a5eSKenneth D. Merry goto bailout; 12728db0a5eSKenneth D. Merry } 12828db0a5eSKenneth D. Merry 12928db0a5eSKenneth D. Merry if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { 13028db0a5eSKenneth D. Merry cam_error_print(device, ccb, CAM_ESF_ALL, 13128db0a5eSKenneth D. Merry CAM_EPF_ALL, stderr); 13228db0a5eSKenneth D. Merry error = 1; 13328db0a5eSKenneth D. Merry goto bailout; 13428db0a5eSKenneth D. Merry } 13528db0a5eSKenneth D. Merry 13628db0a5eSKenneth D. Merry mode_hdr = (struct scsi_mode_header_10 *)&mode_buf[0]; 13728db0a5eSKenneth D. Merry blk_desc_length = scsi_2btoul(mode_hdr->blk_desc_len); 13828db0a5eSKenneth D. Merry hdr_and_blk_length = sizeof(struct scsi_mode_header_10)+blk_desc_length; 13928db0a5eSKenneth D. Merry /* 14028db0a5eSKenneth D. Merry * Create the control page at the correct point in the mode_buf, it 14128db0a5eSKenneth D. Merry * starts after the header and the blk description. 14228db0a5eSKenneth D. Merry */ 14377d9b0efSAlan Somers assert(hdr_and_blk_length <= 14477d9b0efSAlan Somers sizeof(mode_buf) - sizeof(struct scsi_control_ext_page)); 14528db0a5eSKenneth D. Merry control_page = (struct scsi_control_ext_page *)&mode_buf 14628db0a5eSKenneth D. Merry [hdr_and_blk_length]; 14728db0a5eSKenneth D. Merry if (set_flag != 0) { 14828db0a5eSKenneth D. Merry *flags = control_page->flags; 14928db0a5eSKenneth D. Merry /* 15028db0a5eSKenneth D. Merry * Set the SCSIP flag to enable SCSI to change the 15128db0a5eSKenneth D. Merry * tape drive's timestamp. 15228db0a5eSKenneth D. Merry */ 15328db0a5eSKenneth D. Merry control_page->flags |= SCEP_SCSIP; 15428db0a5eSKenneth D. Merry } else { 15528db0a5eSKenneth D. Merry control_page->flags = *flags; 15628db0a5eSKenneth D. Merry } 15728db0a5eSKenneth D. Merry 15828db0a5eSKenneth D. Merry scsi_mode_select_len(&ccb->csio, 15928db0a5eSKenneth D. Merry /*retries*/ retry_count, 16028db0a5eSKenneth D. Merry /*cbfcnp*/ NULL, 161492a2ef5SKenneth D. Merry /*tag_action*/ task_attr, 16228db0a5eSKenneth D. Merry /*scsi_page_fmt*/ 1, 16328db0a5eSKenneth D. Merry /*save_pages*/ 0, 16428db0a5eSKenneth D. Merry /*param_buf*/ &mode_buf[0], 16528db0a5eSKenneth D. Merry /*param_len*/ mode_buf_size, 16628db0a5eSKenneth D. Merry /*minimum_cmd_size*/ 10, 16728db0a5eSKenneth D. Merry /*sense_len*/ SSD_FULL_SIZE, 16828db0a5eSKenneth D. Merry /*timeout*/ timeout ? timeout : 5000); 16928db0a5eSKenneth D. Merry 17028db0a5eSKenneth D. Merry ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; 17128db0a5eSKenneth D. Merry if (retry_count > 0) 17228db0a5eSKenneth D. Merry ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER; 17328db0a5eSKenneth D. Merry 17428db0a5eSKenneth D. Merry error = cam_send_ccb(device, ccb); 17528db0a5eSKenneth D. Merry if (error != 0) { 17628db0a5eSKenneth D. Merry warn("error sending Mode Select"); 17728db0a5eSKenneth D. Merry goto bailout; 17828db0a5eSKenneth D. Merry } 17928db0a5eSKenneth D. Merry 18028db0a5eSKenneth D. Merry if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { 18128db0a5eSKenneth D. Merry cam_error_print(device, ccb, CAM_ESF_ALL, 18228db0a5eSKenneth D. Merry CAM_EPF_ALL, stderr); 18328db0a5eSKenneth D. Merry error = 1; 18428db0a5eSKenneth D. Merry goto bailout; 18528db0a5eSKenneth D. Merry } 18628db0a5eSKenneth D. Merry 18728db0a5eSKenneth D. Merry bailout: 18828db0a5eSKenneth D. Merry if (ccb != NULL) 18928db0a5eSKenneth D. Merry cam_freeccb(ccb); 19028db0a5eSKenneth D. Merry 19128db0a5eSKenneth D. Merry return error; 19228db0a5eSKenneth D. Merry } 19328db0a5eSKenneth D. Merry 19428db0a5eSKenneth D. Merry static int 195492a2ef5SKenneth D. Merry report_timestamp(struct cam_device *device, uint64_t *ts, int task_attr, 19628db0a5eSKenneth D. Merry int retry_count, int timeout) 19728db0a5eSKenneth D. Merry { 19828db0a5eSKenneth D. Merry int error = 0; 19928db0a5eSKenneth D. Merry struct scsi_report_timestamp_data *report_buf = malloc( 20028db0a5eSKenneth D. Merry sizeof(struct scsi_report_timestamp_data)); 20128db0a5eSKenneth D. Merry uint8_t temp_timestamp[8]; 20228db0a5eSKenneth D. Merry uint32_t report_buf_size = sizeof( 20328db0a5eSKenneth D. Merry struct scsi_report_timestamp_data); 20428db0a5eSKenneth D. Merry union ccb *ccb = NULL; 20528db0a5eSKenneth D. Merry 20628db0a5eSKenneth D. Merry ccb = cam_getccb(device); 20728db0a5eSKenneth D. Merry if (ccb == NULL) { 20828db0a5eSKenneth D. Merry warnx("%s: error allocating CCB", __func__); 20928db0a5eSKenneth D. Merry error = 1; 21028db0a5eSKenneth D. Merry goto bailout; 21128db0a5eSKenneth D. Merry } 21228db0a5eSKenneth D. Merry 21328db0a5eSKenneth D. Merry scsi_report_timestamp(&ccb->csio, 21428db0a5eSKenneth D. Merry /*retries*/ retry_count, 21528db0a5eSKenneth D. Merry /*cbfcnp*/ NULL, 216492a2ef5SKenneth D. Merry /*tag_action*/ task_attr, 21728db0a5eSKenneth D. Merry /*pdf*/ 0, 21828db0a5eSKenneth D. Merry /*buf*/ report_buf, 21928db0a5eSKenneth D. Merry /*buf_len*/ report_buf_size, 22028db0a5eSKenneth D. Merry /*sense_len*/ SSD_FULL_SIZE, 22128db0a5eSKenneth D. Merry /*timeout*/ timeout ? timeout : 5000); 22228db0a5eSKenneth D. Merry 22328db0a5eSKenneth D. Merry ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; 22428db0a5eSKenneth D. Merry if (retry_count > 0) 22528db0a5eSKenneth D. Merry ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER; 22628db0a5eSKenneth D. Merry 22728db0a5eSKenneth D. Merry error = cam_send_ccb(device, ccb); 22828db0a5eSKenneth D. Merry if (error != 0) { 22928db0a5eSKenneth D. Merry warn("error sending Report Timestamp"); 23028db0a5eSKenneth D. Merry goto bailout; 23128db0a5eSKenneth D. Merry } 23228db0a5eSKenneth D. Merry if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { 23328db0a5eSKenneth D. Merry cam_error_print(device, ccb, CAM_ESF_ALL, 23428db0a5eSKenneth D. Merry CAM_EPF_ALL, stderr); 23528db0a5eSKenneth D. Merry error = 1; 23628db0a5eSKenneth D. Merry goto bailout; 23728db0a5eSKenneth D. Merry } 23828db0a5eSKenneth D. Merry 23928db0a5eSKenneth D. Merry bzero(temp_timestamp, sizeof(temp_timestamp)); 24028db0a5eSKenneth D. Merry memcpy(&temp_timestamp[2], &report_buf->timestamp, 6); 24128db0a5eSKenneth D. Merry 24228db0a5eSKenneth D. Merry *ts = scsi_8btou64(temp_timestamp); 24328db0a5eSKenneth D. Merry 24428db0a5eSKenneth D. Merry bailout: 24528db0a5eSKenneth D. Merry if (ccb != NULL) 24628db0a5eSKenneth D. Merry cam_freeccb(ccb); 24777d9b0efSAlan Somers free(report_buf); 24828db0a5eSKenneth D. Merry 24928db0a5eSKenneth D. Merry return error; 25028db0a5eSKenneth D. Merry } 25128db0a5eSKenneth D. Merry 25228db0a5eSKenneth D. Merry static int 25328db0a5eSKenneth D. Merry set_timestamp(struct cam_device *device, char *format_string, 254492a2ef5SKenneth D. Merry char *timestamp_string, int task_attr, int retry_count, 25528db0a5eSKenneth D. Merry int timeout) 25628db0a5eSKenneth D. Merry { 25728db0a5eSKenneth D. Merry struct scsi_set_timestamp_parameters ts_p; 25828db0a5eSKenneth D. Merry time_t time_value; 25928db0a5eSKenneth D. Merry struct tm time_struct; 26028db0a5eSKenneth D. Merry uint8_t flags = 0; 26128db0a5eSKenneth D. Merry int error = 0; 26228db0a5eSKenneth D. Merry uint64_t ts = 0; 26328db0a5eSKenneth D. Merry union ccb *ccb = NULL; 26428db0a5eSKenneth D. Merry int do_restore_flags = 0; 26528db0a5eSKenneth D. Merry 266492a2ef5SKenneth D. Merry error = set_restore_flags(device, &flags, /*set_flag*/ 1, task_attr, 267492a2ef5SKenneth D. Merry retry_count, timeout); 26828db0a5eSKenneth D. Merry if (error != 0) 26928db0a5eSKenneth D. Merry goto bailout; 27028db0a5eSKenneth D. Merry 27128db0a5eSKenneth D. Merry do_restore_flags = 1; 27228db0a5eSKenneth D. Merry 27328db0a5eSKenneth D. Merry ccb = cam_getccb(device); 27428db0a5eSKenneth D. Merry if (ccb == NULL) { 27528db0a5eSKenneth D. Merry warnx("%s: error allocating CCB", __func__); 27628db0a5eSKenneth D. Merry error = 1; 27728db0a5eSKenneth D. Merry goto bailout; 27828db0a5eSKenneth D. Merry } 27928db0a5eSKenneth D. Merry 28028db0a5eSKenneth D. Merry if (strcmp(format_string, UTC) == 0) { 28128db0a5eSKenneth D. Merry time(&time_value); 28228db0a5eSKenneth D. Merry ts = (uint64_t) time_value; 28328db0a5eSKenneth D. Merry } else { 28428db0a5eSKenneth D. Merry bzero(&time_struct, sizeof(struct tm)); 285*d618624cSKenneth D. Merry if (strptime(timestamp_string, format_string, 286*d618624cSKenneth D. Merry &time_struct) == NULL) { 287*d618624cSKenneth D. Merry warnx("%s: strptime(3) failed", __func__); 288*d618624cSKenneth D. Merry error = 1; 289*d618624cSKenneth D. Merry goto bailout; 290*d618624cSKenneth D. Merry } 29128db0a5eSKenneth D. Merry time_value = mktime(&time_struct); 29228db0a5eSKenneth D. Merry ts = (uint64_t) time_value; 29328db0a5eSKenneth D. Merry } 29428db0a5eSKenneth D. Merry /* Convert time from seconds to milliseconds */ 29528db0a5eSKenneth D. Merry ts *= 1000; 296*d618624cSKenneth D. Merry bzero(&ts_p, sizeof(ts_p)); 29728db0a5eSKenneth D. Merry scsi_create_timestamp(ts_p.timestamp, ts); 29828db0a5eSKenneth D. Merry 29928db0a5eSKenneth D. Merry scsi_set_timestamp(&ccb->csio, 30028db0a5eSKenneth D. Merry /*retries*/ retry_count, 30128db0a5eSKenneth D. Merry /*cbfcnp*/ NULL, 302492a2ef5SKenneth D. Merry /*tag_action*/ task_attr, 30328db0a5eSKenneth D. Merry /*buf*/ &ts_p, 30428db0a5eSKenneth D. Merry /*buf_len*/ sizeof(ts_p), 30528db0a5eSKenneth D. Merry /*sense_len*/ SSD_FULL_SIZE, 30628db0a5eSKenneth D. Merry /*timeout*/ timeout ? timeout : 5000); 30728db0a5eSKenneth D. Merry 30828db0a5eSKenneth D. Merry ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; 30928db0a5eSKenneth D. Merry if (retry_count > 0) 31028db0a5eSKenneth D. Merry ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER; 31128db0a5eSKenneth D. Merry 31228db0a5eSKenneth D. Merry error = cam_send_ccb(device, ccb); 31328db0a5eSKenneth D. Merry if (error != 0) { 31428db0a5eSKenneth D. Merry warn("error sending Set Timestamp"); 31528db0a5eSKenneth D. Merry goto bailout; 31628db0a5eSKenneth D. Merry } 31728db0a5eSKenneth D. Merry 31828db0a5eSKenneth D. Merry if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { 31928db0a5eSKenneth D. Merry cam_error_print(device, ccb, CAM_ESF_ALL, 32028db0a5eSKenneth D. Merry CAM_EPF_ALL, stderr); 32128db0a5eSKenneth D. Merry error = 1; 32228db0a5eSKenneth D. Merry goto bailout; 32328db0a5eSKenneth D. Merry } 32428db0a5eSKenneth D. Merry 32528db0a5eSKenneth D. Merry printf("Timestamp set to %ju\n", (uintmax_t)ts); 32628db0a5eSKenneth D. Merry 32728db0a5eSKenneth D. Merry bailout: 32828db0a5eSKenneth D. Merry if (do_restore_flags != 0) 32928db0a5eSKenneth D. Merry error = set_restore_flags(device, &flags, /*set_flag*/ 0, 330492a2ef5SKenneth D. Merry task_attr, retry_count, timeout); 33128db0a5eSKenneth D. Merry if (ccb != NULL) 33228db0a5eSKenneth D. Merry cam_freeccb(ccb); 33328db0a5eSKenneth D. Merry 33428db0a5eSKenneth D. Merry return error; 33528db0a5eSKenneth D. Merry } 33628db0a5eSKenneth D. Merry 33728db0a5eSKenneth D. Merry int 33828db0a5eSKenneth D. Merry timestamp(struct cam_device *device, int argc, char **argv, char *combinedopt, 339492a2ef5SKenneth D. Merry int task_attr, int retry_count, int timeout, int verbosemode __unused) 34028db0a5eSKenneth D. Merry { 34128db0a5eSKenneth D. Merry int c; 342a1fa2391SAdrian Chadd uint64_t ts = 0; 34328db0a5eSKenneth D. Merry char *format_string = NULL; 34428db0a5eSKenneth D. Merry char *timestamp_string = NULL; 34528db0a5eSKenneth D. Merry int action = -1; 34628db0a5eSKenneth D. Merry int error = 0; 34728db0a5eSKenneth D. Merry int single_arg = 0; 34828db0a5eSKenneth D. Merry int do_utc = 0; 34928db0a5eSKenneth D. Merry 35028db0a5eSKenneth D. Merry while ((c = getopt(argc, argv, combinedopt)) != -1) { 35128db0a5eSKenneth D. Merry switch (c) { 35228db0a5eSKenneth D. Merry case 'r': { 35328db0a5eSKenneth D. Merry if (action != -1) { 35428db0a5eSKenneth D. Merry warnx("Use only one -r or only one -s"); 35528db0a5eSKenneth D. Merry error =1; 35628db0a5eSKenneth D. Merry goto bailout; 35728db0a5eSKenneth D. Merry } 35828db0a5eSKenneth D. Merry action = TIMESTAMP_REPORT; 35928db0a5eSKenneth D. Merry break; 36028db0a5eSKenneth D. Merry } 36128db0a5eSKenneth D. Merry case 's': { 36228db0a5eSKenneth D. Merry if (action != -1) { 36328db0a5eSKenneth D. Merry warnx("Use only one -r or only one -s"); 36428db0a5eSKenneth D. Merry error = 1; 36528db0a5eSKenneth D. Merry goto bailout; 36628db0a5eSKenneth D. Merry } 36728db0a5eSKenneth D. Merry action = TIMESTAMP_SET; 36828db0a5eSKenneth D. Merry break; 36928db0a5eSKenneth D. Merry } 37028db0a5eSKenneth D. Merry case 'f': { 37128db0a5eSKenneth D. Merry single_arg++; 372f2a12bceSEnji Cooper free(format_string); 37328db0a5eSKenneth D. Merry format_string = strdup(optarg); 37428db0a5eSKenneth D. Merry if (format_string == NULL) { 37528db0a5eSKenneth D. Merry warn("Error allocating memory for format " 37628db0a5eSKenneth D. Merry "argument"); 37728db0a5eSKenneth D. Merry error = 1; 37828db0a5eSKenneth D. Merry goto bailout; 37928db0a5eSKenneth D. Merry } 38028db0a5eSKenneth D. Merry break; 38128db0a5eSKenneth D. Merry } 38228db0a5eSKenneth D. Merry case 'm': { 38328db0a5eSKenneth D. Merry single_arg++; 384f2a12bceSEnji Cooper free(format_string); 38528db0a5eSKenneth D. Merry format_string = strdup(MIL); 38628db0a5eSKenneth D. Merry if (format_string == NULL) { 38728db0a5eSKenneth D. Merry warn("Error allocating memory"); 38828db0a5eSKenneth D. Merry error = 1; 38928db0a5eSKenneth D. Merry goto bailout; 39028db0a5eSKenneth D. Merry } 39128db0a5eSKenneth D. Merry break; 39228db0a5eSKenneth D. Merry } 39328db0a5eSKenneth D. Merry case 'U': { 39428db0a5eSKenneth D. Merry do_utc = 1; 39528db0a5eSKenneth D. Merry break; 39628db0a5eSKenneth D. Merry } 39728db0a5eSKenneth D. Merry case 'T': 398f2a12bceSEnji Cooper free(timestamp_string); 39928db0a5eSKenneth D. Merry timestamp_string = strdup(optarg); 40028db0a5eSKenneth D. Merry if (timestamp_string == NULL) { 40128db0a5eSKenneth D. Merry warn("Error allocating memory for format " 40228db0a5eSKenneth D. Merry "argument"); 40328db0a5eSKenneth D. Merry error = 1; 40428db0a5eSKenneth D. Merry goto bailout; 40528db0a5eSKenneth D. Merry } 40628db0a5eSKenneth D. Merry break; 40728db0a5eSKenneth D. Merry default: 40828db0a5eSKenneth D. Merry break; 40928db0a5eSKenneth D. Merry } 41028db0a5eSKenneth D. Merry } 41128db0a5eSKenneth D. Merry 41228db0a5eSKenneth D. Merry if (action == -1) { 41328db0a5eSKenneth D. Merry warnx("Must specify an action, either -r or -s"); 41428db0a5eSKenneth D. Merry error = 1; 41528db0a5eSKenneth D. Merry goto bailout; 41628db0a5eSKenneth D. Merry } 41728db0a5eSKenneth D. Merry 41828db0a5eSKenneth D. Merry if (single_arg > 1) { 41928db0a5eSKenneth D. Merry warnx("Select only one: %s", 42028db0a5eSKenneth D. Merry (action == TIMESTAMP_REPORT) ? 42128db0a5eSKenneth D. Merry "-f format or -m for the -r flag" : 42228db0a5eSKenneth D. Merry "-f format -T time or -U for the -s flag"); 42328db0a5eSKenneth D. Merry error = 1; 42428db0a5eSKenneth D. Merry goto bailout; 42528db0a5eSKenneth D. Merry } 42628db0a5eSKenneth D. Merry 42728db0a5eSKenneth D. Merry if (action == TIMESTAMP_SET) { 42828db0a5eSKenneth D. Merry if ((format_string == NULL) 42928db0a5eSKenneth D. Merry && (do_utc == 0)) { 43028db0a5eSKenneth D. Merry warnx("Must specify either -f format or -U for " 43128db0a5eSKenneth D. Merry "setting the timestamp"); 43228db0a5eSKenneth D. Merry error = 1; 43328db0a5eSKenneth D. Merry } else if ((format_string != NULL) 43428db0a5eSKenneth D. Merry && (do_utc != 0)) { 43528db0a5eSKenneth D. Merry warnx("Must specify only one of -f or -U to set " 43628db0a5eSKenneth D. Merry "the timestamp"); 43728db0a5eSKenneth D. Merry error = 1; 43828db0a5eSKenneth D. Merry } else if ((format_string != NULL) 43928db0a5eSKenneth D. Merry && (strcmp(format_string, MIL) == 0)) { 44028db0a5eSKenneth D. Merry warnx("-m is not allowed for setting the " 44128db0a5eSKenneth D. Merry "timestamp"); 44228db0a5eSKenneth D. Merry error = 1; 44328db0a5eSKenneth D. Merry } else if ((do_utc == 0) 44428db0a5eSKenneth D. Merry && (timestamp_string == NULL)) { 44528db0a5eSKenneth D. Merry warnx("Must specify the time (-T) to set as the " 44628db0a5eSKenneth D. Merry "timestamp"); 44728db0a5eSKenneth D. Merry error = 1; 44828db0a5eSKenneth D. Merry } 44928db0a5eSKenneth D. Merry if (error != 0) 45028db0a5eSKenneth D. Merry goto bailout; 45128db0a5eSKenneth D. Merry } else if (action == TIMESTAMP_REPORT) { 45228db0a5eSKenneth D. Merry if (format_string == NULL) { 45328db0a5eSKenneth D. Merry format_string = strdup("%c %Z"); 45428db0a5eSKenneth D. Merry if (format_string == NULL) { 45528db0a5eSKenneth D. Merry warn("Error allocating memory for format " 45628db0a5eSKenneth D. Merry "string"); 45728db0a5eSKenneth D. Merry error = 1; 45828db0a5eSKenneth D. Merry goto bailout; 45928db0a5eSKenneth D. Merry } 46028db0a5eSKenneth D. Merry } 46128db0a5eSKenneth D. Merry } 46228db0a5eSKenneth D. Merry 46328db0a5eSKenneth D. Merry if (action == TIMESTAMP_REPORT) { 464492a2ef5SKenneth D. Merry error = report_timestamp(device, &ts, task_attr, retry_count, 46528db0a5eSKenneth D. Merry timeout); 46628db0a5eSKenneth D. Merry if (error != 0) { 46728db0a5eSKenneth D. Merry goto bailout; 46828db0a5eSKenneth D. Merry } else if (strcmp(format_string, MIL) == 0) { 46928db0a5eSKenneth D. Merry printf("Timestamp in milliseconds: %ju\n", 47028db0a5eSKenneth D. Merry (uintmax_t)ts); 47128db0a5eSKenneth D. Merry } else { 47228db0a5eSKenneth D. Merry char temp_timestamp_string[100]; 47328db0a5eSKenneth D. Merry time_t time_var = ts / 1000; 47428db0a5eSKenneth D. Merry const struct tm *restrict cur_time; 47528db0a5eSKenneth D. Merry 47628db0a5eSKenneth D. Merry setlocale(LC_TIME, ""); 47728db0a5eSKenneth D. Merry if (do_utc != 0) 47828db0a5eSKenneth D. Merry cur_time = gmtime(&time_var); 47928db0a5eSKenneth D. Merry else 48028db0a5eSKenneth D. Merry cur_time = localtime(&time_var); 48128db0a5eSKenneth D. Merry 48228db0a5eSKenneth D. Merry strftime(temp_timestamp_string, 48328db0a5eSKenneth D. Merry sizeof(temp_timestamp_string), format_string, 48428db0a5eSKenneth D. Merry cur_time); 48528db0a5eSKenneth D. Merry printf("Formatted timestamp: %s\n", 48628db0a5eSKenneth D. Merry temp_timestamp_string); 48728db0a5eSKenneth D. Merry } 48828db0a5eSKenneth D. Merry } else if (action == TIMESTAMP_SET) { 48928db0a5eSKenneth D. Merry if (do_utc != 0) { 49028db0a5eSKenneth D. Merry format_string = strdup(UTC); 49128db0a5eSKenneth D. Merry if (format_string == NULL) { 49228db0a5eSKenneth D. Merry warn("Error allocating memory for format " 49328db0a5eSKenneth D. Merry "string"); 49428db0a5eSKenneth D. Merry error = 1; 49528db0a5eSKenneth D. Merry goto bailout; 49628db0a5eSKenneth D. Merry } 49728db0a5eSKenneth D. Merry } 49828db0a5eSKenneth D. Merry 49928db0a5eSKenneth D. Merry error = set_timestamp(device, format_string, timestamp_string, 500492a2ef5SKenneth D. Merry task_attr, retry_count, timeout); 50128db0a5eSKenneth D. Merry } 50228db0a5eSKenneth D. Merry 50328db0a5eSKenneth D. Merry bailout: 50428db0a5eSKenneth D. Merry free(format_string); 50528db0a5eSKenneth D. Merry free(timestamp_string); 50628db0a5eSKenneth D. Merry 50728db0a5eSKenneth D. Merry return (error); 50828db0a5eSKenneth D. Merry } 509