1 /*
2 * Copyright (c) 1999-2018 Douglas Gilbert.
3 * All rights reserved.
4 * Use of this source code is governed by a BSD-style
5 * license that can be found in the BSD_LICENSE file.
6 */
7
8 /*
9 * CONTENTS
10 * Some SCSI commands are executed in many contexts and hence began
11 * to appear in several sg3_utils utilities. This files centralizes
12 * some of the low level command execution code. In most cases the
13 * interpretation of the command response is left to the each
14 * utility.
15 */
16
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <stdarg.h>
20 #include <stdbool.h>
21 #include <string.h>
22 #include <unistd.h>
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27
28 #include "sg_lib.h"
29 #include "sg_cmds_basic.h"
30 #include "sg_pt.h"
31 #include "sg_unaligned.h"
32
33 /* Needs to be after config.h */
34 #ifdef SG_LIB_LINUX
35 #include <errno.h>
36 #endif
37
38
39 static const char * const version_str = "1.83 20180204";
40
41
42 #define SENSE_BUFF_LEN 64 /* Arbitrary, could be larger */
43 #define EBUFF_SZ 256
44
45 #define DEF_PT_TIMEOUT 60 /* 60 seconds */
46 #define START_PT_TIMEOUT 120 /* 120 seconds == 2 minutes */
47 #define LONG_PT_TIMEOUT 7200 /* 7,200 seconds == 120 minutes */
48
49 #define INQUIRY_CMD 0x12
50 #define INQUIRY_CMDLEN 6
51 #define REQUEST_SENSE_CMD 0x3
52 #define REQUEST_SENSE_CMDLEN 6
53 #define REPORT_LUNS_CMD 0xa0
54 #define REPORT_LUNS_CMDLEN 12
55 #define TUR_CMD 0x0
56 #define TUR_CMDLEN 6
57
58 #define SAFE_STD_INQ_RESP_LEN 36 /* other lengths lock up some devices */
59
60
61 const char *
sg_cmds_version()62 sg_cmds_version()
63 {
64 return version_str;
65 }
66
67 #if defined(__GNUC__) || defined(__clang__)
68 static int pr2ws(const char * fmt, ...)
69 __attribute__ ((format (printf, 1, 2)));
70 #else
71 static int pr2ws(const char * fmt, ...);
72 #endif
73
74
75 static int
pr2ws(const char * fmt,...)76 pr2ws(const char * fmt, ...)
77 {
78 va_list args;
79 int n;
80
81 va_start(args, fmt);
82 n = vfprintf(sg_warnings_strm ? sg_warnings_strm : stderr, fmt, args);
83 va_end(args);
84 return n;
85 }
86
87 /* Returns file descriptor >= 0 if successful. If error in Unix returns
88 negated errno. */
89 int
sg_cmds_open_device(const char * device_name,bool read_only,int verbose)90 sg_cmds_open_device(const char * device_name, bool read_only, int verbose)
91 {
92 /* The following 2 lines are temporary. It is to avoid a NULL pointer
93 * crash when an old utility is used with a newer library built after
94 * the sg_warnings_strm cleanup */
95 if (NULL == sg_warnings_strm)
96 sg_warnings_strm = stderr;
97
98 return scsi_pt_open_device(device_name, read_only, verbose);
99 }
100
101 /* Returns file descriptor >= 0 if successful. If error in Unix returns
102 negated errno. */
103 int
sg_cmds_open_flags(const char * device_name,int flags,int verbose)104 sg_cmds_open_flags(const char * device_name, int flags, int verbose)
105 {
106 return scsi_pt_open_flags(device_name, flags, verbose);
107 }
108
109 /* Returns 0 if successful. If error in Unix returns negated errno. */
110 int
sg_cmds_close_device(int device_fd)111 sg_cmds_close_device(int device_fd)
112 {
113 return scsi_pt_close_device(device_fd);
114 }
115
116 static const char * const pass_through_s = "pass-through";
117
118 static int
sg_cmds_process_helper(const char * leadin,int mx_di_len,int resid,const unsigned char * sbp,int slen,bool noisy,int verbose,int * o_sense_cat)119 sg_cmds_process_helper(const char * leadin, int mx_di_len, int resid,
120 const unsigned char * sbp, int slen, bool noisy,
121 int verbose, int * o_sense_cat)
122 {
123 int scat, got;
124 bool n = false;
125 bool check_data_in = false;
126 char b[512];
127
128 scat = sg_err_category_sense(sbp, slen);
129 switch (scat) {
130 case SG_LIB_CAT_NOT_READY:
131 case SG_LIB_CAT_INVALID_OP:
132 case SG_LIB_CAT_ILLEGAL_REQ:
133 case SG_LIB_CAT_ABORTED_COMMAND:
134 case SG_LIB_CAT_COPY_ABORTED:
135 case SG_LIB_CAT_DATA_PROTECT:
136 case SG_LIB_CAT_PROTECTION:
137 case SG_LIB_CAT_NO_SENSE:
138 case SG_LIB_CAT_MISCOMPARE:
139 n = false;
140 break;
141 case SG_LIB_CAT_RECOVERED:
142 case SG_LIB_CAT_MEDIUM_HARD:
143 check_data_in = true;
144 #if defined(__GNUC__)
145 #if (__GNUC__ >= 7)
146 __attribute__((fallthrough));
147 /* FALL THROUGH */
148 #endif
149 #endif
150 case SG_LIB_CAT_UNIT_ATTENTION:
151 case SG_LIB_CAT_SENSE:
152 default:
153 n = noisy;
154 break;
155 }
156 if (verbose || n) {
157 if (leadin && (strlen(leadin) > 0))
158 pr2ws("%s:\n", leadin);
159 sg_get_sense_str(NULL, sbp, slen, (verbose > 1),
160 sizeof(b), b);
161 pr2ws("%s", b);
162 if ((mx_di_len > 0) && (resid > 0)) {
163 got = mx_di_len - resid;
164 if ((verbose > 2) || check_data_in || (got > 0))
165 pr2ws(" %s requested %d bytes (data-in) but got %d "
166 "bytes\n", pass_through_s, mx_di_len, got);
167 }
168 }
169 if (o_sense_cat)
170 *o_sense_cat = scat;
171 return -2;
172 }
173
174 /* This is a helper function used by sg_cmds_* implementations after the
175 * call to the pass-through. pt_res is returned from do_scsi_pt(). If valid
176 * sense data is found it is decoded and output to sg_warnings_strm (def:
177 * stderr); depending on the 'noisy' and 'verbose' settings. Returns -2 for
178 * "sense" category (may not be fatal), -1 for failed, 0, or a positive
179 * number. If 'mx_di_len > 0' then asks pass-through for resid and returns
180 * (mx_di_len - resid); otherwise returns 0. So for data-in it should return
181 * the actual number of bytes received. For data-out (to device) or no data
182 * call with 'mx_di_len' set to 0 or less. If -2 returned then sense category
183 * output via 'o_sense_cat' pointer (if not NULL). Note that several sense
184 * categories also have data in bytes received; -2 is still returned. */
185 int
sg_cmds_process_resp(struct sg_pt_base * ptvp,const char * leadin,int pt_res,int mx_di_len,const unsigned char * sbp,bool noisy,int verbose,int * o_sense_cat)186 sg_cmds_process_resp(struct sg_pt_base * ptvp, const char * leadin,
187 int pt_res, int mx_di_len, const unsigned char * sbp,
188 bool noisy, int verbose, int * o_sense_cat)
189 {
190 int got, cat, duration, slen, resid, resp_code, sstat;
191 bool transport_sense;
192 char b[1024];
193
194 if (NULL == leadin)
195 leadin = "";
196 if (pt_res < 0) {
197 #ifdef SG_LIB_LINUX
198 if (verbose)
199 pr2ws("%s: %s os error: %s\n", leadin, pass_through_s,
200 safe_strerror(-pt_res));
201 if ((-ENXIO == pt_res) && o_sense_cat) {
202 if (verbose > 2)
203 pr2ws("map ENXIO to SG_LIB_CAT_NOT_READY\n");
204 *o_sense_cat = SG_LIB_CAT_NOT_READY;
205 return -2;
206 } else if (noisy && (0 == verbose))
207 pr2ws("%s: %s os error: %s\n", leadin, pass_through_s,
208 safe_strerror(-pt_res));
209 #else
210 if (noisy || verbose)
211 pr2ws("%s: %s os error: %s\n", leadin, pass_through_s,
212 safe_strerror(-pt_res));
213 #endif
214 return -1;
215 } else if (SCSI_PT_DO_BAD_PARAMS == pt_res) {
216 pr2ws("%s: bad %s setup\n", leadin, pass_through_s);
217 return -1;
218 } else if (SCSI_PT_DO_TIMEOUT == pt_res) {
219 pr2ws("%s: %s timeout\n", leadin, pass_through_s);
220 return -1;
221 }
222 if ((verbose > 2) && ((duration = get_scsi_pt_duration_ms(ptvp)) >= 0))
223 pr2ws(" duration=%d ms\n", duration);
224 resid = (mx_di_len > 0) ? get_scsi_pt_resid(ptvp) : 0;
225 slen = get_scsi_pt_sense_len(ptvp);
226 switch ((cat = get_scsi_pt_result_category(ptvp))) {
227 case SCSI_PT_RESULT_GOOD:
228 if (sbp && (slen > 7)) {
229 resp_code = sbp[0] & 0x7f;
230 /* SBC referrals can have status=GOOD and sense_key=COMPLETED */
231 if (resp_code >= 0x70) {
232 if (resp_code < 0x72) {
233 if (SPC_SK_NO_SENSE != (0xf & sbp[2]))
234 sg_err_category_sense(sbp, slen);
235 } else if (resp_code < 0x74) {
236 if (SPC_SK_NO_SENSE != (0xf & sbp[1]))
237 sg_err_category_sense(sbp, slen);
238 }
239 }
240 }
241 if (mx_di_len > 0) {
242 got = mx_di_len - resid;
243 if ((verbose > 1) && (resid != 0))
244 pr2ws(" %s: %s requested %d bytes (data-in) but got %d "
245 "bytes\n", leadin, pass_through_s, mx_di_len, got);
246 if (got >= 0)
247 return got;
248 else {
249 if (verbose)
250 pr2ws(" %s: %s can't get negative bytes, say it got "
251 "none\n", leadin, pass_through_s);
252 return 0;
253 }
254 } else
255 return 0;
256 case SCSI_PT_RESULT_STATUS: /* other than GOOD and CHECK CONDITION */
257 sstat = get_scsi_pt_status_response(ptvp);
258 if (o_sense_cat) {
259 switch (sstat) {
260 case SAM_STAT_RESERVATION_CONFLICT:
261 *o_sense_cat = SG_LIB_CAT_RES_CONFLICT;
262 return -2;
263 case SAM_STAT_CONDITION_MET:
264 *o_sense_cat = SG_LIB_CAT_CONDITION_MET;
265 return -2;
266 case SAM_STAT_BUSY:
267 *o_sense_cat = SG_LIB_CAT_BUSY;
268 return -2;
269 case SAM_STAT_TASK_SET_FULL:
270 *o_sense_cat = SG_LIB_CAT_TS_FULL;
271 return -2;
272 case SAM_STAT_ACA_ACTIVE:
273 *o_sense_cat = SG_LIB_CAT_ACA_ACTIVE;
274 return -2;
275 case SAM_STAT_TASK_ABORTED:
276 *o_sense_cat = SG_LIB_CAT_TASK_ABORTED;
277 return -2;
278 default:
279 break;
280 }
281 }
282 if (verbose || noisy) {
283 sg_get_scsi_status_str(sstat, sizeof(b), b);
284 pr2ws("%s: scsi status: %s\n", leadin, b);
285 }
286 return -1;
287 case SCSI_PT_RESULT_SENSE:
288 return sg_cmds_process_helper(leadin, mx_di_len, resid, sbp, slen,
289 noisy, verbose, o_sense_cat);
290 case SCSI_PT_RESULT_TRANSPORT_ERR:
291 if (verbose || noisy) {
292 get_scsi_pt_transport_err_str(ptvp, sizeof(b), b);
293 pr2ws("%s: transport: %s\n", leadin, b);
294 }
295 #ifdef SG_LIB_LINUX
296 transport_sense = (slen > 0);
297 #else
298 transport_sense = ((SAM_STAT_CHECK_CONDITION ==
299 get_scsi_pt_status_response(ptvp)) && (slen > 0));
300 #endif
301 if (transport_sense)
302 return sg_cmds_process_helper(leadin, mx_di_len, resid, sbp,
303 slen, noisy, verbose, o_sense_cat);
304 else
305 return -1;
306 case SCSI_PT_RESULT_OS_ERR:
307 if (verbose || noisy) {
308 get_scsi_pt_os_err_str(ptvp, sizeof(b), b);
309 pr2ws("%s: os: %s\n", leadin, b);
310 }
311 return -1;
312 default:
313 pr2ws("%s: unknown %s result category (%d)\n", leadin, pass_through_s,
314 cat);
315 return -1;
316 }
317 }
318
319 bool
sg_cmds_is_nvme(const struct sg_pt_base * ptvp)320 sg_cmds_is_nvme(const struct sg_pt_base * ptvp)
321 {
322 return pt_device_is_nvme(ptvp);
323 }
324
325 static struct sg_pt_base *
create_pt_obj(const char * cname)326 create_pt_obj(const char * cname)
327 {
328 struct sg_pt_base * ptvp = construct_scsi_pt_obj();
329 if (NULL == ptvp)
330 pr2ws("%s: out of memory\n", cname);
331 return ptvp;
332 }
333
334 static const char * const inquiry_s = "inquiry";
335
336 static int
sg_ll_inquiry_com(int sg_fd,bool cmddt,bool evpd,int pg_op,void * resp,int mx_resp_len,int timeout_secs,int * residp,bool noisy,int verbose)337 sg_ll_inquiry_com(int sg_fd, bool cmddt, bool evpd, int pg_op, void * resp,
338 int mx_resp_len, int timeout_secs, int * residp,
339 bool noisy, int verbose)
340 {
341 int res, ret, k, sense_cat, resid;
342 unsigned char inq_cdb[INQUIRY_CMDLEN] = {INQUIRY_CMD, 0, 0, 0, 0, 0};
343 unsigned char sense_b[SENSE_BUFF_LEN];
344 unsigned char * up;
345 struct sg_pt_base * ptvp;
346
347 if (cmddt)
348 inq_cdb[1] |= 0x2;
349 if (evpd)
350 inq_cdb[1] |= 0x1;
351 inq_cdb[2] = (unsigned char)pg_op;
352 /* 16 bit allocation length (was 8, increased in spc3r09, 200209) */
353 sg_put_unaligned_be16((uint16_t)mx_resp_len, inq_cdb + 3);
354 if (verbose) {
355 pr2ws(" %s cdb: ", inquiry_s);
356 for (k = 0; k < INQUIRY_CMDLEN; ++k)
357 pr2ws("%02x ", inq_cdb[k]);
358 pr2ws("\n");
359 }
360 if (resp && (mx_resp_len > 0)) {
361 up = (unsigned char *)resp;
362 up[0] = 0x7f; /* defensive prefill */
363 if (mx_resp_len > 4)
364 up[4] = 0;
365 }
366 if (timeout_secs <= 0)
367 timeout_secs = DEF_PT_TIMEOUT;
368 ptvp = construct_scsi_pt_obj();
369 if (NULL == ptvp) {
370 pr2ws("%s: out of memory\n", __func__);
371 if (residp)
372 *residp = 0;
373 return -1;
374 }
375 set_scsi_pt_cdb(ptvp, inq_cdb, sizeof(inq_cdb));
376 set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
377 set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len);
378 res = do_scsi_pt(ptvp, sg_fd, timeout_secs, verbose);
379 ret = sg_cmds_process_resp(ptvp, inquiry_s, res, mx_resp_len, sense_b,
380 noisy, verbose, &sense_cat);
381 resid = get_scsi_pt_resid(ptvp);
382 if (residp)
383 *residp = resid;
384 if (-1 == ret)
385 ret = sg_convert_errno(get_scsi_pt_os_err(ptvp));
386 else if (-2 == ret) {
387 switch (sense_cat) {
388 case SG_LIB_CAT_RECOVERED:
389 case SG_LIB_CAT_NO_SENSE:
390 ret = 0;
391 break;
392 default:
393 ret = sense_cat;
394 break;
395 }
396 } else if (ret < 4) {
397 if (verbose)
398 pr2ws("%s: got too few bytes (%d)\n", __func__, ret);
399 ret = SG_LIB_CAT_MALFORMED;
400 } else
401 ret = 0;
402 destruct_scsi_pt_obj(ptvp);
403
404 if (resid > 0) {
405 if (resid > mx_resp_len) {
406 pr2ws("%s resid (%d) should never exceed requested "
407 "len=%d\n", inquiry_s, resid, mx_resp_len);
408 return ret ? ret : SG_LIB_CAT_MALFORMED;
409 }
410 /* zero unfilled section of response buffer, based on resid */
411 memset((unsigned char *)resp + (mx_resp_len - resid), 0, resid);
412 }
413 return ret;
414 }
415
416 /* Invokes a SCSI INQUIRY command and yields the response. Returns 0 when
417 * successful, various SG_LIB_CAT_* positive values or -1 -> other errors.
418 * The CMDDT field is obsolete in the INQUIRY cdb. */
419 int
sg_ll_inquiry(int sg_fd,bool cmddt,bool evpd,int pg_op,void * resp,int mx_resp_len,bool noisy,int verbose)420 sg_ll_inquiry(int sg_fd, bool cmddt, bool evpd, int pg_op, void * resp,
421 int mx_resp_len, bool noisy, int verbose)
422 {
423 return sg_ll_inquiry_com(sg_fd, cmddt, evpd, pg_op, resp, mx_resp_len,
424 0 /* timeout_sec */, NULL, noisy, verbose);
425 }
426
427 /* Yields most of first 36 bytes of a standard INQUIRY (evpd==0) response.
428 * Returns 0 when successful, various SG_LIB_CAT_* positive values or
429 * -1 -> other errors */
430 int
sg_simple_inquiry(int sg_fd,struct sg_simple_inquiry_resp * inq_data,bool noisy,int verbose)431 sg_simple_inquiry(int sg_fd, struct sg_simple_inquiry_resp * inq_data,
432 bool noisy, int verbose)
433 {
434 int ret;
435 unsigned char inq_resp[SAFE_STD_INQ_RESP_LEN];
436
437 if (inq_data) {
438 memset(inq_data, 0, sizeof(* inq_data));
439 inq_data->peripheral_qualifier = 0x3;
440 inq_data->peripheral_type = 0x1f;
441 }
442 ret = sg_ll_inquiry_com(sg_fd, false, false, 0, inq_resp,
443 sizeof(inq_resp), 0, NULL, noisy, verbose);
444
445 if (inq_data && (0 == ret)) {
446 inq_data->peripheral_qualifier = (inq_resp[0] >> 5) & 0x7;
447 inq_data->peripheral_type = inq_resp[0] & 0x1f;
448 inq_data->byte_1 = inq_resp[1];
449 inq_data->version = inq_resp[2];
450 inq_data->byte_3 = inq_resp[3];
451 inq_data->byte_5 = inq_resp[5];
452 inq_data->byte_6 = inq_resp[6];
453 inq_data->byte_7 = inq_resp[7];
454 memcpy(inq_data->vendor, inq_resp + 8, 8);
455 memcpy(inq_data->product, inq_resp + 16, 16);
456 memcpy(inq_data->revision, inq_resp + 32, 4);
457 }
458 return ret;
459 }
460
461 /* Invokes a SCSI INQUIRY command and yields the response. Returns 0 when
462 * successful, various SG_LIB_CAT_* positive values or -1 -> other errors.
463 * The CMDDT field is obsolete in the INQUIRY cdb (since spc3r16 in 2003) so
464 * an argument to set it has been removed (use the REPORT SUPPORTED OPERATION
465 * CODES command instead). Adds the ability to set the command abort timeout
466 * and the ability to report the residual count. If timeout_secs is zero
467 * or less the default command abort timeout (60 seconds) is used.
468 * If residp is non-NULL then the residual value is written where residp
469 * points. A residual value of 0 implies mx_resp_len bytes have be written
470 * where resp points. If the residual value equals mx_resp_len then no
471 * bytes have been written. */
472 int
sg_ll_inquiry_v2(int sg_fd,bool evpd,int pg_op,void * resp,int mx_resp_len,int timeout_secs,int * residp,bool noisy,int verbose)473 sg_ll_inquiry_v2(int sg_fd, bool evpd, int pg_op, void * resp,
474 int mx_resp_len, int timeout_secs, int * residp,
475 bool noisy, int verbose)
476 {
477 return sg_ll_inquiry_com(sg_fd, false, evpd, pg_op, resp, mx_resp_len,
478 timeout_secs, residp, noisy, verbose);
479 }
480
481 /* Invokes a SCSI TEST UNIT READY command.
482 * 'pack_id' is just for diagnostics, safe to set to 0.
483 * Looks for progress indicator if 'progress' non-NULL;
484 * if found writes value [0..65535] else write -1.
485 * Returns 0 when successful, various SG_LIB_CAT_* positive values or
486 * -1 -> other errors */
487 int
sg_ll_test_unit_ready_progress(int sg_fd,int pack_id,int * progress,bool noisy,int verbose)488 sg_ll_test_unit_ready_progress(int sg_fd, int pack_id, int * progress,
489 bool noisy, int verbose)
490 {
491 static const char * const tur_s = "test unit ready";
492 int res, ret, k, sense_cat;
493 unsigned char tur_cdb[TUR_CMDLEN] = {TUR_CMD, 0, 0, 0, 0, 0};
494 unsigned char sense_b[SENSE_BUFF_LEN];
495 struct sg_pt_base * ptvp;
496
497 if (verbose) {
498 pr2ws(" %s cdb: ", tur_s);
499 for (k = 0; k < TUR_CMDLEN; ++k)
500 pr2ws("%02x ", tur_cdb[k]);
501 pr2ws("\n");
502 }
503
504 if (NULL == ((ptvp = create_pt_obj(tur_s))))
505 return -1;
506 set_scsi_pt_cdb(ptvp, tur_cdb, sizeof(tur_cdb));
507 set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
508 set_scsi_pt_packet_id(ptvp, pack_id);
509 res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
510 ret = sg_cmds_process_resp(ptvp, tur_s, res, SG_NO_DATA_IN, sense_b,
511 noisy, verbose, &sense_cat);
512 if (-1 == ret) {
513 int os_err = get_scsi_pt_os_err(ptvp);
514
515 if ((os_err > 0) && (os_err < 47))
516 ret = SG_LIB_OS_BASE_ERR + os_err;
517 } else if (-2 == ret) {
518 if (progress) {
519 int slen = get_scsi_pt_sense_len(ptvp);
520
521 if (! sg_get_sense_progress_fld(sense_b, slen, progress))
522 *progress = -1;
523 }
524 switch (sense_cat) {
525 case SG_LIB_CAT_RECOVERED:
526 case SG_LIB_CAT_NO_SENSE:
527 ret = 0;
528 break;
529 default:
530 ret = sense_cat;
531 break;
532 }
533 } else
534 ret = 0;
535
536 destruct_scsi_pt_obj(ptvp);
537 return ret;
538 }
539
540 /* Invokes a SCSI TEST UNIT READY command.
541 * 'pack_id' is just for diagnostics, safe to set to 0.
542 * Returns 0 when successful, various SG_LIB_CAT_* positive values or
543 * -1 -> other errors */
544 int
sg_ll_test_unit_ready(int sg_fd,int pack_id,bool noisy,int verbose)545 sg_ll_test_unit_ready(int sg_fd, int pack_id, bool noisy, int verbose)
546 {
547 return sg_ll_test_unit_ready_progress(sg_fd, pack_id, NULL, noisy,
548 verbose);
549 }
550
551 /* Invokes a SCSI REQUEST SENSE command. Returns 0 when successful, various
552 * SG_LIB_CAT_* positive values or -1 -> other errors */
553 int
sg_ll_request_sense(int sg_fd,bool desc,void * resp,int mx_resp_len,bool noisy,int verbose)554 sg_ll_request_sense(int sg_fd, bool desc, void * resp, int mx_resp_len,
555 bool noisy, int verbose)
556 {
557 static const char * const rq_s = "request sense";
558 int k, ret, res, sense_cat;
559 unsigned char rs_cdb[REQUEST_SENSE_CMDLEN] =
560 {REQUEST_SENSE_CMD, 0, 0, 0, 0, 0};
561 unsigned char sense_b[SENSE_BUFF_LEN];
562 struct sg_pt_base * ptvp;
563
564 if (desc)
565 rs_cdb[1] |= 0x1;
566 if (mx_resp_len > 0xff) {
567 pr2ws("mx_resp_len cannot exceed 255\n");
568 return -1;
569 }
570 rs_cdb[4] = mx_resp_len & 0xff;
571 if (verbose) {
572 pr2ws(" %s cmd: ", rq_s);
573 for (k = 0; k < REQUEST_SENSE_CMDLEN; ++k)
574 pr2ws("%02x ", rs_cdb[k]);
575 pr2ws("\n");
576 }
577
578 if (NULL == ((ptvp = create_pt_obj(rq_s))))
579 return -1;
580 set_scsi_pt_cdb(ptvp, rs_cdb, sizeof(rs_cdb));
581 set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
582 set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len);
583 res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
584 ret = sg_cmds_process_resp(ptvp, rq_s, res, mx_resp_len, sense_b, noisy,
585 verbose, &sense_cat);
586 if (-1 == ret) {
587 int os_err = get_scsi_pt_os_err(ptvp);
588
589 if ((os_err > 0) && (os_err < 47))
590 ret = SG_LIB_OS_BASE_ERR + os_err;
591 } else if (-2 == ret) {
592 switch (sense_cat) {
593 case SG_LIB_CAT_RECOVERED:
594 case SG_LIB_CAT_NO_SENSE:
595 ret = 0;
596 break;
597 default:
598 ret = sense_cat;
599 break;
600 }
601 } else {
602 if ((mx_resp_len >= 8) && (ret < 8)) {
603 if (verbose)
604 pr2ws(" %s: got %d bytes in response, too short\n", rq_s,
605 ret);
606 ret = -1;
607 } else
608 ret = 0;
609 }
610 destruct_scsi_pt_obj(ptvp);
611 return ret;
612 }
613
614 /* Invokes a SCSI REPORT LUNS command. Return of 0 -> success,
615 * various SG_LIB_CAT_* positive values or -1 -> other errors */
616 int
sg_ll_report_luns(int sg_fd,int select_report,void * resp,int mx_resp_len,bool noisy,int verbose)617 sg_ll_report_luns(int sg_fd, int select_report, void * resp, int mx_resp_len,
618 bool noisy, int verbose)
619 {
620 static const char * const report_luns_s = "report luns";
621 int k, ret, res, sense_cat;
622 unsigned char rl_cdb[REPORT_LUNS_CMDLEN] =
623 {REPORT_LUNS_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
624 unsigned char sense_b[SENSE_BUFF_LEN];
625 struct sg_pt_base * ptvp;
626
627 rl_cdb[2] = select_report & 0xff;
628 sg_put_unaligned_be32((uint32_t)mx_resp_len, rl_cdb + 6);
629 if (verbose) {
630 pr2ws(" %s cdb: ", report_luns_s);
631 for (k = 0; k < REPORT_LUNS_CMDLEN; ++k)
632 pr2ws("%02x ", rl_cdb[k]);
633 pr2ws("\n");
634 }
635
636 if (NULL == ((ptvp = create_pt_obj(report_luns_s))))
637 return -1;
638 set_scsi_pt_cdb(ptvp, rl_cdb, sizeof(rl_cdb));
639 set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
640 set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len);
641 res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
642 ret = sg_cmds_process_resp(ptvp, report_luns_s, res, mx_resp_len,
643 sense_b, noisy, verbose, &sense_cat);
644 if (-1 == ret) {
645 int os_err = get_scsi_pt_os_err(ptvp);
646
647 if ((os_err > 0) && (os_err < 47))
648 ret = SG_LIB_OS_BASE_ERR + os_err;
649 } else if (-2 == ret) {
650 switch (sense_cat) {
651 case SG_LIB_CAT_RECOVERED:
652 case SG_LIB_CAT_NO_SENSE:
653 ret = 0;
654 break;
655 default:
656 ret = sense_cat;
657 break;
658 }
659 } else
660 ret = 0;
661 destruct_scsi_pt_obj(ptvp);
662 return ret;
663 }
664