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