xref: /aosp_15_r20/external/sg3_utils/src/sg_persist.c (revision 44704f698541f6367e81f991ef8bb54ccbf3fc18)
1 /* A utility program originally written for the Linux OS SCSI subsystem.
2  *  Copyright (C) 2004-2022 D. Gilbert
3  *  This program is free software; you can redistribute it and/or modify
4  *  it under the terms of the GNU General Public License as published by
5  *  the Free Software Foundation; either version 2, or (at your option)
6  *  any later version.
7  *
8  * SPDX-License-Identifier: GPL-2.0-or-later
9  *
10  *  This program issues the SCSI PERSISTENT IN and OUT commands.
11  */
12 
13 #include <unistd.h>
14 #include <fcntl.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <stdint.h>
18 #include <stdarg.h>
19 #include <stdbool.h>
20 #include <string.h>
21 #include <ctype.h>
22 #include <errno.h>
23 #include <getopt.h>
24 #define __STDC_FORMAT_MACROS 1
25 
26 #include <inttypes.h>
27 
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31 
32 #include "sg_lib.h"
33 #include "sg_cmds_basic.h"
34 #include "sg_cmds_extra.h"
35 #include "sg_unaligned.h"
36 #include "sg_pr2serr.h"
37 
38 static const char * version_str = "0.69 20220118";
39 
40 
41 #define PRIN_RKEY_SA     0x0
42 #define PRIN_RRES_SA     0x1
43 #define PRIN_RCAP_SA     0x2
44 #define PRIN_RFSTAT_SA   0x3
45 #define PROUT_REG_SA     0x0
46 #define PROUT_RES_SA     0x1
47 #define PROUT_REL_SA     0x2
48 #define PROUT_CLEAR_SA   0x3
49 #define PROUT_PREE_SA    0x4
50 #define PROUT_PREE_AB_SA 0x5
51 #define PROUT_REG_IGN_SA 0x6
52 #define PROUT_REG_MOVE_SA 0x7
53 #define PROUT_REPL_LOST_SA 0x8
54 #define MX_ALLOC_LEN 8192
55 #define MX_TIDS 32
56 #define MX_TID_LEN 256
57 
58 #define ME "sg_persist"
59 
60 #define SG_PERSIST_IN_RDONLY "SG_PERSIST_IN_RDONLY"
61 
62 struct opts_t {
63     bool inquiry;       /* set true by default (unlike most bools) */
64     bool param_alltgpt;
65     bool param_aptpl;
66     bool param_unreg;
67     bool pr_in;         /* true: PR_IN (def); false: PR_OUT */
68     bool readonly;
69     bool readwrite_force;/* set when '-yy' given. Ooverrides environment
70                             variable SG_PERSIST_IN_RDONLY and opens RW */
71     bool verbose_given;
72     bool version_given;
73     int hex;
74     int num_transportids;
75     int prin_sa;
76     int prout_sa;
77     int verbose;
78     uint32_t alloc_len;
79     uint32_t param_rtp;
80     uint32_t prout_type;
81     uint64_t param_rk;
82     uint64_t param_sark;
83     uint8_t transportid_arr[MX_TIDS * MX_TID_LEN];
84 };
85 
86 
87 static struct option long_options[] = {
88     {"alloc-length", required_argument, 0, 'l'},
89     {"alloc_length", required_argument, 0, 'l'},
90     {"clear", no_argument, 0, 'C'},
91     {"device", required_argument, 0, 'd'},
92     {"help", no_argument, 0, 'h'},
93     {"hex", no_argument, 0, 'H'},
94     {"in", no_argument, 0, 'i'},
95     {"maxlen", required_argument, 0, 'm'},
96     {"no-inquiry", no_argument, 0, 'n'},
97     {"no_inquiry", no_argument, 0, 'n'},
98     {"out", no_argument, 0, 'o'},
99     {"param-alltgpt", no_argument, 0, 'Y'},
100     {"param_alltgpt", no_argument, 0, 'Y'},
101     {"param-aptpl", no_argument, 0, 'Z'},
102     {"param_aptpl", no_argument, 0, 'Z'},
103     {"param-rk", required_argument, 0, 'K'},
104     {"param_rk", required_argument, 0, 'K'},
105     {"param-sark", required_argument, 0, 'S'},
106     {"param_sark", required_argument, 0, 'S'},
107     {"param-unreg", no_argument, 0, 'U'},
108     {"param_unreg", no_argument, 0, 'U'},
109     {"preempt", no_argument, 0, 'P'},
110     {"preempt-abort", no_argument, 0, 'A'},
111     {"preempt_abort", no_argument, 0, 'A'},
112     {"prout-type", required_argument, 0, 'T'},
113     {"prout_type", required_argument, 0, 'T'},
114     {"read-full-status", no_argument, 0, 's'},
115     {"read_full_status", no_argument, 0, 's'},
116     {"read-keys", no_argument, 0, 'k'},
117     {"read_keys", no_argument, 0, 'k'},
118     {"readonly", no_argument, 0, 'y'},
119     {"read-reservation", no_argument, 0, 'r'},
120     {"read_reservation", no_argument, 0, 'r'},
121     {"read-status", no_argument, 0, 's'},
122     {"read_status", no_argument, 0, 's'},
123     {"register", no_argument, 0, 'G'},
124     {"register-ignore", no_argument, 0, 'I'},
125     {"register_ignore", no_argument, 0, 'I'},
126     {"register-move", no_argument, 0, 'M'},
127     {"register_move", no_argument, 0, 'M'},
128     {"release", no_argument, 0, 'L'},
129     {"relative-target-port", required_argument, 0, 'Q'},
130     {"relative_target_port", required_argument, 0, 'Q'},
131     {"replace-lost", no_argument, 0, 'z'},
132     {"replace_lost", no_argument, 0, 'z'},
133     {"report-capabilities", no_argument, 0, 'c'},
134     {"report_capabilities", no_argument, 0, 'c'},
135     {"reserve", no_argument, 0, 'R'},
136     {"transport-id", required_argument, 0, 'X'},
137     {"transport_id", required_argument, 0, 'X'},
138     {"unreg", no_argument, 0, 'U'},
139     {"verbose", no_argument, 0, 'v'},
140     {"version", no_argument, 0, 'V'},
141     {0, 0, 0, 0}
142 };
143 
144 static const char * prin_sa_strs[] = {
145     "Read keys",
146     "Read reservation",
147     "Report capabilities",
148     "Read full status",
149     "[reserved 0x4]",
150     "[reserved 0x5]",
151     "[reserved 0x6]",
152     "[reserved 0x7]",
153 };
154 static const int num_prin_sa_strs = SG_ARRAY_SIZE(prin_sa_strs);
155 
156 static const char * prout_sa_strs[] = {
157     "Register",
158     "Reserve",
159     "Release",
160     "Clear",
161     "Preempt",
162     "Preempt and abort",
163     "Register and ignore existing key",
164     "Register and move",
165     "Replace lost reservation",
166     "[reserved 0x9]",
167 };
168 static const int num_prout_sa_strs = SG_ARRAY_SIZE(prout_sa_strs);
169 
170 static const char * pr_type_strs[] = {
171     "obsolete [0]",
172     "Write Exclusive",
173     "obsolete [2]",
174     "Exclusive Access",
175     "obsolete [4]",
176     "Write Exclusive, registrants only",
177     "Exclusive Access, registrants only",
178     "Write Exclusive, all registrants",
179     "Exclusive Access, all registrants",
180     "obsolete [9]", "obsolete [0xa]", "obsolete [0xb]", "obsolete [0xc]",
181     "obsolete [0xd]", "obsolete [0xe]", "obsolete [0xf]",
182 };
183 
184 
185 static void
usage(int help)186 usage(int help)
187 {
188     if (help < 2) {
189         pr2serr("Usage: sg_persist [OPTIONS] [DEVICE]\n"
190                 "  where the main OPTIONS are:\n"
191                 "    --clear|-C                 PR Out: Clear\n"
192                 "    --help|-h                  print usage message, "
193                 "twice for more\n"
194                 "    --in|-i                    request PR In command "
195                 "(default)\n"
196                 "    --out|-o                   request PR Out command\n"
197                 "    --param-rk=RK|-K RK        PR Out parameter reservation "
198                 "key\n"
199                 "                               (RK is in hex)\n"
200                 "    --param-sark=SARK|-S SARK    PR Out parameter service "
201                 "action\n"
202                 "                                 reservation key (SARK is "
203                 "in hex)\n"
204                 "    --preempt|-P               PR Out: Preempt\n"
205                 "    --preempt-abort|-A         PR Out: Preempt and Abort\n"
206                 "    --prout-type=TYPE|-T TYPE    PR Out type field (see "
207                 "'-hh')\n"
208                 "    --read-full-status|-s      PR In: Read Full Status\n"
209                 "    --read-keys|-k             PR In: Read Keys "
210                 "(default)\n");
211         pr2serr("    --read-reservation|-r      PR In: Read Reservation\n"
212                 "    --read-status|-s           PR In: Read Full Status\n"
213                 "    --register|-G              PR Out: Register\n"
214                 "    --register-ignore|-I       PR Out: Register and Ignore\n"
215                 "    --register-move|-M         PR Out: Register and Move\n"
216                 "                               for '--register-move'\n"
217                 "    --release|-L               PR Out: Release\n"
218                 "    --replace-lost|-x          PR Out: Replace Lost "
219                 "Reservation\n"
220                 "    --report-capabilities|-c   PR In: Report Capabilities\n"
221                 "    --reserve|-R               PR Out: Reserve\n"
222                 "    --unreg|-U                 optional with PR Out "
223                 "Register and Move\n\n"
224                 "Performs a SCSI PERSISTENT RESERVE (IN or OUT) command. "
225                 "Invoking\n'sg_persist DEVICE' will do a PR In Read Keys "
226                 "command. Use '-hh'\nfor more options and TYPE meanings.\n");
227     } else {
228         pr2serr("Usage: sg_persist [OPTIONS] [DEVICE]\n"
229                 "  where the other OPTIONS are:\n"
230                 "    --alloc-length=LEN|-l LEN    allocation length hex "
231                 "value (used with\n"
232                 "                                 PR In only) (default: 8192 "
233                 "(2000 in hex))\n"
234                 "    --device=DEVICE|-d DEVICE    supply DEVICE as an option "
235                 "rather than\n"
236                 "                                 an argument\n"
237                 "    --hex|-H                   output response in hex (for "
238                 "PR In commands)\n"
239                 "    --maxlen=LEN|-m LEN        allocation length in "
240                 "decimal, by default.\n"
241                 "                               like --alloc-len= "
242                 "(def: 8192, 8k, 2000h)\n"
243                 "    --no-inquiry|-n            skip INQUIRY (default: do "
244                 "INQUIRY)\n"
245                 "    --param-alltgpt|-Y         PR Out parameter "
246                 "'ALL_TG_PT'\n"
247                 "    --param-aptpl|-Z           PR Out parameter 'APTPL'\n"
248                 "    --readonly|-y              open DEVICE read-only (def: "
249                 "read-write)\n"
250                 "    --relative-target-port=RTPI|-Q RTPI    relative target "
251                 "port "
252                 "identifier\n"
253                 "    --transport-id=TIDS|-X TIDS    one or more "
254                 "TransportIDs can\n"
255                 "                                   be given in several "
256                 "forms\n"
257                 "    --verbose|-v               output additional debug "
258                 "information\n"
259                 "    --version|-V               output version string\n\n"
260                 "For the main options use '--help' or '-h' once.\n\n\n");
261         pr2serr("PR Out TYPE field value meanings:\n"
262                 "  0:    obsolete (was 'read shared' in SPC)\n"
263                 "  1:    write exclusive\n"
264                 "  2:    obsolete (was 'read exclusive')\n"
265                 "  3:    exclusive access\n"
266                 "  4:    obsolete (was 'shared access')\n"
267                 "  5:    write exclusive, registrants only\n"
268                 "  6:    exclusive access, registrants only\n"
269                 "  7:    write exclusive, all registrants\n"
270                 "  8:    exclusive access, all registrants\n");
271     }
272 }
273 
274 static int
prin_work(int sg_fd,const struct opts_t * op)275 prin_work(int sg_fd, const struct opts_t * op)
276 {
277     int k, j, num, add_len, add_desc_len;
278     int res = 0;
279     unsigned int pr_gen;
280     uint8_t * bp;
281     uint8_t * pr_buff = NULL;
282     uint8_t * free_pr_buff = NULL;
283 
284     pr_buff = sg_memalign(op->alloc_len, 0 /* page aligned */, &free_pr_buff,
285                           false);
286     if (NULL == pr_buff) {
287         pr2serr("%s: unable to allocate %d bytes on heap\n", __func__,
288                 op->alloc_len);
289         return sg_convert_errno(ENOMEM);
290     }
291     res = sg_ll_persistent_reserve_in(sg_fd, op->prin_sa, pr_buff,
292                                       op->alloc_len, true, op->verbose);
293     if (res) {
294         char b[64];
295         char bb[80];
296 
297         if (op->prin_sa < num_prin_sa_strs)
298             snprintf(b, sizeof(b), "%s", prin_sa_strs[op->prin_sa]);
299         else
300             snprintf(b, sizeof(b), "service action=0x%x", op->prin_sa);
301 
302         if (SG_LIB_CAT_INVALID_OP == res)
303             pr2serr("PR in (%s): command not supported\n", b);
304         else if (SG_LIB_CAT_ILLEGAL_REQ == res)
305             pr2serr("PR in (%s): bad field in cdb or parameter list (perhaps "
306                     "unsupported service action)\n", b);
307         else {
308             sg_get_category_sense_str(res, sizeof(bb), bb, op->verbose);
309             pr2serr("PR in (%s): %s\n", b, bb);
310         }
311         goto fini;
312     }
313     if (PRIN_RCAP_SA == op->prin_sa) {
314         if (8 != pr_buff[1]) {
315             pr2serr("Unexpected response for PRIN Report Capabilities\n");
316             if (op->hex)
317                 hex2stdout(pr_buff, pr_buff[1], 1);
318             res = SG_LIB_CAT_MALFORMED;
319             goto fini;
320         }
321         if (op->hex)
322             hex2stdout(pr_buff, 8, 1);
323         else {
324             printf("Report capabilities response:\n");
325             printf("  Replace Lost Reservation Capable(RLR_C): %d\n",
326                    !!(pr_buff[2] & 0x80));      /* added spc4r26 */
327             printf("  Compatible Reservation Handling(CRH): %d\n",
328                    !!(pr_buff[2] & 0x10));
329             printf("  Specify Initiator Ports Capable(SIP_C): %d\n",
330                    !!(pr_buff[2] & 0x8));
331             printf("  All Target Ports Capable(ATP_C): %d\n",
332                    !!(pr_buff[2] & 0x4));
333             printf("  Persist Through Power Loss Capable(PTPL_C): %d\n",
334                    !!(pr_buff[2] & 0x1));
335             printf("  Type Mask Valid(TMV): %d\n", !!(pr_buff[3] & 0x80));
336             printf("  Allow Commands: %d\n", (pr_buff[3] >> 4) & 0x7);
337             printf("  Persist Through Power Loss Active(PTPL_A): %d\n",
338                    !!(pr_buff[3] & 0x1));
339             if (pr_buff[3] & 0x80) {
340                 printf("    Support indicated in Type mask:\n");
341                 printf("      %s: %d\n", pr_type_strs[7],
342                        !!(pr_buff[4] & 0x80));  /* WR_EX_AR */
343                 printf("      %s: %d\n", pr_type_strs[6],
344                        !!(pr_buff[4] & 0x40));  /* EX_AC_RO */
345                 printf("      %s: %d\n", pr_type_strs[5],
346                        !!(pr_buff[4] & 0x20));  /* WR_EX_RO */
347                 printf("      %s: %d\n", pr_type_strs[3],
348                        !!(pr_buff[4] & 0x8));   /* EX_AC */
349                 printf("      %s: %d\n", pr_type_strs[1],
350                        !!(pr_buff[4] & 0x2));   /* WR_EX */
351                 printf("      %s: %d\n", pr_type_strs[8],
352                        !!(pr_buff[5] & 0x1));   /* EX_AC_AR */
353             }
354         }
355     } else {
356         pr_gen =  sg_get_unaligned_be32(pr_buff + 0);
357         add_len = sg_get_unaligned_be32(pr_buff + 4);
358         if (op->hex) {
359             if (op->hex > 1)
360                 hex2stdout(pr_buff, add_len + 8, ((2 == op->hex) ? 1 : -1));
361             else {
362                 printf("  PR generation=0x%x, ", pr_gen);
363                 if (add_len <= 0)
364                     printf("Additional length=%d\n", add_len);
365                 if ((uint32_t)add_len > (op->alloc_len - 8)) {
366                     printf("Additional length too large=%d, truncate\n",
367                            add_len);
368                     hex2stdout((pr_buff + 8), op->alloc_len - 8, 1);
369                 } else {
370                     printf("Additional length=%d\n", add_len);
371                     hex2stdout((pr_buff + 8), add_len, 1);
372                 }
373             }
374         } else if (PRIN_RKEY_SA == op->prin_sa) {
375             printf("  PR generation=0x%x, ", pr_gen);
376             num = add_len / 8;
377             if (num > 0) {
378                 if (1 == num)
379                     printf("1 registered reservation key follows:\n");
380                 else
381                     printf("%d registered reservation keys follow:\n", num);
382                 bp = pr_buff + 8;
383                 for (k = 0; k < num; ++k, bp += 8)
384                     printf("    0x%" PRIx64 "\n",
385                            sg_get_unaligned_be64(bp + 0));
386             } else
387                 printf("there are NO registered reservation keys\n");
388         } else if (PRIN_RRES_SA == op->prin_sa) {
389             printf("  PR generation=0x%x, ", pr_gen);
390             num = add_len / 16;
391             if (num > 0) {
392                 printf("Reservation follows:\n");
393                 bp = pr_buff + 8;
394                 printf("    Key=0x%" PRIx64 "\n", sg_get_unaligned_be64(bp));
395                 j = ((bp[13] >> 4) & 0xf);
396                 if (0 == j)
397                     printf("    scope: LU_SCOPE, ");
398                 else
399                     printf("    scope: %d ", j);
400                 j = (bp[13] & 0xf);
401                 printf(" type: %s\n", pr_type_strs[j]);
402             } else
403                 printf("there is NO reservation held\n");
404         } else if (PRIN_RFSTAT_SA == op->prin_sa) {
405             printf("  PR generation=0x%x\n", pr_gen);
406             bp = pr_buff + 8;
407             if (0 == add_len) {
408                 printf("  No full status descriptors\n");
409                 if (op->verbose)
410                 printf("  So there are no registered IT nexuses\n");
411             }
412             for (k = 0; k < add_len; k += num, bp += num) {
413                 add_desc_len = sg_get_unaligned_be32(bp + 20);
414                 num = 24 + add_desc_len;
415                 printf("    Key=0x%" PRIx64 "\n", sg_get_unaligned_be64(bp));
416                 if (bp[12] & 0x2)
417                     printf("      All target ports bit set\n");
418                 else {
419                     printf("      All target ports bit clear\n");
420                     printf("      Relative port address: 0x%x\n",
421                            sg_get_unaligned_be16(bp + 18));
422                 }
423                 if (bp[12] & 0x1) {
424                     printf("      << Reservation holder >>\n");
425                     j = ((bp[13] >> 4) & 0xf);
426                     if (0 == j)
427                         printf("      scope: LU_SCOPE, ");
428                     else
429                         printf("      scope: %d ", j);
430                     j = (bp[13] & 0xf);
431                     printf(" type: %s\n", pr_type_strs[j]);
432                 } else
433                     printf("      not reservation holder\n");
434                 if (add_desc_len > 0) {
435                     char b[1024];
436 
437                     printf("%s", sg_decode_transportid_str("      ", bp + 24,
438                                         add_desc_len, true, sizeof(b), b));
439                 }
440             }
441         }
442     }
443 fini:
444     if (free_pr_buff)
445         free(free_pr_buff);
446     return res;
447 }
448 
449 /* Compact the 2 dimensional transportid_arr into a one dimensional
450  * array in place returning the length. */
451 static int
compact_transportid_array(struct opts_t * op)452 compact_transportid_array(struct opts_t * op)
453 {
454     int k, off, protocol_id, len;
455     int compact_len = 0;
456     uint8_t * bp = op->transportid_arr;
457 
458     for (k = 0, off = 0; ((k < op->num_transportids) && (k < MX_TIDS));
459          ++k, off += MX_TID_LEN) {
460         protocol_id = bp[off] & 0xf;
461         if (TPROTO_ISCSI == protocol_id) {
462             len = sg_get_unaligned_be16(bp + off + 2) + 4;
463             if (len < 24)
464                 len = 24;
465             if (off > compact_len)
466                 memmove(bp + compact_len, bp + off, len);
467             compact_len += len;
468 
469         } else {
470             if (off > compact_len)
471                 memmove(bp + compact_len, bp + off, 24);
472             compact_len += 24;
473         }
474     }
475     return compact_len;
476 }
477 
478 static int
prout_work(int sg_fd,struct opts_t * op)479 prout_work(int sg_fd, struct opts_t * op)
480 {
481     int len, t_arr_len;
482     int res = 0;
483     uint8_t * pr_buff = NULL;
484     uint8_t * free_pr_buff = NULL;
485     char b[64];
486     char bb[80];
487 
488     t_arr_len = compact_transportid_array(op);
489     pr_buff = sg_memalign(op->alloc_len, 0 /* page aligned */, &free_pr_buff,
490                           false);
491     if (NULL == pr_buff) {
492         pr2serr("%s: unable to allocate %d bytes on heap\n", __func__,
493                 op->alloc_len);
494         return sg_convert_errno(ENOMEM);
495     }
496     sg_put_unaligned_be64(op->param_rk, pr_buff + 0);
497     sg_put_unaligned_be64(op->param_sark, pr_buff + 8);
498     if (op->param_alltgpt)
499         pr_buff[20] |= 0x4;
500     if (op->param_aptpl)
501         pr_buff[20] |= 0x1;
502     len = 24;
503     if (t_arr_len > 0) {
504         pr_buff[20] |= 0x8;     /* set SPEC_I_PT bit */
505         memcpy(&pr_buff[28], op->transportid_arr, t_arr_len);
506         len += (t_arr_len + 4);
507         sg_put_unaligned_be32((uint32_t)t_arr_len, pr_buff + 24);
508     }
509     res = sg_ll_persistent_reserve_out(sg_fd, op->prout_sa, 0 /* rq_scope */,
510                                        op->prout_type, pr_buff, len, true,
511                                        op->verbose);
512     if (res || op->verbose) {
513         if (op->prout_sa < num_prout_sa_strs)
514             snprintf(b, sizeof(b), "%s", prout_sa_strs[op->prout_sa]);
515         else
516             snprintf(b, sizeof(b), "service action=0x%x", op->prout_sa);
517         if (res) {
518             if (SG_LIB_CAT_INVALID_OP == res)
519                 pr2serr("PR out (%s): command not supported\n", b);
520             else if (SG_LIB_CAT_ILLEGAL_REQ == res)
521                 pr2serr("PR out (%s): bad field in cdb or parameter list "
522                         "(perhaps unsupported service action)\n", b);
523             else {
524                 sg_get_category_sense_str(res, sizeof(bb), bb, op->verbose);
525                 pr2serr("PR out (%s): %s\n", b, bb);
526             }
527             goto fini;
528         } else if (op->verbose)
529             pr2serr("PR out: command (%s) successful\n", b);
530     }
531 fini:
532     if (free_pr_buff)
533         free(free_pr_buff);
534     return res;
535 }
536 
537 static int
prout_reg_move_work(int sg_fd,struct opts_t * op)538 prout_reg_move_work(int sg_fd, struct opts_t * op)
539 {
540     int len, t_arr_len;
541     int res = 0;
542     uint8_t * pr_buff = NULL;
543     uint8_t * free_pr_buff = NULL;
544 
545     t_arr_len = compact_transportid_array(op);
546     pr_buff = sg_memalign(op->alloc_len, 0 /* page aligned */, &free_pr_buff,
547                           false);
548     if (NULL == pr_buff) {
549         pr2serr("%s: unable to allocate %d bytes on heap\n", __func__,
550                 op->alloc_len);
551         return sg_convert_errno(ENOMEM);
552     }
553     sg_put_unaligned_be64(op->param_rk, pr_buff + 0);
554     sg_put_unaligned_be64(op->param_sark, pr_buff + 8);
555     if (op->param_unreg)
556         pr_buff[17] |= 0x2;
557     if (op->param_aptpl)
558         pr_buff[17] |= 0x1;
559     sg_put_unaligned_be16(op->param_rtp, pr_buff + 18);
560     len = 24;
561     if (t_arr_len > 0) {
562         memcpy(&pr_buff[24], op->transportid_arr, t_arr_len);
563         len += t_arr_len;
564         sg_put_unaligned_be32((uint32_t)t_arr_len, pr_buff + 20);
565     }
566     res = sg_ll_persistent_reserve_out(sg_fd, PROUT_REG_MOVE_SA,
567                                        0 /* rq_scope */, op->prout_type,
568                                        pr_buff, len, true, op->verbose);
569     if (res) {
570        if (SG_LIB_CAT_INVALID_OP == res)
571             pr2serr("PR out (register and move): command not supported\n");
572         else if (SG_LIB_CAT_ILLEGAL_REQ == res)
573             pr2serr("PR out (register and move): bad field in cdb or "
574                     "parameter list (perhaps unsupported service action)\n");
575         else {
576             char bb[80];
577 
578             sg_get_category_sense_str(res, sizeof(bb), bb, op->verbose);
579             pr2serr("PR out (register and move): %s\n", bb);
580         }
581         goto fini;
582     } else if (op->verbose)
583         pr2serr("PR out: 'register and move' command successful\n");
584 fini:
585     if (free_pr_buff)
586         free(free_pr_buff);
587     return res;
588 }
589 
590 /* Decode various symbolic forms of TransportIDs into SPC-4 format.
591  * Returns 1 if one found, else returns 0. */
592 static int
decode_sym_transportid(const char * lcp,uint8_t * tidp)593 decode_sym_transportid(const char * lcp, uint8_t * tidp)
594 {
595     int k, j, n, b, c, len, alen;
596     unsigned int ui;
597     const char * ecp;
598     const char * isip;
599 
600     memset(tidp, 0, 24);
601     if ((0 == memcmp("sas,", lcp, 4)) || (0 == memcmp("SAS,", lcp, 4))) {
602         lcp += 4;
603         k = strspn(lcp, "0123456789aAbBcCdDeEfF");
604         if (16 != k) {
605             pr2serr("badly formed symbolic SAS TransportID: %s\n", lcp);
606             return 0;
607         }
608         tidp[0] = TPROTO_SAS;
609         for (k = 0, j = 0, b = 0; k < 16; ++k) {
610             c = lcp[k];
611             if (isdigit(c))
612                 n = c - 0x30;
613             else if (isupper(c))
614                 n = c - 0x37;
615             else
616                 n = c - 0x57;
617             if (k & 1) {
618                 tidp[4 + j] = b | n;
619                 ++j;
620             } else
621                 b = n << 4;
622         }
623         return 1;
624     } else if ((0 == memcmp("spi,", lcp, 4)) ||
625                (0 == memcmp("SPI,", lcp, 4))) {
626         lcp += 4;
627         if (2 != sscanf(lcp, "%d,%d", &b, &c)) {
628             pr2serr("badly formed symbolic SPI TransportID: %s\n", lcp);
629             return 0;
630         }
631         tidp[0] = TPROTO_SPI;
632         sg_put_unaligned_be16((uint16_t)b, tidp + 2);
633         sg_put_unaligned_be16((uint16_t)c, tidp + 6);
634         return 1;
635     } else if ((0 == memcmp("fcp,", lcp, 4)) ||
636                (0 == memcmp("FCP,", lcp, 4))) {
637         lcp += 4;
638         k = strspn(lcp, "0123456789aAbBcCdDeEfF");
639         if (16 != k) {
640             pr2serr("badly formed symbolic FCP TransportID: %s\n", lcp);
641             return 0;
642         }
643         tidp[0] = TPROTO_FCP;
644         for (k = 0, j = 0, b = 0; k < 16; ++k) {
645             c = lcp[k];
646             if (isdigit(c))
647                 n = c - 0x30;
648             else if (isupper(c))
649                 n = c - 0x37;
650             else
651                 n = c - 0x57;
652             if (k & 1) {
653                 tidp[8 + j] = b | n;
654                 ++j;
655             } else
656                 b = n << 4;
657         }
658         return 1;
659     } else if ((0 == memcmp("sbp,", lcp, 4)) ||
660                (0 == memcmp("SBP,", lcp, 4))) {
661         lcp += 4;
662         k = strspn(lcp, "0123456789aAbBcCdDeEfF");
663         if (16 != k) {
664             pr2serr("badly formed symbolic SBP TransportID: %s\n", lcp);
665             return 0;
666         }
667         tidp[0] = TPROTO_1394;
668         for (k = 0, j = 0, b = 0; k < 16; ++k) {
669             c = lcp[k];
670             if (isdigit(c))
671                 n = c - 0x30;
672             else if (isupper(c))
673                 n = c - 0x37;
674             else
675                 n = c - 0x57;
676             if (k & 1) {
677                 tidp[8 + j] = b | n;
678                 ++j;
679             } else
680                 b = n << 4;
681         }
682         return 1;
683     } else if ((0 == memcmp("srp,", lcp, 4)) ||
684                (0 == memcmp("SRP,", lcp, 4))) {
685         lcp += 4;
686         k = strspn(lcp, "0123456789aAbBcCdDeEfF");
687         if (16 != k) {
688             pr2serr("badly formed symbolic SRP TransportID: %s\n", lcp);
689             return 0;
690         }
691         tidp[0] = TPROTO_SRP;
692         for (k = 0, j = 0, b = 0; k < 32; ++k) {
693             c = lcp[k];
694             if (isdigit(c))
695                 n = c - 0x30;
696             else if (isupper(c))
697                 n = c - 0x37;
698             else
699                 n = c - 0x57;
700             if (k & 1) {
701                 tidp[8 + j] = b | n;
702                 ++j;
703             } else
704                 b = n << 4;
705         }
706         return 1;
707     } else if (0 == memcmp("iqn.", lcp, 4)) {
708         ecp = strpbrk(lcp, " \t");
709         isip = strstr(lcp, ",i,0x");
710         if (ecp && (isip > ecp))
711             isip = NULL;
712         len = ecp ? (ecp - lcp) : (int)strlen(lcp);
713         tidp[0] = TPROTO_ISCSI | (isip ? 0x40 : 0x0);
714         alen = len + 1; /* at least one trailing null */
715         if (alen < 20)
716             alen = 20;
717         else if (0 != (alen % 4))
718             alen = ((alen / 4) + 1) * 4;
719         if (alen > 241) { /* sam5r02.pdf A.2 (Annex) */
720             pr2serr("iSCSI name too long, alen=%d\n", alen);
721             return 0;
722         }
723         tidp[3] = alen & 0xff;
724         memcpy(tidp + 4, lcp, len);
725         return 1;
726     } else if ((0 == memcmp("sop,", lcp, 4)) ||
727                (0 == memcmp("SOP,", lcp, 4))) {
728         lcp += 4;
729         if (2 != sscanf(lcp, "%x", &ui)) {
730             pr2serr("badly formed symbolic SOP TransportID: %s\n", lcp);
731             return 0;
732         }
733         tidp[0] = TPROTO_SOP;
734         sg_put_unaligned_be16((uint16_t)ui, tidp + 2);
735         return 1;
736     }
737     pr2serr("unable to parse symbolic TransportID: %s\n", lcp);
738     return 0;
739 }
740 
741 /* Read one or more TransportIDs from the given file or stdin. Reads from
742  * stdin when 'fnp' is NULL. Returns 0 if successful, 1 otherwise. */
743 static int
decode_file_tids(const char * fnp,struct opts_t * op)744 decode_file_tids(const char * fnp, struct opts_t * op)
745 {
746     bool split_line;
747     int in_len, k, j, m;
748     int off = 0;
749     int num = 0;
750     unsigned int h;
751     FILE * fp = stdin;
752     const char * lcp;
753     uint8_t * tid_arr = op->transportid_arr;
754     char line[1024];
755     char carry_over[4];
756 
757     if (fnp) {
758         fp = fopen(fnp, "r");
759         if (NULL == fp) {
760             pr2serr("%s: unable to open %s\n", __func__, fnp);
761             return 1;
762         }
763     }
764     carry_over[0] = 0;
765     for (j = 0, off = 0; j < 512; ++j) {
766         if (NULL == fgets(line, sizeof(line), fp))
767             break;
768         in_len = strlen(line);
769         if (in_len > 0) {
770             if ('\n' == line[in_len - 1]) {
771                 --in_len;
772                 line[in_len] = '\0';
773                 split_line = false;
774             } else
775                 split_line = true;
776         }
777         if (in_len < 1) {
778             carry_over[0] = 0;
779             continue;
780         }
781         if (carry_over[0]) {
782             if (isxdigit((uint8_t)line[0])) {
783                 carry_over[1] = line[0];
784                 carry_over[2] = '\0';
785                 if (1 == sscanf(carry_over, "%x", &h))
786                     tid_arr[off - 1] = h;       /* back up and overwrite */
787                 else {
788                     pr2serr("%s: carry_over error ['%s'] around line %d\n",
789                             __func__, carry_over, j + 1);
790                     goto bad;
791                 }
792                 lcp = line + 1;
793                 --in_len;
794             } else
795                 lcp = line;
796             carry_over[0] = 0;
797         } else
798             lcp = line;
799         m = strspn(lcp, " \t");
800         if (m == in_len)
801             continue;
802         lcp += m;
803         in_len -= m;
804         if ('#' == *lcp)
805             continue;
806         if (decode_sym_transportid(lcp, tid_arr + off))
807             goto my_cont_a;
808         k = strspn(lcp, "0123456789aAbBcCdDeEfF ,\t");
809         if ((k < in_len) && ('#' != lcp[k])) {
810             pr2serr("%s: syntax error at line %d, pos %d\n", __func__, j + 1,
811                     m + k + 1);
812             goto bad;
813         }
814         for (k = 0; k < 1024; ++k) {
815             if (1 == sscanf(lcp, "%x", &h)) {
816                 if (h > 0xff) {
817                     pr2serr("%s: hex number larger than 0xff in line %d, pos "
818                             "%d\n", __func__, j + 1, (int)(lcp - line + 1));
819                     goto bad;
820                 }
821                 if (split_line && (1 == strlen(lcp))) {
822                     /* single trailing hex digit might be a split pair */
823                     carry_over[0] = *lcp;
824                 }
825                 if ((off + k) >= (int)sizeof(op->transportid_arr)) {
826                     pr2serr("%s: array length exceeded\n", __func__);
827                     goto bad;
828                 }
829                 op->transportid_arr[off + k] = h;/* keep code checker happy */
830                 lcp = strpbrk(lcp, " ,\t");
831                 if (NULL == lcp)
832                     break;
833                 lcp += strspn(lcp, " ,\t");
834                 if ('\0' == *lcp)
835                     break;
836             } else {
837                 if ('#' == *lcp) {
838                     --k;
839                     break;
840                 }
841                 pr2serr("%s: error in line %d, at pos %d\n", __func__, j + 1,
842                         (int)(lcp - line + 1));
843                 goto bad;
844             }
845         }
846 my_cont_a:
847         off += MX_TID_LEN;
848         if (off >= (MX_TIDS * MX_TID_LEN)) {
849             pr2serr("%s: array length exceeded\n", __func__);
850             goto bad;
851         }
852         ++num;
853     }
854     op->num_transportids = num;
855    if (fnp)
856         fclose(fp);
857     return 0;
858 
859 bad:
860    if (fnp)
861         fclose(fp);
862    return 1;
863 }
864 
865 /* Build transportid array which may contain one or more TransportIDs.
866  * A single TransportID can appear on the command line either as a list of
867  * comma (or single space) separated ASCII hex bytes, or in some transport
868  * protocol specific form (e.g. "sas,5000c50005b32001"). One or more
869  * TransportIDs may be given in a file (syntax: "file=<name>") or read from
870  * stdin in (when "-" is given). Fuller description in manpage of
871  * sg_persist(8). Returns 0 if successful, else 1 .
872  */
873 static int
build_transportid(const char * inp,struct opts_t * op)874 build_transportid(const char * inp, struct opts_t * op)
875 {
876     int in_len;
877     int k = 0;
878     unsigned int h;
879     const char * lcp;
880     uint8_t * tid_arr = op->transportid_arr;
881     char * cp;
882     char * c2p;
883 
884     lcp = inp;
885     in_len = strlen(inp);
886     if (0 == in_len) {
887         op->num_transportids = 0;
888     }
889     if (('-' == inp[0]) ||
890         (0 == memcmp("file=", inp, 5)) ||
891         (0 == memcmp("FILE=", inp, 5))) {
892         if ('-' == inp[0])
893             lcp = NULL;         /* read from stdin */
894         else
895             lcp = inp + 5;      /* read from given file */
896         return decode_file_tids(lcp, op);
897     } else {        /* TransportID given directly on command line */
898         if (decode_sym_transportid(lcp, tid_arr))
899             goto my_cont_b;
900         k = strspn(inp, "0123456789aAbBcCdDeEfF, ");
901         if (in_len != k) {
902             pr2serr("%s: error at pos %d\n", __func__, k + 1);
903             return 1;
904         }
905         for (k = 0; k < (int)sizeof(op->transportid_arr); ++k) {
906             if (1 == sscanf(lcp, "%x", &h)) {
907                 if (h > 0xff) {
908                     pr2serr("%s: hex number larger than 0xff at pos %d\n",
909                             __func__, (int)(lcp - inp + 1));
910                     return 1;
911                 }
912                 tid_arr[k] = h;
913                 cp = (char *)strchr(lcp, ',');
914                 c2p = (char *)strchr(lcp, ' ');
915                 if (NULL == cp)
916                     cp = c2p;
917                 if (NULL == cp)
918                     break;
919                 if (c2p && (c2p < cp))
920                     cp = c2p;
921                 lcp = cp + 1;
922             } else {
923                 pr2serr("%s: error at pos %d\n", __func__,
924                         (int)(lcp - inp + 1));
925                 return 1;
926             }
927         }
928 my_cont_b:
929         op->num_transportids = 1;
930         if (k >= (int)sizeof(op->transportid_arr)) {
931             pr2serr("%s: array length exceeded\n", __func__);
932             return 1;
933         }
934     }
935     return 0;
936 }
937 
938 
939 int
main(int argc,char * argv[])940 main(int argc, char * argv[])
941 {
942     bool got_maxlen, ok;
943     bool flagged = false;
944     bool want_prin = false;
945     bool want_prout = false;
946     int c, k, res;
947     int help = 0;
948     int num_prin_sa = 0;
949     int num_prout_sa = 0;
950     int num_prout_param = 0;
951     int peri_type = 0;
952     int sg_fd = -1;
953     int ret = 0;
954     const char * cp;
955     const char * device_name = NULL;
956     struct opts_t * op;
957     char buff[48];
958     struct opts_t opts;
959     struct sg_simple_inquiry_resp inq_resp;
960 
961     op = &opts;
962     memset(op, 0, sizeof(opts));
963     op->pr_in = true;
964     op->prin_sa = -1;
965     op->prout_sa = -1;
966     op->inquiry = true;
967     op->alloc_len = MX_ALLOC_LEN;
968 
969     while (1) {
970         int option_index = 0;
971 
972         c = getopt_long(argc, argv,
973                         "AcCd:GHhiIkK:l:Lm:MnoPQ:rRsS:T:UvVX:yYzZ",
974                         long_options, &option_index);
975         if (c == -1)
976             break;
977 
978         switch (c) {
979         case 'A':
980             op->prout_sa = PROUT_PREE_AB_SA;
981             ++num_prout_sa;
982             break;
983         case 'c':
984             op->prin_sa = PRIN_RCAP_SA;
985             ++num_prin_sa;
986             break;
987         case 'C':
988             op->prout_sa = PROUT_CLEAR_SA;
989             ++num_prout_sa;
990             break;
991         case 'd':
992             device_name = optarg;
993             break;
994         case 'G':
995             op->prout_sa = PROUT_REG_SA;
996             ++num_prout_sa;
997             break;
998         case 'h':
999             ++help;
1000             break;
1001         case 'H':
1002             ++op->hex;
1003             break;
1004         case 'i':
1005             want_prin = true;
1006             break;
1007         case 'I':
1008             op->prout_sa = PROUT_REG_IGN_SA;
1009             ++num_prout_sa;
1010             break;
1011         case 'k':
1012             op->prin_sa = PRIN_RKEY_SA;
1013             ++num_prin_sa;
1014             break;
1015         case 'K':
1016             if (1 != sscanf(optarg, "%" SCNx64 "", &op->param_rk)) {
1017                 pr2serr("bad argument to '--param-rk'\n");
1018                 return SG_LIB_SYNTAX_ERROR;
1019             }
1020             ++num_prout_param;
1021             break;
1022         case 'm':       /* --maxlen= and --alloc_length= are similar */
1023         case 'l':
1024             got_maxlen = ('m' == c);
1025             cp =  (got_maxlen ? "maxlen" : "alloc-length");
1026             if (got_maxlen) {
1027                 k = sg_get_num(optarg);
1028                 ok = (-1 != k);
1029                 op->alloc_len = (unsigned int)k;
1030             } else
1031                 ok = (1 == sscanf(optarg, "%x", &op->alloc_len));
1032             if (! ok) {
1033                 pr2serr("bad argument to '--%s'\n", cp);
1034                 return SG_LIB_SYNTAX_ERROR;
1035             } else if (MX_ALLOC_LEN < op->alloc_len) {
1036                 pr2serr("'--%s' argument exceeds maximum value (%d)\n", cp,
1037                         MX_ALLOC_LEN);
1038                 return SG_LIB_SYNTAX_ERROR;
1039             }
1040             break;
1041         case 'L':
1042             op->prout_sa = PROUT_REL_SA;
1043             ++num_prout_sa;
1044             break;
1045         case 'M':
1046             op->prout_sa = PROUT_REG_MOVE_SA;
1047             ++num_prout_sa;
1048             break;
1049         case 'n':
1050             op->inquiry = false;
1051             break;
1052         case 'o':
1053             want_prout = true;
1054             break;
1055         case 'P':
1056             op->prout_sa = PROUT_PREE_SA;
1057             ++num_prout_sa;
1058             break;
1059         case 'Q':
1060             if (1 != sscanf(optarg, "%x", &op->param_rtp)) {
1061                 pr2serr("bad argument to '--relative-target-port'\n");
1062                 return SG_LIB_SYNTAX_ERROR;
1063             }
1064             if (op->param_rtp > 0xffff) {
1065                 pr2serr("argument to '--relative-target-port' 0 to ffff "
1066                         "inclusive\n");
1067                 return SG_LIB_SYNTAX_ERROR;
1068             }
1069             ++num_prout_param;
1070             break;
1071         case 'r':
1072             op->prin_sa = PRIN_RRES_SA;
1073             ++num_prin_sa;
1074             break;
1075         case 'R':
1076             op->prout_sa = PROUT_RES_SA;
1077             ++num_prout_sa;
1078             break;
1079         case 's':
1080             op->prin_sa = PRIN_RFSTAT_SA;
1081             ++num_prin_sa;
1082             break;
1083         case 'S':
1084             if (1 != sscanf(optarg, "%" SCNx64 "", &op->param_sark)) {
1085                 pr2serr("bad argument to '--param-sark'\n");
1086                 return SG_LIB_SYNTAX_ERROR;
1087             }
1088             ++num_prout_param;
1089             break;
1090         case 'T':
1091             if (1 != sscanf(optarg, "%x", &op->prout_type)) {
1092                 pr2serr("bad argument to '--prout-type'\n");
1093                 return SG_LIB_SYNTAX_ERROR;
1094             }
1095             ++num_prout_param;
1096             break;
1097         case 'U':
1098             op->param_unreg = true;
1099             break;
1100         case 'v':
1101             op->verbose_given = true;
1102             ++op->verbose;
1103             break;
1104         case 'V':
1105             op->version_given = true;
1106             break;
1107         case 'X':
1108             if (0 != build_transportid(optarg, op)) {
1109                 pr2serr("bad argument to '--transport-id'\n");
1110                 return SG_LIB_SYNTAX_ERROR;
1111             }
1112             ++num_prout_param;
1113             break;
1114         case 'y':       /* differentiates -y, -yy and -yyy */
1115             if (! op->readwrite_force) {
1116                 if (op->readonly) {
1117                     op->readwrite_force = true;
1118                     op->readonly = false;
1119                 } else
1120                     op->readonly = true;
1121             }
1122             break;
1123         case 'Y':
1124             op->param_alltgpt = true;
1125             ++num_prout_param;
1126             break;
1127         case 'z':
1128             op->prout_sa = PROUT_REPL_LOST_SA;
1129             ++num_prout_sa;
1130             break;
1131         case 'Z':
1132             op->param_aptpl = true;
1133             ++num_prout_param;
1134             break;
1135         case '?':
1136             usage(1);
1137             return 0;
1138         default:
1139             pr2serr("unrecognised switch code 0x%x ??\n", c);
1140             usage(1);
1141             return SG_LIB_SYNTAX_ERROR;
1142         }
1143     }
1144     if (optind < argc) {
1145         if (NULL == device_name) {
1146             device_name = argv[optind];
1147             ++optind;
1148         }
1149         if (optind < argc) {
1150             for (; optind < argc; ++optind)
1151                 pr2serr("Unexpected extra argument: %s\n", argv[optind]);
1152             usage(1);
1153             return SG_LIB_SYNTAX_ERROR;
1154         }
1155     }
1156     if (help > 0) {
1157         usage(help);
1158         return 0;
1159     }
1160 #ifdef DEBUG
1161     pr2serr("In DEBUG mode, ");
1162     if (op->verbose_given && op->version_given) {
1163         pr2serr("but override: '-vV' given, zero verbose and "
1164                 "continue\n");
1165         op->verbose_given = false;
1166         op->version_given = false;
1167         op->verbose = 0;
1168     } else if (! op->verbose_given) {
1169         pr2serr("set '-vv'\n");
1170         op->verbose = 2;
1171     } else
1172         pr2serr("keep verbose=%d\n", op->verbose);
1173 #else
1174     if (op->verbose_given && op->version_given)
1175         pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
1176 #endif
1177     if (op->version_given) {
1178         pr2serr("version: %s\n", version_str);
1179         return 0;
1180     }
1181 
1182     if (NULL == device_name) {
1183         pr2serr("No device name given\n");
1184         usage(1);
1185         return SG_LIB_SYNTAX_ERROR;
1186     }
1187     if (want_prout && want_prin) {
1188         pr2serr("choose '--in' _or_ '--out' (not both)\n");
1189         usage(1);
1190         return SG_LIB_CONTRADICT;
1191     } else if (want_prout) { /* syntax check on PROUT arguments */
1192         op->pr_in = false;
1193         if ((1 != num_prout_sa) || (0 != num_prin_sa)) {
1194             pr2serr(">> For Persistent Reserve Out one and only one "
1195                     "appropriate\n>> service action must be chosen (e.g. "
1196                     "'--register')\n");
1197             return SG_LIB_CONTRADICT;
1198         }
1199     } else { /* syntax check on PRIN arguments */
1200         if (num_prout_sa > 0) {
1201             pr2serr(">> When a service action for Persistent Reserve Out "
1202                     "is chosen the\n>> '--out' option must be given (as a "
1203                     "safeguard)\n");
1204             return SG_LIB_CONTRADICT;
1205         }
1206         if (0 == num_prin_sa) {
1207             pr2serr(">> No service action given; assume Persistent Reserve "
1208                     "In command\n>> with Read Keys service action\n");
1209             op->prin_sa = 0;
1210             ++num_prin_sa;
1211         } else if (num_prin_sa > 1)  {
1212             pr2serr("Too many service actions given; choose one only\n");
1213             usage(1);
1214             return SG_LIB_CONTRADICT;
1215         }
1216     }
1217     if ((op->param_unreg || op->param_rtp) &&
1218         (PROUT_REG_MOVE_SA != op->prout_sa)) {
1219         pr2serr("--unreg or --relative-target-port only useful with "
1220                 "--register-move\n");
1221         usage(1);
1222         return SG_LIB_CONTRADICT;
1223     }
1224     if ((PROUT_REG_MOVE_SA == op->prout_sa) &&
1225         (1 != op->num_transportids)) {
1226         pr2serr("with --register-move one (and only one) --transport-id "
1227                 "should be given\n");
1228         usage(1);
1229         return SG_LIB_CONTRADICT;
1230     }
1231     if (((PROUT_RES_SA == op->prout_sa) ||
1232          (PROUT_REL_SA == op->prout_sa) ||
1233          (PROUT_PREE_SA == op->prout_sa) ||
1234          (PROUT_PREE_AB_SA == op->prout_sa)) &&
1235         (0 == op->prout_type)) {
1236         pr2serr("warning>>> --prout-type probably needs to be given\n");
1237     }
1238     if ((op->verbose > 2) && op->num_transportids) {
1239         char b[1024];
1240         uint8_t * bp;
1241 
1242         pr2serr("number of tranport-ids decoded from command line (or "
1243                 "stdin): %d\n", op->num_transportids);
1244         pr2serr("  Decode given transport-ids:\n");
1245         for (k = 0; k < op->num_transportids; ++k) {
1246             bp = op->transportid_arr + (MX_TID_LEN * k);
1247             printf("%s", sg_decode_transportid_str("      ", bp, MX_TID_LEN,
1248                                                    true, sizeof(b), b));
1249         }
1250     }
1251 
1252     if (op->inquiry) {
1253         if ((sg_fd = sg_cmds_open_device(device_name, true /* ro */,
1254                                          op->verbose)) < 0) {
1255             pr2serr("%s: error opening file (ro): %s: %s\n", ME,
1256                     device_name, safe_strerror(-sg_fd));
1257             ret = sg_convert_errno(-sg_fd);
1258             flagged = true;
1259             goto fini;
1260         }
1261         ret = sg_simple_inquiry(sg_fd, &inq_resp, true, op->verbose);
1262         if (0 == ret) {
1263             printf("  %.8s  %.16s  %.4s\n", inq_resp.vendor, inq_resp.product,
1264                    inq_resp.revision);
1265             peri_type = inq_resp.peripheral_type;
1266             cp = sg_get_pdt_str(peri_type, sizeof(buff), buff);
1267             if (strlen(cp) > 0)
1268                 printf("  Peripheral device type: %s\n", cp);
1269             else
1270                 printf("  Peripheral device type: 0x%x\n", peri_type);
1271         } else {
1272             printf("%s: SCSI INQUIRY failed on %s", ME, device_name);
1273             if (ret < 0) {
1274                 ret = -ret;
1275                 printf(": %s\n", safe_strerror(ret));
1276                 ret = sg_convert_errno(ret);
1277             } else
1278                 printf("\n");
1279             flagged = true;
1280             goto fini;
1281         }
1282         res = sg_cmds_close_device(sg_fd);
1283         if (res < 0)
1284             pr2serr("%s: sg_cmds_close_device() failed res=%d\n", ME, res);
1285     }
1286 
1287     if (! op->readwrite_force) {
1288         cp = getenv(SG_PERSIST_IN_RDONLY);
1289         if (cp && op->pr_in)
1290             op->readonly = true;  /* SG_PERSIST_IN_RDONLY overrides default
1291                                      which is open(RW) */
1292     } else
1293         op->readonly = false;      /* '-yy' force open(RW) */
1294     sg_fd = sg_cmds_open_device(device_name, op->readonly, op->verbose);
1295     if (sg_fd < 0) {
1296         pr2serr("%s: error opening file %s (r%s): %s\n", ME, device_name,
1297                 (op->readonly ? "o" : "w"), safe_strerror(-sg_fd));
1298         ret = sg_convert_errno(-sg_fd);
1299         flagged = true;
1300         goto fini;
1301     }
1302 
1303     if (op->pr_in)
1304         ret = prin_work(sg_fd, op);
1305     else if (PROUT_REG_MOVE_SA == op->prout_sa)
1306         ret = prout_reg_move_work(sg_fd, op);
1307     else /* PROUT commands other than 'register and move' */
1308         ret = prout_work(sg_fd, op);
1309 
1310 fini:
1311     if (ret && (0 == op->verbose) && (! flagged)) {
1312         if (! sg_if_can2stderr("sg_persist failed: ", ret))
1313             pr2serr("Some error occurred [%d]\n", ret);
1314     }
1315     if (sg_fd >= 0) {
1316         res = sg_cmds_close_device(sg_fd);
1317         if (res < 0) {
1318             pr2serr("close error: %s\n", safe_strerror(-res));
1319             if (0 == ret)
1320                 ret = sg_convert_errno(-res);
1321         }
1322     }
1323     return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
1324 }
1325