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