libc/locale: Fix type breakage in __collate_range_cmp().
[freebsd-src.git] / usr.sbin / zonectl / zonectl.c
blob6ab38cbd95038b6c673c9409d782e52f8f014fa4
1 /*-
2 * Copyright (c) 2015, 2016 Spectra Logic Corporation
3 * All rights reserved.
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.
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.
30 * Authors: Ken Merry (Spectra Logic Corporation)
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
36 #include <sys/ioctl.h>
37 #include <sys/stdint.h>
38 #include <sys/types.h>
39 #include <sys/endian.h>
40 #include <sys/sbuf.h>
41 #include <sys/queue.h>
42 #include <sys/disk.h>
43 #include <sys/disk_zone.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <inttypes.h>
47 #include <unistd.h>
48 #include <string.h>
49 #include <strings.h>
50 #include <fcntl.h>
51 #include <ctype.h>
52 #include <limits.h>
53 #include <err.h>
54 #include <locale.h>
56 #include <cam/cam.h>
57 #include <cam/cam_debug.h>
58 #include <cam/cam_ccb.h>
59 #include <cam/scsi/scsi_all.h>
61 static struct scsi_nv zone_cmd_map[] = {
62 { "rz", DISK_ZONE_REPORT_ZONES },
63 { "reportzones", DISK_ZONE_REPORT_ZONES },
64 { "close", DISK_ZONE_CLOSE },
65 { "finish", DISK_ZONE_FINISH },
66 { "open", DISK_ZONE_OPEN },
67 { "rwp", DISK_ZONE_RWP },
68 { "params", DISK_ZONE_GET_PARAMS }
71 static struct scsi_nv zone_rep_opts[] = {
72 { "all", DISK_ZONE_REP_ALL },
73 { "empty", DISK_ZONE_REP_EMPTY },
74 { "imp_open", DISK_ZONE_REP_IMP_OPEN },
75 { "exp_open", DISK_ZONE_REP_EXP_OPEN },
76 { "closed", DISK_ZONE_REP_CLOSED },
77 { "full", DISK_ZONE_REP_FULL },
78 { "readonly", DISK_ZONE_REP_READONLY },
79 { "ro", DISK_ZONE_REP_READONLY },
80 { "offline", DISK_ZONE_REP_OFFLINE },
81 { "reset", DISK_ZONE_REP_RWP },
82 { "rwp", DISK_ZONE_REP_RWP },
83 { "nonseq", DISK_ZONE_REP_NON_SEQ },
84 { "nonwp", DISK_ZONE_REP_NON_WP }
88 typedef enum {
89 ZONE_OF_NORMAL = 0x00,
90 ZONE_OF_SUMMARY = 0x01,
91 ZONE_OF_SCRIPT = 0x02
92 } zone_output_flags;
94 static struct scsi_nv zone_print_opts[] = {
95 { "normal", ZONE_OF_NORMAL },
96 { "summary", ZONE_OF_SUMMARY },
97 { "script", ZONE_OF_SCRIPT }
100 static struct scsi_nv zone_cmd_desc_table[] = {
101 {"Report Zones", DISK_ZONE_RZ_SUP },
102 {"Open", DISK_ZONE_OPEN_SUP },
103 {"Close", DISK_ZONE_CLOSE_SUP },
104 {"Finish", DISK_ZONE_FINISH_SUP },
105 {"Reset Write Pointer", DISK_ZONE_RWP_SUP }
108 typedef enum {
109 ZONE_PRINT_OK,
110 ZONE_PRINT_MORE_DATA,
111 ZONE_PRINT_ERROR
112 } zone_print_status;
114 typedef enum {
115 ZONE_FW_START,
116 ZONE_FW_LEN,
117 ZONE_FW_WP,
118 ZONE_FW_TYPE,
119 ZONE_FW_COND,
120 ZONE_FW_SEQ,
121 ZONE_FW_RESET,
122 ZONE_NUM_FIELDS
123 } zone_field_widths;
126 static void usage(int error);
127 static void zonectl_print_params(struct disk_zone_disk_params *params);
128 zone_print_status zonectl_print_rz(struct disk_zone_report *report,
129 zone_output_flags out_flags, int first_pass);
131 static void
132 usage(int error)
134 fprintf(error ? stderr : stdout,
135 "usage: zonectl <-d dev> <-c cmd> [-a][-o rep_opts] [-l lba][-P print_opts]\n"
139 static void
140 zonectl_print_params(struct disk_zone_disk_params *params)
142 unsigned int i;
143 int first;
145 printf("Zone Mode: ");
146 switch (params->zone_mode) {
147 case DISK_ZONE_MODE_NONE:
148 printf("None");
149 break;
150 case DISK_ZONE_MODE_HOST_AWARE:
151 printf("Host Aware");
152 break;
153 case DISK_ZONE_MODE_DRIVE_MANAGED:
154 printf("Drive Managed");
155 break;
156 case DISK_ZONE_MODE_HOST_MANAGED:
157 printf("Host Managed");
158 break;
159 default:
160 printf("Unknown mode %#x", params->zone_mode);
161 break;
163 printf("\n");
165 first = 1;
166 printf("Command support: ");
167 for (i = 0; i < sizeof(zone_cmd_desc_table) /
168 sizeof(zone_cmd_desc_table[0]); i++) {
169 if (params->flags & zone_cmd_desc_table[i].value) {
170 if (first == 0)
171 printf(", ");
172 else
173 first = 0;
174 printf("%s", zone_cmd_desc_table[i].name);
177 if (first == 1)
178 printf("None");
179 printf("\n");
181 printf("Unrestricted Read in Sequential Write Required Zone "
182 "(URSWRZ): %s\n", (params->flags & DISK_ZONE_DISK_URSWRZ) ?
183 "Yes" : "No");
185 printf("Optimal Number of Open Sequential Write Preferred Zones: ");
186 if (params->flags & DISK_ZONE_OPT_SEQ_SET)
187 if (params->optimal_seq_zones == SVPD_ZBDC_OPT_SEQ_NR)
188 printf("Not Reported");
189 else
190 printf("%ju", (uintmax_t)params->optimal_seq_zones);
191 else
192 printf("Not Set");
193 printf("\n");
196 printf("Optimal Number of Non-Sequentially Written Sequential Write "
197 "Preferred Zones: ");
198 if (params->flags & DISK_ZONE_OPT_NONSEQ_SET)
199 if (params->optimal_nonseq_zones == SVPD_ZBDC_OPT_NONSEQ_NR)
200 printf("Not Reported");
201 else
202 printf("%ju",(uintmax_t)params->optimal_nonseq_zones);
203 else
204 printf("Not Set");
205 printf("\n");
207 printf("Maximum Number of Open Sequential Write Required Zones: ");
208 if (params->flags & DISK_ZONE_MAX_SEQ_SET)
209 if (params->max_seq_zones == SVPD_ZBDC_MAX_SEQ_UNLIMITED)
210 printf("Unlimited");
211 else
212 printf("%ju", (uintmax_t)params->max_seq_zones);
213 else
214 printf("Not Set");
215 printf("\n");
218 zone_print_status
219 zonectl_print_rz(struct disk_zone_report *report, zone_output_flags out_flags,
220 int first_pass)
222 zone_print_status status = ZONE_PRINT_OK;
223 struct disk_zone_rep_header *header = &report->header;
224 int field_widths[ZONE_NUM_FIELDS];
225 struct disk_zone_rep_entry *entry;
226 uint64_t next_lba = 0;
227 char tmpstr[80];
228 char word_sep;
229 int more_data = 0;
230 uint32_t i;
232 field_widths[ZONE_FW_START] = 11;
233 field_widths[ZONE_FW_LEN] = 6;
234 field_widths[ZONE_FW_WP] = 11;
235 field_widths[ZONE_FW_TYPE] = 13;
236 field_widths[ZONE_FW_COND] = 13;
237 field_widths[ZONE_FW_SEQ] = 14;
238 field_widths[ZONE_FW_RESET] = 16;
240 if ((report->entries_available - report->entries_filled) > 0) {
241 more_data = 1;
242 status = ZONE_PRINT_MORE_DATA;
245 if (out_flags == ZONE_OF_SCRIPT)
246 word_sep = '_';
247 else
248 word_sep = ' ';
250 if ((out_flags != ZONE_OF_SCRIPT)
251 && (first_pass != 0)) {
252 printf("%u zones, Maximum LBA %#jx (%ju)\n",
253 report->entries_available,
254 (uintmax_t)header->maximum_lba,
255 (uintmax_t)header->maximum_lba);
257 switch (header->same) {
258 case DISK_ZONE_SAME_ALL_DIFFERENT:
259 printf("Zone lengths and types may vary\n");
260 break;
261 case DISK_ZONE_SAME_ALL_SAME:
262 printf("Zone lengths and types are all the same\n");
263 break;
264 case DISK_ZONE_SAME_LAST_DIFFERENT:
265 printf("Zone types are the same, last zone length "
266 "differs\n");
267 break;
268 case DISK_ZONE_SAME_TYPES_DIFFERENT:
269 printf("Zone lengths are the same, types vary\n");
270 break;
271 default:
272 printf("Unknown SAME field value %#x\n",header->same);
273 break;
276 if (out_flags == ZONE_OF_SUMMARY) {
277 status = ZONE_PRINT_OK;
278 goto bailout;
281 if ((out_flags == ZONE_OF_NORMAL)
282 && (first_pass != 0)) {
283 printf("%*s %*s %*s %*s %*s %*s %*s\n",
284 field_widths[ZONE_FW_START], "Start LBA",
285 field_widths[ZONE_FW_LEN], "Length",
286 field_widths[ZONE_FW_WP], "WP LBA",
287 field_widths[ZONE_FW_TYPE], "Zone Type",
288 field_widths[ZONE_FW_COND], "Condition",
289 field_widths[ZONE_FW_SEQ], "Sequential",
290 field_widths[ZONE_FW_RESET], "Reset");
293 for (i = 0; i < report->entries_filled; i++) {
294 entry = &report->entries[i];
296 printf("%#*jx, %*ju, %#*jx, ", field_widths[ZONE_FW_START],
297 (uintmax_t)entry->zone_start_lba,
298 field_widths[ZONE_FW_LEN],
299 (uintmax_t)entry->zone_length, field_widths[ZONE_FW_WP],
300 (uintmax_t)entry->write_pointer_lba);
302 switch (entry->zone_type) {
303 case DISK_ZONE_TYPE_CONVENTIONAL:
304 snprintf(tmpstr, sizeof(tmpstr), "Conventional");
305 break;
306 case DISK_ZONE_TYPE_SEQ_PREFERRED:
307 case DISK_ZONE_TYPE_SEQ_REQUIRED:
308 snprintf(tmpstr, sizeof(tmpstr), "Seq%c%s",
309 word_sep, (entry->zone_type ==
310 DISK_ZONE_TYPE_SEQ_PREFERRED) ? "Preferred" :
311 "Required");
312 break;
313 default:
314 snprintf(tmpstr, sizeof(tmpstr), "Zone%ctype%c%#x",
315 word_sep, word_sep, entry->zone_type);
316 break;
318 printf("%*s, ", field_widths[ZONE_FW_TYPE], tmpstr);
320 switch (entry->zone_condition) {
321 case DISK_ZONE_COND_NOT_WP:
322 snprintf(tmpstr, sizeof(tmpstr), "NWP");
323 break;
324 case DISK_ZONE_COND_EMPTY:
325 snprintf(tmpstr, sizeof(tmpstr), "Empty");
326 break;
327 case DISK_ZONE_COND_IMPLICIT_OPEN:
328 snprintf(tmpstr, sizeof(tmpstr), "Implicit%cOpen",
329 word_sep);
330 break;
331 case DISK_ZONE_COND_EXPLICIT_OPEN:
332 snprintf(tmpstr, sizeof(tmpstr), "Explicit%cOpen",
333 word_sep);
334 break;
335 case DISK_ZONE_COND_CLOSED:
336 snprintf(tmpstr, sizeof(tmpstr), "Closed");
337 break;
338 case DISK_ZONE_COND_READONLY:
339 snprintf(tmpstr, sizeof(tmpstr), "Readonly");
340 break;
341 case DISK_ZONE_COND_FULL:
342 snprintf(tmpstr, sizeof(tmpstr), "Full");
343 break;
344 case DISK_ZONE_COND_OFFLINE:
345 snprintf(tmpstr, sizeof(tmpstr), "Offline");
346 break;
347 default:
348 snprintf(tmpstr, sizeof(tmpstr), "%#x",
349 entry->zone_condition);
350 break;
353 printf("%*s, ", field_widths[ZONE_FW_COND], tmpstr);
355 if (entry->zone_flags & DISK_ZONE_FLAG_NON_SEQ)
356 snprintf(tmpstr, sizeof(tmpstr), "Non%cSequential",
357 word_sep);
358 else
359 snprintf(tmpstr, sizeof(tmpstr), "Sequential");
361 printf("%*s, ", field_widths[ZONE_FW_SEQ], tmpstr);
363 if (entry->zone_flags & DISK_ZONE_FLAG_RESET)
364 snprintf(tmpstr, sizeof(tmpstr), "Reset%cNeeded",
365 word_sep);
366 else
367 snprintf(tmpstr, sizeof(tmpstr), "No%cReset%cNeeded",
368 word_sep, word_sep);
370 printf("%*s\n", field_widths[ZONE_FW_RESET], tmpstr);
372 next_lba = entry->zone_start_lba + entry->zone_length;
374 bailout:
375 report->starting_id = next_lba;
377 return (status);
381 main(int argc, char **argv)
383 int c;
384 int all_zones = 0;
385 int error = 0;
386 int action = -1, rep_option = -1;
387 int fd = -1;
388 uint64_t lba = 0;
389 zone_output_flags out_flags = ZONE_OF_NORMAL;
390 char *filename = NULL;
391 struct disk_zone_args zone_args;
392 struct disk_zone_rep_entry *entries = NULL;
393 uint32_t num_entries = 16384;
394 zone_print_status zp_status;
395 int first_pass = 1;
396 size_t entry_alloc_size;
397 int open_flags = O_RDONLY;
399 while ((c = getopt(argc, argv, "ac:d:hl:o:P:?")) != -1) {
400 switch (c) {
401 case 'a':
402 all_zones = 1;
403 break;
404 case 'c': {
405 scsi_nv_status status;
406 int entry_num;
408 status = scsi_get_nv(zone_cmd_map,
409 (sizeof(zone_cmd_map) / sizeof(zone_cmd_map[0])),
410 optarg, &entry_num, SCSI_NV_FLAG_IG_CASE);
411 if (status == SCSI_NV_FOUND)
412 action = zone_cmd_map[entry_num].value;
413 else {
414 warnx("%s: %s: %s option %s", __func__,
415 (status == SCSI_NV_AMBIGUOUS) ?
416 "ambiguous" : "invalid", "zone command",
417 optarg);
418 error = 1;
419 goto bailout;
421 break;
423 case 'd':
424 filename = strdup(optarg);
425 if (filename == NULL)
426 err(1, "Unable to allocate memory for "
427 "filename");
428 break;
429 case 'l': {
430 char *endptr;
432 lba = strtoull(optarg, &endptr, 0);
433 if (*endptr != '\0') {
434 warnx("%s: invalid lba argument %s", __func__,
435 optarg);
436 error = 1;
437 goto bailout;
439 break;
441 case 'o': {
442 scsi_nv_status status;
443 int entry_num;
445 status = scsi_get_nv(zone_rep_opts,
446 (sizeof(zone_rep_opts) /
447 sizeof(zone_rep_opts[0])),
448 optarg, &entry_num, SCSI_NV_FLAG_IG_CASE);
449 if (status == SCSI_NV_FOUND)
450 rep_option = zone_rep_opts[entry_num].value;
451 else {
452 warnx("%s: %s: %s option %s", __func__,
453 (status == SCSI_NV_AMBIGUOUS) ?
454 "ambiguous" : "invalid", "report zones",
455 optarg);
456 error = 1;
457 goto bailout;
459 break;
461 case 'P': {
462 scsi_nv_status status;
463 int entry_num;
465 status = scsi_get_nv(zone_print_opts,
466 (sizeof(zone_print_opts) /
467 sizeof(zone_print_opts[0])), optarg, &entry_num,
468 SCSI_NV_FLAG_IG_CASE);
469 if (status == SCSI_NV_FOUND)
470 out_flags = zone_print_opts[entry_num].value;
471 else {
472 warnx("%s: %s: %s option %s", __func__,
473 (status == SCSI_NV_AMBIGUOUS) ?
474 "ambiguous" : "invalid", "print",
475 optarg);
476 error = 1;
477 goto bailout;
479 break;
481 default:
482 error = 1;
483 case 'h': /*FALLTHROUGH*/
484 usage(error);
485 goto bailout;
486 break; /*NOTREACHED*/
490 if (filename == NULL) {
491 warnx("You must specify a device with -d");
492 error = 1;
494 if (action == -1) {
495 warnx("You must specify an action with -c");
496 error = 1;
499 if (error != 0) {
500 usage(error);
501 goto bailout;
504 bzero(&zone_args, sizeof(zone_args));
506 zone_args.zone_cmd = action;
508 switch (action) {
509 case DISK_ZONE_OPEN:
510 case DISK_ZONE_CLOSE:
511 case DISK_ZONE_FINISH:
512 case DISK_ZONE_RWP:
513 open_flags = O_RDWR;
514 zone_args.zone_params.rwp.id = lba;
515 if (all_zones != 0)
516 zone_args.zone_params.rwp.flags |=
517 DISK_ZONE_RWP_FLAG_ALL;
518 break;
519 case DISK_ZONE_REPORT_ZONES: {
520 entry_alloc_size = num_entries *
521 sizeof(struct disk_zone_rep_entry);
522 entries = malloc(entry_alloc_size);
523 if (entries == NULL) {
524 warn("Could not allocate %zu bytes",
525 entry_alloc_size);
526 error = 1;
527 goto bailout;
529 zone_args.zone_params.report.entries_allocated = num_entries;
530 zone_args.zone_params.report.entries = entries;
531 zone_args.zone_params.report.starting_id = lba;
532 if (rep_option != -1)
533 zone_args.zone_params.report.rep_options = rep_option;
534 break;
536 case DISK_ZONE_GET_PARAMS:
537 break;
538 default:
539 warnx("Unknown action %d", action);
540 error = 1;
541 goto bailout;
542 break; /*NOTREACHED*/
545 fd = open(filename, open_flags);
546 if (fd == -1) {
547 warn("Unable to open device %s", filename);
548 error = 1;
549 goto bailout;
551 next_chunk:
552 error = ioctl(fd, DIOCZONECMD, &zone_args);
553 if (error == -1) {
554 warn("DIOCZONECMD ioctl failed");
555 error = 1;
556 goto bailout;
559 switch (action) {
560 case DISK_ZONE_OPEN:
561 case DISK_ZONE_CLOSE:
562 case DISK_ZONE_FINISH:
563 case DISK_ZONE_RWP:
564 break;
565 case DISK_ZONE_REPORT_ZONES:
566 zp_status = zonectl_print_rz(&zone_args.zone_params.report,
567 out_flags, first_pass);
568 if (zp_status == ZONE_PRINT_MORE_DATA) {
569 first_pass = 0;
570 bzero(entries, entry_alloc_size);
571 zone_args.zone_params.report.entries_filled = 0;
572 goto next_chunk;
573 } else if (zp_status == ZONE_PRINT_ERROR)
574 error = 1;
575 break;
576 case DISK_ZONE_GET_PARAMS:
577 zonectl_print_params(&zone_args.zone_params.disk_params);
578 break;
579 default:
580 warnx("Unknown action %d", action);
581 error = 1;
582 goto bailout;
583 break; /*NOTREACHED*/
585 bailout:
586 free(entries);
588 if (fd != -1)
589 close(fd);
590 exit (error);