2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A
2N/A#include <scsi/libses.h>
2N/A#include "ses_impl.h"
2N/A
2N/A__thread ses_errno_t _ses_errno;
2N/A__thread char _ses_errmsg[1024];
2N/A__thread char _ses_nverr_member[256];
2N/A
2N/Astatic void ses_vpanic(const char *, va_list) __NORETURN;
2N/A
2N/Astatic void
2N/Ases_vpanic(const char *fmt, va_list ap)
2N/A{
2N/A int oserr = errno;
2N/A char msg[BUFSIZ];
2N/A size_t len;
2N/A
2N/A (void) snprintf(msg, sizeof (msg), "ABORT: ");
2N/A len = strlen(msg);
2N/A (void) vsnprintf(msg + len, sizeof (msg) - len, fmt, ap);
2N/A
2N/A if (strchr(fmt, '\n') == NULL) {
2N/A len = strlen(msg);
2N/A (void) snprintf(msg + len, sizeof (msg) - len, ": %s\n",
2N/A strerror(oserr));
2N/A }
2N/A
2N/A (void) write(STDERR_FILENO, msg, strlen(msg));
2N/A
2N/Aabort:
2N/A abort();
2N/A /*NOTREACHED*/
2N/A _exit(1);
2N/A}
2N/A
2N/A/*PRINTFLIKE1*/
2N/Avoid
2N/Ases_panic(const char *fmt, ...)
2N/A{
2N/A va_list ap;
2N/A
2N/A va_start(ap, fmt);
2N/A ses_vpanic(fmt, ap);
2N/A /*NOTREACHED*/
2N/A va_end(ap);
2N/A}
2N/A
2N/Aint
2N/Ases_assert(const char *expr, const char *file, int line)
2N/A{
2N/A ses_panic("\"%s\", line %d: assertion failed: %s\n", file, line, expr);
2N/A
2N/A /*NOTREACHED*/
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/Anvlist_add_fixed_string(nvlist_t *nvl, const char *name,
2N/A const char *buf, size_t len)
2N/A{
2N/A char *str = alloca(len + 1);
2N/A bcopy(buf, str, len);
2N/A str[len] = '\0';
2N/A
2N/A return (nvlist_add_string(nvl, name, str));
2N/A}
2N/A
2N/A/*
2N/A * Like fixed_string, but clears any leading or trailing spaces.
2N/A */
2N/Aint
2N/Anvlist_add_fixed_string_trunc(nvlist_t *nvl, const char *name,
2N/A const char *buf, size_t len)
2N/A{
2N/A while (buf[0] == ' ' && len > 0) {
2N/A buf++;
2N/A len--;
2N/A }
2N/A
2N/A while (len > 0 && buf[len - 1] == ' ')
2N/A len--;
2N/A
2N/A return (nvlist_add_fixed_string(nvl, name, buf, len));
2N/A}
2N/A
2N/Ases_errno_t
2N/Ases_errno(void)
2N/A{
2N/A return (_ses_errno);
2N/A}
2N/A
2N/Aconst char *
2N/Ases_errmsg(void)
2N/A{
2N/A if (_ses_errmsg[0] == '\0')
2N/A (void) snprintf(_ses_errmsg, sizeof (_ses_errmsg), "%s",
2N/A ses_strerror(_ses_errno));
2N/A
2N/A return (_ses_errmsg);
2N/A}
2N/A
2N/Aconst char *
2N/Ases_nv_error_member(void)
2N/A{
2N/A if (_ses_nverr_member[0] != '\0')
2N/A return (_ses_nverr_member);
2N/A else
2N/A return (NULL);
2N/A}
2N/A
2N/Astatic int
2N/A__ses_set_errno(ses_errno_t err, const char *nvm)
2N/A{
2N/A if (nvm == NULL) {
2N/A _ses_nverr_member[0] = '\0';
2N/A } else {
2N/A (void) strlcpy(_ses_nverr_member, nvm,
2N/A sizeof (_ses_nverr_member));
2N/A }
2N/A _ses_errmsg[0] = '\0';
2N/A _ses_errno = err;
2N/A
2N/A return (-1);
2N/A}
2N/A
2N/Aint
2N/Ases_set_errno(ses_errno_t err)
2N/A{
2N/A return (__ses_set_errno(err, NULL));
2N/A}
2N/A
2N/Aint
2N/Ases_set_nverrno(int err, const char *member)
2N/A{
2N/A ses_errno_t se = (err == ENOMEM || err == EAGAIN) ?
2N/A ESES_NOMEM : ESES_NVL;
2N/A
2N/A /*
2N/A * If the error is ESES_NVL, then we should always have a member
2N/A * available. The only time 'member' is NULL is when nvlist_alloc()
2N/A * fails, which should only be possible if memory allocation fails.
2N/A */
2N/A assert(se == ESES_NOMEM || member != NULL);
2N/A
2N/A return (__ses_set_errno(se, member));
2N/A}
2N/A
2N/Astatic int
2N/Ases_verror(ses_errno_t err, const char *fmt, va_list ap)
2N/A{
2N/A int syserr = errno;
2N/A size_t n;
2N/A char *errmsg;
2N/A
2N/A errmsg = alloca(sizeof (_ses_errmsg));
2N/A (void) vsnprintf(errmsg, sizeof (_ses_errmsg), fmt, ap);
2N/A (void) ses_set_errno(err);
2N/A
2N/A n = strlen(errmsg);
2N/A
2N/A while (n != 0 && errmsg[n - 1] == '\n')
2N/A errmsg[--n] = '\0';
2N/A
2N/A bcopy(errmsg, _ses_errmsg, sizeof (_ses_errmsg));
2N/A errno = syserr;
2N/A
2N/A return (-1);
2N/A}
2N/A
2N/Astatic int
2N/Ases_vnverror(int err, const char *member, const char *fmt,
2N/A va_list ap)
2N/A{
2N/A int syserr = errno;
2N/A size_t n;
2N/A char *errmsg;
2N/A
2N/A errmsg = alloca(sizeof (_ses_errmsg));
2N/A (void) vsnprintf(errmsg, sizeof (_ses_errmsg), fmt, ap);
2N/A (void) ses_set_nverrno(err, member);
2N/A
2N/A n = strlen(errmsg);
2N/A
2N/A while (n != 0 && errmsg[n - 1] == '\n')
2N/A errmsg[--n] = '\0';
2N/A
2N/A (void) snprintf(errmsg + n, sizeof (_ses_errmsg) - n, ": %s",
2N/A strerror(err));
2N/A
2N/A bcopy(errmsg, _ses_errmsg, sizeof (_ses_errmsg));
2N/A errno = syserr;
2N/A
2N/A return (-1);
2N/A}
2N/A
2N/Aint
2N/Ases_error(ses_errno_t err, const char *fmt, ...)
2N/A{
2N/A va_list ap;
2N/A int rv;
2N/A
2N/A va_start(ap, fmt);
2N/A rv = ses_verror(err, fmt, ap);
2N/A va_end(ap);
2N/A
2N/A return (rv);
2N/A}
2N/A
2N/Aint
2N/Ases_nverror(int err, const char *member, const char *fmt, ...)
2N/A{
2N/A va_list ap;
2N/A int rv;
2N/A
2N/A va_start(ap, fmt);
2N/A rv = ses_vnverror(err, member, fmt, ap);
2N/A va_end(ap);
2N/A
2N/A return (rv);
2N/A}
2N/A
2N/Aint
2N/Ases_libscsi_error(libscsi_hdl_t *shp, const char *fmt, ...)
2N/A{
2N/A va_list ap;
2N/A char errmsg[LIBSES_ERRMSGLEN];
2N/A libscsi_errno_t se = libscsi_errno(shp);
2N/A ses_errno_t e;
2N/A
2N/A switch (se) {
2N/A case ESCSI_NONE:
2N/A return (0);
2N/A case ESCSI_NOMEM:
2N/A e = ESES_NOMEM;
2N/A break;
2N/A case ESCSI_NOTSUP:
2N/A e = ESES_NOTSUP;
2N/A break;
2N/A case ESCSI_ZERO_LENGTH:
2N/A case ESCSI_VERSION:
2N/A case ESCSI_BADFLAGS:
2N/A case ESCSI_BOGUSFLAGS:
2N/A case ESCSI_BADLENGTH:
2N/A case ESCSI_NEEDBUF:
2N/A va_start(ap, fmt);
2N/A (void) vsnprintf(errmsg, sizeof (errmsg), fmt, ap);
2N/A va_end(ap);
2N/A ses_panic("%s: unexpected libscsi error %s: %s", errmsg,
2N/A libscsi_errname(se), libscsi_errmsg(shp));
2N/A break;
2N/A case ESCSI_UNKNOWN:
2N/A e = ESES_UNKNOWN;
2N/A break;
2N/A default:
2N/A e = ESES_LIBSCSI;
2N/A break;
2N/A }
2N/A
2N/A va_start(ap, fmt);
2N/A (void) vsnprintf(errmsg, sizeof (errmsg), fmt, ap);
2N/A va_end(ap);
2N/A
2N/A return (ses_error(e, "%s: %s", errmsg, libscsi_errmsg(shp)));
2N/A}
2N/A
2N/Aint
2N/Ases_scsi_error(libscsi_action_t *ap, const char *fmt, ...)
2N/A{
2N/A va_list args;
2N/A char errmsg[LIBSES_ERRMSGLEN];
2N/A uint64_t asc = 0, ascq = 0, key = 0;
2N/A const char *code, *keystr;
2N/A
2N/A va_start(args, fmt);
2N/A (void) vsnprintf(errmsg, sizeof (errmsg), fmt, args);
2N/A va_end(args);
2N/A
2N/A if (libscsi_action_parse_sense(ap, &key, &asc, &ascq, NULL) != 0)
2N/A return (ses_error(ESES_LIBSCSI,
2N/A "%s: SCSI status %d (no sense data available)", errmsg,
2N/A libscsi_action_get_status(ap)));
2N/A
2N/A code = libscsi_sense_code_name(asc, ascq);
2N/A keystr = libscsi_sense_key_name(key);
2N/A
2N/A return (ses_error(ESES_LIBSCSI, "%s: SCSI status %d sense key %llu "
2N/A "(%s) additional sense code 0x%llx/0x%llx (%s)", errmsg,
2N/A libscsi_action_get_status(ap), key, keystr ? keystr : "<unknown>",
2N/A asc, ascq, code ? code : "<unknown>"));
2N/A}
2N/A
2N/Avoid *
2N/Ases_alloc(size_t sz)
2N/A{
2N/A void *p;
2N/A
2N/A if (sz == 0)
2N/A ses_panic("attempted zero-length allocation");
2N/A
2N/A if ((p = malloc(sz)) == NULL)
2N/A (void) ses_set_errno(ESES_NOMEM);
2N/A
2N/A return (p);
2N/A}
2N/A
2N/Avoid *
2N/Ases_zalloc(size_t sz)
2N/A{
2N/A void *p;
2N/A
2N/A if ((p = ses_alloc(sz)) != NULL)
2N/A bzero(p, sz);
2N/A
2N/A return (p);
2N/A}
2N/A
2N/Achar *
2N/Ases_strdup(const char *s)
2N/A{
2N/A char *p;
2N/A size_t len;
2N/A
2N/A if (s == NULL)
2N/A ses_panic("attempted zero-length allocation");
2N/A
2N/A len = strlen(s) + 1;
2N/A
2N/A if ((p = ses_alloc(len)) != NULL)
2N/A bcopy(s, p, len);
2N/A
2N/A return (p);
2N/A}
2N/A
2N/Avoid *
2N/Ases_realloc(void *p, size_t sz)
2N/A{
2N/A if (sz == 0)
2N/A ses_panic("attempted zero-length allocation");
2N/A
2N/A if ((p = realloc(p, sz)) == NULL)
2N/A (void) ses_set_errno(ESES_NOMEM);
2N/A
2N/A return (p);
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/Avoid
2N/Ases_free(void *p)
2N/A{
2N/A free(p);
2N/A}