]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/mt/mt.c
usr.bin: Remove ancient SCCS tags.
[FreeBSD/FreeBSD.git] / usr.bin / mt / mt.c
1 /*
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  *      The Regents of the University of California.  All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. Neither the name of the University nor the names of its contributors
15  *    may be used to endorse or promote products derived from this software
16  *    without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, 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, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30 /*-
31  * Copyright (c) 2013, 2014, 2015 Spectra Logic Corporation
32  * All rights reserved.
33  *
34  * Redistribution and use in source and binary forms, with or without
35  * modification, are permitted provided that the following conditions
36  * are met:
37  * 1. Redistributions of source code must retain the above copyright
38  *    notice, this list of conditions, and the following disclaimer,
39  *    without modification.
40  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
41  *    substantially similar to the "NO WARRANTY" disclaimer below
42  *    ("Disclaimer") and any redistribution must be conditioned upon
43  *    including a substantially similar Disclaimer requirement for further
44  *    binary redistribution.
45  *
46  * NO WARRANTY
47  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
48  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
49  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
50  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
51  * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
52  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
53  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
54  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
55  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
56  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
57  * POSSIBILITY OF SUCH DAMAGES.
58  *
59  * Authors: Ken Merry           (Spectra Logic Corporation)
60  */
61
62 #ifndef lint
63 static const char copyright[] =
64 "@(#) Copyright (c) 1980, 1993\n\
65         The Regents of the University of California.  All rights reserved.\n";
66 #endif /* not lint */
67
68 #ifndef lint
69 #endif /* not lint */
70
71 #include <sys/cdefs.h>
72 /*
73  * mt --
74  *   magnetic tape manipulation program
75  */
76 #include <sys/types.h>
77 #include <sys/ioctl.h>
78 #include <sys/mtio.h>
79 #include <sys/queue.h>
80
81 #include <ctype.h>
82 #include <err.h>
83 #include <fcntl.h>
84 #include <stdio.h>
85 #include <stdlib.h>
86 #include <string.h>
87 #include <unistd.h>
88 #include <stdint.h>
89 #include <errno.h>
90 #include <bsdxml.h>
91 #include <mtlib.h>
92
93 #include <cam/cam.h>
94 #include <cam/cam_ccb.h>
95 #include <cam/cam_periph.h>
96 #include <cam/scsi/scsi_all.h>
97 #include <cam/scsi/scsi_sa.h>
98
99 /* the appropriate sections of <sys/mtio.h> are also #ifdef'd for FreeBSD */
100 /* c_flags */
101 #define NEED_2ARGS      0x01
102 #define ZERO_ALLOWED    0x02
103 #define IS_DENSITY      0x04
104 #define DISABLE_THIS    0x08
105 #define IS_COMP         0x10
106 #define USE_GETOPT      0x20
107
108 #ifndef TRUE
109 #define TRUE 1
110 #endif
111 #ifndef FALSE
112 #define FALSE 0
113 #endif
114 #ifndef MAX
115 #define MAX(a, b) (a > b) ? a : b
116 #endif
117 #define MT_PLURAL(a) (a == 1) ? "" : "s"
118
119 typedef enum {
120         MT_CMD_NONE     = MTLOAD + 1,
121         MT_CMD_PROTECT,
122         MT_CMD_GETDENSITY
123 } mt_commands;
124
125 static const struct commands {
126         const char *c_name;
127         unsigned long c_code;
128         int c_ronly;
129         int c_flags;
130 } com[] = {
131         { "bsf",        MTBSF,  1, 0 },
132         { "bsr",        MTBSR,  1, 0 },
133         /* XXX FreeBSD considered "eof" dangerous, since it's being
134            confused with "eom" (and is an alias for "weof" anyway) */
135         { "eof",        MTWEOF, 0, DISABLE_THIS },
136         { "fsf",        MTFSF,  1, 0 },
137         { "fsr",        MTFSR,  1, 0 },
138         { "offline",    MTOFFL, 1, 0 },
139         { "load",       MTLOAD, 1, 0 },
140         { "rewind",     MTREW,  1, 0 },
141         { "rewoffl",    MTOFFL, 1, 0 },
142         { "ostatus",    MTNOP,  1, 0 },
143         { "weof",       MTWEOF, 0, ZERO_ALLOWED },
144         { "weofi",      MTWEOFI, 0, ZERO_ALLOWED },
145         { "erase",      MTERASE, 0, ZERO_ALLOWED},
146         { "blocksize",  MTSETBSIZ, 0, NEED_2ARGS|ZERO_ALLOWED },
147         { "density",    MTSETDNSTY, 0, NEED_2ARGS|ZERO_ALLOWED|IS_DENSITY },
148         { "eom",        MTEOD, 1, 0 },
149         { "eod",        MTEOD, 1, 0 },
150         { "smk",        MTWSS, 0, 0 },
151         { "wss",        MTWSS, 0, 0 },
152         { "fss",        MTFSS, 1, 0 },
153         { "bss",        MTBSS, 1, 0 },
154         { "comp",       MTCOMP, 0, NEED_2ARGS|ZERO_ALLOWED|IS_COMP },
155         { "retension",  MTRETENS, 1, 0 },
156         { "rdhpos",     MTIOCRDHPOS,  0, 0 },
157         { "rdspos",     MTIOCRDSPOS,  0, 0 },
158         { "sethpos",    MTIOCHLOCATE, 0, NEED_2ARGS|ZERO_ALLOWED },
159         { "setspos",    MTIOCSLOCATE, 0, NEED_2ARGS|ZERO_ALLOWED },
160         { "errstat",    MTIOCERRSTAT, 0, 0 },
161         { "setmodel",   MTIOCSETEOTMODEL, 0, NEED_2ARGS|ZERO_ALLOWED },
162         { "seteotmodel",        MTIOCSETEOTMODEL, 0, NEED_2ARGS|ZERO_ALLOWED },
163         { "getmodel",   MTIOCGETEOTMODEL, 0, 0 },
164         { "geteotmodel",        MTIOCGETEOTMODEL, 0, 0 },
165         { "rblim",      MTIOCRBLIM, 0, 0},
166         { "getdensity", MT_CMD_GETDENSITY, 0, USE_GETOPT},
167         { "status",     MTIOCEXTGET, 0, USE_GETOPT },
168         { "locate",     MTIOCEXTLOCATE, 0, USE_GETOPT },
169         { "param",      MTIOCPARAMGET, 0, USE_GETOPT },
170         { "protect",    MT_CMD_PROTECT, 0, USE_GETOPT },
171         { NULL, 0, 0, 0 }
172 };
173
174
175 static const char *getblksiz(int);
176 static void printreg(const char *, u_int, const char *);
177 static void status(struct mtget *);
178 static void usage(void) __dead2;
179 const char *get_driver_state_str(int dsreg);
180 static void st_status (struct mtget *);
181 static int mt_locate(int argc, char **argv, int mtfd, const char *tape);
182 static int nstatus_print(int argc, char **argv, char *xml_str,
183                          struct mt_status_data *status_data);
184 static int mt_xml_cmd(unsigned long cmd, int argc, char **argv, int mtfd,
185                       const char *tape);
186 static int mt_print_density_entry(struct mt_status_entry *density_root, int indent);
187 static int mt_print_density_report(struct mt_status_entry *report_root, int indent);
188 static int mt_print_density(struct mt_status_entry *density_root, int indent);
189 static int mt_getdensity(int argc, char **argv, char *xml_str,
190                          struct mt_status_data *status_data);
191 static int mt_set_param(int mtfd, struct mt_status_data *status_data,
192                         char *param_name, char *param_value);
193 static int mt_protect(int argc, char **argv, int mtfd,
194                       struct mt_status_data *status_data);
195 static int mt_param(int argc, char **argv, int mtfd, char *xml_str,
196                     struct mt_status_data *status_data);
197 static const char *denstostring (int d);
198 static u_int32_t stringtocomp(const char *s);
199 static const char *comptostring(u_int32_t comp);
200 static void warn_eof(void);
201
202 int
203 main(int argc, char *argv[])
204 {
205         const struct commands *comp;
206         struct mtget mt_status;
207         struct mtop mt_com;
208         int ch, len, mtfd;
209         const char *p, *tape;
210
211         bzero(&mt_com, sizeof(mt_com));
212         
213         if ((tape = getenv("TAPE")) == NULL)
214                 tape = DEFTAPE;
215
216         while ((ch = getopt(argc, argv, "f:t:")) != -1)
217                 switch(ch) {
218                 case 'f':
219                 case 't':
220                         tape = optarg;
221                         break;
222                 case '?':
223                         usage();
224                         break;
225                 default:
226                         break;
227                 }
228         argc -= optind;
229         argv += optind;
230
231         if (argc < 1)
232                 usage();
233
234         len = strlen(p = *argv++);
235         for (comp = com;; comp++) {
236                 if (comp->c_name == NULL)
237                         errx(1, "%s: unknown command", p);
238                 if (strncmp(p, comp->c_name, len) == 0)
239                         break;
240         }
241         if((comp->c_flags & NEED_2ARGS) && argc != 2)
242                 usage();
243         if(comp->c_flags & DISABLE_THIS) {
244                 warn_eof();
245         }
246         if (comp->c_flags & USE_GETOPT) {
247                 argc--;
248                 optind = 0;
249         }
250
251         if ((mtfd = open(tape, comp->c_ronly ? O_RDONLY : O_RDWR)) < 0)
252                 err(1, "%s", tape);
253         if (comp->c_code != MTNOP) {
254                 mt_com.mt_op = comp->c_code;
255                 if (*argv) {
256                         if (!isdigit(**argv) &&
257                             (comp->c_flags & IS_DENSITY)) {
258                                 const char *dcanon;
259                                 mt_com.mt_count = mt_density_num(*argv);
260                                 if (mt_com.mt_count == 0)
261                                         errx(1, "%s: unknown density", *argv);
262                                 dcanon = denstostring(mt_com.mt_count);
263                                 if (strcmp(dcanon, *argv) != 0)
264                                         printf(
265                                         "Using \"%s\" as an alias for %s\n",
266                                                *argv, dcanon);
267                                 p = "";
268                         } else if (!isdigit(**argv) &&
269                                    (comp->c_flags & IS_COMP)) {
270
271                                 mt_com.mt_count = stringtocomp(*argv);
272                                 if ((u_int32_t)mt_com.mt_count == 0xf0f0f0f0)
273                                         errx(1, "%s: unknown compression",
274                                              *argv);
275                                 p = "";
276                         } else if ((comp->c_flags & USE_GETOPT) == 0) {
277                                 char *q;
278                                 /* allow for hex numbers; useful for density */
279                                 mt_com.mt_count = strtol(*argv, &q, 0);
280                                 p = q;
281                         }
282                         if (((comp->c_flags & USE_GETOPT) == 0)
283                          && (((mt_com.mt_count <=
284                              ((comp->c_flags & ZERO_ALLOWED)? -1: 0))
285                            && ((comp->c_flags & IS_COMP) == 0))
286                           || *p))
287                                 errx(1, "%s: illegal count", *argv);
288                 }
289                 else
290                         mt_com.mt_count = 1;
291                 switch (comp->c_code) {
292                 case MTIOCERRSTAT:
293                 {
294                         unsigned int i;
295                         union mterrstat umn;
296                         struct scsi_tape_errors *s = &umn.scsi_errstat;
297
298                         if (ioctl(mtfd, comp->c_code, (caddr_t)&umn) < 0)
299                                 err(2, "%s", tape);
300                         (void)printf("Last I/O Residual: %u\n", s->io_resid);
301                         (void)printf(" Last I/O Command:");
302                         for (i = 0; i < sizeof (s->io_cdb); i++)
303                                 (void)printf(" %02X", s->io_cdb[i]);
304                         (void)printf("\n");
305                         (void)printf("   Last I/O Sense:\n\n\t");
306                         for (i = 0; i < sizeof (s->io_sense); i++) {
307                                 (void)printf(" %02X", s->io_sense[i]);
308                                 if (((i + 1) & 0xf) == 0) {
309                                         (void)printf("\n\t");
310                                 }
311                         }
312                         (void)printf("\n");
313                         (void)printf("Last Control Residual: %u\n",
314                             s->ctl_resid);
315                         (void)printf(" Last Control Command:");
316                         for (i = 0; i < sizeof (s->ctl_cdb); i++)
317                                 (void)printf(" %02X", s->ctl_cdb[i]);
318                         (void)printf("\n");
319                         (void)printf("   Last Control Sense:\n\n\t");
320                         for (i = 0; i < sizeof (s->ctl_sense); i++) {
321                                 (void)printf(" %02X", s->ctl_sense[i]);
322                                 if (((i + 1) & 0xf) == 0) {
323                                         (void)printf("\n\t");
324                                 }
325                         }
326                         (void)printf("\n\n");
327                         exit(0);
328                         /* NOTREACHED */
329                 }
330                 case MTIOCRDHPOS:
331                 case MTIOCRDSPOS:
332                 {
333                         u_int32_t block;
334                         if (ioctl(mtfd, comp->c_code, (caddr_t)&block) < 0)
335                                 err(2, "%s", tape);
336                         (void)printf("%s: %s block location %u\n", tape,
337                             (comp->c_code == MTIOCRDHPOS)? "hardware" :
338                             "logical", block);
339                         exit(0);
340                         /* NOTREACHED */
341                 }
342                 case MTIOCSLOCATE:
343                 case MTIOCHLOCATE:
344                 {
345                         u_int32_t block = (u_int32_t)mt_com.mt_count;
346                         if (ioctl(mtfd, comp->c_code, (caddr_t)&block) < 0)
347                                 err(2, "%s", tape);
348                         exit(0);
349                         /* NOTREACHED */
350                 }
351                 case MTIOCGETEOTMODEL:
352                 {
353                         u_int32_t om;
354                         if (ioctl(mtfd, MTIOCGETEOTMODEL, (caddr_t)&om) < 0)
355                                 err(2, "%s", tape);
356                         (void)printf("%s: the model is %u filemar%s at EOT\n",
357                             tape, om, (om > 1)? "ks" : "k");
358                         exit(0);
359                         /* NOTREACHED */
360                 }
361                 case MTIOCSETEOTMODEL:
362                 {
363                         u_int32_t om, nm = (u_int32_t)mt_com.mt_count;
364                         if (ioctl(mtfd, MTIOCGETEOTMODEL, (caddr_t)&om) < 0)
365                                 err(2, "%s", tape);
366                         if (ioctl(mtfd, comp->c_code, (caddr_t)&nm) < 0)
367                                 err(2, "%s", tape);
368                         (void)printf("%s: old model was %u filemar%s at EOT\n",
369                             tape, om, (om > 1)? "ks" : "k");
370                         (void)printf("%s: new model  is %u filemar%s at EOT\n",
371                             tape, nm, (nm > 1)? "ks" : "k");
372                         exit(0);
373                         /* NOTREACHED */
374                 }
375                 case MTIOCRBLIM:
376                 {
377                         struct mtrblim rblim;
378
379                         bzero(&rblim, sizeof(rblim));
380
381                         if (ioctl(mtfd, MTIOCRBLIM, (caddr_t)&rblim) < 0)
382                                 err(2, "%s", tape);
383                         (void)printf("%s:\n"
384                             "    min blocksize %u byte%s\n"
385                             "    max blocksize %u byte%s\n"
386                             "    granularity %u byte%s\n",
387                             tape, rblim.min_block_length,
388                             MT_PLURAL(rblim.min_block_length),
389                             rblim.max_block_length,
390                             MT_PLURAL(rblim.max_block_length),
391                             (1 << rblim.granularity),
392                             MT_PLURAL((1 << rblim.granularity)));
393                         exit(0);
394                         /* NOTREACHED */
395                 }
396                 case MTIOCPARAMGET:
397                 case MTIOCEXTGET:
398                 case MT_CMD_PROTECT:
399                 case MT_CMD_GETDENSITY:
400                 {
401                         int retval = 0;
402
403                         retval = mt_xml_cmd(comp->c_code, argc, argv, mtfd,
404                             tape);
405
406                         exit(retval);
407                 }
408                 case MTIOCEXTLOCATE:
409                 {
410                         int retval = 0;
411
412                         retval = mt_locate(argc, argv, mtfd, tape);
413
414                         exit(retval);
415                 }
416                 default:
417                         break;
418                 }
419                 if (ioctl(mtfd, MTIOCTOP, &mt_com) < 0)
420                         err(1, "%s: %s", tape, comp->c_name);
421         } else {
422                 if (ioctl(mtfd, MTIOCGET, &mt_status) < 0)
423                         err(1, NULL);
424                 status(&mt_status);
425         }
426         exit(0);
427         /* NOTREACHED */
428 }
429
430 static const struct tape_desc {
431         short   t_type;         /* type of magtape device */
432         const char *t_name;     /* printing name */
433         const char *t_dsbits;   /* "drive status" register */
434         const char *t_erbits;   /* "error" register */
435 } tapes[] = {
436         { MT_ISAR,      "SCSI tape drive", 0,           0 },
437         { 0, NULL, 0, 0 }
438 };
439
440 /*
441  * Interpret the status buffer returned
442  */
443 static void
444 status(struct mtget *bp)
445 {
446         const struct tape_desc *mt;
447
448         for (mt = tapes;; mt++) {
449                 if (mt->t_type == 0) {
450                         (void)printf("%d: unknown tape drive type\n",
451                             bp->mt_type);
452                         return;
453                 }
454                 if (mt->t_type == bp->mt_type)
455                         break;
456         }
457         if(mt->t_type == MT_ISAR)
458                 st_status(bp);
459         else {
460                 (void)printf("%s tape drive, residual=%d\n", 
461                     mt->t_name, bp->mt_resid);
462                 printreg("ds", (unsigned short)bp->mt_dsreg, mt->t_dsbits);
463                 printreg("\ner", (unsigned short)bp->mt_erreg, mt->t_erbits);
464                 (void)putchar('\n');
465         }
466 }
467
468 /*
469  * Print a register a la the %b format of the kernel's printf.
470  */
471 static void
472 printreg(const char *s, u_int v, const char *bits)
473 {
474         int i, any = 0;
475         char c;
476
477         if (bits && *bits == 8)
478                 printf("%s=%o", s, v);
479         else
480                 printf("%s=%x", s, v);
481         if (!bits)
482                 return;
483         bits++;
484         if (v && bits) {
485                 putchar('<');
486                 while ((i = *bits++)) {
487                         if (v & (1 << (i-1))) {
488                                 if (any)
489                                         putchar(',');
490                                 any = 1;
491                                 for (; (c = *bits) > 32; bits++)
492                                         putchar(c);
493                         } else
494                                 for (; *bits > 32; bits++)
495                                         ;
496                 }
497                 putchar('>');
498         }
499 }
500
501 static void
502 usage(void)
503 {
504         (void)fprintf(stderr, "usage: mt [-f device] command [count]\n");
505         exit(1);
506 }
507
508 static const struct compression_types {
509         u_int32_t       comp_number;
510         const char      *name;
511 } comp_types[] = {
512         { 0x00, "none" },
513         { 0x00, "off" },
514         { 0x10, "IDRC" },
515         { 0x20, "DCLZ" },
516         { 0xffffffff, "enable" },
517         { 0xffffffff, "on" },
518         { 0xf0f0f0f0, NULL}
519 };
520
521 static const char *
522 denstostring(int d)
523 {
524         static char buf[20];
525         const char *name = mt_density_name(d);
526
527         if (name == NULL)
528                 sprintf(buf, "0x%02x", d);
529         else 
530                 sprintf(buf, "0x%02x:%s", d, name);
531         return buf;
532 }
533
534 static const char *
535 getblksiz(int bs)
536 {
537         static char buf[25];
538         if (bs == 0)
539                 return "variable";
540         else {
541                 sprintf(buf, "%d bytes", bs);
542                 return buf;
543         }
544 }
545
546 static const char *
547 comptostring(u_int32_t comp)
548 {
549         static char buf[20];
550         const struct compression_types *ct;
551
552         if (comp == MT_COMP_DISABLED)
553                 return "disabled";
554         else if (comp == MT_COMP_UNSUPP)
555                 return "unsupported";
556
557         for (ct = comp_types; ct->name; ct++)
558                 if (ct->comp_number == comp)
559                         break;
560
561         if (ct->comp_number == 0xf0f0f0f0) {
562                 sprintf(buf, "0x%x", comp);
563                 return(buf);
564         } else
565                 return(ct->name);
566 }
567
568 static u_int32_t
569 stringtocomp(const char *s)
570 {
571         const struct compression_types *ct;
572         size_t l = strlen(s);
573
574         for (ct = comp_types; ct->name; ct++)
575                 if (strncasecmp(ct->name, s, l) == 0)
576                         break;
577
578         return(ct->comp_number);
579 }
580
581 static struct driver_state {
582         int dsreg;
583         const char *desc;
584 } driver_states[] = {
585         { MTIO_DSREG_REST, "at rest" },
586         { MTIO_DSREG_RBSY, "Communicating with drive" },
587         { MTIO_DSREG_WR, "Writing" },
588         { MTIO_DSREG_FMK, "Writing Filemarks" },
589         { MTIO_DSREG_ZER, "Erasing" },
590         { MTIO_DSREG_RD, "Reading" },
591         { MTIO_DSREG_FWD, "Spacing Forward" },
592         { MTIO_DSREG_REV, "Spacing Reverse" },
593         { MTIO_DSREG_POS, "Hardware Positioning (direction unknown)" },
594         { MTIO_DSREG_REW, "Rewinding" },
595         { MTIO_DSREG_TEN, "Retensioning" },
596         { MTIO_DSREG_UNL, "Unloading" },
597         { MTIO_DSREG_LD, "Loading" },
598 };
599
600 const char *
601 get_driver_state_str(int dsreg)
602 {
603         unsigned int i;
604
605         for (i = 0; i < (sizeof(driver_states)/sizeof(driver_states[0])); i++) {
606                 if (driver_states[i].dsreg == dsreg)
607                         return (driver_states[i].desc);
608         }
609
610         return (NULL);
611 }
612
613 static void
614 st_status(struct mtget *bp)
615 {
616         printf("Mode      Density              Blocksize      bpi      "
617                "Compression\n"
618                "Current:  %-17s    %-12s   %-7d  %s\n"
619                "---------available modes---------\n"
620                "0:        %-17s    %-12s   %-7d  %s\n"
621                "1:        %-17s    %-12s   %-7d  %s\n"
622                "2:        %-17s    %-12s   %-7d  %s\n"
623                "3:        %-17s    %-12s   %-7d  %s\n",
624                denstostring(bp->mt_density), getblksiz(bp->mt_blksiz),
625                mt_density_bp(bp->mt_density, TRUE), comptostring(bp->mt_comp),
626                denstostring(bp->mt_density0), getblksiz(bp->mt_blksiz0),
627                mt_density_bp(bp->mt_density0, TRUE), comptostring(bp->mt_comp0),
628                denstostring(bp->mt_density1), getblksiz(bp->mt_blksiz1),
629                mt_density_bp(bp->mt_density1, TRUE), comptostring(bp->mt_comp1),
630                denstostring(bp->mt_density2), getblksiz(bp->mt_blksiz2),
631                mt_density_bp(bp->mt_density2, TRUE), comptostring(bp->mt_comp2),
632                denstostring(bp->mt_density3), getblksiz(bp->mt_blksiz3),
633                mt_density_bp(bp->mt_density3, TRUE), comptostring(bp->mt_comp3));
634
635         if (bp->mt_dsreg != MTIO_DSREG_NIL) {
636                 const char sfmt[] = "Current Driver State: %s.\n";
637                 printf("---------------------------------\n");
638                 const char *state_str;
639
640                 state_str = get_driver_state_str(bp->mt_dsreg);
641                 if (state_str == NULL) {
642                         char foo[32];
643                         (void) sprintf(foo, "Unknown state 0x%x", bp->mt_dsreg);
644                         printf(sfmt, foo);
645                 } else {
646                         printf(sfmt, state_str);
647                 }
648         }
649         if (bp->mt_resid == 0 && bp->mt_fileno == (daddr_t) -1 &&
650             bp->mt_blkno == (daddr_t) -1)
651                 return;
652         printf("---------------------------------\n");
653         printf("File Number: %d\tRecord Number: %d\tResidual Count %d\n",
654             bp->mt_fileno, bp->mt_blkno, bp->mt_resid);
655 }
656
657 static int
658 mt_locate(int argc, char **argv, int mtfd, const char *tape)
659 {
660         struct mtlocate mtl;
661         uint64_t logical_id = 0;
662         mt_locate_dest_type dest_type = MT_LOCATE_DEST_FILE;
663         int eod = 0, explicit = 0, immediate = 0;
664         int64_t partition = 0;
665         int block_addr_set = 0, partition_set = 0, file_set = 0, set_set = 0;
666         int c, retval;
667
668         retval = 0;
669         bzero(&mtl, sizeof(mtl));
670
671         while ((c = getopt(argc, argv, "b:eEf:ip:s:")) != -1) {
672                 switch (c) {
673                 case 'b':
674                         /* Block address */
675                         logical_id = strtoull(optarg, NULL, 0);
676                         dest_type = MT_LOCATE_DEST_OBJECT;
677                         block_addr_set = 1;
678                         break;
679                 case 'e':
680                         /* end of data */
681                         eod = 1;
682                         dest_type = MT_LOCATE_DEST_EOD;
683                         break;
684                 case 'E':
685                         /*
686                          * XXX KDM explicit address mode.  Should we even
687                          * allow this, since the driver doesn't operate in
688                          * explicit address mode?
689                          */
690                         explicit = 1;
691                         break;
692                 case 'f':
693                         /* file number */
694                         logical_id = strtoull(optarg, NULL, 0);
695                         dest_type = MT_LOCATE_DEST_FILE;
696                         file_set = 1;
697                         break;
698                 case 'i':
699                         /*
700                          * Immediate address mode.  XXX KDM do we want to
701                          * implement this?  The other commands in the
702                          * tape driver will need to be able to handle this.
703                          */
704                         immediate = 1;
705                         break;
706                 case 'p':
707                         /*
708                          * Change partition to the given partition.
709                          */
710                         partition = strtol(optarg, NULL, 0);
711                         partition_set = 1;
712                         break;
713                 case 's':
714                         /* Go to the given set mark */
715                         logical_id = strtoull(optarg, NULL, 0);
716                         dest_type = MT_LOCATE_DEST_SET;
717                         set_set = 1;
718                         break;
719                 default:
720                         break;
721                 }
722         }
723
724         /*
725          * These options are mutually exclusive.  The user may only specify
726          * one.
727          */
728         if ((block_addr_set + file_set + eod + set_set) != 1)
729                 errx(1, "You must specify only one of -b, -f, -e, or -s");
730
731         mtl.dest_type = dest_type;
732         switch (dest_type) {
733         case MT_LOCATE_DEST_OBJECT:
734         case MT_LOCATE_DEST_FILE:
735         case MT_LOCATE_DEST_SET:
736                 mtl.logical_id = logical_id;
737                 break;
738         case MT_LOCATE_DEST_EOD:
739                 break;
740         }
741
742         if (immediate != 0)
743                 mtl.flags |= MT_LOCATE_FLAG_IMMED;
744
745         if (partition_set != 0) {
746                 mtl.flags |= MT_LOCATE_FLAG_CHANGE_PART;
747                 mtl.partition = partition;
748         }
749
750         if (explicit != 0)
751                 mtl.block_address_mode = MT_LOCATE_BAM_EXPLICIT;
752         else
753                 mtl.block_address_mode = MT_LOCATE_BAM_IMPLICIT;
754
755         if (ioctl(mtfd, MTIOCEXTLOCATE, &mtl) == -1)
756                 err(1, "MTIOCEXTLOCATE ioctl failed on %s", tape);
757
758         return (retval);
759 }
760
761 typedef enum {
762         MT_PERIPH_NAME                  = 0,
763         MT_UNIT_NUMBER                  = 1,
764         MT_VENDOR                       = 2,
765         MT_PRODUCT                      = 3,
766         MT_REVISION                     = 4,
767         MT_COMPRESSION_SUPPORTED        = 5,
768         MT_COMPRESSION_ENABLED          = 6,
769         MT_COMPRESSION_ALGORITHM        = 7,
770         MT_MEDIA_DENSITY                = 8,
771         MT_MEDIA_BLOCKSIZE              = 9,
772         MT_CALCULATED_FILENO            = 10,
773         MT_CALCULATED_REL_BLKNO         = 11,
774         MT_REPORTED_FILENO              = 12,
775         MT_REPORTED_BLKNO               = 13,
776         MT_PARTITION                    = 14,
777         MT_BOP                          = 15,
778         MT_EOP                          = 16,
779         MT_BPEW                         = 17,
780         MT_DSREG                        = 18,
781         MT_RESID                        = 19,
782         MT_FIXED_MODE                   = 20,
783         MT_SERIAL_NUM                   = 21,
784         MT_MAXIO                        = 22,
785         MT_CPI_MAXIO                    = 23,
786         MT_MAX_BLK                      = 24,
787         MT_MIN_BLK                      = 25,
788         MT_BLK_GRAN                     = 26,
789         MT_MAX_EFF_IOSIZE               = 27
790 } status_item_index;
791
792 static struct mt_status_items {
793         const char *name;
794         struct mt_status_entry *entry;
795 } req_status_items[] = {
796         { "periph_name", NULL },
797         { "unit_number", NULL },
798         { "vendor", NULL },
799         { "product", NULL },
800         { "revision", NULL },
801         { "compression_supported", NULL },
802         { "compression_enabled", NULL },
803         { "compression_algorithm", NULL },
804         { "media_density", NULL },
805         { "media_blocksize", NULL },
806         { "calculated_fileno", NULL },
807         { "calculated_rel_blkno", NULL },
808         { "reported_fileno", NULL },
809         { "reported_blkno", NULL },
810         { "partition", NULL },
811         { "bop", NULL },
812         { "eop", NULL },
813         { "bpew", NULL },
814         { "dsreg", NULL },
815         { "residual", NULL },
816         { "fixed_mode", NULL },
817         { "serial_num", NULL },
818         { "maxio", NULL },
819         { "cpi_maxio", NULL },
820         { "max_blk", NULL },
821         { "min_blk", NULL },
822         { "blk_gran", NULL },
823         { "max_effective_iosize", NULL }
824 };
825
826 int
827 nstatus_print(int argc, char **argv, char *xml_str,
828               struct mt_status_data *status_data)
829 {
830         unsigned int i;
831         int64_t calculated_fileno, calculated_rel_blkno;
832         int64_t rep_fileno, rep_blkno, partition, resid;
833         char block_str[32];
834         const char *dens_str;
835         int dsreg, bop, eop, bpew;
836         int xml_dump = 0;
837         size_t dens_len;
838         unsigned int field_width;
839         int verbose = 0;
840         int c;
841
842         while ((c = getopt(argc, argv, "xv")) != -1) {
843                 switch (c) {
844                 case 'x':
845                         xml_dump = 1;
846                         break;
847                 case 'v':
848                         verbose = 1;
849                         break;
850                 default:
851                         break;
852                 }
853         }
854
855         if (xml_dump != 0) {
856                 printf("%s", xml_str);
857                 return (0);
858         }
859
860         for (i = 0; i < (sizeof(req_status_items)/sizeof(req_status_items[0]));
861              i++) {
862                 char *name;
863
864                 name = __DECONST(char *, req_status_items[i].name);
865                 req_status_items[i].entry = mt_status_entry_find(status_data,
866                     name);
867                 if (req_status_items[i].entry == NULL) {
868                         errx(1, "Cannot find status entry %s",
869                             req_status_items[i].name);
870                 }
871         }
872
873         printf("Drive: %s%ju: <%s %s %s> Serial Number: %s\n",
874                req_status_items[MT_PERIPH_NAME].entry->value,
875                (uintmax_t)req_status_items[MT_UNIT_NUMBER].entry->value_unsigned,
876                req_status_items[MT_VENDOR].entry->value,
877                req_status_items[MT_PRODUCT].entry->value,
878                req_status_items[MT_REVISION].entry->value,
879                (req_status_items[MT_SERIAL_NUM].entry->value) ? 
880                req_status_items[MT_SERIAL_NUM].entry->value : "none");
881         printf("---------------------------------\n");
882
883         /*
884          * We check to see whether we're in fixed mode or not, and don't
885          * just believe the blocksize.  If the SILI bit is turned on, the
886          * blocksize will be set to 4, even though we're doing variable
887          * length (well, multiples of 4) blocks.
888          */
889         if (req_status_items[MT_FIXED_MODE].entry->value_signed == 0)
890                 snprintf(block_str, sizeof(block_str), "variable");
891         else
892                 snprintf(block_str, sizeof(block_str), "%s",
893                     getblksiz(req_status_items[
894                               MT_MEDIA_BLOCKSIZE].entry->value_unsigned));
895
896         dens_str = denstostring(req_status_items[
897             MT_MEDIA_DENSITY].entry->value_unsigned);
898         if (dens_str == NULL)
899                 dens_len = 0;
900         else
901                 dens_len = strlen(dens_str);
902         field_width = MAX(dens_len, 17);
903         printf("Mode      %-*s    Blocksize      bpi      Compression\n"
904                "Current:  %-*s    %-12s   %-7d  ",
905                field_width, "Density", field_width, dens_str, block_str,
906                mt_density_bp(req_status_items[
907                MT_MEDIA_DENSITY].entry->value_unsigned, TRUE));
908
909         if (req_status_items[MT_COMPRESSION_SUPPORTED].entry->value_signed == 0)
910                 printf("unsupported\n");
911         else if (req_status_items[
912                  MT_COMPRESSION_ENABLED].entry->value_signed == 0)
913                 printf("disabled\n");
914         else {
915                 printf("enabled (%s)\n",
916                        comptostring(req_status_items[
917                        MT_COMPRESSION_ALGORITHM].entry->value_unsigned));
918         }
919
920         dsreg = req_status_items[MT_DSREG].entry->value_signed;
921         if (dsreg != MTIO_DSREG_NIL) {
922                 const char sfmt[] = "Current Driver State: %s.\n";
923                 printf("---------------------------------\n");
924                 const char *state_str;
925
926                 state_str = get_driver_state_str(dsreg);
927                 if (state_str == NULL) {
928                         char foo[32];
929                         (void) sprintf(foo, "Unknown state 0x%x", dsreg);
930                         printf(sfmt, foo);
931                 } else {
932                         printf(sfmt, state_str);
933                 }
934         }
935         resid = req_status_items[MT_RESID].entry->value_signed;
936         calculated_fileno = req_status_items[
937             MT_CALCULATED_FILENO].entry->value_signed;
938         calculated_rel_blkno = req_status_items[
939             MT_CALCULATED_REL_BLKNO].entry->value_signed;
940         rep_fileno = req_status_items[
941             MT_REPORTED_FILENO].entry->value_signed;
942         rep_blkno = req_status_items[
943             MT_REPORTED_BLKNO].entry->value_signed;
944         bop = req_status_items[MT_BOP].entry->value_signed;
945         eop = req_status_items[MT_EOP].entry->value_signed;
946         bpew = req_status_items[MT_BPEW].entry->value_signed;
947         partition = req_status_items[MT_PARTITION].entry->value_signed;
948
949         printf("---------------------------------\n");
950         printf("Partition: %3jd      Calc File Number: %3jd "
951                "    Calc Record Number: %jd\n"
952                "Residual:  %3jd  Reported File Number: %3jd "
953                "Reported Record Number: %jd\n", partition, calculated_fileno,
954                calculated_rel_blkno, resid, rep_fileno, rep_blkno);
955
956         printf("Flags: ");
957         if (bop > 0 || eop > 0 || bpew > 0) {
958                 int need_comma = 0;
959
960                 if (bop > 0) {
961                         printf("BOP");
962                         need_comma = 1;
963                 }
964                 if (eop > 0) {
965                         if (need_comma != 0)
966                                 printf(",");
967                         printf("EOP");
968                         need_comma = 1;
969                 }
970                 if (bpew > 0) {
971                         if (need_comma != 0)
972                                 printf(",");
973                         printf("BPEW");
974                         need_comma = 1;
975                 }
976         } else {
977                 printf("None");
978         }
979         printf("\n");
980         if (verbose != 0) {
981                 printf("---------------------------------\n");
982                 printf("Tape I/O parameters:\n");
983                 for (i = MT_MAXIO; i <= MT_MAX_EFF_IOSIZE; i++) {
984                         printf("  %s (%s): %ju bytes\n",
985                             req_status_items[i].entry->desc,
986                             req_status_items[i].name,
987                             req_status_items[i].entry->value_unsigned);
988                 }
989         }
990
991         return (0);
992 }
993
994 int
995 mt_xml_cmd(unsigned long cmd, int argc, char **argv, int mtfd, const char *tape)
996 {
997         struct mt_status_data status_data;
998 #if 0
999         struct mt_status_entry *entry;
1000 #endif
1001         char *xml_str;
1002         int retval;
1003         unsigned long ioctl_cmd;
1004
1005         switch (cmd) {
1006         case MT_CMD_PROTECT:
1007         case MTIOCPARAMGET:
1008                 ioctl_cmd = MTIOCPARAMGET;
1009                 break;
1010         default:
1011                 ioctl_cmd = MTIOCEXTGET;
1012                 break;
1013         }
1014
1015         retval = mt_get_xml_str(mtfd, ioctl_cmd, &xml_str);
1016         if (retval != 0)
1017                 err(1, "Couldn't get mt XML string");
1018
1019         retval = mt_get_status(xml_str, &status_data);
1020         if (retval != XML_STATUS_OK) {
1021                 warn("Couldn't get mt status for %s", tape);
1022                 goto bailout;
1023         }
1024
1025         /*
1026          * This gets set if there are memory allocation or other errors in
1027          * our parsing of the XML. 
1028          */
1029         if (status_data.error != 0) {
1030                 warnx("%s", status_data.error_str);
1031                 retval = 1;
1032                 goto bailout;
1033         }
1034 #if 0
1035         STAILQ_FOREACH(entry, &status_data.entries, links)
1036                 mt_status_tree_print(entry, 0, NULL);
1037 #endif
1038
1039         switch (cmd) {
1040         case MTIOCEXTGET:
1041                 retval = nstatus_print(argc, argv, xml_str, &status_data);
1042                 break;
1043         case MTIOCPARAMGET:
1044                 retval = mt_param(argc, argv, mtfd, xml_str, &status_data);
1045                 break;
1046         case MT_CMD_PROTECT:
1047                 retval = mt_protect(argc, argv, mtfd, &status_data);
1048                 break;
1049         case MT_CMD_GETDENSITY:
1050                 retval = mt_getdensity(argc, argv, xml_str, &status_data);
1051                 break;
1052         }
1053
1054 bailout:
1055         if (xml_str != NULL)
1056                 free(xml_str);
1057
1058         mt_status_free(&status_data);
1059
1060         return (retval);
1061 }
1062
1063 static int
1064 mt_set_param(int mtfd, struct mt_status_data *status_data, char *param_name,
1065     char *param_value)
1066 {
1067         struct mt_status_entry *entry;
1068         struct mtparamset param_set;
1069
1070         entry = mt_status_entry_find(status_data,
1071             __DECONST(char *, "mtparamget"));
1072         if (entry == NULL)
1073                 errx(1, "Cannot find parameter root node");
1074
1075         bzero(&param_set, sizeof(param_set));
1076         entry = mt_entry_find(entry, param_name);
1077         if (entry == NULL)
1078                 errx(1, "Unknown parameter name \"%s\"", param_name);
1079
1080         strlcpy(param_set.value_name, param_name, sizeof(param_set.value_name));
1081
1082         switch (entry->var_type) {
1083         case MT_TYPE_INT:
1084                 param_set.value.value_signed = strtoll(param_value, NULL, 0);
1085                 param_set.value_type = MT_PARAM_SET_SIGNED;
1086                 param_set.value_len = entry->size;
1087                 break;
1088         case MT_TYPE_UINT:
1089                 param_set.value.value_unsigned = strtoull(param_value, NULL, 0);
1090                 param_set.value_type = MT_PARAM_SET_UNSIGNED;
1091                 param_set.value_len = entry->size;
1092                 break;
1093         case MT_TYPE_STRING: {
1094                 size_t param_len;
1095
1096                 param_len = strlen(param_value) + 1;
1097                 if (param_len > sizeof(param_set.value.value_fixed_str)) {
1098                         param_set.value_type = MT_PARAM_SET_VAR_STR;
1099                         param_set.value.value_var_str = param_value;
1100                 } else {
1101                         param_set.value_type = MT_PARAM_SET_FIXED_STR;
1102                         strlcpy(param_set.value.value_fixed_str, param_value,
1103                             sizeof(param_set.value.value_fixed_str));
1104                 }
1105                 param_set.value_len = param_len;
1106                 break;
1107         }
1108         default:
1109                 errx(1, "Unknown parameter type %d for %s", entry->var_type,
1110                     param_name);
1111                 break;
1112         }
1113
1114         if (ioctl(mtfd, MTIOCPARAMSET, &param_set) == -1)
1115                 err(1, "MTIOCPARAMSET");
1116
1117         if (param_set.status != MT_PARAM_STATUS_OK)
1118                 errx(1, "Failed to set %s: %s", param_name,
1119                     param_set.error_str);
1120
1121         return (0);
1122 }
1123
1124
1125 typedef enum {
1126         MT_PP_LBP_R,
1127         MT_PP_LBP_W,
1128         MT_PP_RBDP,
1129         MT_PP_PI_LENGTH,
1130         MT_PP_PROT_METHOD
1131 } mt_protect_param;
1132
1133 static struct mt_protect_info {
1134         const char *name;
1135         struct mt_status_entry *entry;
1136         uint32_t value;
1137 } mt_protect_list[] = {
1138         { "lbp_r", NULL, 0 },
1139         { "lbp_w", NULL, 0 },
1140         { "rbdp", NULL, 0 },
1141         { "pi_length", NULL, 0 },
1142         { "prot_method", NULL, 0 }
1143 };
1144
1145 #define MT_NUM_PROTECT_PARAMS   (sizeof(mt_protect_list)/sizeof(mt_protect_list[0]))
1146
1147 #define MT_PROT_NAME    "protection"
1148
1149 static int
1150 mt_protect(int argc, char **argv, int mtfd, struct mt_status_data *status_data)
1151 {
1152         int retval = 0;
1153         int do_enable = 0, do_disable = 0, do_list = 0;
1154         int rbdp_set = 0, lbp_w_set = 0, lbp_r_set = 0;
1155         int prot_method_set = 0, pi_length_set = 0;
1156         int verbose = 0;
1157         uint32_t rbdp = 0, lbp_w = 0, lbp_r = 0;
1158         uint32_t prot_method = 0, pi_length = 0;
1159         struct mt_status_entry *prot_entry, *supported_entry;
1160         struct mt_status_entry *entry;
1161         struct mtparamset params[MT_NUM_PROTECT_PARAMS];
1162         struct mtsetlist param_list;
1163         unsigned int i;
1164         int c;
1165
1166         while ((c = getopt(argc, argv, "b:delL:m:r:vw:")) != -1) {
1167                 switch (c) {
1168                 case 'b':
1169                         rbdp_set = 1;
1170                         rbdp = strtoul(optarg, NULL, 0);
1171                         if ((rbdp != 0) && (rbdp != 1))
1172                                 errx(1, "valid values for -b are 0 and 1");
1173                         break;
1174                 case 'd':
1175                         do_disable = 1;
1176                         break;
1177                 case 'e':
1178                         do_enable = 1;
1179                         break;
1180                 case 'l':
1181                         do_list = 1;
1182                         break;
1183                 case 'L':
1184                         pi_length_set = 1;
1185                         pi_length = strtoul(optarg, NULL, 0);
1186                         if (pi_length > SA_CTRL_DP_PI_LENGTH_MASK)
1187                                 errx(1, "PI length %u > maximum %u",
1188                                     pi_length, SA_CTRL_DP_PI_LENGTH_MASK);
1189                         break;
1190                 case 'm':
1191                         prot_method_set = 1;
1192                         prot_method = strtoul(optarg, NULL, 0);
1193                         if (prot_method > SA_CTRL_DP_METHOD_MAX)
1194                                 errx(1, "Method %u > maximum %u",
1195                                     prot_method, SA_CTRL_DP_METHOD_MAX);
1196                         break;
1197                 case 'r':
1198                         lbp_r_set = 1;
1199                         lbp_r = strtoul(optarg, NULL, 0);
1200                         if ((lbp_r != 0) && (lbp_r != 1))
1201                                 errx(1, "valid values for -r are 0 and 1");
1202                         break;
1203                 case 'v':
1204                         verbose = 1;
1205                         break;
1206                 case 'w':
1207                         lbp_w_set = 1;
1208                         lbp_w = strtoul(optarg, NULL, 0);
1209                         if ((lbp_w != 0) && (lbp_r != 1))
1210                                 errx(1, "valid values for -r are 0 and 1");
1211                         break;
1212                 default:
1213                         break;
1214                 }
1215         }
1216
1217         if ((rbdp_set + do_disable + do_enable + do_list + pi_length_set +
1218             prot_method_set + lbp_r_set + lbp_w_set) == 0)
1219                 errx(1, "Need an argument for protect");
1220
1221         if ((do_disable + do_enable + do_list) != 1)
1222                 errx(1, "You must specify only one of -e, -d or -l");
1223
1224         if (do_list != 0) {
1225                 retval = mt_protect_print(status_data, verbose);
1226                 goto bailout;
1227         }
1228         if (do_enable != 0) {
1229                 /*
1230                  * Enable protection, but allow the user to override
1231                  * settings if he doesn't want everything turned on.
1232                  */
1233                 if (rbdp_set == 0)
1234                         rbdp = 1;
1235                 if (lbp_w_set == 0)
1236                         lbp_w = 1;
1237                 if (lbp_r_set == 0)
1238                         lbp_r = 1;
1239                 /*
1240                  * If the user doesn't override it, we default to enabling
1241                  * Reed-Solomon checkums.
1242                  */
1243                 if (prot_method_set == 0)
1244                         prot_method = SA_CTRL_DP_REED_SOLOMON;
1245                 if (pi_length_set == 0)
1246                         pi_length = SA_CTRL_DP_RS_LENGTH;
1247         } else if (do_disable != 0) {
1248                 /*
1249                  * If the user wants to disable protection, we ignore any
1250                  * other parameters he has set.  Everything gets set to 0.
1251                  */
1252                 rbdp = lbp_w = lbp_r = 0;
1253                 prot_method = pi_length = 0;
1254         }
1255
1256         prot_entry = mt_status_entry_find(status_data,
1257             __DECONST(char *, MT_PROT_NAME));
1258         if (prot_entry == NULL)
1259                 errx(1, "Unable to find protection information status");
1260
1261         supported_entry = mt_entry_find(prot_entry,
1262             __DECONST(char *, "protection_supported"));
1263         if (supported_entry == NULL)
1264                 errx(1, "Unable to find protection support information");
1265
1266         if (((supported_entry->var_type == MT_TYPE_INT)
1267           && (supported_entry->value_signed == 0))
1268          || ((supported_entry->var_type == MT_TYPE_UINT)
1269           && (supported_entry->value_unsigned == 0)))
1270                 errx(1, "This device does not support protection information");
1271
1272         mt_protect_list[MT_PP_LBP_R].value = lbp_r;
1273         mt_protect_list[MT_PP_LBP_W].value = lbp_w;
1274         mt_protect_list[MT_PP_RBDP].value = rbdp;
1275         mt_protect_list[MT_PP_PI_LENGTH].value = pi_length;
1276         mt_protect_list[MT_PP_PROT_METHOD].value = prot_method;
1277
1278         bzero(&params, sizeof(params));
1279         bzero(&param_list, sizeof(param_list));
1280
1281         /*
1282          * Go through the list and make sure that we have this parameter,
1283          * and that it is still an unsigned integer.  If not, we've got a
1284          * problem.
1285          */
1286         for (i = 0; i < MT_NUM_PROTECT_PARAMS; i++) {
1287                 entry = mt_entry_find(prot_entry,
1288                     __DECONST(char *, mt_protect_list[i].name));
1289                 if (entry == NULL) {
1290                         errx(1, "Unable to find parameter %s",
1291                             mt_protect_list[i].name);
1292                 }
1293                 mt_protect_list[i].entry = entry;
1294
1295                 if (entry->var_type != MT_TYPE_UINT)
1296                         errx(1, "Parameter %s is type %d, not unsigned, "
1297                             "cannot proceed", mt_protect_list[i].name,
1298                             entry->var_type);
1299                 snprintf(params[i].value_name, sizeof(params[i].value_name),
1300                     "%s.%s", MT_PROT_NAME, mt_protect_list[i].name);
1301                 /* XXX KDM unify types here */
1302                 params[i].value_type = MT_PARAM_SET_UNSIGNED;
1303                 params[i].value_len = sizeof(mt_protect_list[i].value);
1304                 params[i].value.value_unsigned = mt_protect_list[i].value;
1305                 
1306         }
1307         param_list.num_params = MT_NUM_PROTECT_PARAMS;
1308         param_list.param_len = sizeof(params);
1309         param_list.params = params;
1310
1311         if (ioctl(mtfd, MTIOCSETLIST, &param_list) == -1)
1312                 err(1, "error issuing MTIOCSETLIST ioctl");
1313
1314         for (i = 0; i < MT_NUM_PROTECT_PARAMS; i++) {
1315                 if (params[i].status != MT_PARAM_STATUS_OK) {
1316                         warnx("%s", params[i].error_str);
1317                         retval = 1;
1318                 }
1319         }
1320 bailout:
1321
1322         return (retval);
1323 }
1324
1325 static int
1326 mt_param(int argc, char **argv, int mtfd, char *xml_str,
1327          struct mt_status_data *status_data)
1328 {
1329         int list = 0, do_set = 0, xml_dump = 0;
1330         char *param_name = NULL, *param_value = NULL;
1331         int retval = 0, quiet = 0;
1332         int c;
1333
1334         while ((c = getopt(argc, argv, "lp:qs:x")) != -1) {
1335                 switch (c) {
1336                 case 'l':
1337                         list = 1;
1338                         break;
1339                 case 'p':
1340                         if (param_name != NULL) {
1341                                 warnx("Only one parameter name may be "
1342                                     "specified");
1343                                 retval = 1;
1344                                 goto bailout;
1345                         }
1346                         param_name = strdup(optarg);
1347                         break;
1348                 case 'q':
1349                         quiet = 1;
1350                         break;
1351                 case 's':
1352                         if (param_value != NULL) {
1353                                 warnx("Only one parameter value may be "
1354                                     "specified");
1355                                 retval = 1;
1356                                 goto bailout;
1357                         }
1358                         param_value = strdup(optarg);
1359                         do_set = 1;
1360                         break;
1361                 case 'x':
1362                         xml_dump = 1;
1363                         break;
1364                 default:
1365                         break;
1366                 }
1367         }
1368
1369         if ((list + do_set + xml_dump) != 1) {
1370                 warnx("You must specify only one of -s, -l or -x");
1371                 retval = 1;
1372                 goto bailout;
1373         }
1374
1375         if (xml_dump != 0) {
1376                 printf("%s", xml_str);
1377                 retval = 0;
1378                 goto bailout;
1379         }
1380
1381         if (do_set != 0) {
1382                 if (param_name == NULL)
1383                         errx(1, "You must specify -p with -s");
1384
1385                 retval = mt_set_param(mtfd, status_data, param_name,
1386                     param_value);
1387         } else if (list != 0)
1388                 retval = mt_param_list(status_data, param_name, quiet);
1389
1390 bailout:
1391         free(param_name);
1392         free(param_value);
1393         return (retval);
1394 }
1395
1396 int
1397 mt_print_density_entry(struct mt_status_entry *density_root, int indent)
1398 {
1399         struct mt_status_entry *entry;
1400         int retval = 0;
1401
1402         STAILQ_FOREACH(entry, &density_root->child_entries, links) {
1403                 if (entry->var_type == MT_TYPE_NODE) {
1404                         retval = mt_print_density_entry(entry, indent + 2);
1405                         if (retval != 0)
1406                                 break;
1407                         else
1408                                 continue;
1409                 }
1410                 if ((strcmp(entry->entry_name, "primary_density_code") == 0)
1411                  || (strcmp(entry->entry_name, "secondary_density_code") == 0)
1412                  || (strcmp(entry->entry_name, "density_code") == 0)) {
1413
1414                         printf("%*s%s (%s): %s\n", indent, "", entry->desc ?
1415                             entry->desc : "", entry->entry_name,
1416                             denstostring(entry->value_unsigned));
1417                 } else if (strcmp(entry->entry_name, "density_flags") == 0) {
1418                         printf("%*sMedium Access: ", indent, "");
1419                         if (entry->value_unsigned & MT_DENS_WRITE_OK) {
1420                                 printf("Read and Write\n");
1421                         } else {
1422                                 printf("Read Only\n");
1423                         }
1424                         printf("%*sDefault Density: %s\n", indent, "",
1425                             (entry->value_unsigned & MT_DENS_DEFLT) ? "Yes" :
1426                             "No");
1427                         printf("%*sDuplicate Density: %s\n", indent, "",
1428                             (entry->value_unsigned & MT_DENS_DUP) ? "Yes" :
1429                             "No");
1430                 } else if (strcmp(entry->entry_name, "media_width") == 0) {
1431                         printf("%*s%s (%s): %.1f mm\n", indent, "",
1432                             entry->desc ?  entry->desc : "", entry->entry_name,
1433                             (double)((double)entry->value_unsigned / 10));
1434                 } else if (strcmp(entry->entry_name, "medium_length") == 0) {
1435                         printf("%*s%s (%s): %ju m\n", indent, "",
1436                             entry->desc ?  entry->desc : "", entry->entry_name,
1437                             (uintmax_t)entry->value_unsigned);
1438                 } else if (strcmp(entry->entry_name, "capacity") == 0) {
1439                         printf("%*s%s (%s): %ju MB\n", indent, "", entry->desc ?
1440                             entry->desc : "", entry->entry_name,
1441                             (uintmax_t)entry->value_unsigned);
1442                 } else {
1443                         printf("%*s%s (%s): %s\n", indent, "", entry->desc ?
1444                             entry->desc : "", entry->entry_name, entry->value);
1445                 }
1446         }
1447
1448         return (retval);
1449 }
1450
1451 int
1452 mt_print_density_report(struct mt_status_entry *report_root, int indent)
1453 {
1454         struct mt_status_entry *mt_report, *media_report;
1455         struct mt_status_entry *entry;
1456         int retval = 0;
1457
1458         mt_report = mt_entry_find(report_root,
1459             __DECONST(char *, MT_MEDIUM_TYPE_REPORT_NAME));
1460         if (mt_report == NULL)
1461                 return (1);
1462
1463         media_report = mt_entry_find(report_root,
1464             __DECONST(char *, MT_MEDIA_REPORT_NAME));
1465         if (media_report == NULL)
1466                 return (1);
1467
1468         if ((mt_report->value_signed == 0)
1469          && (media_report->value_signed == 0)) {
1470                 printf("%*sThis tape drive supports the following "
1471                     "media densities:\n", indent, "");
1472         } else if ((mt_report->value_signed == 0)
1473                 && (media_report->value_signed != 0)) {
1474                 printf("%*sThe tape currently in this drive supports "
1475                     "the following media densities:\n", indent, "");
1476         } else if ((mt_report->value_signed != 0)
1477                 && (media_report->value_signed == 0)) {
1478                 printf("%*sThis tape drive supports the following "
1479                     "media types:\n", indent, "");
1480         } else {
1481                 printf("%*sThis tape currently in this drive supports "
1482                     "the following media types:\n", indent, "");
1483         }
1484
1485         STAILQ_FOREACH(entry, &report_root->child_entries, links) {
1486                 struct mt_status_nv *nv;
1487
1488                 if (strcmp(entry->entry_name, MT_DENSITY_ENTRY_NAME) != 0)
1489                         continue;
1490
1491                 STAILQ_FOREACH(nv, &entry->nv_list, links) {
1492                         if (strcmp(nv->name, "num") != 0)
1493                                 continue;
1494
1495                         break;
1496                 }
1497
1498                 indent += 2;
1499
1500                 printf("%*sDensity Entry", indent, "");
1501                 if (nv != NULL)
1502                         printf(" %s", nv->value);
1503                 printf(":\n");
1504
1505                 retval = mt_print_density_entry(entry, indent + 2);
1506
1507                 indent -= 2;
1508         }
1509
1510         return (retval);
1511 }
1512
1513 int
1514 mt_print_density(struct mt_status_entry *density_root, int indent)
1515 {
1516         struct mt_status_entry *entry;
1517         int retval = 0;
1518
1519         /*
1520          * We should have this entry for every tape drive.  This particular
1521          * value is reported via the mode page block header, not the
1522          * SCSI REPORT DENSITY SUPPORT command.
1523          */
1524         entry = mt_entry_find(density_root,
1525             __DECONST(char *, MT_MEDIA_DENSITY_NAME));
1526         if (entry == NULL)
1527                 errx(1, "Unable to find node %s", MT_MEDIA_DENSITY_NAME);
1528         
1529         printf("%*sCurrent density: %s\n", indent, "",
1530             denstostring(entry->value_unsigned));
1531
1532         /*
1533          * It isn't an error if we don't have any density reports.  Tape
1534          * drives that don't support the REPORT DENSITY SUPPORT command
1535          * won't have any; they will only have the current density entry
1536          * above.
1537          */
1538         STAILQ_FOREACH(entry, &density_root->child_entries, links) {
1539                 if (strcmp(entry->entry_name, MT_DENSITY_REPORT_NAME) != 0)
1540                         continue;
1541
1542                 retval = mt_print_density_report(entry, indent);
1543         }
1544
1545         return (retval);
1546 }
1547
1548 int
1549 mt_getdensity(int argc, char **argv, char *xml_str,
1550     struct mt_status_data *status_data)
1551 {
1552         int retval = 0;
1553         int xml_dump = 0;
1554         struct mt_status_entry *density_root = NULL;
1555         int c;
1556
1557         while ((c = getopt(argc, argv, "vx")) != -1) {
1558                 switch (c) {
1559                 case 'v':
1560                         /* Ignore. */
1561                         break;
1562                 case 'x':
1563                         xml_dump = 1;
1564                         break;
1565                 }
1566         }
1567
1568         if (xml_dump != 0) {
1569                 printf("%s", xml_str);
1570                 return (0);
1571         }
1572
1573         density_root = mt_status_entry_find(status_data,
1574             __DECONST(char *, MT_DENSITY_ROOT_NAME));
1575         if (density_root == NULL)
1576                 errx(1, "Cannot find density root node %s",
1577                     MT_DENSITY_ROOT_NAME);
1578
1579         retval = mt_print_density(density_root, 0);
1580
1581         return (retval);
1582 }
1583
1584 static void
1585 warn_eof(void)
1586 {
1587         fprintf(stderr,
1588                 "The \"eof\" command has been disabled.\n"
1589                 "Use \"weof\" if you really want to write end-of-file marks,\n"
1590                 "or \"eom\" if you rather want to skip to the end of "
1591                 "recorded medium.\n");
1592         exit(1);
1593 }