1275c9da8Seschrock /*
2275c9da8Seschrock  * CDDL HEADER START
3275c9da8Seschrock  *
4275c9da8Seschrock  * The contents of this file are subject to the terms of the
5275c9da8Seschrock  * Common Development and Distribution License (the "License").
6275c9da8Seschrock  * You may not use this file except in compliance with the License.
7275c9da8Seschrock  *
8275c9da8Seschrock  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9275c9da8Seschrock  * or http://www.opensolaris.org/os/licensing.
10275c9da8Seschrock  * See the License for the specific language governing permissions
11275c9da8Seschrock  * and limitations under the License.
12275c9da8Seschrock  *
13275c9da8Seschrock  * When distributing Covered Code, include this CDDL HEADER in each
14275c9da8Seschrock  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15275c9da8Seschrock  * If applicable, add the following below this CDDL HEADER, with the
16275c9da8Seschrock  * fields enclosed by brackets "[]" replaced with your own identifying
17275c9da8Seschrock  * information: Portions Copyright [yyyy] [name of copyright owner]
18275c9da8Seschrock  *
19275c9da8Seschrock  * CDDL HEADER END
20275c9da8Seschrock  */
21275c9da8Seschrock 
22275c9da8Seschrock /*
23275c9da8Seschrock  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24275c9da8Seschrock  * Use is subject to license terms.
25275c9da8Seschrock  */
26275c9da8Seschrock 
27*44bf619dSJohn Levon /*
28*44bf619dSJohn Levon  * Copyright 2019 Joyent, Inc.
29*44bf619dSJohn Levon  */
30275c9da8Seschrock 
31275c9da8Seschrock #include <scsi/libses.h>
32275c9da8Seschrock #include "ses_impl.h"
33275c9da8Seschrock 
34275c9da8Seschrock __thread ses_errno_t _ses_errno;
35275c9da8Seschrock __thread char _ses_errmsg[1024];
36275c9da8Seschrock __thread char _ses_nverr_member[256];
37275c9da8Seschrock 
38275c9da8Seschrock static void ses_vpanic(const char *, va_list) __NORETURN;
39275c9da8Seschrock 
40275c9da8Seschrock static void
ses_vpanic(const char * fmt,va_list ap)41275c9da8Seschrock ses_vpanic(const char *fmt, va_list ap)
42275c9da8Seschrock {
43275c9da8Seschrock 	int oserr = errno;
44275c9da8Seschrock 	char msg[BUFSIZ];
45275c9da8Seschrock 	size_t len;
46275c9da8Seschrock 
47275c9da8Seschrock 	(void) snprintf(msg, sizeof (msg), "ABORT: ");
48275c9da8Seschrock 	len = strlen(msg);
49275c9da8Seschrock 	(void) vsnprintf(msg + len, sizeof (msg) - len, fmt, ap);
50275c9da8Seschrock 
51275c9da8Seschrock 	if (strchr(fmt, '\n') == NULL) {
52275c9da8Seschrock 		len = strlen(msg);
53275c9da8Seschrock 		(void) snprintf(msg + len, sizeof (msg) - len, ": %s\n",
54275c9da8Seschrock 		    strerror(oserr));
55275c9da8Seschrock 	}
56275c9da8Seschrock 
57275c9da8Seschrock 	(void) write(STDERR_FILENO, msg, strlen(msg));
58275c9da8Seschrock 
59275c9da8Seschrock 	abort();
60275c9da8Seschrock }
61275c9da8Seschrock 
62275c9da8Seschrock /*PRINTFLIKE1*/
63275c9da8Seschrock void
ses_panic(const char * fmt,...)64275c9da8Seschrock ses_panic(const char *fmt, ...)
65275c9da8Seschrock {
66275c9da8Seschrock 	va_list ap;
67275c9da8Seschrock 
68275c9da8Seschrock 	va_start(ap, fmt);
69275c9da8Seschrock 	ses_vpanic(fmt, ap);
70275c9da8Seschrock 	va_end(ap);
71275c9da8Seschrock }
72275c9da8Seschrock 
73275c9da8Seschrock int
ses_assert(const char * expr,const char * file,int line)74275c9da8Seschrock ses_assert(const char *expr, const char *file, int line)
75275c9da8Seschrock {
76275c9da8Seschrock 	ses_panic("\"%s\", line %d: assertion failed: %s\n", file, line, expr);
77275c9da8Seschrock 
78275c9da8Seschrock 	/*NOTREACHED*/
79275c9da8Seschrock 	return (0);
80275c9da8Seschrock }
81275c9da8Seschrock 
82275c9da8Seschrock int
nvlist_add_fixed_string(nvlist_t * nvl,const char * name,const char * buf,size_t len)83275c9da8Seschrock nvlist_add_fixed_string(nvlist_t *nvl, const char *name,
84275c9da8Seschrock     const char *buf, size_t len)
85275c9da8Seschrock {
86275c9da8Seschrock 	char *str = alloca(len + 1);
87275c9da8Seschrock 	bcopy(buf, str, len);
88275c9da8Seschrock 	str[len] = '\0';
89275c9da8Seschrock 
90275c9da8Seschrock 	return (nvlist_add_string(nvl, name, str));
91275c9da8Seschrock }
92275c9da8Seschrock 
93275c9da8Seschrock /*
94275c9da8Seschrock  * Like fixed_string, but clears any leading or trailing spaces.
95275c9da8Seschrock  */
96275c9da8Seschrock int
nvlist_add_fixed_string_trunc(nvlist_t * nvl,const char * name,const char * buf,size_t len)97275c9da8Seschrock nvlist_add_fixed_string_trunc(nvlist_t *nvl, const char *name,
98275c9da8Seschrock     const char *buf, size_t len)
99275c9da8Seschrock {
100275c9da8Seschrock 	while (buf[0] == ' ' && len > 0) {
101275c9da8Seschrock 		buf++;
102275c9da8Seschrock 		len--;
103275c9da8Seschrock 	}
104275c9da8Seschrock 
105275c9da8Seschrock 	while (len > 0 && buf[len - 1] == ' ')
106275c9da8Seschrock 		len--;
107275c9da8Seschrock 
108275c9da8Seschrock 	return (nvlist_add_fixed_string(nvl, name, buf, len));
109275c9da8Seschrock }
110275c9da8Seschrock 
111275c9da8Seschrock ses_errno_t
ses_errno(void)112275c9da8Seschrock ses_errno(void)
113275c9da8Seschrock {
114275c9da8Seschrock 	return (_ses_errno);
115275c9da8Seschrock }
116275c9da8Seschrock 
117275c9da8Seschrock const char *
ses_errmsg(void)118275c9da8Seschrock ses_errmsg(void)
119275c9da8Seschrock {
120275c9da8Seschrock 	if (_ses_errmsg[0] == '\0')
121275c9da8Seschrock 		(void) snprintf(_ses_errmsg, sizeof (_ses_errmsg), "%s",
122275c9da8Seschrock 		    ses_strerror(_ses_errno));
123275c9da8Seschrock 
124275c9da8Seschrock 	return (_ses_errmsg);
125275c9da8Seschrock }
126275c9da8Seschrock 
127275c9da8Seschrock const char *
ses_nv_error_member(void)128275c9da8Seschrock ses_nv_error_member(void)
129275c9da8Seschrock {
130275c9da8Seschrock 	if (_ses_nverr_member[0] != '\0')
131275c9da8Seschrock 		return (_ses_nverr_member);
132275c9da8Seschrock 	else
133275c9da8Seschrock 		return (NULL);
134275c9da8Seschrock }
135275c9da8Seschrock 
136275c9da8Seschrock static int
__ses_set_errno(ses_errno_t err,const char * nvm)137275c9da8Seschrock __ses_set_errno(ses_errno_t err, const char *nvm)
138275c9da8Seschrock {
139275c9da8Seschrock 	if (nvm == NULL) {
140275c9da8Seschrock 		_ses_nverr_member[0] = '\0';
141275c9da8Seschrock 	} else {
142275c9da8Seschrock 		(void) strlcpy(_ses_nverr_member, nvm,
143275c9da8Seschrock 		    sizeof (_ses_nverr_member));
144275c9da8Seschrock 	}
145275c9da8Seschrock 	_ses_errmsg[0] = '\0';
146275c9da8Seschrock 	_ses_errno = err;
147275c9da8Seschrock 
148275c9da8Seschrock 	return (-1);
149275c9da8Seschrock }
150275c9da8Seschrock 
151275c9da8Seschrock int
ses_set_errno(ses_errno_t err)152275c9da8Seschrock ses_set_errno(ses_errno_t err)
153275c9da8Seschrock {
154275c9da8Seschrock 	return (__ses_set_errno(err, NULL));
155275c9da8Seschrock }
156275c9da8Seschrock 
157275c9da8Seschrock int
ses_set_nverrno(int err,const char * member)158275c9da8Seschrock ses_set_nverrno(int err, const char *member)
159275c9da8Seschrock {
160275c9da8Seschrock 	ses_errno_t se = (err == ENOMEM || err == EAGAIN) ?
161275c9da8Seschrock 	    ESES_NOMEM : ESES_NVL;
162275c9da8Seschrock 
163275c9da8Seschrock 	/*
164275c9da8Seschrock 	 * If the error is ESES_NVL, then we should always have a member
165275c9da8Seschrock 	 * available.  The only time 'member' is NULL is when nvlist_alloc()
166275c9da8Seschrock 	 * fails, which should only be possible if memory allocation fails.
167275c9da8Seschrock 	 */
168275c9da8Seschrock 	assert(se == ESES_NOMEM || member != NULL);
169275c9da8Seschrock 
170275c9da8Seschrock 	return (__ses_set_errno(se, member));
171275c9da8Seschrock }
172275c9da8Seschrock 
173275c9da8Seschrock static int
ses_verror(ses_errno_t err,const char * fmt,va_list ap)174275c9da8Seschrock ses_verror(ses_errno_t err, const char *fmt, va_list ap)
175275c9da8Seschrock {
176275c9da8Seschrock 	int syserr = errno;
177275c9da8Seschrock 	size_t n;
178275c9da8Seschrock 	char *errmsg;
179275c9da8Seschrock 
180275c9da8Seschrock 	errmsg = alloca(sizeof (_ses_errmsg));
181275c9da8Seschrock 	(void) vsnprintf(errmsg, sizeof (_ses_errmsg), fmt, ap);
182275c9da8Seschrock 	(void) ses_set_errno(err);
183275c9da8Seschrock 
184275c9da8Seschrock 	n = strlen(errmsg);
185275c9da8Seschrock 
186275c9da8Seschrock 	while (n != 0 && errmsg[n - 1] == '\n')
187275c9da8Seschrock 		errmsg[--n] = '\0';
188275c9da8Seschrock 
189275c9da8Seschrock 	bcopy(errmsg, _ses_errmsg, sizeof (_ses_errmsg));
190275c9da8Seschrock 	errno = syserr;
191275c9da8Seschrock 
192275c9da8Seschrock 	return (-1);
193275c9da8Seschrock }
194275c9da8Seschrock 
195275c9da8Seschrock static int
ses_vnverror(int err,const char * member,const char * fmt,va_list ap)196275c9da8Seschrock ses_vnverror(int err, const char *member, const char *fmt,
197275c9da8Seschrock     va_list ap)
198275c9da8Seschrock {
199275c9da8Seschrock 	int syserr = errno;
200275c9da8Seschrock 	size_t n;
201275c9da8Seschrock 	char *errmsg;
202275c9da8Seschrock 
203275c9da8Seschrock 	errmsg = alloca(sizeof (_ses_errmsg));
204275c9da8Seschrock 	(void) vsnprintf(errmsg, sizeof (_ses_errmsg), fmt, ap);
205275c9da8Seschrock 	(void) ses_set_nverrno(err, member);
206275c9da8Seschrock 
207275c9da8Seschrock 	n = strlen(errmsg);
208275c9da8Seschrock 
209275c9da8Seschrock 	while (n != 0 && errmsg[n - 1] == '\n')
210275c9da8Seschrock 		errmsg[--n] = '\0';
211275c9da8Seschrock 
212275c9da8Seschrock 	(void) snprintf(errmsg + n, sizeof (_ses_errmsg) - n, ": %s",
213275c9da8Seschrock 	    strerror(err));
214275c9da8Seschrock 
215275c9da8Seschrock 	bcopy(errmsg, _ses_errmsg, sizeof (_ses_errmsg));
216275c9da8Seschrock 	errno = syserr;
217275c9da8Seschrock 
218275c9da8Seschrock 	return (-1);
219275c9da8Seschrock }
220275c9da8Seschrock 
221275c9da8Seschrock int
ses_error(ses_errno_t err,const char * fmt,...)222275c9da8Seschrock ses_error(ses_errno_t err, const char *fmt, ...)
223275c9da8Seschrock {
224275c9da8Seschrock 	va_list ap;
225275c9da8Seschrock 	int rv;
226275c9da8Seschrock 
227275c9da8Seschrock 	va_start(ap, fmt);
228275c9da8Seschrock 	rv = ses_verror(err, fmt, ap);
229275c9da8Seschrock 	va_end(ap);
230275c9da8Seschrock 
231275c9da8Seschrock 	return (rv);
232275c9da8Seschrock }
233275c9da8Seschrock 
234275c9da8Seschrock int
ses_nverror(int err,const char * member,const char * fmt,...)235275c9da8Seschrock ses_nverror(int err, const char *member, const char *fmt, ...)
236275c9da8Seschrock {
237275c9da8Seschrock 	va_list ap;
238275c9da8Seschrock 	int rv;
239275c9da8Seschrock 
240275c9da8Seschrock 	va_start(ap, fmt);
241275c9da8Seschrock 	rv = ses_vnverror(err, member, fmt, ap);
242275c9da8Seschrock 	va_end(ap);
243275c9da8Seschrock 
244275c9da8Seschrock 	return (rv);
245275c9da8Seschrock }
246275c9da8Seschrock 
247275c9da8Seschrock int
ses_libscsi_error(libscsi_hdl_t * shp,const char * fmt,...)248275c9da8Seschrock ses_libscsi_error(libscsi_hdl_t *shp, const char *fmt, ...)
249275c9da8Seschrock {
250275c9da8Seschrock 	va_list ap;
251275c9da8Seschrock 	char errmsg[LIBSES_ERRMSGLEN];
252275c9da8Seschrock 	libscsi_errno_t se = libscsi_errno(shp);
253275c9da8Seschrock 	ses_errno_t e;
254275c9da8Seschrock 
255275c9da8Seschrock 	switch (se) {
256275c9da8Seschrock 	case ESCSI_NONE:
257275c9da8Seschrock 		return (0);
258275c9da8Seschrock 	case ESCSI_NOMEM:
259275c9da8Seschrock 		e = ESES_NOMEM;
260275c9da8Seschrock 		break;
261275c9da8Seschrock 	case ESCSI_NOTSUP:
262275c9da8Seschrock 		e = ESES_NOTSUP;
263275c9da8Seschrock 		break;
264275c9da8Seschrock 	case ESCSI_ZERO_LENGTH:
265275c9da8Seschrock 	case ESCSI_VERSION:
266275c9da8Seschrock 	case ESCSI_BADFLAGS:
267275c9da8Seschrock 	case ESCSI_BOGUSFLAGS:
268275c9da8Seschrock 	case ESCSI_BADLENGTH:
269275c9da8Seschrock 	case ESCSI_NEEDBUF:
270275c9da8Seschrock 		va_start(ap, fmt);
271275c9da8Seschrock 		(void) vsnprintf(errmsg, sizeof (errmsg), fmt, ap);
272275c9da8Seschrock 		va_end(ap);
273275c9da8Seschrock 		ses_panic("%s: unexpected libscsi error %s: %s", errmsg,
274275c9da8Seschrock 		    libscsi_errname(se), libscsi_errmsg(shp));
275275c9da8Seschrock 		break;
276275c9da8Seschrock 	case ESCSI_UNKNOWN:
277275c9da8Seschrock 		e = ESES_UNKNOWN;
278275c9da8Seschrock 		break;
279275c9da8Seschrock 	default:
280275c9da8Seschrock 		e = ESES_LIBSCSI;
281275c9da8Seschrock 		break;
282275c9da8Seschrock 	}
283275c9da8Seschrock 
284275c9da8Seschrock 	va_start(ap, fmt);
285275c9da8Seschrock 	(void) vsnprintf(errmsg, sizeof (errmsg), fmt, ap);
286275c9da8Seschrock 	va_end(ap);
287275c9da8Seschrock 
288275c9da8Seschrock 	return (ses_error(e, "%s: %s", errmsg, libscsi_errmsg(shp)));
289275c9da8Seschrock }
290275c9da8Seschrock 
291275c9da8Seschrock int
ses_scsi_error(libscsi_action_t * ap,const char * fmt,...)292275c9da8Seschrock ses_scsi_error(libscsi_action_t *ap, const char *fmt, ...)
293275c9da8Seschrock {
294275c9da8Seschrock 	va_list args;
295275c9da8Seschrock 	char errmsg[LIBSES_ERRMSGLEN];
296275c9da8Seschrock 	uint64_t asc = 0, ascq = 0, key = 0;
297275c9da8Seschrock 	const char *code, *keystr;
298275c9da8Seschrock 
299275c9da8Seschrock 	va_start(args, fmt);
300275c9da8Seschrock 	(void) vsnprintf(errmsg, sizeof (errmsg), fmt, args);
301275c9da8Seschrock 	va_end(args);
302275c9da8Seschrock 
303275c9da8Seschrock 	if (libscsi_action_parse_sense(ap, &key, &asc, &ascq, NULL) != 0)
304275c9da8Seschrock 		return (ses_error(ESES_LIBSCSI,
305275c9da8Seschrock 		    "%s: SCSI status %d (no sense data available)", errmsg,
306275c9da8Seschrock 		    libscsi_action_get_status(ap)));
307275c9da8Seschrock 
308275c9da8Seschrock 	code = libscsi_sense_code_name(asc, ascq);
309275c9da8Seschrock 	keystr = libscsi_sense_key_name(key);
310275c9da8Seschrock 
311275c9da8Seschrock 	return (ses_error(ESES_LIBSCSI, "%s: SCSI status %d sense key %llu "
312275c9da8Seschrock 	    "(%s) additional sense code 0x%llx/0x%llx (%s)", errmsg,
313275c9da8Seschrock 	    libscsi_action_get_status(ap), key, keystr ? keystr : "<unknown>",
314275c9da8Seschrock 	    asc, ascq, code ? code : "<unknown>"));
315275c9da8Seschrock }
316275c9da8Seschrock 
317275c9da8Seschrock void *
ses_alloc(size_t sz)318275c9da8Seschrock ses_alloc(size_t sz)
319275c9da8Seschrock {
320275c9da8Seschrock 	void *p;
321275c9da8Seschrock 
322275c9da8Seschrock 	if (sz == 0)
323275c9da8Seschrock 		ses_panic("attempted zero-length allocation");
324275c9da8Seschrock 
325275c9da8Seschrock 	if ((p = malloc(sz)) == NULL)
326275c9da8Seschrock 		(void) ses_set_errno(ESES_NOMEM);
327275c9da8Seschrock 
328275c9da8Seschrock 	return (p);
329275c9da8Seschrock }
330275c9da8Seschrock 
331275c9da8Seschrock void *
ses_zalloc(size_t sz)332275c9da8Seschrock ses_zalloc(size_t sz)
333275c9da8Seschrock {
334275c9da8Seschrock 	void *p;
335275c9da8Seschrock 
336275c9da8Seschrock 	if ((p = ses_alloc(sz)) != NULL)
337275c9da8Seschrock 		bzero(p, sz);
338275c9da8Seschrock 
339275c9da8Seschrock 	return (p);
340275c9da8Seschrock }
341275c9da8Seschrock 
342275c9da8Seschrock char *
ses_strdup(const char * s)343275c9da8Seschrock ses_strdup(const char *s)
344275c9da8Seschrock {
345275c9da8Seschrock 	char *p;
346275c9da8Seschrock 	size_t len;
347275c9da8Seschrock 
348275c9da8Seschrock 	if (s == NULL)
349275c9da8Seschrock 		ses_panic("attempted zero-length allocation");
350275c9da8Seschrock 
351275c9da8Seschrock 	len = strlen(s) + 1;
352275c9da8Seschrock 
353275c9da8Seschrock 	if ((p = ses_alloc(len)) != NULL)
354275c9da8Seschrock 		bcopy(s, p, len);
355275c9da8Seschrock 
356275c9da8Seschrock 	return (p);
357275c9da8Seschrock }
358275c9da8Seschrock 
359275c9da8Seschrock void *
ses_realloc(void * p,size_t sz)360275c9da8Seschrock ses_realloc(void *p, size_t sz)
361275c9da8Seschrock {
362275c9da8Seschrock 	if (sz == 0)
363275c9da8Seschrock 		ses_panic("attempted zero-length allocation");
364275c9da8Seschrock 
365275c9da8Seschrock 	if ((p = realloc(p, sz)) == NULL)
366275c9da8Seschrock 		(void) ses_set_errno(ESES_NOMEM);
367275c9da8Seschrock 
368275c9da8Seschrock 	return (p);
369275c9da8Seschrock }
370275c9da8Seschrock 
371275c9da8Seschrock /*ARGSUSED*/
372275c9da8Seschrock void
ses_free(void * p)373275c9da8Seschrock ses_free(void *p)
374275c9da8Seschrock {
375275c9da8Seschrock 	free(p);
376275c9da8Seschrock }
377