diff options
author | sjg <sjg@FreeBSD.org> | 2015-05-27 01:19:58 +0000 |
---|---|---|
committer | sjg <sjg@FreeBSD.org> | 2015-05-27 01:19:58 +0000 |
commit | 65145fa4c81da358fcbc3b650156dab705dfa34e (patch) | |
tree | 55c065b6730aaac2afb6c29933ee6ec5fa4c4249 /lib/libmt | |
parent | 60ff4eb0dff94a04d75d0d52a3957aaaf5f8c693 (diff) | |
parent | e6b664c390af88d4a87208bc042ce503da664c3b (diff) | |
download | FreeBSD-src-65145fa4c81da358fcbc3b650156dab705dfa34e.zip FreeBSD-src-65145fa4c81da358fcbc3b650156dab705dfa34e.tar.gz |
Merge sync of head
Diffstat (limited to 'lib/libmt')
-rw-r--r-- | lib/libmt/Makefile | 12 | ||||
-rw-r--r-- | lib/libmt/Makefile.depend | 22 | ||||
-rw-r--r-- | lib/libmt/mt.3 | 454 | ||||
-rw-r--r-- | lib/libmt/mtlib.c | 773 | ||||
-rw-r--r-- | lib/libmt/mtlib.h | 122 |
5 files changed, 1383 insertions, 0 deletions
diff --git a/lib/libmt/Makefile b/lib/libmt/Makefile new file mode 100644 index 0000000..6fe5920 --- /dev/null +++ b/lib/libmt/Makefile @@ -0,0 +1,12 @@ +# $FreeBSD$ + +LIB= mt +SHLIBDIR?= /lib +SRCS= mtlib.c +INCS= mtlib.h + +LIBADD= sbuf bsdxml + +MAN= mt.3 + +.include <bsd.lib.mk> diff --git a/lib/libmt/Makefile.depend b/lib/libmt/Makefile.depend new file mode 100644 index 0000000..acc7f35 --- /dev/null +++ b/lib/libmt/Makefile.depend @@ -0,0 +1,22 @@ +# Autogenerated - do NOT edit! + +DEP_RELDIR := ${_PARSEDIR:S,${SRCTOP}/,,} + +DIRDEPS = \ + gnu/lib/csu \ + gnu/lib/libgcc \ + include \ + include/xlocale \ + lib/${CSU_DIR} \ + lib/libc \ + lib/libcompiler_rt \ + lib/libexpat \ + lib/libsbuf \ + usr.bin/xinstall.host \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/lib/libmt/mt.3 b/lib/libmt/mt.3 new file mode 100644 index 0000000..eaf0194 --- /dev/null +++ b/lib/libmt/mt.3 @@ -0,0 +1,454 @@ +.\" +.\" Copyright (c) 2013, 2015 Spectra Logic Corporation +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions, and the following disclaimer, +.\" without modification. +.\" 2. Redistributions in binary form must reproduce at minimum a disclaimer +.\" substantially similar to the "NO WARRANTY" disclaimer below +.\" ("Disclaimer") and any redistribution must be conditioned upon +.\" including a substantially similar Disclaimer requirement for further +.\" binary redistribution. +.\" +.\" NO WARRANTY +.\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +.\" "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +.\" LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR +.\" A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +.\" HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +.\" IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGES. +.\" +.\" Authors: Ken Merry (Spectra Logic Corporation) +.\" +.\" $FreeBSD$ +.\" +.Dd February 13, 2015 +.Dt MT 3 +.Os +.Sh NAME +.Nm mt_start_element , +.Nm mt_end_element , +.Nm mt_char_handler , +.Nm mt_status_tree_sbuf , +.Nm mt_status_tree_print , +.Nm mt_status_entry_free , +.Nm mt_status_free , +.Nm mt_entry_sbuf , +.Nm mt_param_parent_print , +.Nm mt_param_entry_print , +.Nm mt_protect_print , +.Nm mt_param_list , +.Nm mt_density_name , +.Nm mt_density_bp , +.Nm mt_density_num , +.Nm mt_get_xml_str , +.Nm mt_get_status +.Nd Magnetic Tape library +.Sh LIBRARY +.Lb libmt +.Sh SYNOPSIS +.In sys/sbuf.h +.In bsdxml.h +.In mtlib.h +.Ft void +.Fo mt_start_element +.Fa "void *user_data" +.Fa "const char *name" +.Fa "const char **attr" +.Fc +.Ft void +.Fo mt_end_element +.Fa "void *user_data" +.Fa "const char *name" +.Fc +.Ft void +.Fo mt_char_handler +.Fa "void *user_data" +.Fa "const XML_Char *str" +.Fa "int len" +.Fc +.Ft void +.Fo mt_status_tree_sbuf +.Fa "struct sbuf *sb" +.Fa "struct mt_status_entry *entry" +.Fa "int indent" +.Fa "void (*sbuf_func)(struct sbuf *sb, struct mt_status_entry *entry, void *arg)" +.Fa "void *arg" +.Fc +.Ft void +.Fo mt_status_tree_print +.Fa "struct mt_status_entry *entry" +.Fa "int indent" +.Fa "void (*print_func)(struct mt_status_entry *entry, void *arg)" +.Fa "void *arg" +.Fc +.Ft "struct mt_status_entry *" +.Fo mt_entry_find +.Fa "struct mt_status_entry *entry" +.Fa "char *name" +.Fc +.Ft "struct mt_status_entry *" +.Fo mt_status_entry_find +.Fa "struct mt_status_data *status_data" +.Fa "char *name" +.Fc +.Ft void +.Fo mt_status_entry_free +.Fa "struct mt_status_entry *entry)" +.Fc +.Ft void +.Fo mt_status_free +.Fa "struct mt_status_data *status_data" +.Fc +.Ft void +.Fo mt_entry_sbuf +.Fa "struct sbuf *sb" +.Fa "struct mt_status_entry *entry" +.Fa "char *fmt" +.Fc +.Ft void +.Fo mt_param_parent_sbuf +.Fa "struct sbuf *sb" +.Fa "struct mt_status_entry *entry" +.Fa "struct mt_print_params *print_params" +.Fc +.Ft void +.Fo mt_param_parent_print +.Fa "struct mt_status_entry *entry" +.Fa "struct mt_print_params *print_params" +.Fc +.Ft void +.Fo mt_param_entry_sbuf +.Fa "struct sbuf *sb" +.Fa "struct mt_status_entry *entry" +.Fa "void *arg" +.Fc +.Ft void +.Fo mt_param_entry_print +.Fa "struct mt_status_entry *entry" +.Fa "void *arg" +.Fc +.Ft int +.Fo mt_protect_print +.Fa "struct mt_status_data *status_data" +.Fa "int verbose" +.Fc +.Ft int +.Fo mt_param_list +.Fa "struct mt_status_data *status_data" +.Fa "char *param_name" +.Fa "int quiet" +.Fc +.Ft "const char *" +.Fo mt_density_name +.Fa "int density_num" +.Fc +.Ft int +.Fo mt_density_bp +.Fa "int density_num" +.Fa "int bpi" +.Fc +.Ft int +.Fo mt_density_num +.Fa "const char *density_name" +.Fc +.Ft int +.Fo mt_get_status +.Fa "char *xml_str" +.Fa "struct mt_status_data *status_data" +.Fc +.Sh DESCRIPTION +The MT library consists of a number of functions designed to aid in +interacting with the +.Xr sa 4 +driver. +The +.Xr sa 4 +driver returns some status data as XML-formatted strings, and +the primary purpose of this library is to make it easier for the +software developer to parse those strings and extract the status values. +.Pp +The +.Fn mt_start_element , +.Fn mt_end_element , +and +.Fn mt_char_handler +functions are designed to work with the +.Xr libbbsdxml 3 +library, which is an XML parsing library. +The user data for the XML parser should be set with +.Fn XML_SetUserData +to a zeroed struct +mt_status_data with the entries list initialized. +The element handlers for the XML parser should be set to +.Fn mt_start_element +and +.Fn mt_end_element +with +.Fn XML_SetElementHandler . +The character data handler should be set to +.Fn mt_char_handler +with the +.Fn XML_SetCharacterDataHandler +function. +The error member of the status_data structure will be set to 0 if parsing +is successful, and non-zero if parsing failed. +In the event of a failure, the error_str member will contain an error +message describing the failure. +These functions will build a tree of tape driver status data that can be +searched and printed using the other functions in this library. +.Pp +.Fn mt_status_tree_sbuf +takes the root node of a tree of +.Xr sa 4 +driver status information, and displays it in an +.Xr sbuf 9 . +The +.Ar sb +argument is the destination sbuf. +The +.Ar entry +argument is the root of the tree. +The +.Ar indent +argument is the number of characters to indent the output. +Each recursive call to +.Fn mt_status_tree_sbuf +will have the indent level incremented by 2. +The +.Ar sbuf_func +argument is for a user-supplied alternate printing function. +If it is non-NULL, it will be called instead of the default output printing +code. +The +.Ar arg +argument is an argument for the +.Ar sbuf_func +function. +.Pp +The +.Fn mt_status_tree_print +function is the same as the +.Fn mt_status_tree_sbuf +function, except that the tree is printed to standard out instead of to a +sbuf. +.Pp +The +.Fn mt_entry_find +function returns the first entry in the tree starting at +.Ar entry +that matches +.Ar name . +The supplied node name can be a single level name like "foo", or it can +specify mulitple node names that must be matched, for instance "foo.bar.baz". +In the case of a single level name, it will match any node beneath +.Ar entry +that matches +.Ar name . +In the case of a multi-level name like "foo.bar.baz", it will return the +first entry named "baz" whose immediate parent is "bar" and where the +parent of "bar" is named "foo". +.Pp +The +.Fn mt_status_entry_find +is the same as +.Fn mt_entry_find , +except that it operates on the top level mt_status_data and all +mt_status_entry nodes below it instead of just an mt_status_entry +structure. +.Pp +The +.Fn mt_status_entry_free +function frees the tree of status data underneath +.Ar entry . +.Pp +The +.Fn mt_status_free +function frees the tree of status data underneath +.Ar status_data . +.Pp +The +.Fn mt_entry_sbuf +function prints +.Ar entry +to the supplied sbuf +.Ar sb , +optionally using the +.Xr printf 3 +format +.Ar fmt . +If +.Ar fmt +is NULL, then +.Fn mt_entry_sbuf +will render integer types in base 10 without special formatting and all +other types as they were rendered in the XML. +.Pp +.Fn mt_param_parent_sbuf +prints the parents of the given +.Ar entry +to the supplied sbuf +.Ar sb +subject to the print parameters +.Ar print_params . +The result will be formatted with a period between each level, like +"foo.bar.baz". +.Pp +.Fn mt_param_parent_print +is like +.Fn mt_param_parent_sbuf +except that it prints the results to standard output instead of an sbuf. +.Pp +.Fn mt_param_entry_sbuf +prints the +.Ar entry +to the given sbuf +.Ar sb . +The argument +.Ar arg +is a pointer to struct mt_print_params, which allows the caller to control +the printing output. +This function is intended to be supplied as an argument to +.Fn mt_status_tree_sbuf . +.Pp +.Fn mt_param_entry_print +is like +.Fn mt_param_entry_sbuf +except that it prints to standard output instead of an sbuf. +It is intended to be used as an argument to +.Fn mt_status_tree_print . +.Pp +.Fn mt_protect_print +prints tape drive protection information from the supplied +.Ar status_data +beginning at the node name defined as the root node for protection data. +If the +.Ar verbose +argument is non-zero, protection entry descriptions will be printed. +If it is zero, protection entry descriptions will not be printed. +.Pp +.Fn mt_param_list +prints tape driver parameters information from the supplied +.Ar status_data . +If the +.Ar param_name +is non-NULL, only the named parameter will be printed. +If +.Ar quiet +is non-zero, parameter descriptions will be omitted in the output. +.Pp +.Fn mt_density_name +Returns a text identifier for the supplied numeric +.Ar density_num . +The +.Ar density_num +should currently be a value between 0 and 255 inclusive, since that is the +valid range for +.Tn SCSI +density code values. +See below for notes on the return values. +.Pp +.Fn mt_density_bp +Returns the bits per inch or bits per mm values for a given density entry +specified by the +.Ar density_num . +If the +.Ar bpi +argument is non-zero, the bits per inch value is returned. +Otherwise, the bits per mm value is returned. +.Pp +.Fn mt_density_num +returns a numeric value for a text density description. +It does a case-insensitive comparison of density names in the density table +to the supplied density name. +.Pp +.Fn mt_get_xml_str +gets the current XML status / parameter string from the sa(4) driver +instance referenced by the open file descriptor +.Ar mtfd . +The +.Xr mtio 4 +.Xr ioctl 2 +to be used is supplied as the +.Ar cmd +argument. +Currently the +.Fn mt_get_xml_str +function will work with the +.Dv MTIOCEXTGET +and +.Dv MTIOCPARAMGET +ioctls. +The supplied +.Ar xml_str +will be filled in with a pointer to the complete XML status string. +Multiple calls to the given +.Xr ioctl 2 +are made and more space is malloced until all of the XML string is fetched. +The string returned in the +.Ar xml_str +argument should be freed when it is no longer in use. +.Sh RETURN VALUES +.Fn mt_entry_find +returns the first matching entry, or NULL if it fails to find a match. +.Pp +.Fn mt_status_entry_find +returns the first matching entry, or NULL if it fails to find a match. +.Pp +.Fn mt_protect_print +Returns 0 for success, and non-zero for failure. +.Fn mt_protect_print +can only fail if it cannot find protection information in the supplied +status data. +.Pp +.Fn mt_param_list +Returns 0 for success and non-zero for failure. +.Fn mt_param_list +can only fail if it cannot find parameter information in the supplied +status data. +.Pp +.Fn mt_density_name +returns a text description of a numeric density. +The special density value 0 is decoded as "default". +The special density value 0x7f is decoded as "same". +If the density is not known, +.Fn mt_density_name +will return "UNKNOWN". +.Pp +.Fn mt_density_bp +returns the bits per inch value for the given density (if the +.Ar bpi +field is non-zero), the bits per mm value otherwise, or 0 if the supplied +.Ar density_num +is not in the density table or the table entry does not include bpi / bpmm +values. +.Pp +.Fn mt_density_num +returns a numeric density value between 0 and 255 for the supplied density +name. +It returns 0 if the density name is not recognized. +.Pp +.Fn mt_get_xml_str +returns 0 for success, and -1 for failure. +.Sh SEE ALSO +.Xr mt 1 , +.Xr mtio 4 , +.Xr sa 4 +.Sh HISTORY +The MT library first appeared in +.Fx 10.1 . +.Sh AUTHORS +.An Ken Merry Aq ken@FreeBSD.org +.Sh BUGS +The library interface is not complete, and may change in the future. +Application authors should not rely on the library interface to be +consistent in the immediate future. diff --git a/lib/libmt/mtlib.c b/lib/libmt/mtlib.c new file mode 100644 index 0000000..f6acba2 --- /dev/null +++ b/lib/libmt/mtlib.c @@ -0,0 +1,773 @@ +/*- + * Copyright (c) 2013, 2014, 2015 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * Authors: Ken Merry (Spectra Logic Corporation) + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/mtio.h> +#include <sys/queue.h> +#include <sys/sbuf.h> + +#include <ctype.h> +#include <err.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <stdint.h> +#include <errno.h> +#include <bsdxml.h> +#include <mtlib.h> + +/* + * Called at the start of each XML element, and includes the list of + * attributes for the element. + */ +void +mt_start_element(void *user_data, const char *name, const char **attr) +{ + int i; + struct mt_status_data *mtinfo; + struct mt_status_entry *entry; + + mtinfo = (struct mt_status_data *)user_data; + + if (mtinfo->error != 0) + return; + + mtinfo->level++; + if ((u_int)mtinfo->level >= (sizeof(mtinfo->cur_sb) / + sizeof(mtinfo->cur_sb[0]))) { + mtinfo->error = 1; + snprintf(mtinfo->error_str, sizeof(mtinfo->error_str), + "%s: too many nesting levels, %zd max", __func__, + sizeof(mtinfo->cur_sb) / sizeof(mtinfo->cur_sb[0])); + return; + } + + mtinfo->cur_sb[mtinfo->level] = sbuf_new_auto(); + if (mtinfo->cur_sb[mtinfo->level] == NULL) { + mtinfo->error = 1; + snprintf(mtinfo->error_str, sizeof(mtinfo->error_str), + "%s: Unable to allocate sbuf", __func__); + return; + } + + entry = malloc(sizeof(*entry)); + if (entry == NULL) { + mtinfo->error = 1; + snprintf(mtinfo->error_str, sizeof(mtinfo->error_str), + "%s: unable to allocate %zd bytes", __func__, + sizeof(*entry)); + return; + } + bzero(entry, sizeof(*entry)); + STAILQ_INIT(&entry->nv_list); + STAILQ_INIT(&entry->child_entries); + entry->entry_name = strdup(name); + mtinfo->cur_entry[mtinfo->level] = entry; + if (mtinfo->cur_entry[mtinfo->level - 1] == NULL) { + STAILQ_INSERT_TAIL(&mtinfo->entries, entry, links); + } else { + STAILQ_INSERT_TAIL( + &mtinfo->cur_entry[mtinfo->level - 1]->child_entries, + entry, links); + entry->parent = mtinfo->cur_entry[mtinfo->level - 1]; + } + for (i = 0; attr[i] != NULL; i+=2) { + struct mt_status_nv *nv; + int need_nv; + + need_nv = 0; + + if (strcmp(attr[i], "size") == 0) { + entry->size = strtoull(attr[i+1], NULL, 0); + } else if (strcmp(attr[i], "type") == 0) { + if (strcmp(attr[i+1], "int") == 0) { + entry->var_type = MT_TYPE_INT; + } else if (strcmp(attr[i+1], "uint") == 0) { + entry->var_type = MT_TYPE_UINT; + } else if (strcmp(attr[i+1], "str") == 0) { + entry->var_type = MT_TYPE_STRING; + } else if (strcmp(attr[i+1], "node") == 0) { + entry->var_type = MT_TYPE_NODE; + } else { + need_nv = 1; + } + } else if (strcmp(attr[i], "fmt") == 0) { + entry->fmt = strdup(attr[i+1]); + } else if (strcmp(attr[i], "desc") == 0) { + entry->desc = strdup(attr[i+1]); + } else { + need_nv = 1; + } + if (need_nv != 0) { + nv = malloc(sizeof(*nv)); + if (nv == NULL) { + mtinfo->error = 1; + snprintf(mtinfo->error_str, + sizeof(mtinfo->error_str), + "%s: error allocating %zd bytes", + __func__, sizeof(*nv)); + } + bzero(nv, sizeof(*nv)); + nv->name = strdup(attr[i]); + nv->value = strdup(attr[i+1]); + STAILQ_INSERT_TAIL(&entry->nv_list, nv, links); + } + } +} + +/* + * Called on XML element close. + */ +void +mt_end_element(void *user_data, const char *name) +{ + struct mt_status_data *mtinfo; + char *str; + + mtinfo = (struct mt_status_data *)user_data; + + if (mtinfo->error != 0) + return; + + if (mtinfo->cur_sb[mtinfo->level] == NULL) { + mtinfo->error = 1; + snprintf(mtinfo->error_str, sizeof(mtinfo->error_str), + "%s: no valid sbuf at level %d (name %s)", __func__, + mtinfo->level, name); + return; + } + sbuf_finish(mtinfo->cur_sb[mtinfo->level]); + str = strdup(sbuf_data(mtinfo->cur_sb[mtinfo->level])); + if (str == NULL) { + mtinfo->error = 1; + snprintf(mtinfo->error_str, sizeof(mtinfo->error_str), + "%s can't allocate %zd bytes for string", __func__, + sbuf_len(mtinfo->cur_sb[mtinfo->level])); + return; + } + + if (strlen(str) == 0) { + free(str); + str = NULL; + } + if (str != NULL) { + struct mt_status_entry *entry; + + entry = mtinfo->cur_entry[mtinfo->level]; + switch(entry->var_type) { + case MT_TYPE_INT: + entry->value_signed = strtoll(str, NULL, 0); + break; + case MT_TYPE_UINT: + entry->value_unsigned = strtoull(str, NULL, 0); + break; + default: + break; + } + } + + mtinfo->cur_entry[mtinfo->level]->value = str; + + sbuf_delete(mtinfo->cur_sb[mtinfo->level]); + mtinfo->cur_sb[mtinfo->level] = NULL; + mtinfo->cur_entry[mtinfo->level] = NULL; + mtinfo->level--; +} + +/* + * Called to handle character strings in the current element. + */ +void +mt_char_handler(void *user_data, const XML_Char *str, int len) +{ + struct mt_status_data *mtinfo; + + mtinfo = (struct mt_status_data *)user_data; + if (mtinfo->error != 0) + return; + + sbuf_bcat(mtinfo->cur_sb[mtinfo->level], str, len); +} + +void +mt_status_tree_sbuf(struct sbuf *sb, struct mt_status_entry *entry, int indent, + void (*sbuf_func)(struct sbuf *sb, struct mt_status_entry *entry, + void *arg), void *arg) +{ + struct mt_status_nv *nv; + struct mt_status_entry *entry2; + + if (sbuf_func != NULL) { + sbuf_func(sb, entry, arg); + } else { + sbuf_printf(sb, "%*sname: %s, value: %s, fmt: %s, size: %zd, " + "type: %d, desc: %s\n", indent, "", entry->entry_name, + entry->value, entry->fmt, entry->size, entry->var_type, + entry->desc); + STAILQ_FOREACH(nv, &entry->nv_list, links) { + sbuf_printf(sb, "%*snv: name: %s, value: %s\n", + indent + 1, "", nv->name, nv->value); + } + } + + STAILQ_FOREACH(entry2, &entry->child_entries, links) + mt_status_tree_sbuf(sb, entry2, indent + 2, sbuf_func, arg); +} + +void +mt_status_tree_print(struct mt_status_entry *entry, int indent, + void (*print_func)(struct mt_status_entry *entry, void *arg), void *arg) +{ + + if (print_func != NULL) { + struct mt_status_entry *entry2; + + print_func(entry, arg); + STAILQ_FOREACH(entry2, &entry->child_entries, links) + mt_status_tree_print(entry2, indent + 2, print_func, + arg); + } else { + struct sbuf *sb; + + sb = sbuf_new_auto(); + if (sb == NULL) + return; + mt_status_tree_sbuf(sb, entry, indent, NULL, NULL); + sbuf_finish(sb); + + printf("%s", sbuf_data(sb)); + sbuf_delete(sb); + } +} + +/* + * Given a parameter name in the form "foo" or "foo.bar.baz", traverse the + * tree looking for the parameter (the first case) or series of parameters + * (second case). + */ +struct mt_status_entry * +mt_entry_find(struct mt_status_entry *entry, char *name) +{ + struct mt_status_entry *entry2; + char *tmpname = NULL, *tmpname2 = NULL, *tmpstr = NULL; + + tmpname = strdup(name); + if (tmpname == NULL) + goto bailout; + + /* Save a pointer so we can free this later */ + tmpname2 = tmpname; + + tmpstr = strsep(&tmpname, "."); + + /* + * Is this the entry we're looking for? Or do we have further + * child entries that we need to grab? + */ + if (strcmp(entry->entry_name, tmpstr) == 0) { + if (tmpname == NULL) { + /* + * There are no further child entries to find. We + * have a complete match. + */ + free(tmpname2); + return (entry); + } else { + /* + * There are more child entries that we need to find. + * Fall through to the recursive search off of this + * entry, below. Use tmpname, which will contain + * everything after the first period. + */ + name = tmpname; + } + } + + /* + * Recursively look for further entries. + */ + STAILQ_FOREACH(entry2, &entry->child_entries, links) { + struct mt_status_entry *entry3; + + entry3 = mt_entry_find(entry2, name); + if (entry3 != NULL) { + free(tmpname2); + return (entry3); + } + } + +bailout: + free(tmpname2); + + return (NULL); +} + +struct mt_status_entry * +mt_status_entry_find(struct mt_status_data *status_data, char *name) +{ + struct mt_status_entry *entry, *entry2; + + STAILQ_FOREACH(entry, &status_data->entries, links) { + entry2 = mt_entry_find(entry, name); + if (entry2 != NULL) + return (entry2); + } + + return (NULL); +} + +void +mt_status_entry_free(struct mt_status_entry *entry) +{ + struct mt_status_entry *entry2, *entry3; + struct mt_status_nv *nv, *nv2; + + STAILQ_FOREACH_SAFE(entry2, &entry->child_entries, links, entry3) { + STAILQ_REMOVE(&entry->child_entries, entry2, mt_status_entry, + links); + mt_status_entry_free(entry2); + } + + free(entry->entry_name); + free(entry->value); + free(entry->fmt); + free(entry->desc); + + STAILQ_FOREACH_SAFE(nv, &entry->nv_list, links, nv2) { + STAILQ_REMOVE(&entry->nv_list, nv, mt_status_nv, links); + free(nv->name); + free(nv->value); + free(nv); + } + free(entry); +} + +void +mt_status_free(struct mt_status_data *status_data) +{ + struct mt_status_entry *entry, *entry2; + + STAILQ_FOREACH_SAFE(entry, &status_data->entries, links, entry2) { + STAILQ_REMOVE(&status_data->entries, entry, mt_status_entry, + links); + mt_status_entry_free(entry); + } +} + +void +mt_entry_sbuf(struct sbuf *sb, struct mt_status_entry *entry, char *fmt) +{ + switch(entry->var_type) { + case MT_TYPE_INT: + if (fmt != NULL) + sbuf_printf(sb, fmt, (intmax_t)entry->value_signed); + else + sbuf_printf(sb, "%jd", + (intmax_t)entry->value_signed); + break; + case MT_TYPE_UINT: + if (fmt != NULL) + sbuf_printf(sb, fmt, (uintmax_t)entry->value_unsigned); + else + sbuf_printf(sb, "%ju", + (uintmax_t)entry->value_unsigned); + break; + default: + if (fmt != NULL) + sbuf_printf(sb, fmt, entry->value); + else + sbuf_printf(sb, "%s", entry->value); + break; + } +} + +void +mt_param_parent_print(struct mt_status_entry *entry, + struct mt_print_params *print_params) +{ + if (entry->parent != NULL) + mt_param_parent_print(entry->parent, print_params); + + if (((print_params->flags & MT_PF_INCLUDE_ROOT) == 0) + && (strcmp(entry->entry_name, print_params->root_name) == 0)) + return; + + printf("%s.", entry->entry_name); +} + +void +mt_param_parent_sbuf(struct sbuf *sb, struct mt_status_entry *entry, + struct mt_print_params *print_params) +{ + if (entry->parent != NULL) + mt_param_parent_sbuf(sb, entry->parent, print_params); + + if (((print_params->flags & MT_PF_INCLUDE_ROOT) == 0) + && (strcmp(entry->entry_name, print_params->root_name) == 0)) + return; + + sbuf_printf(sb, "%s.", entry->entry_name); +} + +void +mt_param_entry_sbuf(struct sbuf *sb, struct mt_status_entry *entry, void *arg) +{ + struct mt_print_params *print_params; + + print_params = (struct mt_print_params *)arg; + + /* + * We don't want to print nodes. + */ + if (entry->var_type == MT_TYPE_NODE) + return; + + if ((print_params->flags & MT_PF_FULL_PATH) + && (entry->parent != NULL)) + mt_param_parent_sbuf(sb, entry->parent, print_params); + + sbuf_printf(sb, "%s: %s", entry->entry_name, entry->value); + if ((print_params->flags & MT_PF_VERBOSE) + && (entry->desc != NULL) + && (strlen(entry->desc) > 0)) + sbuf_printf(sb, " (%s)", entry->desc); + sbuf_printf(sb, "\n"); + +} + +void +mt_param_entry_print(struct mt_status_entry *entry, void *arg) +{ + struct mt_print_params *print_params; + + print_params = (struct mt_print_params *)arg; + + /* + * We don't want to print nodes. + */ + if (entry->var_type == MT_TYPE_NODE) + return; + + if ((print_params->flags & MT_PF_FULL_PATH) + && (entry->parent != NULL)) + mt_param_parent_print(entry->parent, print_params); + + printf("%s: %s", entry->entry_name, entry->value); + if ((print_params->flags & MT_PF_VERBOSE) + && (entry->desc != NULL) + && (strlen(entry->desc) > 0)) + printf(" (%s)", entry->desc); + printf("\n"); +} + +int +mt_protect_print(struct mt_status_data *status_data, int verbose) +{ + struct mt_status_entry *entry; + const char *prot_name = MT_PROTECTION_NAME; + struct mt_print_params print_params; + + snprintf(print_params.root_name, sizeof(print_params.root_name), + MT_PARAM_ROOT_NAME); + print_params.flags = MT_PF_FULL_PATH; + if (verbose != 0) + print_params.flags |= MT_PF_VERBOSE; + + entry = mt_status_entry_find(status_data, __DECONST(char *,prot_name)); + if (entry == NULL) + return (1); + mt_status_tree_print(entry, 0, mt_param_entry_print, &print_params); + + return (0); +} + +int +mt_param_list(struct mt_status_data *status_data, char *param_name, int quiet) +{ + struct mt_status_entry *entry; + struct mt_print_params print_params; + char root_name[20]; + + snprintf(root_name, sizeof(root_name), "mtparamget"); + strlcpy(print_params.root_name, root_name, + sizeof(print_params.root_name)); + + print_params.flags = MT_PF_FULL_PATH; + if (quiet == 0) + print_params.flags |= MT_PF_VERBOSE; + + if (param_name != NULL) { + entry = mt_status_entry_find(status_data, param_name); + if (entry == NULL) + return (1); + + mt_param_entry_print(entry, &print_params); + + return (0); + } else { + entry = mt_status_entry_find(status_data, root_name); + + STAILQ_FOREACH(entry, &status_data->entries, links) + mt_status_tree_print(entry, 0, mt_param_entry_print, + &print_params); + } + + return (0); +} + +static struct densities { + int dens; + int bpmm; + int bpi; + const char *name; +} dens[] = { + /* + * Taken from T10 Project 997D + * SCSI-3 Stream Device Commands (SSC) + * Revision 11, 4-Nov-97 + * + * LTO 1-6 definitions obtained from the eighth edition of the + * IBM TotalStorage LTO Ultrium Tape Drive SCSI Reference + * (July 2007) and the second edition of the IBM System Storage LTO + * Tape Drive SCSI Reference (February 13, 2013). + * + * IBM 3592 definitions obtained from second edition of the IBM + * System Storage Tape Drive 3592 SCSI Reference (May 25, 2012). + * + * DAT-72 and DAT-160 bpi values taken from "HP StorageWorks DAT160 + * tape drive white paper", dated June 2007. + * + * DAT-160 / SDLT220 density code (0x48) conflict information + * found here: + * + * http://h20564.www2.hp.com/hpsc/doc/public/display?docId=emr_na-c01065117&sp4ts.oid=429311 + * (Document ID c01065117) + */ + /*Num. bpmm bpi Reference */ + { 0x1, 32, 800, "X3.22-1983" }, + { 0x2, 63, 1600, "X3.39-1986" }, + { 0x3, 246, 6250, "X3.54-1986" }, + { 0x5, 315, 8000, "X3.136-1986" }, + { 0x6, 126, 3200, "X3.157-1987" }, + { 0x7, 252, 6400, "X3.116-1986" }, + { 0x8, 315, 8000, "X3.158-1987" }, + { 0x9, 491, 37871, "X3.180" }, + { 0xA, 262, 6667, "X3B5/86-199" }, + { 0xB, 63, 1600, "X3.56-1986" }, + { 0xC, 500, 12690, "HI-TC1" }, + { 0xD, 999, 25380, "HI-TC2" }, + { 0xF, 394, 10000, "QIC-120" }, + { 0x10, 394, 10000, "QIC-150" }, + { 0x11, 630, 16000, "QIC-320" }, + { 0x12, 2034, 51667, "QIC-1350" }, + { 0x13, 2400, 61000, "X3B5/88-185A" }, + { 0x14, 1703, 43245, "X3.202-1991" }, + { 0x15, 1789, 45434, "ECMA TC17" }, + { 0x16, 394, 10000, "X3.193-1990" }, + { 0x17, 1673, 42500, "X3B5/91-174" }, + { 0x18, 1673, 42500, "X3B5/92-50" }, + { 0x19, 2460, 62500, "DLTapeIII" }, + { 0x1A, 3214, 81633, "DLTapeIV(20GB)" }, + { 0x1B, 3383, 85937, "DLTapeIV(35GB)" }, + { 0x1C, 1654, 42000, "QIC-385M" }, + { 0x1D, 1512, 38400, "QIC-410M" }, + { 0x1E, 1385, 36000, "QIC-1000C" }, + { 0x1F, 2666, 67733, "QIC-2100C" }, + { 0x20, 2666, 67733, "QIC-6GB(M)" }, + { 0x21, 2666, 67733, "QIC-20GB(C)" }, + { 0x22, 1600, 40640, "QIC-2GB(C)" }, + { 0x23, 2666, 67733, "QIC-875M" }, + { 0x24, 2400, 61000, "DDS-2" }, + { 0x25, 3816, 97000, "DDS-3" }, + { 0x26, 3816, 97000, "DDS-4" }, + { 0x27, 3056, 77611, "Mammoth" }, + { 0x28, 1491, 37871, "X3.224" }, + { 0x40, 4880, 123952, "LTO-1" }, + { 0x41, 3868, 98250, "DLTapeIV(40GB)" }, + { 0x42, 7398, 187909, "LTO-2" }, + { 0x44, 9638, 244805, "LTO-3" }, + { 0x46, 12725, 323215, "LTO-4" }, + { 0x47, 6417, 163000, "DAT-72" }, + /* + * XXX KDM note that 0x48 is also the density code for DAT-160. + * For some reason they used overlapping density codes. + */ +#if 0 + { 0x48, 6870, 174500, "DAT-160" }, +#endif + { 0x48, 5236, 133000, "SDLTapeI(110)" }, + { 0x49, 7598, 193000, "SDLTapeI(160)" }, + { 0x4a, 0, 0, "T10000A" }, + { 0x4b, 0, 0, "T10000B" }, + { 0x4c, 0, 0, "T10000C" }, + { 0x4d, 0, 0, "T10000D" }, + { 0x51, 11800, 299720, "3592A1 (unencrypted)" }, + { 0x52, 11800, 299720, "3592A2 (unencrypted)" }, + { 0x53, 13452, 341681, "3592A3 (unencrypted)" }, + { 0x54, 19686, 500024, "3592A4 (unencrypted)" }, + { 0x55, 20670, 525018, "3592A5 (unencrypted)" }, + { 0x58, 15142, 384607, "LTO-5" }, + { 0x5A, 15142, 384607, "LTO-6" }, + { 0x71, 11800, 299720, "3592A1 (encrypted)" }, + { 0x72, 11800, 299720, "3592A2 (encrypted)" }, + { 0x73, 13452, 341681, "3592A3 (encrypted)" }, + { 0x74, 19686, 500024, "3592A4 (encrypted)" }, + { 0x75, 20670, 525018, "3592A5 (encrypted)" }, + { 0x8c, 1789, 45434, "EXB-8500c" }, + { 0x90, 1703, 43245, "EXB-8200c" }, + { 0, 0, 0, NULL } +}; + +const char * +mt_density_name(int density_num) +{ + struct densities *sd; + + /* densities 0 and 0x7f are handled as special cases */ + if (density_num == 0) + return ("default"); + if (density_num == 0x7f) + return ("same"); + + for (sd = dens; sd->dens != 0; sd++) + if (sd->dens == density_num) + break; + if (sd->dens == 0) + return ("UNKNOWN"); + return (sd->name); +} + +/* + * Given a specific density number, return either the bits per inch or bits + * per millimeter for the given density. + */ +int +mt_density_bp(int density_num, int bpi) +{ + struct densities *sd; + + for (sd = dens; sd->dens; sd++) + if (sd->dens == density_num) + break; + if (sd->dens == 0) + return (0); + if (bpi) + return (sd->bpi); + else + return (sd->bpmm); +} + +int +mt_density_num(const char *density_name) +{ + struct densities *sd; + size_t l = strlen(density_name); + + for (sd = dens; sd->dens; sd++) + if (strncasecmp(sd->name, density_name, l) == 0) + break; + return (sd->dens); +} + +/* + * Get the current status XML string. + * Returns 0 on success, -1 on failure (with errno set, and *xml_str == NULL). + */ +int +mt_get_xml_str(int mtfd, unsigned long cmd, char **xml_str) +{ + size_t alloc_len = 32768; + struct mtextget extget; + int error; + + *xml_str = NULL; + + for (;;) { + bzero(&extget, sizeof(extget)); + *xml_str = malloc(alloc_len); + if (*xml_str == NULL) + return (-1); + extget.status_xml = *xml_str; + extget.alloc_len = alloc_len; + + error = ioctl(mtfd, cmd, (caddr_t)&extget); + if (error == 0 && extget.status == MT_EXT_GET_OK) + break; + + free(*xml_str); + *xml_str = NULL; + + if (error != 0 || extget.status != MT_EXT_GET_NEED_MORE_SPACE) + return (-1); + + /* The driver needs more space, so double and try again. */ + alloc_len *= 2; + } + return (0); +} + +/* + * Populate a struct mt_status_data from the XML string via mt_get_xml_str(). + * + * Returns XML_STATUS_OK on success. + * If XML_STATUS_ERROR is returned, errno may be set to indicate the reason. + * The caller must check status_data->error. + */ +int +mt_get_status(char *xml_str, struct mt_status_data *status_data) +{ + XML_Parser parser; + int retval; + + bzero(status_data, sizeof(*status_data)); + STAILQ_INIT(&status_data->entries); + + parser = XML_ParserCreate(NULL); + if (parser == NULL) { + errno = ENOMEM; + return (XML_STATUS_ERROR); + } + + XML_SetUserData(parser, status_data); + XML_SetElementHandler(parser, mt_start_element, mt_end_element); + XML_SetCharacterDataHandler(parser, mt_char_handler); + + retval = XML_Parse(parser, xml_str, strlen(xml_str), 1); + XML_ParserFree(parser); + return (retval); +} diff --git a/lib/libmt/mtlib.h b/lib/libmt/mtlib.h new file mode 100644 index 0000000..a61a15d --- /dev/null +++ b/lib/libmt/mtlib.h @@ -0,0 +1,122 @@ +/*- + * Copyright (c) 2013, 2014 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * Authors: Ken Merry (Spectra Logic Corporation) + * + * $FreeBSD$ + */ + +#ifndef _MTLIB_H +#define _MTLIB_H + +typedef enum { + MT_TYPE_NONE, + MT_TYPE_STRING, + MT_TYPE_INT, + MT_TYPE_UINT, + MT_TYPE_NODE +} mt_variable_type; + +struct mt_status_nv { + char *name; + char *value; + STAILQ_ENTRY(mt_status_nv) links; +}; + +struct mt_status_entry { + char *entry_name; + char *value; + uint64_t value_unsigned; + int64_t value_signed; + char *fmt; + char *desc; + size_t size; + mt_variable_type var_type; + struct mt_status_entry *parent; + STAILQ_HEAD(, mt_status_nv) nv_list; + STAILQ_HEAD(, mt_status_entry) child_entries; + STAILQ_ENTRY(mt_status_entry) links; +}; + +struct mt_status_data { + int level; + struct sbuf *cur_sb[32]; + struct mt_status_entry *cur_entry[32]; + int error; + char error_str[128]; + STAILQ_HEAD(, mt_status_entry) entries; +}; + +typedef enum { + MT_PF_NONE = 0x00, + MT_PF_VERBOSE = 0x01, + MT_PF_FULL_PATH = 0x02, + MT_PF_INCLUDE_ROOT = 0x04 +} mt_print_flags; + +struct mt_print_params { + mt_print_flags flags; + char root_name[64]; +}; + +__BEGIN_DECLS +void mt_start_element(void *user_data, const char *name, const char **attr); +void mt_end_element(void *user_data, const char *name); +void mt_char_handler(void *user_data, const XML_Char *str, int len); +void mt_status_tree_sbuf(struct sbuf *sb, struct mt_status_entry *entry, + int indent, void (*sbuf_func)(struct sbuf *sb, + struct mt_status_entry *entry, void *arg), void *arg); +void mt_status_tree_print(struct mt_status_entry *entry, int indent, + void (*print_func)(struct mt_status_entry *entry, + void *arg), void *arg); +struct mt_status_entry *mt_entry_find(struct mt_status_entry *entry, + char *name); +struct mt_status_entry *mt_status_entry_find(struct mt_status_data *status_data, + char *name); +void mt_status_entry_free(struct mt_status_entry *entry); +void mt_status_free(struct mt_status_data *status_data); +void mt_entry_sbuf(struct sbuf *sb, struct mt_status_entry *entry, char *fmt); +void mt_param_parent_print(struct mt_status_entry *entry, + struct mt_print_params *print_params); +void mt_param_parent_sbuf(struct sbuf *sb, struct mt_status_entry *entry, + struct mt_print_params *print_params); +void mt_param_entry_sbuf(struct sbuf *sb, struct mt_status_entry *entry, + void *arg); +void mt_param_entry_print(struct mt_status_entry *entry, void *arg); +int mt_protect_print(struct mt_status_data *status_data, int verbose); +int mt_param_list(struct mt_status_data *status_data, char *param_name, + int quiet); +const char *mt_density_name(int density_num); +int mt_density_bp(int density_num, int bpi); +int mt_density_num(const char *density_name); +int mt_get_xml_str(int mtfd, unsigned long cmd, char **xml_str); +int mt_get_status(char *xml_str, struct mt_status_data *status_data); +__END_DECLS + +#endif /* _MTLIB_H */ |