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