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
libscsi_assert(const char * expr,const char * file,int line)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
libscsi_set_errno(libscsi_hdl_t * hp,libscsi_errno_t err)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
libscsi_verror(libscsi_hdl_t * hp,libscsi_errno_t err,const char * fmt,va_list ap)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
libscsi_error(libscsi_hdl_t * hp,libscsi_errno_t err,const char * fmt,...)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
libscsi_errno(libscsi_hdl_t * hp)128 libscsi_errno(libscsi_hdl_t *hp)
129 {
130 return (hp->lsh_errno);
131 }
132
133 const char *
libscsi_errmsg(libscsi_hdl_t * hp)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 *
libscsi_alloc(libscsi_hdl_t * hp,size_t size)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 *
libscsi_zalloc(libscsi_hdl_t * hp,size_t size)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 *
libscsi_strdup(libscsi_hdl_t * hp,const char * str)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
libscsi_free(libscsi_hdl_t * hp,void * ptr)186 libscsi_free(libscsi_hdl_t *hp, void *ptr)
187 {
188 free(ptr);
189 }
190
191 libscsi_hdl_t *
libscsi_init(uint_t version,libscsi_errno_t * errp)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
libscsi_fini(libscsi_hdl_t * hp)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
libscsi_cmd_cdblen(libscsi_hdl_t * hp,uint8_t cmd)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 *
libscsi_process_inquiry_string(libscsi_hdl_t * hp,const char * raw,size_t len)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
libscsi_get_inquiry(libscsi_hdl_t * hp,libscsi_target_t * tp)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 *
libscsi_vendor(libscsi_target_t * tp)334 libscsi_vendor(libscsi_target_t *tp)
335 {
336 return (tp->lst_vendor);
337 }
338
339 const char *
libscsi_product(libscsi_target_t * tp)340 libscsi_product(libscsi_target_t *tp)
341 {
342 return (tp->lst_product);
343 }
344
345 const char *
libscsi_revision(libscsi_target_t * tp)346 libscsi_revision(libscsi_target_t *tp)
347 {
348 return (tp->lst_revision);
349 }
350