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
38static void ses_vpanic(const char *, va_list) __NORETURN;
39
40static void
41ses_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*/
63void
64ses_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
73int
74ses_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
82int
83nvlist_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 */
96int
97nvlist_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
111ses_errno_t
112ses_errno(void)
113{
114	return (_ses_errno);
115}
116
117const char *
118ses_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
127const char *
128ses_nv_error_member(void)
129{
130	if (_ses_nverr_member[0] != '\0')
131		return (_ses_nverr_member);
132	else
133		return (NULL);
134}
135
136static 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
151int
152ses_set_errno(ses_errno_t err)
153{
154	return (__ses_set_errno(err, NULL));
155}
156
157int
158ses_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
173static int
174ses_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
195static int
196ses_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
221int
222ses_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
234int
235ses_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
247int
248ses_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
291int
292ses_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
317void *
318ses_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
331void *
332ses_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
342char *
343ses_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
359void *
360ses_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*/
372void
373ses_free(void *p)
374{
375	free(p);
376}
377