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