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