xref: /illumos-gate/usr/src/lib/scsi/libscsi/common/scsi_subr.c (revision 8d0c3d29bb99f6521f2dc5058a7e4debebad7899)
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 
42 int
43 libscsi_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 
65 int
66 libscsi_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  */
80 int
81 libscsi_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*/
112 int
113 libscsi_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 
127 libscsi_errno_t
128 libscsi_errno(libscsi_hdl_t *hp)
129 {
130 	return (hp->lsh_errno);
131 }
132 
133 const char *
134 libscsi_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 
143 void *
144 libscsi_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 
159 void *
160 libscsi_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 
172 char *
173 libscsi_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*/
185 void
186 libscsi_free(libscsi_hdl_t *hp, void *ptr)
187 {
188 	free(ptr);
189 }
190 
191 libscsi_hdl_t *
192 libscsi_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 
208 void
209 libscsi_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 
227 size_t
228 libscsi_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 
268 static char *
269 libscsi_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  */
290 int
291 libscsi_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 
333 const char *
334 libscsi_vendor(libscsi_target_t *tp)
335 {
336 	return (tp->lst_vendor);
337 }
338 
339 const char *
340 libscsi_product(libscsi_target_t *tp)
341 {
342 	return (tp->lst_product);
343 }
344 
345 const char *
346 libscsi_revision(libscsi_target_t *tp)
347 {
348 	return (tp->lst_revision);
349 }
350