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