scsi_subr.c revision 12126:60364f3f65c7
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 (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26#include <sys/types.h>
27#include <sys/scsi/generic/commands.h>
28#include <sys/scsi/impl/spc3_types.h>
29
30#include <stddef.h>
31#include <stdlib.h>
32#include <string.h>
33#include <strings.h>
34#include <alloca.h>
35#include <stdio.h>
36#include <unistd.h>
37#include <dlfcn.h>
38
39#include <scsi/libscsi.h>
40#include "libscsi_impl.h"
41
42int
43libscsi_assert(const char *expr, const char *file, int line)
44{
45	char *msg;
46	size_t len;
47
48	len = snprintf(NULL, 0,
49	    "ABORT: \"%s\", line %d: assertion failed: %s\n", file, line, expr);
50
51	msg = alloca(len + 1);
52
53	(void) snprintf(msg, len + 1,
54	    "ABORT: \"%s\", line %d: assertion failed: %s\n", file, line, expr);
55
56	(void) write(STDERR_FILENO, msg, strlen(msg));
57
58	abort();
59	_exit(1);
60
61	/*NOTREACHED*/
62	return (0);
63}
64
65int
66libscsi_set_errno(libscsi_hdl_t *hp, libscsi_errno_t err)
67{
68	hp->lsh_errno = err;
69	hp->lsh_errmsg[0] = '\0';
70
71	return (-1);
72}
73
74/*
75 * Internal routine for setting both _ue_errno and _ue_errmsg.  We save
76 * and restore the UNIX errno across this routing so the caller can use either
77 * libscsi_set_errno(), libscsi_error(), or libscsi_verror() without this value
78 * changing.
79 */
80int
81libscsi_verror(libscsi_hdl_t *hp, libscsi_errno_t err, const char *fmt,
82    va_list ap)
83{
84	size_t n;
85	char *errmsg;
86
87	/*
88	 * To allow the existing error message to itself be used in an error
89	 * message, we put the new error message into a buffer on the stack,
90	 * and then copy it into lsh_errmsg.  We also need to set the errno,
91	 * but because the call to libscsi_set_errno() is destructive to
92	 * lsh_errmsg, we do this after we print into our temporary buffer
93	 * (in case _libscsi_errmsg is part of the error message) and before we
94	 * copy the temporary buffer on to _libscsi_errmsg (to prevent our new
95	 * message from being nuked by the call to libscsi_set_errno()).
96	 */
97	errmsg = alloca(sizeof (hp->lsh_errmsg));
98	(void) vsnprintf(errmsg, sizeof (hp->lsh_errmsg), fmt, ap);
99	(void) libscsi_set_errno(hp, err);
100
101	n = strlen(errmsg);
102
103	if (n != 0 && errmsg[n - 1] == '\n')
104		errmsg[n - 1] = '\0';
105
106	bcopy(errmsg, hp->lsh_errmsg, n + 1);
107
108	return (-1);
109}
110
111/*PRINTFLIKE3*/
112int
113libscsi_error(libscsi_hdl_t *hp, libscsi_errno_t err, const char *fmt, ...)
114{
115	va_list ap;
116
117	if (fmt == NULL)
118		return (libscsi_set_errno(hp, err));
119
120	va_start(ap, fmt);
121	err = libscsi_verror(hp, err, fmt, ap);
122	va_end(ap);
123
124	return (err);
125}
126
127libscsi_errno_t
128libscsi_errno(libscsi_hdl_t *hp)
129{
130	return (hp->lsh_errno);
131}
132
133const char *
134libscsi_errmsg(libscsi_hdl_t *hp)
135{
136	if (hp->lsh_errmsg[0] == '\0')
137		(void) strlcpy(hp->lsh_errmsg, libscsi_strerror(hp->lsh_errno),
138		    sizeof (hp->lsh_errmsg));
139
140	return (hp->lsh_errmsg);
141}
142
143void *
144libscsi_alloc(libscsi_hdl_t *hp, size_t size)
145{
146	void *mem;
147
148	if (size == 0) {
149		(void) libscsi_set_errno(hp, ESCSI_ZERO_LENGTH);
150		return (NULL);
151	}
152
153	if ((mem = malloc(size)) == NULL)
154		(void) libscsi_set_errno(hp, ESCSI_NOMEM);
155
156	return (mem);
157}
158
159void *
160libscsi_zalloc(libscsi_hdl_t *hp, size_t size)
161{
162	void *mem;
163
164	if ((mem = libscsi_alloc(hp, size)) == NULL)
165		return (NULL);
166
167	bzero(mem, size);
168
169	return (mem);
170}
171
172char *
173libscsi_strdup(libscsi_hdl_t *hp, const char *str)
174{
175	size_t len = strlen(str);
176	char *dup = libscsi_alloc(hp, len + 1);
177
178	if (dup == NULL)
179		return (NULL);
180
181	return (strcpy(dup, str));
182}
183
184/*ARGSUSED*/
185void
186libscsi_free(libscsi_hdl_t *hp, void *ptr)
187{
188	free(ptr);
189}
190
191libscsi_hdl_t *
192libscsi_init(uint_t version, libscsi_errno_t *errp)
193{
194	libscsi_hdl_t *hp;
195
196	if ((hp = malloc(sizeof (libscsi_hdl_t))) == NULL) {
197		if (errp != NULL)
198			*errp = ESCSI_NOMEM;
199		return (NULL);
200	}
201
202	bzero(hp, sizeof (libscsi_hdl_t));
203	hp->lsh_version = version;
204
205	return (hp);
206}
207
208void
209libscsi_fini(libscsi_hdl_t *hp)
210{
211	libscsi_engine_impl_t *eip, *neip;
212
213	if (hp == NULL)
214		return;
215
216	ASSERT(hp->lsh_targets == 0);
217
218	for (eip = hp->lsh_engines; eip != NULL; eip = neip) {
219		neip = eip->lsei_next;
220		(void) dlclose(eip->lsei_dl_hdl);
221		libscsi_free(hp, eip);
222	}
223
224	free(hp);
225}
226
227size_t
228libscsi_cmd_cdblen(libscsi_hdl_t *hp, uint8_t cmd)
229{
230	size_t sz;
231
232	switch (CDB_GROUPID(cmd)) {
233	case CDB_GROUPID_0:
234		sz = CDB_GROUP0;
235		break;
236	case CDB_GROUPID_1:
237		sz = CDB_GROUP1;
238		break;
239	case CDB_GROUPID_2:
240		sz = CDB_GROUP2;
241		break;
242	case CDB_GROUPID_3:
243		sz = CDB_GROUP3;
244		break;
245	case CDB_GROUPID_4:
246		sz = CDB_GROUP4;
247		break;
248	case CDB_GROUPID_5:
249		sz = CDB_GROUP5;
250		break;
251	case CDB_GROUPID_6:
252		sz = CDB_GROUP6;
253		break;
254	case CDB_GROUPID_7:
255		sz = CDB_GROUP7;
256		break;
257	default:
258		sz = 0;
259	}
260
261	if (sz == 0)
262		(void) libscsi_error(hp, ESCSI_BADCMD,
263		    "unknown or unsupported command %u", cmd);
264
265	return (sz);
266}
267
268static char *
269libscsi_process_inquiry_string(libscsi_hdl_t *hp, const char *raw, size_t len)
270{
271	char *buf;
272
273	buf = alloca(len + 1);
274	bcopy(raw, buf, len);
275
276	for (; len > 0; len--) {
277		if (buf[len - 1] != ' ')
278			break;
279	}
280
281	buf[len] = '\0';
282
283	return (libscsi_strdup(hp, buf));
284}
285
286/*
287 * As part of basic initialization, we always retrieve the INQUIRY information
288 * to have the vendor/product/revision information available for all consumers.
289 */
290int
291libscsi_get_inquiry(libscsi_hdl_t *hp, libscsi_target_t *tp)
292{
293	libscsi_action_t *ap;
294	spc3_inquiry_cdb_t *cp;
295	spc3_inquiry_data_t data;
296	size_t len;
297
298	if ((ap = libscsi_action_alloc(hp, SPC3_CMD_INQUIRY,
299	    LIBSCSI_AF_READ | LIBSCSI_AF_SILENT | LIBSCSI_AF_DIAGNOSE, &data,
300	    offsetof(spc3_inquiry_data_t, id_vs_36[0]))) == NULL)
301		return (-1);
302
303	cp = (spc3_inquiry_cdb_t *)libscsi_action_get_cdb(ap);
304
305	SCSI_WRITE16(&cp->ic_allocation_length,
306	    offsetof(spc3_inquiry_data_t, id_vs_36[0]));
307
308	if (libscsi_exec(ap, tp) != 0 ||
309	    libscsi_action_get_status(ap) != 0) {
310		libscsi_action_free(ap);
311		return (libscsi_set_errno(hp, ESCSI_INQUIRY_FAILED));
312	}
313
314	(void) libscsi_action_get_buffer(ap, NULL, NULL, &len);
315	libscsi_action_free(ap);
316
317	if (len < offsetof(spc3_inquiry_data_t, id_vs_36))
318		return (libscsi_set_errno(hp, ESCSI_INQUIRY_FAILED));
319
320	if ((tp->lst_vendor = libscsi_process_inquiry_string(hp,
321	    data.id_vendor_id, sizeof (data.id_vendor_id))) == NULL ||
322	    (tp->lst_product = libscsi_process_inquiry_string(hp,
323	    data.id_product_id, sizeof (data.id_product_id))) == NULL ||
324	    (tp->lst_revision = libscsi_process_inquiry_string(hp,
325	    data.id_product_revision,
326	    sizeof (data.id_product_revision))) == NULL) {
327		return (-1);
328	}
329
330	return (0);
331}
332
333const char *
334libscsi_vendor(libscsi_target_t *tp)
335{
336	return (tp->lst_vendor);
337}
338
339const char *
340libscsi_product(libscsi_target_t *tp)
341{
342	return (tp->lst_product);
343}
344
345const char *
346libscsi_revision(libscsi_target_t *tp)
347{
348	return (tp->lst_revision);
349}
350