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