Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sbin/camcontrol/attrib.c
39481 views
1
/*-
2
* Copyright (c) 2014 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
*/
32
/*
33
* SCSI Read and Write Attribute support for camcontrol(8).
34
*/
35
36
#include <sys/cdefs.h>
37
#include <sys/ioctl.h>
38
#include <sys/stdint.h>
39
#include <sys/param.h>
40
#include <sys/endian.h>
41
#include <sys/sbuf.h>
42
#include <sys/queue.h>
43
#include <sys/chio.h>
44
45
#include <stdio.h>
46
#include <stdlib.h>
47
#include <inttypes.h>
48
#include <unistd.h>
49
#include <string.h>
50
#include <strings.h>
51
#include <fcntl.h>
52
#include <ctype.h>
53
#include <limits.h>
54
#include <err.h>
55
#include <locale.h>
56
57
#include <cam/cam.h>
58
#include <cam/cam_debug.h>
59
#include <cam/cam_ccb.h>
60
#include <cam/scsi/scsi_all.h>
61
#include <cam/scsi/scsi_pass.h>
62
#include <cam/scsi/scsi_ch.h>
63
#include <cam/scsi/scsi_message.h>
64
#include <camlib.h>
65
#include "camcontrol.h"
66
67
#if 0
68
struct scsi_attr_desc {
69
int attr_id;
70
71
STAILQ_ENTRY(scsi_attr_desc) links;
72
};
73
#endif
74
75
static struct scsi_nv elem_type_map[] = {
76
{ "all", ELEMENT_TYPE_ALL },
77
{ "picker", ELEMENT_TYPE_MT },
78
{ "slot", ELEMENT_TYPE_ST },
79
{ "portal", ELEMENT_TYPE_IE },
80
{ "drive", ELEMENT_TYPE_DT },
81
};
82
83
static struct scsi_nv sa_map[] = {
84
{ "attr_values", SRA_SA_ATTR_VALUES },
85
{ "attr_list", SRA_SA_ATTR_LIST },
86
{ "lv_list", SRA_SA_LOG_VOL_LIST },
87
{ "part_list", SRA_SA_PART_LIST },
88
{ "supp_attr", SRA_SA_SUPPORTED_ATTRS }
89
};
90
91
static struct scsi_nv output_format_map[] = {
92
{ "text_esc", SCSI_ATTR_OUTPUT_TEXT_ESC },
93
{ "text_raw", SCSI_ATTR_OUTPUT_TEXT_RAW },
94
{ "nonascii_esc", SCSI_ATTR_OUTPUT_NONASCII_ESC },
95
{ "nonascii_trim", SCSI_ATTR_OUTPUT_NONASCII_TRIM },
96
{ "nonascii_raw", SCSI_ATTR_OUTPUT_NONASCII_RAW },
97
{ "field_all", SCSI_ATTR_OUTPUT_FIELD_ALL },
98
{ "field_none", SCSI_ATTR_OUTPUT_FIELD_NONE },
99
{ "field_desc", SCSI_ATTR_OUTPUT_FIELD_DESC },
100
{ "field_num", SCSI_ATTR_OUTPUT_FIELD_NUM },
101
{ "field_size", SCSI_ATTR_OUTPUT_FIELD_SIZE },
102
{ "field_rw", SCSI_ATTR_OUTPUT_FIELD_RW },
103
};
104
105
int
106
scsiattrib(struct cam_device *device, int argc, char **argv, char *combinedopt,
107
int task_attr, int retry_count, int timeout, int verbosemode,
108
int err_recover)
109
{
110
union ccb *ccb = NULL;
111
int attr_num = -1;
112
#if 0
113
int num_attrs = 0;
114
#endif
115
int start_attr = 0;
116
int cached_attr = 0;
117
int read_service_action = -1;
118
int read_attr = 0, write_attr = 0;
119
int element_address = 0;
120
int element_type = ELEMENT_TYPE_ALL;
121
int partition = 0;
122
int logical_volume = 0;
123
char *endptr;
124
uint8_t *data_buf = NULL;
125
uint32_t dxfer_len = UINT16_MAX - 1;
126
uint32_t valid_len;
127
uint32_t output_format;
128
STAILQ_HEAD(, scsi_attr_desc) write_attr_list;
129
int error = 0;
130
int c;
131
132
ccb = cam_getccb(device);
133
if (ccb == NULL) {
134
warnx("%s: error allocating CCB", __func__);
135
error = 1;
136
goto bailout;
137
}
138
139
STAILQ_INIT(&write_attr_list);
140
141
/*
142
* By default, when displaying attribute values, we trim out
143
* non-ASCII characters in ASCII fields. We display all fields
144
* (description, attribute number, attribute size, and readonly
145
* status). We default to displaying raw text.
146
*
147
* XXX KDM need to port this to stable/10 and newer FreeBSD
148
* versions that have iconv built in and can convert codesets.
149
*/
150
output_format = SCSI_ATTR_OUTPUT_NONASCII_TRIM |
151
SCSI_ATTR_OUTPUT_FIELD_ALL |
152
SCSI_ATTR_OUTPUT_TEXT_RAW;
153
154
data_buf = malloc(dxfer_len);
155
if (data_buf == NULL) {
156
warn("%s: error allocating %u bytes", __func__, dxfer_len);
157
error = 1;
158
goto bailout;
159
}
160
161
while ((c = getopt(argc, argv, combinedopt)) != -1) {
162
switch (c) {
163
case 'a':
164
attr_num = strtol(optarg, &endptr, 0);
165
if (*endptr != '\0') {
166
warnx("%s: invalid attribute number %s",
167
__func__, optarg);
168
error = 1;
169
goto bailout;
170
}
171
start_attr = attr_num;
172
break;
173
case 'c':
174
cached_attr = 1;
175
break;
176
case 'e':
177
element_address = strtol(optarg, &endptr, 0);
178
if (*endptr != '\0') {
179
warnx("%s: invalid element address %s",
180
__func__, optarg);
181
error = 1;
182
goto bailout;
183
}
184
break;
185
case 'F': {
186
scsi_nv_status status;
187
scsi_attrib_output_flags new_outflags;
188
int entry_num = 0;
189
char *tmpstr;
190
191
if (isdigit(optarg[0])) {
192
output_format = strtoul(optarg, &endptr, 0);
193
if (*endptr != '\0') {
194
warnx("%s: invalid numeric output "
195
"format argument %s", __func__,
196
optarg);
197
error = 1;
198
goto bailout;
199
}
200
break;
201
}
202
new_outflags = SCSI_ATTR_OUTPUT_NONE;
203
204
while ((tmpstr = strsep(&optarg, ",")) != NULL) {
205
status = scsi_get_nv(output_format_map,
206
sizeof(output_format_map) /
207
sizeof(output_format_map[0]), tmpstr,
208
&entry_num, SCSI_NV_FLAG_IG_CASE);
209
210
if (status == SCSI_NV_FOUND)
211
new_outflags |=
212
output_format_map[entry_num].value;
213
else {
214
warnx("%s: %s format option %s",
215
__func__,
216
(status == SCSI_NV_AMBIGUOUS) ?
217
"ambiguous" : "invalid", tmpstr);
218
error = 1;
219
goto bailout;
220
}
221
}
222
output_format = new_outflags;
223
break;
224
}
225
case 'p':
226
partition = strtol(optarg, &endptr, 0);
227
if (*endptr != '\0') {
228
warnx("%s: invalid partition number %s",
229
__func__, optarg);
230
error = 1;
231
goto bailout;
232
}
233
break;
234
case 'r': {
235
scsi_nv_status status;
236
int entry_num = 0;
237
238
status = scsi_get_nv(sa_map, sizeof(sa_map) /
239
sizeof(sa_map[0]), optarg, &entry_num,
240
SCSI_NV_FLAG_IG_CASE);
241
if (status == SCSI_NV_FOUND)
242
read_service_action = sa_map[entry_num].value;
243
else {
244
warnx("%s: %s %s option %s", __func__,
245
(status == SCSI_NV_AMBIGUOUS) ?
246
"ambiguous" : "invalid", "service action",
247
optarg);
248
error = 1;
249
goto bailout;
250
}
251
read_attr = 1;
252
break;
253
}
254
case 's':
255
start_attr = strtol(optarg, &endptr, 0);
256
if (*endptr != '\0') {
257
warnx("%s: invalid starting attr argument %s",
258
__func__, optarg);
259
error = 1;
260
goto bailout;
261
}
262
break;
263
case 'T': {
264
scsi_nv_status status;
265
int entry_num = 0;
266
267
status = scsi_get_nv(elem_type_map,
268
nitems(elem_type_map),
269
optarg, &entry_num, SCSI_NV_FLAG_IG_CASE);
270
if (status == SCSI_NV_FOUND)
271
element_type = elem_type_map[entry_num].value;
272
else {
273
warnx("%s: %s %s option %s", __func__,
274
(status == SCSI_NV_AMBIGUOUS) ?
275
"ambiguous" : "invalid", "element type",
276
optarg);
277
error = 1;
278
goto bailout;
279
}
280
break;
281
}
282
case 'w':
283
warnx("%s: writing attributes is not implemented yet",
284
__func__);
285
error = 1;
286
goto bailout;
287
break;
288
case 'V':
289
logical_volume = strtol(optarg, &endptr, 0);
290
291
if (*endptr != '\0') {
292
warnx("%s: invalid logical volume argument %s",
293
__func__, optarg);
294
error = 1;
295
goto bailout;
296
}
297
break;
298
default:
299
break;
300
}
301
}
302
303
/*
304
* Default to reading attributes
305
*/
306
if (((read_attr == 0) && (write_attr == 0))
307
|| ((read_attr != 0) && (write_attr != 0))) {
308
warnx("%s: Must specify either -r or -w", __func__);
309
error = 1;
310
goto bailout;
311
}
312
313
if (read_attr != 0) {
314
scsi_read_attribute(&ccb->csio,
315
/*retries*/ retry_count,
316
/*cbfcnp*/ NULL,
317
/*tag_action*/ task_attr,
318
/*service_action*/ read_service_action,
319
/*element*/ element_address,
320
/*elem_type*/ element_type,
321
/*logical_volume*/ logical_volume,
322
/*partition*/ partition,
323
/*first_attribute*/ start_attr,
324
/*cache*/ cached_attr,
325
/*data_ptr*/ data_buf,
326
/*length*/ dxfer_len,
327
/*sense_len*/ SSD_FULL_SIZE,
328
/*timeout*/ timeout ? timeout : 60000);
329
#if 0
330
} else {
331
#endif
332
333
}
334
335
ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
336
337
if (err_recover != 0)
338
ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
339
340
if (cam_send_ccb(device, ccb) < 0) {
341
warn("error sending %s ATTRIBUTE", (read_attr != 0) ?
342
"READ" : "WRITE");
343
error = 1;
344
goto bailout;
345
}
346
347
if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
348
if (verbosemode != 0) {
349
cam_error_print(device, ccb, CAM_ESF_ALL,
350
CAM_EPF_ALL, stderr);
351
}
352
error = 1;
353
goto bailout;
354
}
355
356
if (read_attr == 0)
357
goto bailout;
358
359
valid_len = dxfer_len - ccb->csio.resid;
360
361
switch (read_service_action) {
362
case SRA_SA_ATTR_VALUES: {
363
uint32_t len_left, hdr_len, cur_len;
364
struct scsi_read_attribute_values *hdr;
365
struct scsi_mam_attribute_header *cur_id;
366
char error_str[512];
367
uint8_t *cur_pos;
368
struct sbuf *sb;
369
370
hdr = (struct scsi_read_attribute_values *)data_buf;
371
372
if (valid_len < sizeof(*hdr)) {
373
fprintf(stdout, "No attributes returned.\n");
374
error = 0;
375
goto bailout;
376
}
377
378
sb = sbuf_new_auto();
379
if (sb == NULL) {
380
warn("%s: Unable to allocate sbuf", __func__);
381
error = 1;
382
goto bailout;
383
}
384
/*
385
* XXX KDM grab more data if it is available.
386
*/
387
hdr_len = scsi_4btoul(hdr->length);
388
389
for (len_left = MIN(valid_len, hdr_len),
390
cur_pos = &hdr->attribute_0[0]; len_left > sizeof(*cur_id);
391
len_left -= cur_len, cur_pos += cur_len) {
392
int cur_attr_num;
393
cur_id = (struct scsi_mam_attribute_header *)cur_pos;
394
cur_len = scsi_2btoul(cur_id->length) + sizeof(*cur_id);
395
cur_attr_num = scsi_2btoul(cur_id->id);
396
397
if ((attr_num != -1)
398
&& (cur_attr_num != attr_num))
399
continue;
400
401
error = scsi_attrib_sbuf(sb, cur_id, len_left,
402
/*user_table*/ NULL, /*num_user_entries*/ 0,
403
/*prefer_user_table*/ 0, output_format, error_str,
404
sizeof(error_str));
405
if (error != 0) {
406
warnx("%s: %s", __func__, error_str);
407
sbuf_delete(sb);
408
error = 1;
409
goto bailout;
410
}
411
if (attr_num != -1)
412
break;
413
}
414
415
sbuf_finish(sb);
416
fprintf(stdout, "%s", sbuf_data(sb));
417
sbuf_delete(sb);
418
break;
419
}
420
case SRA_SA_SUPPORTED_ATTRS:
421
case SRA_SA_ATTR_LIST: {
422
uint32_t len_left, hdr_len;
423
struct scsi_attrib_list_header *hdr;
424
struct scsi_attrib_table_entry *entry = NULL;
425
const char *sa_name = "Supported Attributes";
426
const char *at_name = "Available Attributes";
427
int attr_id;
428
uint8_t *cur_id;
429
430
hdr = (struct scsi_attrib_list_header *)data_buf;
431
if (valid_len < sizeof(*hdr)) {
432
fprintf(stdout, "No %s\n",
433
(read_service_action == SRA_SA_SUPPORTED_ATTRS)?
434
sa_name : at_name);
435
error = 0;
436
goto bailout;
437
}
438
fprintf(stdout, "%s:\n",
439
(read_service_action == SRA_SA_SUPPORTED_ATTRS) ?
440
sa_name : at_name);
441
hdr_len = scsi_4btoul(hdr->length);
442
for (len_left = MIN(valid_len, hdr_len),
443
cur_id = &hdr->first_attr_0[0]; len_left > 1;
444
len_left -= sizeof(uint16_t), cur_id += sizeof(uint16_t)) {
445
attr_id = scsi_2btoul(cur_id);
446
447
if ((attr_num != -1)
448
&& (attr_id != attr_num))
449
continue;
450
451
entry = scsi_get_attrib_entry(attr_id);
452
fprintf(stdout, "0x%.4x", attr_id);
453
if (entry == NULL)
454
fprintf(stdout, "\n");
455
else
456
fprintf(stdout, ": %s\n", entry->desc);
457
458
if (attr_num != -1)
459
break;
460
}
461
break;
462
}
463
case SRA_SA_PART_LIST:
464
case SRA_SA_LOG_VOL_LIST: {
465
struct scsi_attrib_lv_list *lv_list;
466
const char *partition_name = "Partition";
467
const char *lv_name = "Logical Volume";
468
469
if (valid_len < sizeof(*lv_list)) {
470
fprintf(stdout, "No %s list returned\n",
471
(read_service_action == SRA_SA_PART_LIST) ?
472
partition_name : lv_name);
473
error = 0;
474
goto bailout;
475
}
476
477
lv_list = (struct scsi_attrib_lv_list *)data_buf;
478
479
fprintf(stdout, "First %s: %d\n",
480
(read_service_action == SRA_SA_PART_LIST) ?
481
partition_name : lv_name,
482
lv_list->first_lv_number);
483
fprintf(stdout, "Number of %ss: %d\n",
484
(read_service_action == SRA_SA_PART_LIST) ?
485
partition_name : lv_name,
486
lv_list->num_logical_volumes);
487
break;
488
}
489
default:
490
break;
491
}
492
bailout:
493
if (ccb != NULL)
494
cam_freeccb(ccb);
495
496
free(data_buf);
497
498
return (error);
499
}
500
501