1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 /*
  28  * Copyright 2019 Joyent, Inc.
  29  */
  30 
  31 #include <scsi/libses.h>
  32 #include "ses_impl.h"
  33 
  34 __thread ses_errno_t _ses_errno;
  35 __thread char _ses_errmsg[1024];
  36 __thread char _ses_nverr_member[256];
  37 
  38 static void ses_vpanic(const char *, va_list) __NORETURN;
  39 
  40 static void
  41 ses_vpanic(const char *fmt, va_list ap)
  42 {
  43         int oserr = errno;
  44         char msg[BUFSIZ];
  45         size_t len;
  46 
  47         (void) snprintf(msg, sizeof (msg), "ABORT: ");
  48         len = strlen(msg);
  49         (void) vsnprintf(msg + len, sizeof (msg) - len, fmt, ap);
  50 
  51         if (strchr(fmt, '\n') == NULL) {
  52                 len = strlen(msg);
  53                 (void) snprintf(msg + len, sizeof (msg) - len, ": %s\n",
  54                     strerror(oserr));
  55         }
  56 
  57         (void) write(STDERR_FILENO, msg, strlen(msg));
  58 
  59         abort();
  60 }
  61 
  62 /*PRINTFLIKE1*/
  63 void
  64 ses_panic(const char *fmt, ...)
  65 {
  66         va_list ap;
  67 
  68         va_start(ap, fmt);
  69         ses_vpanic(fmt, ap);
  70         va_end(ap);
  71 }
  72 
  73 int
  74 ses_assert(const char *expr, const char *file, int line)
  75 {
  76         ses_panic("\"%s\", line %d: assertion failed: %s\n", file, line, expr);
  77 
  78         /*NOTREACHED*/
  79         return (0);
  80 }
  81 
  82 int
  83 nvlist_add_fixed_string(nvlist_t *nvl, const char *name,
  84     const char *buf, size_t len)
  85 {
  86         char *str = alloca(len + 1);
  87         bcopy(buf, str, len);
  88         str[len] = '\0';
  89 
  90         return (nvlist_add_string(nvl, name, str));
  91 }
  92 
  93 /*
  94  * Like fixed_string, but clears any leading or trailing spaces.
  95  */
  96 int
  97 nvlist_add_fixed_string_trunc(nvlist_t *nvl, const char *name,
  98     const char *buf, size_t len)
  99 {
 100         while (buf[0] == ' ' && len > 0) {
 101                 buf++;
 102                 len--;
 103         }
 104 
 105         while (len > 0 && buf[len - 1] == ' ')
 106                 len--;
 107 
 108         return (nvlist_add_fixed_string(nvl, name, buf, len));
 109 }
 110 
 111 ses_errno_t
 112 ses_errno(void)
 113 {
 114         return (_ses_errno);
 115 }
 116 
 117 const char *
 118 ses_errmsg(void)
 119 {
 120         if (_ses_errmsg[0] == '\0')
 121                 (void) snprintf(_ses_errmsg, sizeof (_ses_errmsg), "%s",
 122                     ses_strerror(_ses_errno));
 123 
 124         return (_ses_errmsg);
 125 }
 126 
 127 const char *
 128 ses_nv_error_member(void)
 129 {
 130         if (_ses_nverr_member[0] != '\0')
 131                 return (_ses_nverr_member);
 132         else
 133                 return (NULL);
 134 }
 135 
 136 static int
 137 __ses_set_errno(ses_errno_t err, const char *nvm)
 138 {
 139         if (nvm == NULL) {
 140                 _ses_nverr_member[0] = '\0';
 141         } else {
 142                 (void) strlcpy(_ses_nverr_member, nvm,
 143                     sizeof (_ses_nverr_member));
 144         }
 145         _ses_errmsg[0] = '\0';
 146         _ses_errno = err;
 147 
 148         return (-1);
 149 }
 150 
 151 int
 152 ses_set_errno(ses_errno_t err)
 153 {
 154         return (__ses_set_errno(err, NULL));
 155 }
 156 
 157 int
 158 ses_set_nverrno(int err, const char *member)
 159 {
 160         ses_errno_t se = (err == ENOMEM || err == EAGAIN) ?
 161             ESES_NOMEM : ESES_NVL;
 162 
 163         /*
 164          * If the error is ESES_NVL, then we should always have a member
 165          * available.  The only time 'member' is NULL is when nvlist_alloc()
 166          * fails, which should only be possible if memory allocation fails.
 167          */
 168         assert(se == ESES_NOMEM || member != NULL);
 169 
 170         return (__ses_set_errno(se, member));
 171 }
 172 
 173 static int
 174 ses_verror(ses_errno_t err, const char *fmt, va_list ap)
 175 {
 176         int syserr = errno;
 177         size_t n;
 178         char *errmsg;
 179 
 180         errmsg = alloca(sizeof (_ses_errmsg));
 181         (void) vsnprintf(errmsg, sizeof (_ses_errmsg), fmt, ap);
 182         (void) ses_set_errno(err);
 183 
 184         n = strlen(errmsg);
 185 
 186         while (n != 0 && errmsg[n - 1] == '\n')
 187                 errmsg[--n] = '\0';
 188 
 189         bcopy(errmsg, _ses_errmsg, sizeof (_ses_errmsg));
 190         errno = syserr;
 191 
 192         return (-1);
 193 }
 194 
 195 static int
 196 ses_vnverror(int err, const char *member, const char *fmt,
 197     va_list ap)
 198 {
 199         int syserr = errno;
 200         size_t n;
 201         char *errmsg;
 202 
 203         errmsg = alloca(sizeof (_ses_errmsg));
 204         (void) vsnprintf(errmsg, sizeof (_ses_errmsg), fmt, ap);
 205         (void) ses_set_nverrno(err, member);
 206 
 207         n = strlen(errmsg);
 208 
 209         while (n != 0 && errmsg[n - 1] == '\n')
 210                 errmsg[--n] = '\0';
 211 
 212         (void) snprintf(errmsg + n, sizeof (_ses_errmsg) - n, ": %s",
 213             strerror(err));
 214 
 215         bcopy(errmsg, _ses_errmsg, sizeof (_ses_errmsg));
 216         errno = syserr;
 217 
 218         return (-1);
 219 }
 220 
 221 int
 222 ses_error(ses_errno_t err, const char *fmt, ...)
 223 {
 224         va_list ap;
 225         int rv;
 226 
 227         va_start(ap, fmt);
 228         rv = ses_verror(err, fmt, ap);
 229         va_end(ap);
 230 
 231         return (rv);
 232 }
 233 
 234 int
 235 ses_nverror(int err, const char *member, const char *fmt, ...)
 236 {
 237         va_list ap;
 238         int rv;
 239 
 240         va_start(ap, fmt);
 241         rv = ses_vnverror(err, member, fmt, ap);
 242         va_end(ap);
 243 
 244         return (rv);
 245 }
 246 
 247 int
 248 ses_libscsi_error(libscsi_hdl_t *shp, const char *fmt, ...)
 249 {
 250         va_list ap;
 251         char errmsg[LIBSES_ERRMSGLEN];
 252         libscsi_errno_t se = libscsi_errno(shp);
 253         ses_errno_t e;
 254 
 255         switch (se) {
 256         case ESCSI_NONE:
 257                 return (0);
 258         case ESCSI_NOMEM:
 259                 e = ESES_NOMEM;
 260                 break;
 261         case ESCSI_NOTSUP:
 262                 e = ESES_NOTSUP;
 263                 break;
 264         case ESCSI_ZERO_LENGTH:
 265         case ESCSI_VERSION:
 266         case ESCSI_BADFLAGS:
 267         case ESCSI_BOGUSFLAGS:
 268         case ESCSI_BADLENGTH:
 269         case ESCSI_NEEDBUF:
 270                 va_start(ap, fmt);
 271                 (void) vsnprintf(errmsg, sizeof (errmsg), fmt, ap);
 272                 va_end(ap);
 273                 ses_panic("%s: unexpected libscsi error %s: %s", errmsg,
 274                     libscsi_errname(se), libscsi_errmsg(shp));
 275                 break;
 276         case ESCSI_UNKNOWN:
 277                 e = ESES_UNKNOWN;
 278                 break;
 279         default:
 280                 e = ESES_LIBSCSI;
 281                 break;
 282         }
 283 
 284         va_start(ap, fmt);
 285         (void) vsnprintf(errmsg, sizeof (errmsg), fmt, ap);
 286         va_end(ap);
 287 
 288         return (ses_error(e, "%s: %s", errmsg, libscsi_errmsg(shp)));
 289 }
 290 
 291 int
 292 ses_scsi_error(libscsi_action_t *ap, const char *fmt, ...)
 293 {
 294         va_list args;
 295         char errmsg[LIBSES_ERRMSGLEN];
 296         uint64_t asc = 0, ascq = 0, key = 0;
 297         const char *code, *keystr;
 298 
 299         va_start(args, fmt);
 300         (void) vsnprintf(errmsg, sizeof (errmsg), fmt, args);
 301         va_end(args);
 302 
 303         if (libscsi_action_parse_sense(ap, &key, &asc, &ascq, NULL) != 0)
 304                 return (ses_error(ESES_LIBSCSI,
 305                     "%s: SCSI status %d (no sense data available)", errmsg,
 306                     libscsi_action_get_status(ap)));
 307 
 308         code = libscsi_sense_code_name(asc, ascq);
 309         keystr = libscsi_sense_key_name(key);
 310 
 311         return (ses_error(ESES_LIBSCSI, "%s: SCSI status %d sense key %llu "
 312             "(%s) additional sense code 0x%llx/0x%llx (%s)", errmsg,
 313             libscsi_action_get_status(ap), key, keystr ? keystr : "<unknown>",
 314             asc, ascq, code ? code : "<unknown>"));
 315 }
 316 
 317 void *
 318 ses_alloc(size_t sz)
 319 {
 320         void *p;
 321 
 322         if (sz == 0)
 323                 ses_panic("attempted zero-length allocation");
 324 
 325         if ((p = malloc(sz)) == NULL)
 326                 (void) ses_set_errno(ESES_NOMEM);
 327 
 328         return (p);
 329 }
 330 
 331 void *
 332 ses_zalloc(size_t sz)
 333 {
 334         void *p;
 335 
 336         if ((p = ses_alloc(sz)) != NULL)
 337                 bzero(p, sz);
 338 
 339         return (p);
 340 }
 341 
 342 char *
 343 ses_strdup(const char *s)
 344 {
 345         char *p;
 346         size_t len;
 347 
 348         if (s == NULL)
 349                 ses_panic("attempted zero-length allocation");
 350 
 351         len = strlen(s) + 1;
 352 
 353         if ((p = ses_alloc(len)) != NULL)
 354                 bcopy(s, p, len);
 355 
 356         return (p);
 357 }
 358 
 359 void *
 360 ses_realloc(void *p, size_t sz)
 361 {
 362         if (sz == 0)
 363                 ses_panic("attempted zero-length allocation");
 364 
 365         if ((p = realloc(p, sz)) == NULL)
 366                 (void) ses_set_errno(ESES_NOMEM);
 367 
 368         return (p);
 369 }
 370 
 371 /*ARGSUSED*/
 372 void
 373 ses_free(void *p)
 374 {
 375         free(p);
 376 }