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