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 #pragma ident "%Z%%M% %I% %E% SMI"
28
29 #include <scsi/libses.h>
30 #include "ses_impl.h"
31
32 __thread ses_errno_t _ses_errno;
33 __thread char _ses_errmsg[1024];
34 __thread char _ses_nverr_member[256];
35
36 static void ses_vpanic(const char *, va_list) __NORETURN;
37
38 static void
ses_vpanic(const char * fmt,va_list ap)39 ses_vpanic(const char *fmt, va_list ap)
40 {
41 int oserr = errno;
42 char msg[BUFSIZ];
43 size_t len;
44
45 (void) snprintf(msg, sizeof (msg), "ABORT: ");
46 len = strlen(msg);
47 (void) vsnprintf(msg + len, sizeof (msg) - len, fmt, ap);
48
49 if (strchr(fmt, '\n') == NULL) {
50 len = strlen(msg);
51 (void) snprintf(msg + len, sizeof (msg) - len, ": %s\n",
52 strerror(oserr));
53 }
54
55 (void) write(STDERR_FILENO, msg, strlen(msg));
56
57 abort:
58 abort();
59 _exit(1);
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