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