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