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 }