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 45 int 46 libscsi_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 68 int 69 libscsi_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 */ 83 int 84 libscsi_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*/ 115 int 116 libscsi_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 130 libscsi_errno_t 131 libscsi_errno(libscsi_hdl_t *hp) 132 { 133 return (hp->lsh_errno); 134 } 135 136 const char * 137 libscsi_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 146 void * 147 libscsi_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 162 void * 163 libscsi_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 175 char * 176 libscsi_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*/ 188 void 189 libscsi_free(libscsi_hdl_t *hp, void *ptr) 190 { 191 free(ptr); 192 } 193 194 libscsi_hdl_t * 195 libscsi_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 211 void 212 libscsi_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 230 size_t 231 libscsi_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 271 static char * 272 libscsi_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 */ 293 int 294 libscsi_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 335 const char * 336 libscsi_vendor(libscsi_target_t *tp) 337 { 338 return (tp->lst_vendor); 339 } 340 341 const char * 342 libscsi_product(libscsi_target_t *tp) 343 { 344 return (tp->lst_product); 345 } 346 347 const char * 348 libscsi_revision(libscsi_target_t *tp) 349 { 350 return (tp->lst_revision); 351 } 352