mtlib.c revision 279261
1279219Sken/*-
2279219Sken * Copyright (c) 2013, 2014, 2015 Spectra Logic Corporation
3279219Sken * All rights reserved.
4279219Sken *
5279219Sken * Redistribution and use in source and binary forms, with or without
6279219Sken * modification, are permitted provided that the following conditions
7279219Sken * are met:
8279219Sken * 1. Redistributions of source code must retain the above copyright
9279219Sken *    notice, this list of conditions, and the following disclaimer,
10279219Sken *    without modification.
11279219Sken * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12279219Sken *    substantially similar to the "NO WARRANTY" disclaimer below
13279219Sken *    ("Disclaimer") and any redistribution must be conditioned upon
14279219Sken *    including a substantially similar Disclaimer requirement for further
15279219Sken *    binary redistribution.
16279219Sken *
17279219Sken * NO WARRANTY
18279219Sken * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19279219Sken * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20279219Sken * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
21279219Sken * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22279219Sken * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23279219Sken * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24279219Sken * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25279219Sken * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26279219Sken * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
27279219Sken * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28279219Sken * POSSIBILITY OF SUCH DAMAGES.
29279219Sken *
30279219Sken * Authors: Ken Merry           (Spectra Logic Corporation)
31279219Sken */
32279219Sken
33279219Sken#include <sys/cdefs.h>
34279219Sken__FBSDID("$FreeBSD: head/lib/libmt/mtlib.c 279261 2015-02-25 04:30:23Z ken $");
35279219Sken
36279219Sken#include <sys/types.h>
37279219Sken#include <sys/ioctl.h>
38279219Sken#include <sys/mtio.h>
39279219Sken#include <sys/queue.h>
40279219Sken#include <sys/sbuf.h>
41279219Sken
42279219Sken#include <ctype.h>
43279219Sken#include <err.h>
44279219Sken#include <fcntl.h>
45279219Sken#include <stdio.h>
46279219Sken#include <stdlib.h>
47279219Sken#include <string.h>
48279219Sken#include <unistd.h>
49279219Sken#include <stdint.h>
50279219Sken#include <errno.h>
51279219Sken#include <bsdxml.h>
52279219Sken#include <mtlib.h>
53279219Sken
54279219Sken/*
55279219Sken * Called at the start of each XML element, and includes the list of
56279219Sken * attributes for the element.
57279219Sken */
58279219Skenvoid
59279219Skenmt_start_element(void *user_data, const char *name, const char **attr)
60279219Sken{
61279219Sken	int i;
62279219Sken	struct mt_status_data *mtinfo;
63279219Sken	struct mt_status_entry *entry;
64279219Sken
65279219Sken	mtinfo = (struct mt_status_data *)user_data;
66279219Sken
67279219Sken	if (mtinfo->error != 0)
68279219Sken		return;
69279219Sken
70279219Sken	mtinfo->level++;
71279261Sken	if ((u_int)mtinfo->level >= (sizeof(mtinfo->cur_sb) /
72279219Sken            sizeof(mtinfo->cur_sb[0]))) {
73279219Sken		mtinfo->error = 1;
74279219Sken                snprintf(mtinfo->error_str, sizeof(mtinfo->error_str),
75279219Sken		    "%s: too many nesting levels, %zd max", __func__,
76279219Sken		    sizeof(mtinfo->cur_sb) / sizeof(mtinfo->cur_sb[0]));
77279219Sken		return;
78279219Sken	}
79279219Sken
80279219Sken        mtinfo->cur_sb[mtinfo->level] = sbuf_new_auto();
81279219Sken        if (mtinfo->cur_sb[mtinfo->level] == NULL) {
82279219Sken		mtinfo->error = 1;
83279219Sken                snprintf(mtinfo->error_str, sizeof(mtinfo->error_str),
84279219Sken		    "%s: Unable to allocate sbuf", __func__);
85279219Sken		return;
86279219Sken	}
87279219Sken
88279219Sken	entry = malloc(sizeof(*entry));
89279219Sken	if (entry == NULL) {
90279219Sken		mtinfo->error = 1;
91279219Sken		snprintf(mtinfo->error_str, sizeof(mtinfo->error_str),
92279219Sken		    "%s: unable to allocate %zd bytes", __func__,
93279219Sken		    sizeof(*entry));
94279219Sken		return;
95279219Sken	}
96279219Sken	bzero(entry, sizeof(*entry));
97279219Sken	STAILQ_INIT(&entry->nv_list);
98279219Sken	STAILQ_INIT(&entry->child_entries);
99279219Sken	entry->entry_name = strdup(name);
100279219Sken	mtinfo->cur_entry[mtinfo->level] = entry;
101279219Sken	if (mtinfo->cur_entry[mtinfo->level - 1] == NULL) {
102279219Sken		STAILQ_INSERT_TAIL(&mtinfo->entries, entry, links);
103279219Sken	} else {
104279219Sken		STAILQ_INSERT_TAIL(
105279219Sken		    &mtinfo->cur_entry[mtinfo->level - 1]->child_entries,
106279219Sken		    entry, links);
107279219Sken		entry->parent = mtinfo->cur_entry[mtinfo->level - 1];
108279219Sken	}
109279219Sken	for (i = 0; attr[i] != NULL; i+=2) {
110279219Sken		struct mt_status_nv *nv;
111279219Sken		int need_nv;
112279219Sken
113279219Sken		need_nv = 0;
114279219Sken
115279219Sken		if (strcmp(attr[i], "size") == 0) {
116279219Sken			entry->size = strtoull(attr[i+1], NULL, 0);
117279219Sken		} else if (strcmp(attr[i], "type") == 0) {
118279219Sken			if (strcmp(attr[i+1], "int") == 0) {
119279219Sken				entry->var_type = MT_TYPE_INT;
120279219Sken			} else if (strcmp(attr[i+1], "uint") == 0) {
121279219Sken				entry->var_type = MT_TYPE_UINT;
122279219Sken			} else if (strcmp(attr[i+1], "str") == 0) {
123279219Sken				entry->var_type = MT_TYPE_STRING;
124279219Sken			} else if (strcmp(attr[i+1], "node") == 0) {
125279219Sken				entry->var_type = MT_TYPE_NODE;
126279219Sken			} else {
127279219Sken				need_nv = 1;
128279219Sken			}
129279219Sken		} else if (strcmp(attr[i], "fmt") == 0) {
130279219Sken			entry->fmt = strdup(attr[i+1]);
131279219Sken		} else if (strcmp(attr[i], "desc") == 0) {
132279219Sken			entry->desc = strdup(attr[i+1]);
133279219Sken		} else {
134279219Sken			need_nv = 1;
135279219Sken		}
136279219Sken		if (need_nv != 0) {
137279219Sken			nv = malloc(sizeof(*nv));
138279219Sken			if (nv == NULL) {
139279219Sken				mtinfo->error = 1;
140279219Sken				snprintf(mtinfo->error_str,
141279219Sken				    sizeof(mtinfo->error_str),
142279219Sken				    "%s: error allocating %zd bytes",
143279219Sken				    __func__, sizeof(*nv));
144279219Sken			}
145279219Sken			bzero(nv, sizeof(*nv));
146279219Sken			nv->name = strdup(attr[i]);
147279219Sken			nv->value = strdup(attr[i+1]);
148279219Sken			STAILQ_INSERT_TAIL(&entry->nv_list, nv, links);
149279219Sken		}
150279219Sken	}
151279219Sken}
152279219Sken
153279219Sken/*
154279219Sken * Called on XML element close.
155279219Sken */
156279219Skenvoid
157279219Skenmt_end_element(void *user_data, const char *name)
158279219Sken{
159279219Sken	struct mt_status_data *mtinfo;
160279219Sken	char *str;
161279219Sken
162279219Sken	mtinfo = (struct mt_status_data *)user_data;
163279219Sken
164279219Sken	if (mtinfo->error != 0)
165279219Sken		return;
166279219Sken
167279219Sken	if (mtinfo->cur_sb[mtinfo->level] == NULL) {
168279219Sken		mtinfo->error = 1;
169279219Sken		snprintf(mtinfo->error_str, sizeof(mtinfo->error_str),
170279219Sken		    "%s: no valid sbuf at level %d (name %s)", __func__,
171279219Sken		    mtinfo->level, name);
172279219Sken		return;
173279219Sken	}
174279219Sken	sbuf_finish(mtinfo->cur_sb[mtinfo->level]);
175279219Sken	str = strdup(sbuf_data(mtinfo->cur_sb[mtinfo->level]));
176279219Sken	if (str == NULL) {
177279219Sken		mtinfo->error = 1;
178279219Sken		snprintf(mtinfo->error_str, sizeof(mtinfo->error_str),
179279219Sken		    "%s can't allocate %zd bytes for string", __func__,
180279219Sken		    sbuf_len(mtinfo->cur_sb[mtinfo->level]));
181279219Sken		return;
182279219Sken	}
183279219Sken
184279219Sken	if (strlen(str) == 0) {
185279219Sken		free(str);
186279219Sken		str = NULL;
187279219Sken	}
188279219Sken	if (str != NULL) {
189279219Sken		struct mt_status_entry *entry;
190279219Sken
191279219Sken		entry = mtinfo->cur_entry[mtinfo->level];
192279219Sken		switch(entry->var_type) {
193279219Sken		case MT_TYPE_INT:
194279219Sken			entry->value_signed = strtoll(str, NULL, 0);
195279219Sken			break;
196279219Sken		case MT_TYPE_UINT:
197279219Sken			entry->value_unsigned = strtoull(str, NULL, 0);
198279219Sken			break;
199279219Sken		default:
200279219Sken			break;
201279219Sken		}
202279219Sken	}
203279219Sken
204279219Sken	mtinfo->cur_entry[mtinfo->level]->value = str;
205279219Sken
206279219Sken	sbuf_delete(mtinfo->cur_sb[mtinfo->level]);
207279219Sken	mtinfo->cur_sb[mtinfo->level] = NULL;
208279219Sken	mtinfo->cur_entry[mtinfo->level] = NULL;
209279219Sken	mtinfo->level--;
210279219Sken}
211279219Sken
212279219Sken/*
213279219Sken * Called to handle character strings in the current element.
214279219Sken */
215279219Skenvoid
216279219Skenmt_char_handler(void *user_data, const XML_Char *str, int len)
217279219Sken{
218279219Sken	struct mt_status_data *mtinfo;
219279219Sken
220279219Sken	mtinfo = (struct mt_status_data *)user_data;
221279219Sken	if (mtinfo->error != 0)
222279219Sken		return;
223279219Sken
224279219Sken	sbuf_bcat(mtinfo->cur_sb[mtinfo->level], str, len);
225279219Sken}
226279219Sken
227279219Skenvoid
228279219Skenmt_status_tree_sbuf(struct sbuf *sb, struct mt_status_entry *entry, int indent,
229279219Sken    void (*sbuf_func)(struct sbuf *sb, struct mt_status_entry *entry,
230279219Sken    void *arg), void *arg)
231279219Sken{
232279219Sken	struct mt_status_nv *nv;
233279219Sken	struct mt_status_entry *entry2;
234279219Sken
235279219Sken	if (sbuf_func != NULL) {
236279219Sken		sbuf_func(sb, entry, arg);
237279219Sken	} else {
238279219Sken		sbuf_printf(sb, "%*sname: %s, value: %s, fmt: %s, size: %zd, "
239279219Sken		    "type: %d, desc: %s\n", indent, "", entry->entry_name,
240279219Sken		    entry->value, entry->fmt, entry->size, entry->var_type,
241279219Sken		    entry->desc);
242279219Sken		STAILQ_FOREACH(nv, &entry->nv_list, links) {
243279219Sken			sbuf_printf(sb, "%*snv: name: %s, value: %s\n",
244279219Sken			    indent + 1, "", nv->name, nv->value);
245279219Sken		}
246279219Sken	}
247279219Sken
248279219Sken	STAILQ_FOREACH(entry2, &entry->child_entries, links)
249279219Sken		mt_status_tree_sbuf(sb, entry2, indent + 2, sbuf_func, arg);
250279219Sken}
251279219Sken
252279219Skenvoid
253279219Skenmt_status_tree_print(struct mt_status_entry *entry, int indent,
254279219Sken    void (*print_func)(struct mt_status_entry *entry, void *arg), void *arg)
255279219Sken{
256279219Sken
257279219Sken	if (print_func != NULL) {
258279219Sken		struct mt_status_entry *entry2;
259279219Sken
260279219Sken		print_func(entry, arg);
261279219Sken		STAILQ_FOREACH(entry2, &entry->child_entries, links)
262279219Sken			mt_status_tree_print(entry2, indent + 2, print_func,
263279219Sken			    arg);
264279219Sken	} else {
265279219Sken		struct sbuf *sb;
266279219Sken
267279219Sken		sb = sbuf_new_auto();
268279219Sken		if (sb == NULL)
269279219Sken			return;
270279219Sken		mt_status_tree_sbuf(sb, entry, indent, NULL, NULL);
271279219Sken		sbuf_finish(sb);
272279219Sken
273279219Sken		printf("%s", sbuf_data(sb));
274279219Sken		sbuf_delete(sb);
275279219Sken	}
276279219Sken}
277279219Sken
278279219Sken/*
279279219Sken * Given a parameter name in the form "foo" or "foo.bar.baz", traverse the
280279219Sken * tree looking for the parameter (the first case) or series of parameters
281279219Sken * (second case).
282279219Sken */
283279219Skenstruct mt_status_entry *
284279219Skenmt_entry_find(struct mt_status_entry *entry, char *name)
285279219Sken{
286279219Sken	struct mt_status_entry *entry2;
287279219Sken	char *tmpname = NULL, *tmpname2 = NULL, *tmpstr = NULL;
288279219Sken
289279219Sken	tmpname = strdup(name);
290279219Sken	if (tmpname == NULL)
291279219Sken		goto bailout;
292279219Sken
293279219Sken	/* Save a pointer so we can free this later */
294279219Sken	tmpname2 = tmpname;
295279219Sken
296279219Sken	tmpstr = strsep(&tmpname, ".");
297279219Sken
298279219Sken	/*
299279219Sken	 * Is this the entry we're looking for?  Or do we have further
300279219Sken	 * child entries that we need to grab?
301279219Sken	 */
302279219Sken	if (strcmp(entry->entry_name, tmpstr) == 0) {
303279219Sken	 	if (tmpname == NULL) {
304279219Sken			/*
305279219Sken			 * There are no further child entries to find.  We
306279219Sken			 * have a complete match.
307279219Sken			 */
308279219Sken			free(tmpname2);
309279219Sken			return (entry);
310279219Sken		} else {
311279219Sken			/*
312279219Sken			 * There are more child entries that we need to find.
313279219Sken			 * Fall through to the recursive search off of this
314279219Sken			 * entry, below.  Use tmpname, which will contain
315279219Sken			 * everything after the first period.
316279219Sken			 */
317279219Sken			name = tmpname;
318279219Sken		}
319279219Sken	}
320279219Sken
321279219Sken	/*
322279219Sken	 * Recursively look for further entries.
323279219Sken	 */
324279219Sken	STAILQ_FOREACH(entry2, &entry->child_entries, links) {
325279219Sken		struct mt_status_entry *entry3;
326279219Sken
327279219Sken		entry3 = mt_entry_find(entry2, name);
328279219Sken		if (entry3 != NULL) {
329279219Sken			free(tmpname2);
330279219Sken			return (entry3);
331279219Sken		}
332279219Sken	}
333279219Sken
334279219Skenbailout:
335279219Sken	free(tmpname2);
336279219Sken
337279219Sken	return (NULL);
338279219Sken}
339279219Sken
340279219Skenstruct mt_status_entry *
341279219Skenmt_status_entry_find(struct mt_status_data *status_data, char *name)
342279219Sken{
343279219Sken	struct mt_status_entry *entry, *entry2;
344279219Sken
345279219Sken	STAILQ_FOREACH(entry, &status_data->entries, links) {
346279219Sken		entry2 = mt_entry_find(entry, name);
347279219Sken		if (entry2 != NULL)
348279219Sken			return (entry2);
349279219Sken	}
350279219Sken
351279219Sken	return (NULL);
352279219Sken}
353279219Sken
354279219Skenvoid
355279219Skenmt_status_entry_free(struct mt_status_entry *entry)
356279219Sken{
357279219Sken	struct mt_status_entry *entry2, *entry3;
358279219Sken	struct mt_status_nv *nv, *nv2;
359279219Sken
360279219Sken	STAILQ_FOREACH_SAFE(entry2, &entry->child_entries, links, entry3) {
361279219Sken		STAILQ_REMOVE(&entry->child_entries, entry2, mt_status_entry,
362279219Sken		    links);
363279219Sken		mt_status_entry_free(entry2);
364279219Sken	}
365279219Sken
366279219Sken	free(entry->entry_name);
367279219Sken	free(entry->value);
368279219Sken	free(entry->fmt);
369279219Sken	free(entry->desc);
370279219Sken
371279219Sken	STAILQ_FOREACH_SAFE(nv, &entry->nv_list, links, nv2) {
372279219Sken		STAILQ_REMOVE(&entry->nv_list, nv, mt_status_nv, links);
373279219Sken		free(nv->name);
374279219Sken		free(nv->value);
375279219Sken		free(nv);
376279219Sken	}
377279219Sken	free(entry);
378279219Sken}
379279219Sken
380279219Skenvoid
381279219Skenmt_status_free(struct mt_status_data *status_data)
382279219Sken{
383279219Sken	struct mt_status_entry *entry, *entry2;
384279219Sken
385279219Sken	STAILQ_FOREACH_SAFE(entry, &status_data->entries, links, entry2) {
386279219Sken		STAILQ_REMOVE(&status_data->entries, entry, mt_status_entry,
387279219Sken		    links);
388279219Sken		mt_status_entry_free(entry);
389279219Sken	}
390279219Sken}
391279219Sken
392279219Skenvoid
393279219Skenmt_entry_sbuf(struct sbuf *sb, struct mt_status_entry *entry, char *fmt)
394279219Sken{
395279219Sken	switch(entry->var_type) {
396279219Sken	case MT_TYPE_INT:
397279219Sken		if (fmt != NULL)
398279219Sken			sbuf_printf(sb, fmt, (intmax_t)entry->value_signed);
399279219Sken		else
400279219Sken			sbuf_printf(sb, "%jd",
401279219Sken				    (intmax_t)entry->value_signed);
402279219Sken		break;
403279219Sken	case MT_TYPE_UINT:
404279219Sken		if (fmt != NULL)
405279219Sken			sbuf_printf(sb, fmt, (uintmax_t)entry->value_unsigned);
406279219Sken		else
407279219Sken			sbuf_printf(sb, "%ju",
408279219Sken				    (uintmax_t)entry->value_unsigned);
409279219Sken		break;
410279219Sken	default:
411279219Sken		if (fmt != NULL)
412279219Sken			sbuf_printf(sb, fmt, entry->value);
413279219Sken		else
414279219Sken			sbuf_printf(sb, "%s", entry->value);
415279219Sken		break;
416279219Sken	}
417279219Sken}
418279219Sken
419279219Skenvoid
420279219Skenmt_param_parent_print(struct mt_status_entry *entry,
421279219Sken    struct mt_print_params *print_params)
422279219Sken{
423279219Sken	if (entry->parent != NULL)
424279219Sken		mt_param_parent_print(entry->parent, print_params);
425279219Sken
426279219Sken	if (((print_params->flags & MT_PF_INCLUDE_ROOT) == 0)
427279219Sken	 && (strcmp(entry->entry_name, print_params->root_name) == 0))
428279219Sken		return;
429279219Sken
430279219Sken	printf("%s.", entry->entry_name);
431279219Sken}
432279219Sken
433279219Skenvoid
434279219Skenmt_param_parent_sbuf(struct sbuf *sb, struct mt_status_entry *entry,
435279219Sken    struct mt_print_params *print_params)
436279219Sken{
437279219Sken	if (entry->parent != NULL)
438279219Sken		mt_param_parent_sbuf(sb, entry->parent, print_params);
439279219Sken
440279219Sken	if (((print_params->flags & MT_PF_INCLUDE_ROOT) == 0)
441279219Sken	 && (strcmp(entry->entry_name, print_params->root_name) == 0))
442279219Sken		return;
443279219Sken
444279219Sken	sbuf_printf(sb, "%s.", entry->entry_name);
445279219Sken}
446279219Sken
447279219Skenvoid
448279219Skenmt_param_entry_sbuf(struct sbuf *sb, struct mt_status_entry *entry, void *arg)
449279219Sken{
450279219Sken	struct mt_print_params *print_params;
451279219Sken
452279219Sken	print_params = (struct mt_print_params *)arg;
453279219Sken
454279219Sken	/*
455279219Sken	 * We don't want to print nodes.
456279219Sken	 */
457279219Sken	if (entry->var_type == MT_TYPE_NODE)
458279219Sken		return;
459279219Sken
460279219Sken	if ((print_params->flags & MT_PF_FULL_PATH)
461279219Sken	 && (entry->parent != NULL))
462279219Sken		mt_param_parent_sbuf(sb, entry->parent, print_params);
463279219Sken
464279219Sken	sbuf_printf(sb, "%s: %s", entry->entry_name, entry->value);
465279219Sken	if ((print_params->flags & MT_PF_VERBOSE)
466279219Sken	 && (entry->desc != NULL)
467279219Sken	 && (strlen(entry->desc) > 0))
468279219Sken		sbuf_printf(sb, " (%s)", entry->desc);
469279219Sken	sbuf_printf(sb, "\n");
470279219Sken
471279219Sken}
472279219Sken
473279219Skenvoid
474279219Skenmt_param_entry_print(struct mt_status_entry *entry, void *arg)
475279219Sken{
476279219Sken	struct mt_print_params *print_params;
477279219Sken
478279219Sken	print_params = (struct mt_print_params *)arg;
479279219Sken
480279219Sken	/*
481279219Sken	 * We don't want to print nodes.
482279219Sken	 */
483279219Sken	if (entry->var_type == MT_TYPE_NODE)
484279219Sken		return;
485279219Sken
486279219Sken	if ((print_params->flags & MT_PF_FULL_PATH)
487279219Sken	 && (entry->parent != NULL))
488279219Sken		mt_param_parent_print(entry->parent, print_params);
489279219Sken
490279219Sken	printf("%s: %s", entry->entry_name, entry->value);
491279219Sken	if ((print_params->flags & MT_PF_VERBOSE)
492279219Sken	 && (entry->desc != NULL)
493279219Sken	 && (strlen(entry->desc) > 0))
494279219Sken		printf(" (%s)", entry->desc);
495279219Sken	printf("\n");
496279219Sken}
497279219Sken
498279219Skenint
499279219Skenmt_protect_print(struct mt_status_data *status_data, int verbose)
500279219Sken{
501279219Sken	struct mt_status_entry *entry;
502279219Sken	const char *prot_name = MT_PROTECTION_NAME;
503279219Sken	struct mt_print_params print_params;
504279219Sken
505279219Sken	snprintf(print_params.root_name, sizeof(print_params.root_name),
506279219Sken	    MT_PARAM_ROOT_NAME);
507279219Sken	print_params.flags = MT_PF_FULL_PATH;
508279219Sken	if (verbose != 0)
509279219Sken		print_params.flags |= MT_PF_VERBOSE;
510279219Sken
511279219Sken	entry = mt_status_entry_find(status_data, __DECONST(char *,prot_name));
512279219Sken	if (entry == NULL)
513279219Sken		return (1);
514279219Sken	mt_status_tree_print(entry, 0, mt_param_entry_print, &print_params);
515279219Sken
516279219Sken	return (0);
517279219Sken}
518279219Sken
519279219Skenint
520279219Skenmt_param_list(struct mt_status_data *status_data, char *param_name, int quiet)
521279219Sken{
522279219Sken	struct mt_status_entry *entry;
523279219Sken	struct mt_print_params print_params;
524279219Sken	char root_name[20];
525279219Sken
526279219Sken	snprintf(root_name, sizeof(root_name), "mtparamget");
527279219Sken	strlcpy(print_params.root_name, root_name,
528279219Sken	    sizeof(print_params.root_name));
529279219Sken
530279219Sken	print_params.flags = MT_PF_FULL_PATH;
531279219Sken	if (quiet == 0)
532279219Sken		print_params.flags |= MT_PF_VERBOSE;
533279219Sken
534279219Sken	if (param_name != NULL) {
535279219Sken		entry = mt_status_entry_find(status_data, param_name);
536279219Sken		if (entry == NULL)
537279219Sken			return (1);
538279219Sken
539279219Sken		mt_param_entry_print(entry, &print_params);
540279219Sken
541279219Sken		return (0);
542279219Sken	} else {
543279219Sken		entry = mt_status_entry_find(status_data, root_name);
544279219Sken
545279219Sken		STAILQ_FOREACH(entry, &status_data->entries, links)
546279219Sken			mt_status_tree_print(entry, 0, mt_param_entry_print,
547279219Sken			    &print_params);
548279219Sken	}
549279219Sken
550279219Sken	return (0);
551279219Sken}
552279219Sken
553279219Skenstatic struct densities {
554279219Sken	int dens;
555279219Sken	int bpmm;
556279219Sken	int bpi;
557279219Sken	const char *name;
558279219Sken} dens[] = {
559279219Sken	/*
560279219Sken	 * Taken from T10 Project 997D
561279219Sken	 * SCSI-3 Stream Device Commands (SSC)
562279219Sken	 * Revision 11, 4-Nov-97
563279219Sken	 *
564279219Sken	 * LTO 1-6 definitions obtained from the eighth edition of the
565279219Sken	 * IBM TotalStorage LTO Ultrium Tape Drive SCSI Reference
566279219Sken	 * (July 2007) and the second edition of the IBM System Storage LTO
567279219Sken	 * Tape Drive SCSI Reference (February 13, 2013).
568279219Sken	 *
569279219Sken	 * IBM 3592 definitions obtained from second edition of the IBM
570279219Sken	 * System Storage Tape Drive 3592 SCSI Reference (May 25, 2012).
571279219Sken	 */
572279219Sken	/*Num.  bpmm    bpi     Reference     */
573279219Sken	{ 0x1,	32,	800,	"X3.22-1983" },
574279219Sken	{ 0x2,	63,	1600,	"X3.39-1986" },
575279219Sken	{ 0x3,	246,	6250,	"X3.54-1986" },
576279219Sken	{ 0x5,	315,	8000,	"X3.136-1986" },
577279219Sken	{ 0x6,	126,	3200,	"X3.157-1987" },
578279219Sken	{ 0x7,	252,	6400,	"X3.116-1986" },
579279219Sken	{ 0x8,	315,	8000,	"X3.158-1987" },
580279219Sken	{ 0x9,	491,	37871,	"X3.180" },
581279219Sken	{ 0xA,	262,	6667,	"X3B5/86-199" },
582279219Sken	{ 0xB,	63,	1600,	"X3.56-1986" },
583279219Sken	{ 0xC,	500,	12690,	"HI-TC1" },
584279219Sken	{ 0xD,	999,	25380,	"HI-TC2" },
585279219Sken	{ 0xF,	394,	10000,	"QIC-120" },
586279219Sken	{ 0x10,	394,	10000,	"QIC-150" },
587279219Sken	{ 0x11,	630,	16000,	"QIC-320" },
588279219Sken	{ 0x12,	2034,	51667,	"QIC-1350" },
589279219Sken	{ 0x13,	2400,	61000,	"X3B5/88-185A" },
590279219Sken	{ 0x14,	1703,	43245,	"X3.202-1991" },
591279219Sken	{ 0x15,	1789,	45434,	"ECMA TC17" },
592279219Sken	{ 0x16,	394,	10000,	"X3.193-1990" },
593279219Sken	{ 0x17,	1673,	42500,	"X3B5/91-174" },
594279219Sken	{ 0x18,	1673,	42500,	"X3B5/92-50" },
595279219Sken	{ 0x19, 2460,   62500,  "DLTapeIII" },
596279219Sken	{ 0x1A, 3214,   81633,  "DLTapeIV(20GB)" },
597279219Sken	{ 0x1B, 3383,   85937,  "DLTapeIV(35GB)" },
598279219Sken	{ 0x1C, 1654,	42000,	"QIC-385M" },
599279219Sken	{ 0x1D,	1512,	38400,	"QIC-410M" },
600279219Sken	{ 0x1E, 1385,	36000,	"QIC-1000C" },
601279219Sken	{ 0x1F,	2666,	67733,	"QIC-2100C" },
602279219Sken	{ 0x20, 2666,	67733,	"QIC-6GB(M)" },
603279219Sken	{ 0x21,	2666,	67733,	"QIC-20GB(C)" },
604279219Sken	{ 0x22,	1600,	40640,	"QIC-2GB(C)" },
605279219Sken	{ 0x23, 2666,	67733,	"QIC-875M" },
606279219Sken	{ 0x24,	2400,	61000,	"DDS-2" },
607279219Sken	{ 0x25,	3816,	97000,	"DDS-3" },
608279219Sken	{ 0x26,	3816,	97000,	"DDS-4" },
609279219Sken	{ 0x27,	3056,	77611,	"Mammoth" },
610279219Sken	{ 0x28,	1491,	37871,	"X3.224" },
611279219Sken	{ 0x40, 4880,   123952, "LTO-1" },
612279219Sken	{ 0x41, 3868,   98250,  "DLTapeIV(40GB)" },
613279219Sken	{ 0x42, 7398,   187909, "LTO-2" },
614279219Sken	{ 0x44, 9638,   244805, "LTO-3" },
615279219Sken	{ 0x46, 12725,  323215, "LTO-4" },
616279219Sken	{ 0x48, 5236,   133000, "SDLTapeI(110)" },
617279219Sken	{ 0x49, 7598,   193000, "SDLTapeI(160)" },
618279219Sken	{ 0x4a,     0,       0, "T10000A" },
619279219Sken	{ 0x4b,     0,       0, "T10000B" },
620279219Sken	{ 0x4c,     0,       0, "T10000C" },
621279219Sken	{ 0x4d,     0,       0, "T10000D" },
622279219Sken	{ 0x51, 11800,  299720, "3592A1 (unencrypted)" },
623279219Sken	{ 0x52, 11800,  299720, "3592A2 (unencrypted)" },
624279219Sken	{ 0x53, 13452,  341681, "3592A3 (unencrypted)" },
625279219Sken	{ 0x54, 19686,  500024, "3592A4 (unencrypted)" },
626279219Sken	{ 0x55, 20670,  525018, "3592A5 (unencrypted)" },
627279219Sken	{ 0x58, 15142,  384607, "LTO-5" },
628279219Sken	{ 0x5A, 15142,  384607, "LTO-6" },
629279219Sken	{ 0x71, 11800,  299720, "3592A1 (encrypted)" },
630279219Sken	{ 0x72, 11800,  299720, "3592A2 (encrypted)" },
631279219Sken	{ 0x73, 13452,  341681, "3592A3 (encrypted)" },
632279219Sken	{ 0x74, 19686,  500024, "3592A4 (encrypted)" },
633279219Sken	{ 0x75, 20670,  525018, "3592A5 (encrypted)" },
634279219Sken	{ 0x8c,  1789,   45434, "EXB-8500c" },
635279219Sken	{ 0x90,  1703,   43245, "EXB-8200c" },
636279219Sken	{ 0, 0, 0, NULL }
637279219Sken};
638279219Sken
639279219Skenconst char *
640279219Skenmt_density_name(int density_num)
641279219Sken{
642279219Sken	struct densities *sd;
643279219Sken
644279219Sken	/* densities 0 and 0x7f are handled as special cases */
645279219Sken	if (density_num == 0)
646279219Sken		return ("default");
647279219Sken	if (density_num == 0x7f)
648279219Sken		return ("same");
649279219Sken
650279219Sken	for (sd = dens; sd->dens != 0; sd++)
651279219Sken		if (sd->dens == density_num)
652279219Sken			break;
653279219Sken	if (sd->dens == 0)
654279219Sken		return ("UNKNOWN");
655279219Sken	return (sd->name);
656279219Sken}
657279219Sken
658279219Sken/*
659279219Sken * Given a specific density number, return either the bits per inch or bits
660279219Sken * per millimeter for the given density.
661279219Sken */
662279219Skenint
663279219Skenmt_density_bp(int density_num, int bpi)
664279219Sken{
665279219Sken	struct densities *sd;
666279219Sken
667279219Sken	for (sd = dens; sd->dens; sd++)
668279219Sken		if (sd->dens == density_num)
669279219Sken			break;
670279219Sken	if (sd->dens == 0)
671279219Sken		return (0);
672279219Sken	if (bpi)
673279219Sken		return (sd->bpi);
674279219Sken	else
675279219Sken		return (sd->bpmm);
676279219Sken}
677279219Sken
678279219Skenint
679279219Skenmt_density_num(const char *density_name)
680279219Sken{
681279219Sken	struct densities *sd;
682279219Sken	size_t l = strlen(density_name);
683279219Sken
684279219Sken	for (sd = dens; sd->dens; sd++)
685279219Sken		if (strncasecmp(sd->name, density_name, l) == 0)
686279219Sken			break;
687279219Sken	return (sd->dens);
688279219Sken}
689279219Sken
690279219Sken/*
691279219Sken * Get the current status XML string.
692279219Sken * Returns 0 on success, -1 on failure (with errno set, and *xml_str == NULL).
693279219Sken */
694279219Skenint
695279219Skenmt_get_xml_str(int mtfd, unsigned long cmd, char **xml_str)
696279219Sken{
697279219Sken	size_t alloc_len = 32768;
698279219Sken	struct mtextget extget;
699279219Sken	int error;
700279219Sken
701279219Sken	*xml_str = NULL;
702279219Sken
703279219Sken	for (;;) {
704279219Sken		bzero(&extget, sizeof(extget));
705279219Sken		*xml_str = malloc(alloc_len);
706279219Sken		if (*xml_str == NULL)
707279219Sken			return (-1);
708279219Sken		extget.status_xml = *xml_str;
709279219Sken		extget.alloc_len = alloc_len;
710279219Sken
711279219Sken		error = ioctl(mtfd, cmd, (caddr_t)&extget);
712279219Sken		if (error == 0 && extget.status == MT_EXT_GET_OK)
713279219Sken			break;
714279219Sken
715279219Sken		free(*xml_str);
716279219Sken		*xml_str = NULL;
717279219Sken
718279219Sken		if (error != 0 || extget.status != MT_EXT_GET_NEED_MORE_SPACE)
719279219Sken			return (-1);
720279219Sken
721279219Sken		/* The driver needs more space, so double and try again. */
722279219Sken		alloc_len *= 2;
723279219Sken	}
724279219Sken	return (0);
725279219Sken}
726279219Sken
727279219Sken/*
728279219Sken * Populate a struct mt_status_data from the XML string via mt_get_xml_str().
729279219Sken *
730279219Sken * Returns XML_STATUS_OK on success.
731279219Sken * If XML_STATUS_ERROR is returned, errno may be set to indicate the reason.
732279219Sken * The caller must check status_data->error.
733279219Sken */
734279219Skenint
735279219Skenmt_get_status(char *xml_str, struct mt_status_data *status_data)
736279219Sken{
737279219Sken	XML_Parser parser;
738279219Sken	int retval;
739279219Sken
740279219Sken	bzero(status_data, sizeof(*status_data));
741279219Sken	STAILQ_INIT(&status_data->entries);
742279219Sken
743279219Sken	parser = XML_ParserCreate(NULL);
744279219Sken	if (parser == NULL) {
745279219Sken		errno = ENOMEM;
746279219Sken		return (XML_STATUS_ERROR);
747279219Sken	}
748279219Sken
749279219Sken	XML_SetUserData(parser, status_data);
750279219Sken	XML_SetElementHandler(parser, mt_start_element, mt_end_element);
751279219Sken	XML_SetCharacterDataHandler(parser, mt_char_handler);
752279219Sken
753279219Sken	retval = XML_Parse(parser, xml_str, strlen(xml_str), 1);
754279219Sken	XML_ParserFree(parser);
755279219Sken	return (retval);
756279219Sken}
757