xref: /freebsd/lib/libmt/mtlib.c (revision 22cf89c938886d14f5796fc49f9f020c23ea8eaf)
1 /*-
2  * Copyright (c) 2013, 2014, 2015 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 #include <sys/cdefs.h>
34 #include <sys/types.h>
35 #include <sys/ioctl.h>
36 #include <sys/mtio.h>
37 #include <sys/queue.h>
38 #include <sys/sbuf.h>
39 
40 #include <ctype.h>
41 #include <err.h>
42 #include <fcntl.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <unistd.h>
47 #include <stdint.h>
48 #include <errno.h>
49 #include <bsdxml.h>
50 #include <mtlib.h>
51 
52 /*
53  * Called at the start of each XML element, and includes the list of
54  * attributes for the element.
55  */
56 void
57 mt_start_element(void *user_data, const char *name, const char **attr)
58 {
59 	int i;
60 	struct mt_status_data *mtinfo;
61 	struct mt_status_entry *entry;
62 
63 	mtinfo = (struct mt_status_data *)user_data;
64 
65 	if (mtinfo->error != 0)
66 		return;
67 
68 	mtinfo->level++;
69 	if ((u_int)mtinfo->level >= (sizeof(mtinfo->cur_sb) /
70             sizeof(mtinfo->cur_sb[0]))) {
71 		mtinfo->error = 1;
72                 snprintf(mtinfo->error_str, sizeof(mtinfo->error_str),
73 		    "%s: too many nesting levels, %zd max", __func__,
74 		    sizeof(mtinfo->cur_sb) / sizeof(mtinfo->cur_sb[0]));
75 		return;
76 	}
77 
78         mtinfo->cur_sb[mtinfo->level] = sbuf_new_auto();
79         if (mtinfo->cur_sb[mtinfo->level] == NULL) {
80 		mtinfo->error = 1;
81                 snprintf(mtinfo->error_str, sizeof(mtinfo->error_str),
82 		    "%s: Unable to allocate sbuf", __func__);
83 		return;
84 	}
85 
86 	entry = malloc(sizeof(*entry));
87 	if (entry == NULL) {
88 		mtinfo->error = 1;
89 		snprintf(mtinfo->error_str, sizeof(mtinfo->error_str),
90 		    "%s: unable to allocate %zd bytes", __func__,
91 		    sizeof(*entry));
92 		return;
93 	}
94 	bzero(entry, sizeof(*entry));
95 	STAILQ_INIT(&entry->nv_list);
96 	STAILQ_INIT(&entry->child_entries);
97 	entry->entry_name = strdup(name);
98 	mtinfo->cur_entry[mtinfo->level] = entry;
99 	if (mtinfo->cur_entry[mtinfo->level - 1] == NULL) {
100 		STAILQ_INSERT_TAIL(&mtinfo->entries, entry, links);
101 	} else {
102 		STAILQ_INSERT_TAIL(
103 		    &mtinfo->cur_entry[mtinfo->level - 1]->child_entries,
104 		    entry, links);
105 		entry->parent = mtinfo->cur_entry[mtinfo->level - 1];
106 	}
107 	for (i = 0; attr[i] != NULL; i+=2) {
108 		struct mt_status_nv *nv;
109 		int need_nv;
110 
111 		need_nv = 0;
112 
113 		if (strcmp(attr[i], "size") == 0) {
114 			entry->size = strtoull(attr[i+1], NULL, 0);
115 		} else if (strcmp(attr[i], "type") == 0) {
116 			if (strcmp(attr[i+1], "int") == 0) {
117 				entry->var_type = MT_TYPE_INT;
118 			} else if (strcmp(attr[i+1], "uint") == 0) {
119 				entry->var_type = MT_TYPE_UINT;
120 			} else if (strcmp(attr[i+1], "str") == 0) {
121 				entry->var_type = MT_TYPE_STRING;
122 			} else if (strcmp(attr[i+1], "node") == 0) {
123 				entry->var_type = MT_TYPE_NODE;
124 			} else {
125 				need_nv = 1;
126 			}
127 		} else if (strcmp(attr[i], "fmt") == 0) {
128 			entry->fmt = strdup(attr[i+1]);
129 		} else if (strcmp(attr[i], "desc") == 0) {
130 			entry->desc = strdup(attr[i+1]);
131 		} else {
132 			need_nv = 1;
133 		}
134 		if (need_nv != 0) {
135 			nv = malloc(sizeof(*nv));
136 			if (nv == NULL) {
137 				mtinfo->error = 1;
138 				snprintf(mtinfo->error_str,
139 				    sizeof(mtinfo->error_str),
140 				    "%s: error allocating %zd bytes",
141 				    __func__, sizeof(*nv));
142 			}
143 			bzero(nv, sizeof(*nv));
144 			nv->name = strdup(attr[i]);
145 			nv->value = strdup(attr[i+1]);
146 			STAILQ_INSERT_TAIL(&entry->nv_list, nv, links);
147 		}
148 	}
149 }
150 
151 /*
152  * Called on XML element close.
153  */
154 void
155 mt_end_element(void *user_data, const char *name)
156 {
157 	struct mt_status_data *mtinfo;
158 	char *str;
159 
160 	mtinfo = (struct mt_status_data *)user_data;
161 
162 	if (mtinfo->error != 0)
163 		return;
164 
165 	if (mtinfo->cur_sb[mtinfo->level] == NULL) {
166 		mtinfo->error = 1;
167 		snprintf(mtinfo->error_str, sizeof(mtinfo->error_str),
168 		    "%s: no valid sbuf at level %d (name %s)", __func__,
169 		    mtinfo->level, name);
170 		return;
171 	}
172 	sbuf_finish(mtinfo->cur_sb[mtinfo->level]);
173 	str = strdup(sbuf_data(mtinfo->cur_sb[mtinfo->level]));
174 	if (str == NULL) {
175 		mtinfo->error = 1;
176 		snprintf(mtinfo->error_str, sizeof(mtinfo->error_str),
177 		    "%s can't allocate %zd bytes for string", __func__,
178 		    sbuf_len(mtinfo->cur_sb[mtinfo->level]));
179 		return;
180 	}
181 
182 	if (strlen(str) == 0) {
183 		free(str);
184 		str = NULL;
185 	}
186 	if (str != NULL) {
187 		struct mt_status_entry *entry;
188 
189 		entry = mtinfo->cur_entry[mtinfo->level];
190 		switch(entry->var_type) {
191 		case MT_TYPE_INT:
192 			entry->value_signed = strtoll(str, NULL, 0);
193 			break;
194 		case MT_TYPE_UINT:
195 			entry->value_unsigned = strtoull(str, NULL, 0);
196 			break;
197 		default:
198 			break;
199 		}
200 	}
201 
202 	mtinfo->cur_entry[mtinfo->level]->value = str;
203 
204 	sbuf_delete(mtinfo->cur_sb[mtinfo->level]);
205 	mtinfo->cur_sb[mtinfo->level] = NULL;
206 	mtinfo->cur_entry[mtinfo->level] = NULL;
207 	mtinfo->level--;
208 }
209 
210 /*
211  * Called to handle character strings in the current element.
212  */
213 void
214 mt_char_handler(void *user_data, const XML_Char *str, int len)
215 {
216 	struct mt_status_data *mtinfo;
217 
218 	mtinfo = (struct mt_status_data *)user_data;
219 	if (mtinfo->error != 0)
220 		return;
221 
222 	sbuf_bcat(mtinfo->cur_sb[mtinfo->level], str, len);
223 }
224 
225 void
226 mt_status_tree_sbuf(struct sbuf *sb, struct mt_status_entry *entry, int indent,
227     void (*sbuf_func)(struct sbuf *sb, struct mt_status_entry *entry,
228     void *arg), void *arg)
229 {
230 	struct mt_status_nv *nv;
231 	struct mt_status_entry *entry2;
232 
233 	if (sbuf_func != NULL) {
234 		sbuf_func(sb, entry, arg);
235 	} else {
236 		sbuf_printf(sb, "%*sname: %s, value: %s, fmt: %s, size: %zd, "
237 		    "type: %d, desc: %s\n", indent, "", entry->entry_name,
238 		    entry->value, entry->fmt, entry->size, entry->var_type,
239 		    entry->desc);
240 		STAILQ_FOREACH(nv, &entry->nv_list, links) {
241 			sbuf_printf(sb, "%*snv: name: %s, value: %s\n",
242 			    indent + 1, "", nv->name, nv->value);
243 		}
244 	}
245 
246 	STAILQ_FOREACH(entry2, &entry->child_entries, links)
247 		mt_status_tree_sbuf(sb, entry2, indent + 2, sbuf_func, arg);
248 }
249 
250 void
251 mt_status_tree_print(struct mt_status_entry *entry, int indent,
252     void (*print_func)(struct mt_status_entry *entry, void *arg), void *arg)
253 {
254 
255 	if (print_func != NULL) {
256 		struct mt_status_entry *entry2;
257 
258 		print_func(entry, arg);
259 		STAILQ_FOREACH(entry2, &entry->child_entries, links)
260 			mt_status_tree_print(entry2, indent + 2, print_func,
261 			    arg);
262 	} else {
263 		struct sbuf *sb;
264 
265 		sb = sbuf_new_auto();
266 		if (sb == NULL)
267 			return;
268 		mt_status_tree_sbuf(sb, entry, indent, NULL, NULL);
269 		sbuf_finish(sb);
270 
271 		printf("%s", sbuf_data(sb));
272 		sbuf_delete(sb);
273 	}
274 }
275 
276 /*
277  * Given a parameter name in the form "foo" or "foo.bar.baz", traverse the
278  * tree looking for the parameter (the first case) or series of parameters
279  * (second case).
280  */
281 struct mt_status_entry *
282 mt_entry_find(struct mt_status_entry *entry, char *name)
283 {
284 	struct mt_status_entry *entry2;
285 	char *tmpname = NULL, *tmpname2 = NULL, *tmpstr = NULL;
286 
287 	tmpname = strdup(name);
288 	if (tmpname == NULL)
289 		goto bailout;
290 
291 	/* Save a pointer so we can free this later */
292 	tmpname2 = tmpname;
293 
294 	tmpstr = strsep(&tmpname, ".");
295 
296 	/*
297 	 * Is this the entry we're looking for?  Or do we have further
298 	 * child entries that we need to grab?
299 	 */
300 	if (strcmp(entry->entry_name, tmpstr) == 0) {
301 	 	if (tmpname == NULL) {
302 			/*
303 			 * There are no further child entries to find.  We
304 			 * have a complete match.
305 			 */
306 			free(tmpname2);
307 			return (entry);
308 		} else {
309 			/*
310 			 * There are more child entries that we need to find.
311 			 * Fall through to the recursive search off of this
312 			 * entry, below.  Use tmpname, which will contain
313 			 * everything after the first period.
314 			 */
315 			name = tmpname;
316 		}
317 	}
318 
319 	/*
320 	 * Recursively look for further entries.
321 	 */
322 	STAILQ_FOREACH(entry2, &entry->child_entries, links) {
323 		struct mt_status_entry *entry3;
324 
325 		entry3 = mt_entry_find(entry2, name);
326 		if (entry3 != NULL) {
327 			free(tmpname2);
328 			return (entry3);
329 		}
330 	}
331 
332 bailout:
333 	free(tmpname2);
334 
335 	return (NULL);
336 }
337 
338 struct mt_status_entry *
339 mt_status_entry_find(struct mt_status_data *status_data, char *name)
340 {
341 	struct mt_status_entry *entry, *entry2;
342 
343 	STAILQ_FOREACH(entry, &status_data->entries, links) {
344 		entry2 = mt_entry_find(entry, name);
345 		if (entry2 != NULL)
346 			return (entry2);
347 	}
348 
349 	return (NULL);
350 }
351 
352 void
353 mt_status_entry_free(struct mt_status_entry *entry)
354 {
355 	struct mt_status_entry *entry2, *entry3;
356 	struct mt_status_nv *nv, *nv2;
357 
358 	STAILQ_FOREACH_SAFE(entry2, &entry->child_entries, links, entry3) {
359 		STAILQ_REMOVE(&entry->child_entries, entry2, mt_status_entry,
360 		    links);
361 		mt_status_entry_free(entry2);
362 	}
363 
364 	free(entry->entry_name);
365 	free(entry->value);
366 	free(entry->fmt);
367 	free(entry->desc);
368 
369 	STAILQ_FOREACH_SAFE(nv, &entry->nv_list, links, nv2) {
370 		STAILQ_REMOVE(&entry->nv_list, nv, mt_status_nv, links);
371 		free(nv->name);
372 		free(nv->value);
373 		free(nv);
374 	}
375 	free(entry);
376 }
377 
378 void
379 mt_status_free(struct mt_status_data *status_data)
380 {
381 	struct mt_status_entry *entry, *entry2;
382 
383 	STAILQ_FOREACH_SAFE(entry, &status_data->entries, links, entry2) {
384 		STAILQ_REMOVE(&status_data->entries, entry, mt_status_entry,
385 		    links);
386 		mt_status_entry_free(entry);
387 	}
388 }
389 
390 void
391 mt_entry_sbuf(struct sbuf *sb, struct mt_status_entry *entry, char *fmt)
392 {
393 	switch(entry->var_type) {
394 	case MT_TYPE_INT:
395 		if (fmt != NULL)
396 			sbuf_printf(sb, fmt, (intmax_t)entry->value_signed);
397 		else
398 			sbuf_printf(sb, "%jd",
399 				    (intmax_t)entry->value_signed);
400 		break;
401 	case MT_TYPE_UINT:
402 		if (fmt != NULL)
403 			sbuf_printf(sb, fmt, (uintmax_t)entry->value_unsigned);
404 		else
405 			sbuf_printf(sb, "%ju",
406 				    (uintmax_t)entry->value_unsigned);
407 		break;
408 	default:
409 		if (fmt != NULL)
410 			sbuf_printf(sb, fmt, entry->value);
411 		else
412 			sbuf_printf(sb, "%s", entry->value);
413 		break;
414 	}
415 }
416 
417 void
418 mt_param_parent_print(struct mt_status_entry *entry,
419     struct mt_print_params *print_params)
420 {
421 	if (entry->parent != NULL)
422 		mt_param_parent_print(entry->parent, print_params);
423 
424 	if (((print_params->flags & MT_PF_INCLUDE_ROOT) == 0)
425 	 && (strcmp(entry->entry_name, print_params->root_name) == 0))
426 		return;
427 
428 	printf("%s.", entry->entry_name);
429 }
430 
431 void
432 mt_param_parent_sbuf(struct sbuf *sb, struct mt_status_entry *entry,
433     struct mt_print_params *print_params)
434 {
435 	if (entry->parent != NULL)
436 		mt_param_parent_sbuf(sb, entry->parent, print_params);
437 
438 	if (((print_params->flags & MT_PF_INCLUDE_ROOT) == 0)
439 	 && (strcmp(entry->entry_name, print_params->root_name) == 0))
440 		return;
441 
442 	sbuf_printf(sb, "%s.", entry->entry_name);
443 }
444 
445 void
446 mt_param_entry_sbuf(struct sbuf *sb, struct mt_status_entry *entry, void *arg)
447 {
448 	struct mt_print_params *print_params;
449 
450 	print_params = (struct mt_print_params *)arg;
451 
452 	/*
453 	 * We don't want to print nodes.
454 	 */
455 	if (entry->var_type == MT_TYPE_NODE)
456 		return;
457 
458 	if ((print_params->flags & MT_PF_FULL_PATH)
459 	 && (entry->parent != NULL))
460 		mt_param_parent_sbuf(sb, entry->parent, print_params);
461 
462 	sbuf_printf(sb, "%s: %s", entry->entry_name, entry->value);
463 	if ((print_params->flags & MT_PF_VERBOSE)
464 	 && (entry->desc != NULL)
465 	 && (strlen(entry->desc) > 0))
466 		sbuf_printf(sb, " (%s)", entry->desc);
467 	sbuf_printf(sb, "\n");
468 
469 }
470 
471 void
472 mt_param_entry_print(struct mt_status_entry *entry, void *arg)
473 {
474 	struct mt_print_params *print_params;
475 
476 	print_params = (struct mt_print_params *)arg;
477 
478 	/*
479 	 * We don't want to print nodes.
480 	 */
481 	if (entry->var_type == MT_TYPE_NODE)
482 		return;
483 
484 	if ((print_params->flags & MT_PF_FULL_PATH)
485 	 && (entry->parent != NULL))
486 		mt_param_parent_print(entry->parent, print_params);
487 
488 	printf("%s: %s", entry->entry_name, entry->value);
489 	if ((print_params->flags & MT_PF_VERBOSE)
490 	 && (entry->desc != NULL)
491 	 && (strlen(entry->desc) > 0))
492 		printf(" (%s)", entry->desc);
493 	printf("\n");
494 }
495 
496 int
497 mt_protect_print(struct mt_status_data *status_data, int verbose)
498 {
499 	struct mt_status_entry *entry;
500 	const char *prot_name = MT_PROTECTION_NAME;
501 	struct mt_print_params print_params;
502 
503 	snprintf(print_params.root_name, sizeof(print_params.root_name),
504 	    MT_PARAM_ROOT_NAME);
505 	print_params.flags = MT_PF_FULL_PATH;
506 	if (verbose != 0)
507 		print_params.flags |= MT_PF_VERBOSE;
508 
509 	entry = mt_status_entry_find(status_data, __DECONST(char *,prot_name));
510 	if (entry == NULL)
511 		return (1);
512 	mt_status_tree_print(entry, 0, mt_param_entry_print, &print_params);
513 
514 	return (0);
515 }
516 
517 int
518 mt_param_list(struct mt_status_data *status_data, char *param_name, int quiet)
519 {
520 	struct mt_status_entry *entry;
521 	struct mt_print_params print_params;
522 	char root_name[20];
523 
524 	snprintf(root_name, sizeof(root_name), "mtparamget");
525 	strlcpy(print_params.root_name, root_name,
526 	    sizeof(print_params.root_name));
527 
528 	print_params.flags = MT_PF_FULL_PATH;
529 	if (quiet == 0)
530 		print_params.flags |= MT_PF_VERBOSE;
531 
532 	if (param_name != NULL) {
533 		entry = mt_status_entry_find(status_data, param_name);
534 		if (entry == NULL)
535 			return (1);
536 
537 		mt_param_entry_print(entry, &print_params);
538 
539 		return (0);
540 	} else {
541 		entry = mt_status_entry_find(status_data, root_name);
542 
543 		STAILQ_FOREACH(entry, &status_data->entries, links)
544 			mt_status_tree_print(entry, 0, mt_param_entry_print,
545 			    &print_params);
546 	}
547 
548 	return (0);
549 }
550 
551 static struct densities {
552 	int dens;
553 	int bpmm;
554 	int bpi;
555 	const char *name;
556 } dens[] = {
557 	/*
558 	 * Taken from T10 Project 997D
559 	 * SCSI-3 Stream Device Commands (SSC)
560 	 * Revision 11, 4-Nov-97
561 	 *
562 	 * LTO 1-6 definitions obtained from the eighth edition of the
563 	 * IBM TotalStorage LTO Ultrium Tape Drive SCSI Reference
564 	 * (July 2007) and the second edition of the IBM System Storage LTO
565 	 * Tape Drive SCSI Reference (February 13, 2013).
566 	 *
567 	 * IBM 3592 definitions obtained from second edition of the IBM
568 	 * System Storage Tape Drive 3592 SCSI Reference (May 25, 2012).
569 	 *
570 	 * DAT-72 and DAT-160 bpi values taken from "HP StorageWorks DAT160
571 	 * tape drive white paper", dated June 2007.
572 	 *
573 	 * DAT-160 / SDLT220 density code (0x48) conflict information
574 	 * found here:
575 	 *
576 	 * http://h20564.www2.hp.com/hpsc/doc/public/display?docId=emr_na-c01065117&sp4ts.oid=429311
577  	 * (Document ID c01065117)
578 	 */
579 	/*Num.  bpmm    bpi     Reference     */
580 	{ 0x1,	32,	800,	"X3.22-1983" },
581 	{ 0x2,	63,	1600,	"X3.39-1986" },
582 	{ 0x3,	246,	6250,	"X3.54-1986" },
583 	{ 0x5,	315,	8000,	"X3.136-1986" },
584 	{ 0x6,	126,	3200,	"X3.157-1987" },
585 	{ 0x7,	252,	6400,	"X3.116-1986" },
586 	{ 0x8,	315,	8000,	"X3.158-1987" },
587 	{ 0x9,	491,	37871,	"X3.180" },
588 	{ 0xA,	262,	6667,	"X3B5/86-199" },
589 	{ 0xB,	63,	1600,	"X3.56-1986" },
590 	{ 0xC,	500,	12690,	"HI-TC1" },
591 	{ 0xD,	999,	25380,	"HI-TC2" },
592 	{ 0xF,	394,	10000,	"QIC-120" },
593 	{ 0x10,	394,	10000,	"QIC-150" },
594 	{ 0x11,	630,	16000,	"QIC-320" },
595 	{ 0x12,	2034,	51667,	"QIC-1350" },
596 	{ 0x13,	2400,	61000,	"X3B5/88-185A" },
597 	{ 0x14,	1703,	43245,	"X3.202-1991" },
598 	{ 0x15,	1789,	45434,	"ECMA TC17" },
599 	{ 0x16,	394,	10000,	"X3.193-1990" },
600 	{ 0x17,	1673,	42500,	"X3B5/91-174" },
601 	{ 0x18,	1673,	42500,	"X3B5/92-50" },
602 	{ 0x19, 2460,   62500,  "DLTapeIII" },
603 	{ 0x1A, 3214,   81633,  "DLTapeIV(20GB)" },
604 	{ 0x1B, 3383,   85937,  "DLTapeIV(35GB)" },
605 	{ 0x1C, 1654,	42000,	"QIC-385M" },
606 	{ 0x1D,	1512,	38400,	"QIC-410M" },
607 	{ 0x1E, 1385,	36000,	"QIC-1000C" },
608 	{ 0x1F,	2666,	67733,	"QIC-2100C" },
609 	{ 0x20, 2666,	67733,	"QIC-6GB(M)" },
610 	{ 0x21,	2666,	67733,	"QIC-20GB(C)" },
611 	{ 0x22,	1600,	40640,	"QIC-2GB(C)" },
612 	{ 0x23, 2666,	67733,	"QIC-875M" },
613 	{ 0x24,	2400,	61000,	"DDS-2" },
614 	{ 0x25,	3816,	97000,	"DDS-3" },
615 	{ 0x26,	3816,	97000,	"DDS-4" },
616 	{ 0x27,	3056,	77611,	"Mammoth" },
617 	{ 0x28,	1491,	37871,	"X3.224" },
618 	{ 0x40, 4880,   123952, "LTO-1" },
619 	{ 0x41, 3868,   98250,  "DLTapeIV(40GB)" },
620 	{ 0x42, 7398,   187909, "LTO-2" },
621 	{ 0x44, 9638,   244805, "LTO-3" },
622 	{ 0x46, 12725,  323215, "LTO-4" },
623 	{ 0x47, 6417,   163000, "DAT-72" },
624 	/*
625 	 * XXX KDM note that 0x48 is also the density code for DAT-160.
626 	 * For some reason they used overlapping density codes.
627 	 */
628 #if 0
629 	{ 0x48, 6870,   174500, "DAT-160" },
630 #endif
631 	{ 0x48, 5236,   133000, "SDLTapeI(110)" },
632 	{ 0x49, 7598,   193000, "SDLTapeI(160)" },
633 	{ 0x4a,     0,       0, "T10000A" },
634 	{ 0x4b,     0,       0, "T10000B" },
635 	{ 0x4c,     0,       0, "T10000C" },
636 	{ 0x4d,     0,       0, "T10000D" },
637 	{ 0x51, 11800,  299720, "3592A1 (unencrypted)" },
638 	{ 0x52, 11800,  299720, "3592A2 (unencrypted)" },
639 	{ 0x53, 13452,  341681, "3592A3 (unencrypted)" },
640 	{ 0x54, 19686,  500024, "3592A4 (unencrypted)" },
641 	{ 0x55, 20670,  525018, "3592A5 (unencrypted)" },
642 	{ 0x56, 20670,  525018, "3592B5 (unencrypted)" },
643 	{ 0x57, 21850,  554990, "3592A6 (unencrypted)" },
644 	{ 0x58, 15142,  384607, "LTO-5" },
645 	{ 0x5A, 15142,  384607, "LTO-6" },
646 	{ 0x5C, 19107,  485318, "LTO-7" },
647 	{ 0x5D, 19107,  485318, "LTO-M8" },
648 	{ 0x5E, 20669,  524993, "LTO-8" },
649 	{ 0x60, 23031,  584987, "LTO-9" },
650 	{ 0x71, 11800,  299720, "3592A1 (encrypted)" },
651 	{ 0x72, 11800,  299720, "3592A2 (encrypted)" },
652 	{ 0x73, 13452,  341681, "3592A3 (encrypted)" },
653 	{ 0x74, 19686,  500024, "3592A4 (encrypted)" },
654 	{ 0x75, 20670,  525018, "3592A5 (encrypted)" },
655 	{ 0x76, 20670,  525018, "3592B5 (encrypted)" },
656 	{ 0x77, 21850,  554990, "3592A6 (encrypted)" },
657 	{ 0x8c,  1789,   45434, "EXB-8500c" },
658 	{ 0x90,  1703,   43245, "EXB-8200c" },
659 	{ 0, 0, 0, NULL }
660 };
661 
662 const char *
663 mt_density_name(int density_num)
664 {
665 	struct densities *sd;
666 
667 	/* densities 0 and 0x7f are handled as special cases */
668 	if (density_num == 0)
669 		return ("default");
670 	if (density_num == 0x7f)
671 		return ("same");
672 
673 	for (sd = dens; sd->dens != 0; sd++)
674 		if (sd->dens == density_num)
675 			break;
676 	if (sd->dens == 0)
677 		return ("UNKNOWN");
678 	return (sd->name);
679 }
680 
681 /*
682  * Given a specific density number, return either the bits per inch or bits
683  * per millimeter for the given density.
684  */
685 int
686 mt_density_bp(int density_num, int bpi)
687 {
688 	struct densities *sd;
689 
690 	for (sd = dens; sd->dens; sd++)
691 		if (sd->dens == density_num)
692 			break;
693 	if (sd->dens == 0)
694 		return (0);
695 	if (bpi)
696 		return (sd->bpi);
697 	else
698 		return (sd->bpmm);
699 }
700 
701 int
702 mt_density_num(const char *density_name)
703 {
704 	struct densities *sd;
705 	size_t l = strlen(density_name);
706 
707 	for (sd = dens; sd->dens; sd++)
708 		if (strncasecmp(sd->name, density_name, l) == 0)
709 			break;
710 	return (sd->dens);
711 }
712 
713 /*
714  * Get the current status XML string.
715  * Returns 0 on success, -1 on failure (with errno set, and *xml_str == NULL).
716  */
717 int
718 mt_get_xml_str(int mtfd, unsigned long cmd, char **xml_str)
719 {
720 	size_t alloc_len = 32768;
721 	struct mtextget extget;
722 	int error;
723 
724 	*xml_str = NULL;
725 
726 	for (;;) {
727 		bzero(&extget, sizeof(extget));
728 		*xml_str = malloc(alloc_len);
729 		if (*xml_str == NULL)
730 			return (-1);
731 		extget.status_xml = *xml_str;
732 		extget.alloc_len = alloc_len;
733 
734 		error = ioctl(mtfd, cmd, (caddr_t)&extget);
735 		if (error == 0 && extget.status == MT_EXT_GET_OK)
736 			break;
737 
738 		free(*xml_str);
739 		*xml_str = NULL;
740 
741 		if (error != 0 || extget.status != MT_EXT_GET_NEED_MORE_SPACE)
742 			return (-1);
743 
744 		/* The driver needs more space, so double and try again. */
745 		alloc_len *= 2;
746 	}
747 	return (0);
748 }
749 
750 /*
751  * Populate a struct mt_status_data from the XML string via mt_get_xml_str().
752  *
753  * Returns XML_STATUS_OK on success.
754  * If XML_STATUS_ERROR is returned, errno may be set to indicate the reason.
755  * The caller must check status_data->error.
756  */
757 int
758 mt_get_status(char *xml_str, struct mt_status_data *status_data)
759 {
760 	XML_Parser parser;
761 	int retval;
762 
763 	bzero(status_data, sizeof(*status_data));
764 	STAILQ_INIT(&status_data->entries);
765 
766 	parser = XML_ParserCreate(NULL);
767 	if (parser == NULL) {
768 		errno = ENOMEM;
769 		return (XML_STATUS_ERROR);
770 	}
771 
772 	XML_SetUserData(parser, status_data);
773 	XML_SetElementHandler(parser, mt_start_element, mt_end_element);
774 	XML_SetCharacterDataHandler(parser, mt_char_handler);
775 
776 	retval = XML_Parse(parser, xml_str, strlen(xml_str), 1);
777 	XML_ParserFree(parser);
778 	return (retval);
779 }
780