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
ses_vpanic(const char * fmt,va_list ap)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
ses_panic(const char * fmt,...)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
ses_assert(const char * expr,const char * file,int line)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
nvlist_add_fixed_string(nvlist_t * nvl,const char * name,const char * buf,size_t len)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
nvlist_add_fixed_string_trunc(nvlist_t * nvl,const char * name,const char * buf,size_t len)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
ses_errno(void)112 ses_errno(void)
113 {
114 	return (_ses_errno);
115 }
116 
117 const char *
ses_errmsg(void)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 *
ses_nv_error_member(void)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
__ses_set_errno(ses_errno_t err,const char * nvm)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
ses_set_errno(ses_errno_t err)152 ses_set_errno(ses_errno_t err)
153 {
154 	return (__ses_set_errno(err, NULL));
155 }
156 
157 int
ses_set_nverrno(int err,const char * member)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
ses_verror(ses_errno_t err,const char * fmt,va_list ap)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
ses_vnverror(int err,const char * member,const char * fmt,va_list ap)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
ses_error(ses_errno_t err,const char * fmt,...)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
ses_nverror(int err,const char * member,const char * fmt,...)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
ses_libscsi_error(libscsi_hdl_t * shp,const char * fmt,...)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
ses_scsi_error(libscsi_action_t * ap,const char * fmt,...)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 *
ses_alloc(size_t sz)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 *
ses_zalloc(size_t sz)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 *
ses_strdup(const char * s)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 *
ses_realloc(void * p,size_t sz)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
ses_free(void * p)373 ses_free(void *p)
374 {
375 	free(p);
376 }
377