diff options
Diffstat (limited to 'usr/src/lib/scsi/libses/common/ses_subr.c')
-rw-r--r-- | usr/src/lib/scsi/libses/common/ses_subr.c | 376 |
1 files changed, 376 insertions, 0 deletions
diff --git a/usr/src/lib/scsi/libses/common/ses_subr.c b/usr/src/lib/scsi/libses/common/ses_subr.c new file mode 100644 index 0000000000..aa1013e678 --- /dev/null +++ b/usr/src/lib/scsi/libses/common/ses_subr.c @@ -0,0 +1,376 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <scsi/libses.h> +#include "ses_impl.h" + +__thread ses_errno_t _ses_errno; +__thread char _ses_errmsg[1024]; +__thread char _ses_nverr_member[256]; + +static void ses_vpanic(const char *, va_list) __NORETURN; + +static void +ses_vpanic(const char *fmt, va_list ap) +{ + int oserr = errno; + char msg[BUFSIZ]; + size_t len; + + (void) snprintf(msg, sizeof (msg), "ABORT: "); + len = strlen(msg); + (void) vsnprintf(msg + len, sizeof (msg) - len, fmt, ap); + + if (strchr(fmt, '\n') == NULL) { + len = strlen(msg); + (void) snprintf(msg + len, sizeof (msg) - len, ": %s\n", + strerror(oserr)); + } + + (void) write(STDERR_FILENO, msg, strlen(msg)); + +abort: + abort(); + _exit(1); +} + +/*PRINTFLIKE1*/ +void +ses_panic(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + ses_vpanic(fmt, ap); + va_end(ap); +} + +int +ses_assert(const char *expr, const char *file, int line) +{ + ses_panic("\"%s\", line %d: assertion failed: %s\n", file, line, expr); + + /*NOTREACHED*/ + return (0); +} + +int +nvlist_add_fixed_string(nvlist_t *nvl, const char *name, + const char *buf, size_t len) +{ + char *str = alloca(len + 1); + bcopy(buf, str, len); + str[len] = '\0'; + + return (nvlist_add_string(nvl, name, str)); +} + +/* + * Like fixed_string, but clears any leading or trailing spaces. + */ +int +nvlist_add_fixed_string_trunc(nvlist_t *nvl, const char *name, + const char *buf, size_t len) +{ + while (buf[0] == ' ' && len > 0) { + buf++; + len--; + } + + while (len > 0 && buf[len - 1] == ' ') + len--; + + return (nvlist_add_fixed_string(nvl, name, buf, len)); +} + +ses_errno_t +ses_errno(void) +{ + return (_ses_errno); +} + +const char * +ses_errmsg(void) +{ + if (_ses_errmsg[0] == '\0') + (void) snprintf(_ses_errmsg, sizeof (_ses_errmsg), "%s", + ses_strerror(_ses_errno)); + + return (_ses_errmsg); +} + +const char * +ses_nv_error_member(void) +{ + if (_ses_nverr_member[0] != '\0') + return (_ses_nverr_member); + else + return (NULL); +} + +static int +__ses_set_errno(ses_errno_t err, const char *nvm) +{ + if (nvm == NULL) { + _ses_nverr_member[0] = '\0'; + } else { + (void) strlcpy(_ses_nverr_member, nvm, + sizeof (_ses_nverr_member)); + } + _ses_errmsg[0] = '\0'; + _ses_errno = err; + + return (-1); +} + +int +ses_set_errno(ses_errno_t err) +{ + return (__ses_set_errno(err, NULL)); +} + +int +ses_set_nverrno(int err, const char *member) +{ + ses_errno_t se = (err == ENOMEM || err == EAGAIN) ? + ESES_NOMEM : ESES_NVL; + + /* + * If the error is ESES_NVL, then we should always have a member + * available. The only time 'member' is NULL is when nvlist_alloc() + * fails, which should only be possible if memory allocation fails. + */ + assert(se == ESES_NOMEM || member != NULL); + + return (__ses_set_errno(se, member)); +} + +static int +ses_verror(ses_errno_t err, const char *fmt, va_list ap) +{ + int syserr = errno; + size_t n; + char *errmsg; + + errmsg = alloca(sizeof (_ses_errmsg)); + (void) vsnprintf(errmsg, sizeof (_ses_errmsg), fmt, ap); + (void) ses_set_errno(err); + + n = strlen(errmsg); + + while (n != 0 && errmsg[n - 1] == '\n') + errmsg[--n] = '\0'; + + bcopy(errmsg, _ses_errmsg, sizeof (_ses_errmsg)); + errno = syserr; + + return (-1); +} + +static int +ses_vnverror(int err, const char *member, const char *fmt, + va_list ap) +{ + int syserr = errno; + size_t n; + char *errmsg; + + errmsg = alloca(sizeof (_ses_errmsg)); + (void) vsnprintf(errmsg, sizeof (_ses_errmsg), fmt, ap); + (void) ses_set_nverrno(err, member); + + n = strlen(errmsg); + + while (n != 0 && errmsg[n - 1] == '\n') + errmsg[--n] = '\0'; + + (void) snprintf(errmsg + n, sizeof (_ses_errmsg) - n, ": %s", + strerror(err)); + + bcopy(errmsg, _ses_errmsg, sizeof (_ses_errmsg)); + errno = syserr; + + return (-1); +} + +int +ses_error(ses_errno_t err, const char *fmt, ...) +{ + va_list ap; + int rv; + + va_start(ap, fmt); + rv = ses_verror(err, fmt, ap); + va_end(ap); + + return (rv); +} + +int +ses_nverror(int err, const char *member, const char *fmt, ...) +{ + va_list ap; + int rv; + + va_start(ap, fmt); + rv = ses_vnverror(err, member, fmt, ap); + va_end(ap); + + return (rv); +} + +int +ses_libscsi_error(libscsi_hdl_t *shp, const char *fmt, ...) +{ + va_list ap; + char errmsg[LIBSES_ERRMSGLEN]; + libscsi_errno_t se = libscsi_errno(shp); + ses_errno_t e; + + switch (se) { + case ESCSI_NONE: + return (0); + case ESCSI_NOMEM: + e = ESES_NOMEM; + break; + case ESCSI_NOTSUP: + e = ESES_NOTSUP; + break; + case ESCSI_ZERO_LENGTH: + case ESCSI_VERSION: + case ESCSI_BADFLAGS: + case ESCSI_BOGUSFLAGS: + case ESCSI_BADLENGTH: + case ESCSI_NEEDBUF: + va_start(ap, fmt); + (void) vsnprintf(errmsg, sizeof (errmsg), fmt, ap); + va_end(ap); + ses_panic("%s: unexpected libscsi error %s: %s", errmsg, + libscsi_errname(se), libscsi_errmsg(shp)); + break; + case ESCSI_UNKNOWN: + e = ESES_UNKNOWN; + break; + default: + e = ESES_LIBSCSI; + break; + } + + va_start(ap, fmt); + (void) vsnprintf(errmsg, sizeof (errmsg), fmt, ap); + va_end(ap); + + return (ses_error(e, "%s: %s", errmsg, libscsi_errmsg(shp))); +} + +int +ses_scsi_error(libscsi_action_t *ap, const char *fmt, ...) +{ + va_list args; + char errmsg[LIBSES_ERRMSGLEN]; + uint64_t asc = 0, ascq = 0, key = 0; + const char *code, *keystr; + + va_start(args, fmt); + (void) vsnprintf(errmsg, sizeof (errmsg), fmt, args); + va_end(args); + + if (libscsi_action_parse_sense(ap, &key, &asc, &ascq, NULL) != 0) + return (ses_error(ESES_LIBSCSI, + "%s: SCSI status %d (no sense data available)", errmsg, + libscsi_action_get_status(ap))); + + code = libscsi_sense_code_name(asc, ascq); + keystr = libscsi_sense_key_name(key); + + return (ses_error(ESES_LIBSCSI, "%s: SCSI status %d sense key %llu " + "(%s) additional sense code 0x%llx/0x%llx (%s)", errmsg, + libscsi_action_get_status(ap), key, keystr ? keystr : "<unknown>", + asc, ascq, code ? code : "<unknown>")); +} + +void * +ses_alloc(size_t sz) +{ + void *p; + + if (sz == 0) + ses_panic("attempted zero-length allocation"); + + if ((p = malloc(sz)) == NULL) + (void) ses_set_errno(ESES_NOMEM); + + return (p); +} + +void * +ses_zalloc(size_t sz) +{ + void *p; + + if ((p = ses_alloc(sz)) != NULL) + bzero(p, sz); + + return (p); +} + +char * +ses_strdup(const char *s) +{ + char *p; + size_t len; + + if (s == NULL) + ses_panic("attempted zero-length allocation"); + + len = strlen(s) + 1; + + if ((p = ses_alloc(len)) != NULL) + bcopy(s, p, len); + + return (p); +} + +void * +ses_realloc(void *p, size_t sz) +{ + if (sz == 0) + ses_panic("attempted zero-length allocation"); + + if ((p = realloc(p, sz)) == NULL) + (void) ses_set_errno(ESES_NOMEM); + + return (p); +} + +/*ARGSUSED*/ +void +ses_free(void *p) +{ + free(p); +} |