]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/camcontrol/timestamp.c
Remove spurious newline
[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 <assert.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <unistd.h>
47 #include <string.h>
48 #include <err.h>
49 #include <time.h>
50 #include <locale.h>
51
52 #include <cam/cam.h>
53 #include <cam/cam_debug.h>
54 #include <cam/cam_ccb.h>
55 #include <cam/scsi/scsi_all.h>
56 #include <cam/scsi/scsi_message.h>
57 #include <camlib.h>
58 #include "camcontrol.h"
59
60 #define TIMESTAMP_REPORT 0
61 #define TIMESTAMP_SET    1
62 #define MIL              "milliseconds"
63 #define UTC              "utc"
64
65 static int set_restore_flags(struct cam_device *device, uint8_t *flags,
66                              int set_flag, int task_attr, int retry_count,
67                              int timeout);
68 static int report_timestamp(struct cam_device *device, uint64_t *ts,
69                             int task_attr, int retry_count, int timeout);
70 static int set_timestamp(struct cam_device *device, char *format_string,
71                          char *timestamp_string, int task_attr, int retry_count,
72                          int timeout);
73
74 static int
75 set_restore_flags(struct cam_device *device, uint8_t *flags, int set_flag,
76                   int task_attr, int retry_count, int timeout)
77 {
78         unsigned long blk_desc_length, hdr_and_blk_length;
79         int error = 0;
80         struct scsi_control_ext_page *control_page = NULL;
81         struct scsi_mode_header_10 *mode_hdr = NULL;
82         struct scsi_mode_sense_10 *cdb = NULL;
83         union ccb *ccb = NULL;
84         unsigned long mode_buf_size = sizeof(struct scsi_mode_header_10) +
85             sizeof(struct scsi_mode_blk_desc) +
86             sizeof(struct scsi_control_ext_page);
87         uint8_t mode_buf[mode_buf_size];
88
89         ccb = cam_getccb(device);
90         if (ccb == NULL) {
91                 warnx("%s: error allocating CCB", __func__);
92                 error = 1;
93                 goto bailout;
94         }
95         /*
96          * Get the control extension subpage, we'll send it back modified to
97          * enable SCSI control over the tape drive's timestamp
98          */
99         scsi_mode_sense_len(&ccb->csio,
100             /*retries*/ retry_count,
101             /*cbfcnp*/ NULL,
102             /*tag_action*/ task_attr,
103             /*dbd*/ 0,
104             /*page_control*/ SMS_PAGE_CTRL_CURRENT,
105             /*page*/ SCEP_PAGE_CODE,
106             /*param_buf*/ &mode_buf[0],
107             /*param_len*/ mode_buf_size,
108             /*minimum_cmd_size*/ 10,
109             /*sense_len*/ SSD_FULL_SIZE,
110             /*timeout*/ timeout ? timeout : 5000);
111         /*
112          * scsi_mode_sense_len does not have a subpage argument at the moment,
113          * so we have to manually set the subpage code before calling
114          * cam_send_ccb().
115          */
116         cdb = (struct scsi_mode_sense_10 *)ccb->csio.cdb_io.cdb_bytes;
117         cdb->subpage = SCEP_SUBPAGE_CODE;
118
119         ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
120         if (retry_count > 0)
121                 ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
122
123         error = cam_send_ccb(device, ccb);
124         if (error != 0) {
125                 warn("error sending Mode Sense");
126                 goto bailout;
127         }
128
129         if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
130                 cam_error_print(device, ccb, CAM_ESF_ALL,
131                                 CAM_EPF_ALL, stderr);
132                 error = 1;
133                 goto bailout;
134         }
135
136         mode_hdr = (struct scsi_mode_header_10 *)&mode_buf[0];
137         blk_desc_length = scsi_2btoul(mode_hdr->blk_desc_len);
138         hdr_and_blk_length = sizeof(struct scsi_mode_header_10)+blk_desc_length;
139         /*
140          * Create the control page at the correct point in the mode_buf, it
141          * starts after the header and the blk description.
142          */
143         assert(hdr_and_blk_length <=
144             sizeof(mode_buf) - sizeof(struct scsi_control_ext_page));
145         control_page = (struct scsi_control_ext_page *)&mode_buf
146             [hdr_and_blk_length];
147         if (set_flag != 0) {
148                 *flags = control_page->flags;
149                 /*
150                  * Set the SCSIP flag to enable SCSI to change the
151                  * tape drive's timestamp.
152                  */
153                 control_page->flags |= SCEP_SCSIP;
154         } else {
155                 control_page->flags = *flags;
156         }
157
158         scsi_mode_select_len(&ccb->csio,
159             /*retries*/ retry_count,
160             /*cbfcnp*/ NULL,
161             /*tag_action*/ task_attr,
162             /*scsi_page_fmt*/ 1,
163             /*save_pages*/ 0,
164             /*param_buf*/ &mode_buf[0],
165             /*param_len*/ mode_buf_size,
166             /*minimum_cmd_size*/ 10,
167             /*sense_len*/ SSD_FULL_SIZE,
168             /*timeout*/ timeout ? timeout : 5000);
169
170         ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
171         if (retry_count > 0)
172                 ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
173
174         error = cam_send_ccb(device, ccb);
175         if (error != 0) {
176                 warn("error sending Mode Select");
177                 goto bailout;
178         }
179
180         if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
181                 cam_error_print(device, ccb, CAM_ESF_ALL,
182                                 CAM_EPF_ALL, stderr);
183                 error = 1;
184                 goto bailout;
185         }
186
187 bailout:
188         if (ccb != NULL)
189                 cam_freeccb(ccb);
190
191         return error;
192 }
193
194 static int
195 report_timestamp(struct cam_device *device, uint64_t *ts, int task_attr,
196                  int retry_count, int timeout)
197 {
198         int error = 0;
199         struct scsi_report_timestamp_data *report_buf = malloc(
200                 sizeof(struct scsi_report_timestamp_data));
201         uint8_t temp_timestamp[8];
202         uint32_t report_buf_size = sizeof(
203             struct scsi_report_timestamp_data);
204         union ccb *ccb = NULL;
205
206         ccb = cam_getccb(device);
207         if (ccb == NULL) {
208                 warnx("%s: error allocating CCB", __func__);
209                 error = 1;
210                 goto bailout;
211         }
212
213         scsi_report_timestamp(&ccb->csio,
214             /*retries*/ retry_count,
215             /*cbfcnp*/ NULL,
216             /*tag_action*/ task_attr,
217             /*pdf*/ 0,
218             /*buf*/ report_buf,
219             /*buf_len*/ report_buf_size,
220             /*sense_len*/ SSD_FULL_SIZE,
221             /*timeout*/ timeout ? timeout : 5000);
222
223         ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
224         if (retry_count > 0)
225                 ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
226
227         error = cam_send_ccb(device, ccb);
228         if (error != 0) {
229                 warn("error sending Report Timestamp");
230                 goto bailout;
231         }
232         if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
233                 cam_error_print(device, ccb, CAM_ESF_ALL,
234                                 CAM_EPF_ALL, stderr);
235                 error = 1;
236                 goto bailout;
237         }
238
239         bzero(temp_timestamp, sizeof(temp_timestamp));
240         memcpy(&temp_timestamp[2], &report_buf->timestamp, 6);
241
242         *ts = scsi_8btou64(temp_timestamp);
243
244 bailout:
245         if (ccb != NULL)
246                 cam_freeccb(ccb);
247         free(report_buf);
248
249         return error;
250 }
251
252 static int
253 set_timestamp(struct cam_device *device, char *format_string,
254               char *timestamp_string, int task_attr, int retry_count,
255               int timeout)
256 {
257         struct scsi_set_timestamp_parameters ts_p;
258         time_t time_value;
259         struct tm time_struct;
260         uint8_t flags = 0;
261         int error = 0;
262         uint64_t ts = 0;
263         union ccb *ccb = NULL;
264         int do_restore_flags = 0;
265
266         error = set_restore_flags(device, &flags, /*set_flag*/ 1, task_attr,
267                                   retry_count, timeout);
268         if (error != 0)
269                 goto bailout;
270
271         do_restore_flags = 1;
272
273         ccb = cam_getccb(device);
274         if (ccb == NULL) {
275                 warnx("%s: error allocating CCB", __func__);
276                 error = 1;
277                 goto bailout;
278         }
279
280         if (strcmp(format_string, UTC) == 0) {
281                 time(&time_value);
282                 ts = (uint64_t) time_value;
283         } else {
284                 bzero(&time_struct, sizeof(struct tm));
285                 if (strptime(timestamp_string, format_string,
286                     &time_struct) == NULL) {
287                         warnx("%s: strptime(3) failed", __func__);
288                         error = 1;
289                         goto bailout;
290                 }
291                 time_value = mktime(&time_struct);
292                 ts = (uint64_t) time_value;
293         }
294         /* Convert time from seconds to milliseconds */
295         ts *= 1000;
296         bzero(&ts_p, sizeof(ts_p));
297         scsi_create_timestamp(ts_p.timestamp, ts);
298
299         scsi_set_timestamp(&ccb->csio,
300             /*retries*/ retry_count,
301             /*cbfcnp*/ NULL,
302             /*tag_action*/ task_attr,
303             /*buf*/ &ts_p,
304             /*buf_len*/ sizeof(ts_p),
305             /*sense_len*/ SSD_FULL_SIZE,
306             /*timeout*/ timeout ? timeout : 5000);
307
308         ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
309         if (retry_count > 0)
310                 ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
311
312         error = cam_send_ccb(device, ccb);
313         if (error != 0) {
314                 warn("error sending Set Timestamp");
315                 goto bailout;
316         }
317
318         if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
319                 cam_error_print(device, ccb, CAM_ESF_ALL,
320                                 CAM_EPF_ALL, stderr);
321                 error = 1;
322                 goto bailout;
323         }
324
325         printf("Timestamp set to %ju\n", (uintmax_t)ts);
326
327 bailout:
328         if (do_restore_flags != 0)
329                 error = set_restore_flags(device, &flags, /*set_flag*/ 0,
330                                           task_attr, retry_count, timeout);
331         if (ccb != NULL)
332                 cam_freeccb(ccb);
333
334         return error;
335 }
336
337 int
338 timestamp(struct cam_device *device, int argc, char **argv, char *combinedopt,
339           int task_attr, int retry_count, int timeout, int verbosemode __unused)
340 {
341         int c;
342         uint64_t ts = 0;
343         char *format_string = NULL;
344         char *timestamp_string = NULL;
345         int action = -1;
346         int error = 0;
347         int single_arg = 0;
348         int do_utc = 0;
349
350         while ((c = getopt(argc, argv, combinedopt)) != -1) {
351                 switch (c) {
352                 case 'r': {
353                         if (action != -1) {
354                                 warnx("Use only one -r or only one -s");
355                                 error =1;
356                                 goto bailout;
357                         }
358                         action = TIMESTAMP_REPORT;
359                         break;
360                 }
361                 case 's': {
362                         if (action != -1) {
363                                 warnx("Use only one -r or only one -s");
364                                 error = 1;
365                                 goto bailout;
366                         }
367                         action = TIMESTAMP_SET;
368                         break;
369                 }
370                 case 'f': {
371                         single_arg++;
372                         free(format_string);
373                         format_string = strdup(optarg);
374                         if (format_string == NULL) {
375                                 warn("Error allocating memory for format "
376                                    "argument");
377                                 error = 1;
378                                 goto bailout;
379                         }
380                         break;
381                 }
382                 case 'm': {
383                         single_arg++;
384                         free(format_string);
385                         format_string = strdup(MIL);
386                         if (format_string == NULL) {
387                                 warn("Error allocating memory");
388                                 error = 1;
389                                 goto bailout;
390                         }
391                         break;
392                 }
393                 case 'U': {
394                         do_utc = 1;
395                         break;
396                 }
397                 case 'T':
398                         free(timestamp_string);
399                         timestamp_string = strdup(optarg);
400                         if (timestamp_string == NULL) {
401                                 warn("Error allocating memory for format "
402                                    "argument");
403                                 error = 1;
404                                 goto bailout;
405                         }
406                         break;
407                 default:
408                         break;
409                 }
410         }
411
412         if (action == -1) {
413                 warnx("Must specify an action, either -r or -s");
414                 error = 1;
415                 goto bailout;
416         }
417
418         if (single_arg > 1) {
419                 warnx("Select only one: %s",
420                     (action == TIMESTAMP_REPORT) ?
421                     "-f format or -m for the -r flag" : 
422                     "-f format -T time or -U for the -s flag");
423                 error = 1;
424                 goto bailout;
425         }
426
427         if (action == TIMESTAMP_SET) {
428                 if ((format_string == NULL)
429                  && (do_utc == 0)) {
430                         warnx("Must specify either -f format or -U for "
431                             "setting the timestamp");
432                         error = 1;
433                 } else if ((format_string != NULL)
434                         && (do_utc != 0)) {
435                         warnx("Must specify only one of -f or -U to set "
436                             "the timestamp");
437                         error = 1;
438                 } else if ((format_string != NULL)
439                         && (strcmp(format_string, MIL) == 0)) {
440                         warnx("-m is not allowed for setting the "
441                             "timestamp");
442                         error = 1;
443                 } else if ((do_utc == 0)
444                         && (timestamp_string == NULL)) {
445                         warnx("Must specify the time (-T) to set as the "
446                             "timestamp");
447                         error = 1;
448                 }
449                 if (error != 0)
450                         goto bailout;
451         } else if (action == TIMESTAMP_REPORT) {
452                 if (format_string == NULL) {
453                         format_string = strdup("%c %Z");
454                         if (format_string == NULL) {
455                                 warn("Error allocating memory for format "
456                                     "string");
457                                 error = 1;
458                                 goto bailout;
459                         }
460                 }
461         }
462
463         if (action == TIMESTAMP_REPORT) {
464                 error = report_timestamp(device, &ts, task_attr, retry_count,
465                     timeout);
466                 if (error != 0) {
467                         goto bailout;
468                 } else if (strcmp(format_string, MIL) == 0) {
469                         printf("Timestamp in milliseconds: %ju\n",
470                             (uintmax_t)ts);
471                 } else {
472                         char temp_timestamp_string[100];
473                         time_t time_var = ts / 1000;
474                         const struct tm *restrict cur_time;
475
476                         setlocale(LC_TIME, "");
477                         if (do_utc != 0)
478                                 cur_time = gmtime(&time_var);
479                         else
480                                 cur_time = localtime(&time_var);
481
482                         strftime(temp_timestamp_string,
483                             sizeof(temp_timestamp_string), format_string,
484                             cur_time);
485                         printf("Formatted timestamp: %s\n",
486                             temp_timestamp_string);
487                 }
488         } else if (action == TIMESTAMP_SET) {
489                 if (do_utc != 0) {
490                         format_string = strdup(UTC);
491                         if (format_string == NULL) {
492                                 warn("Error allocating memory for format "
493                                     "string");
494                                 error = 1;
495                                 goto bailout;
496                         }
497                 }
498
499                 error = set_timestamp(device, format_string, timestamp_string,
500                     task_attr, retry_count, timeout);
501         }
502
503 bailout:
504         free(format_string);
505         free(timestamp_string);
506
507         return (error);
508 }