1 /*
2 * Copyright (c) 2011-2018 Hannes Reinecke, SUSE Labs
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 #include <unistd.h>
8 #include <fcntl.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <stdarg.h>
12 #include <stdbool.h>
13 #include <string.h>
14 #include <errno.h>
15 #include <getopt.h>
16 #define __STDC_FORMAT_MACROS 1
17 #include <inttypes.h>
18
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22 #include "sg_lib.h"
23 #include "sg_cmds_basic.h"
24 #include "sg_cmds_extra.h"
25 #include "sg_unaligned.h"
26 #include "sg_pr2serr.h"
27
28 /*
29 * A utility program for the Linux OS SCSI subsystem.
30 * Copyright (C) 2004-2010 D. Gilbert
31 * This program is free software; you can redistribute it and/or modify
32 * it under the terms of the GNU General Public License as published by
33 * the Free Software Foundation; either version 2, or (at your option)
34 * any later version.
35 *
36 * SPDX-License-Identifier: GPL-2.0-or-later
37 *
38 * This program issues the SCSI command RECEIVE COPY RESULTS to a given
39 * SCSI device.
40 * It sends the command with the service action passed as the sa argument,
41 * and the optional list identifier passed as the list_id argument.
42 */
43
44 static const char * version_str = "1.23 20180625";
45
46
47 #define MAX_XFER_LEN 10000
48
49
50 #define ME "sg_copy_results: "
51
52 #define EBUFF_SZ 256
53
54 struct descriptor_type {
55 int code;
56 char desc[124];
57 };
58
59 struct descriptor_type target_descriptor_codes[] = {
60 { 0xe0, "Fibre Channel N_Port_Name"},
61 { 0xe1, "Fibre Channel N_port_ID"},
62 { 0xe2, "Fibre Channesl N_port_ID with N_Port_Name checking"},
63 { 0xe3, "Parallel Interface T_L" },
64 { 0xe4, "Identification descriptor" },
65 { 0xe5, "IPv4" },
66 { 0xe6, "Alias" },
67 { 0xe7, "RDMA" },
68 { 0xe8, "IEEE 1395 EUI-64" },
69 { 0xe9, "SAS Serial SCSI Protocol" },
70 { 0xea, "IPv6" },
71 { 0xeb, "IP Copy Service" },
72 { -1, "" }
73 };
74
75 struct descriptor_type segment_descriptor_codes [] = {
76 { 0x00, "Copy from block device to stream device" },
77 { 0x01, "Copy from stream device to block device" },
78 { 0x02, "Copy from block device to block device" },
79 { 0x03, "Copy from stream device to stream device" },
80 { 0x04, "Copy inline data to stream device" },
81 { 0x05, "Copy embedded data to stream device" },
82 { 0x06, "Read from stream device and discard" },
83 { 0x07, "Verify block or stream device operation" },
84 { 0x08, "Copy block device with offset to stream device" },
85 { 0x09, "Copy stream device to block device with offset" },
86 { 0x0A, "Copy block device with offset to block device with offset" },
87 { 0x0B, "Copy from block device to stream device "
88 "and hold a copy of processed data for the application client" },
89 { 0x0C, "Copy from stream device to block device "
90 "and hold a copy of processed data for the application client" },
91 { 0x0D, "Copy from block device to block device "
92 "and hold a copy of processed data for the application client" },
93 { 0x0E, "Copy from stream device to stream device "
94 "and hold a copy of processed data for the application client" },
95 { 0x0F, "Read from stream device "
96 "and hold a copy of processed data for the application client" },
97 { 0x10, "Write filemarks to sequential-access device" },
98 { 0x11, "Space records or filemarks on sequential-access device" },
99 { 0x12, "Locate on sequential-access device" },
100 { 0x13, "Image copy from sequential-access device to sequential-access "
101 "device" },
102 { 0x14, "Register persistent reservation key" },
103 { 0x15, "Third party persistent reservations source I_T nexus" },
104 { -1, "" }
105 };
106
107
108 static void
scsi_failed_segment_details(uint8_t * rcBuff,unsigned int rcBuffLen)109 scsi_failed_segment_details(uint8_t *rcBuff, unsigned int rcBuffLen)
110 {
111 int senseLen;
112 unsigned int len;
113 char senseBuff[1024];
114
115 if (rcBuffLen < 4) {
116 pr2serr(" <<not enough data to procedd report>>\n");
117 return;
118 }
119 len = sg_get_unaligned_be32(rcBuff + 0);
120 if (len + 4 > rcBuffLen) {
121 pr2serr(" <<report len %d > %d too long for internal buffer, output "
122 "truncated\n", len, rcBuffLen);
123 }
124 if (len < 52) {
125 pr2serr(" <<no segment details, response data length %d\n", len);
126 return;
127 }
128 printf("Receive copy results (failed segment details):\n");
129 printf(" Extended copy command status: %d\n", rcBuff[56]);
130 senseLen = sg_get_unaligned_be16(rcBuff + 58);
131 sg_get_sense_str(" ", &rcBuff[60], senseLen, 0, 1024, senseBuff);
132 printf("%s", senseBuff);
133 }
134
135 static void
scsi_copy_status(uint8_t * rcBuff,unsigned int rcBuffLen)136 scsi_copy_status(uint8_t *rcBuff, unsigned int rcBuffLen)
137 {
138 unsigned int len;
139
140 if (rcBuffLen < 4) {
141 pr2serr(" <<not enough data to proceed report>>\n");
142 return;
143 }
144 len = sg_get_unaligned_be32(rcBuff + 0);
145 if (len + 4 > rcBuffLen) {
146 pr2serr(" <<report len %d > %d too long for internal buffer, output "
147 "truncated\n", len, rcBuffLen);
148 }
149 printf("Receive copy results (copy status):\n");
150 printf(" Held data discarded: %s\n", rcBuff[4] & 0x80 ? "Yes":"No");
151 printf(" Copy manager status: ");
152 switch (rcBuff[4] & 0x7f) {
153 case 0:
154 printf("Operation in progress\n");
155 break;
156 case 1:
157 printf("Operation completed without errors\n");
158 break;
159 case 2:
160 printf("Operation completed with errors\n");
161 break;
162 default:
163 printf("Unknown/Reserved\n");
164 break;
165 }
166 printf(" Segments processed: %u\n", sg_get_unaligned_be16(rcBuff + 5));
167 printf(" Transfer count units: %u\n", rcBuff[7]);
168 printf(" Transfer count: %u\n", sg_get_unaligned_be32(rcBuff + 8));
169 }
170
171 static void
scsi_operating_parameters(uint8_t * rcBuff,unsigned int rcBuffLen)172 scsi_operating_parameters(uint8_t *rcBuff, unsigned int rcBuffLen)
173 {
174 unsigned int len, n;
175
176 len = sg_get_unaligned_be32(rcBuff + 0);
177 if (len + 4 > rcBuffLen) {
178 pr2serr(" <<report len %d > %d too long for internal buffer, output "
179 "truncated\n", len, rcBuffLen);
180 }
181 printf("Receive copy results (report operating parameters):\n");
182 printf(" Supports no list identifier (SNLID): %s\n",
183 rcBuff[4] & 1 ? "yes" : "no");
184 n = sg_get_unaligned_be16(rcBuff + 8);
185 printf(" Maximum target descriptor count: %u\n", n);
186 n = sg_get_unaligned_be16(rcBuff + 10);
187 printf(" Maximum segment descriptor count: %u\n", n);
188 n = sg_get_unaligned_be32(rcBuff + 12);
189 printf(" Maximum descriptor list length: %u bytes\n", n);
190 n = sg_get_unaligned_be32(rcBuff + 16);
191 printf(" Maximum segment length: %u bytes\n", n);
192 n = sg_get_unaligned_be32(rcBuff + 20);
193 if (n == 0) {
194 printf(" Inline data not supported\n");
195 } else {
196 printf(" Maximum inline data length: %u bytes\n", n);
197 }
198 n = sg_get_unaligned_be32(rcBuff + 24);
199 printf(" Held data limit: %u bytes\n", n);
200 n = sg_get_unaligned_be32(rcBuff + 28);
201 printf(" Maximum stream device transfer size: %u bytes\n", n);
202 n = sg_get_unaligned_be16(rcBuff + 34);
203 printf(" Total concurrent copies: %u\n", n);
204 printf(" Maximum concurrent copies: %u\n", rcBuff[36]);
205 if (rcBuff[37] > 30)
206 printf(" Data segment granularity: 2**%u bytes\n", rcBuff[37]);
207 else
208 printf(" Data segment granularity: %u bytes\n",
209 (unsigned int)(1 << rcBuff[37]));
210 if (rcBuff[38] > 30)
211 printf(" Inline data granularity: %u bytes\n", rcBuff[38]);
212 else
213 printf(" Inline data granularity: %u bytes\n",
214 (unsigned int)(1 << rcBuff[38]));
215 if (rcBuff[39] > 30)
216 printf(" Held data granularity: 2**%u bytes\n", rcBuff[39]);
217 else
218 printf(" Held data granularity: %u bytes\n",
219 (unsigned int)(1 << rcBuff[39]));
220
221 printf(" Implemented descriptor list:\n");
222 for (n = 0; n < rcBuff[43]; n++) {
223 int code = rcBuff[44 + n];
224
225 if (code < 0x16) {
226 struct descriptor_type *seg_desc = segment_descriptor_codes;
227 while (strlen(seg_desc->desc)) {
228 if (seg_desc->code == code)
229 break;
230 seg_desc++;
231 }
232 printf(" Segment descriptor 0x%02x: %s\n", code,
233 strlen(seg_desc->desc) ? seg_desc->desc : "Reserved");
234 } else if (code < 0xc0) {
235 printf(" Segment descriptor 0x%02x: Reserved\n", code);
236 } else if (code < 0xe0) {
237 printf(" Vendor specific descriptor 0x%02x\n", code);
238 } else {
239 struct descriptor_type *tgt_desc = target_descriptor_codes;
240
241 while (strlen(tgt_desc->desc)) {
242 if (tgt_desc->code == code)
243 break;
244 tgt_desc++;
245 }
246 printf(" Target descriptor 0x%02x: %s\n", code,
247 strlen(tgt_desc->desc) ? tgt_desc->desc : "Reserved");
248 }
249 }
250 printf("\n");
251 }
252
253 static struct option long_options[] = {
254 {"failed", no_argument, 0, 'f'},
255 {"help", no_argument, 0, 'h'},
256 {"hex", no_argument, 0, 'H'},
257 {"list_id", required_argument, 0, 'l'},
258 {"list-id", required_argument, 0, 'l'},
259 {"params", no_argument, 0, 'p'},
260 {"readonly", no_argument, 0, 'R'},
261 {"receive", no_argument, 0, 'r'},
262 {"status", no_argument, 0, 's'},
263 {"verbose", no_argument, 0, 'v'},
264 {"version", no_argument, 0, 'V'},
265 {"xfer_len", required_argument, 0, 'x'},
266 {0, 0, 0, 0},
267 };
268
269 static void
usage()270 usage()
271 {
272 pr2serr("Usage: "
273 "sg_copy_results [--failed|--params|--receive|--status] [--help]\n"
274 " [--hex] [--list_id=ID] [--readonly] "
275 "[--verbose]\n"
276 " [--version] [--xfer_len=BTL] DEVICE\n"
277 " where:\n"
278 " --failed|-f use FAILED SEGMENT DETAILS service "
279 "action\n"
280 " --help|-h print out usage message\n"
281 " --hex|-H print out response buffer in hex\n"
282 " --list_id=ID|-l ID list identifier (default: 0)\n"
283 " --params|-p use OPERATING PARAMETERS service "
284 "action\n"
285 " --readonly|-R open DEVICE read-only (def: read-write)\n"
286 " --receive|-r use RECEIVE DATA service action\n"
287 " --status|-s use COPY STATUS service action\n"
288 " --verbose|-v increase verbosity\n"
289 " --version|-V print version string then exit\n"
290 " --xfer_len=BTL|-x BTL byte transfer length (< 10000) "
291 "(default:\n"
292 " 520 bytes)\n\n"
293 "Performs a SCSI RECEIVE COPY RESULTS command. Returns the "
294 "response as\nspecified by the service action parameters.\n"
295 );
296 }
297
298 static const char * rec_copy_name_arr[] = {
299 "Receive copy status(LID1)",
300 "Receive copy data(LID1)",
301 "Receive copy [0x2]",
302 "Receive copy operating parameters",
303 "Receive copy failure details(LID1)",
304 };
305
306 int
main(int argc,char * argv[])307 main(int argc, char * argv[])
308 {
309 bool do_hex = false;
310 bool o_readonly = false;
311 bool verbose_given = false;
312 bool version_given = false;
313 int res, c, k;
314 int ret = 1;
315 int sa = 3;
316 int sg_fd = -1;
317 int verbose = 0;
318 int xfer_len = 520;
319 uint32_t list_id = 0;
320 const char * cp;
321 uint8_t * cpResultBuff = NULL;
322 uint8_t * free_cprb = NULL;
323 const char * device_name = NULL;
324 char file_name[256];
325
326 memset(file_name, 0, sizeof file_name);
327 while (1) {
328 int option_index = 0;
329
330 c = getopt_long(argc, argv, "fhHl:prRsvVx:", long_options,
331 &option_index);
332 if (c == -1)
333 break;
334
335 switch (c) {
336 case 'f':
337 sa = 4;
338 break;
339 case 'H':
340 do_hex = true;
341 break;
342 case 'h':
343 case '?':
344 usage();
345 return 0;
346 case 'l':
347 k = sg_get_num(optarg);
348 if (-1 == k) {
349 pr2serr("bad argument to '--list_id'\n");
350 return SG_LIB_SYNTAX_ERROR;
351 }
352 list_id = (uint32_t)k;
353 break;
354 case 'p':
355 sa = 3;
356 break;
357 case 'r':
358 sa = 1;
359 break;
360 case 'R':
361 o_readonly = true;
362 break;
363 case 's':
364 sa = 0;
365 break;
366 case 'v':
367 ++verbose;
368 verbose_given = true;
369 break;
370 case 'V':
371 version_given = true;
372 break;
373 case 'x':
374 xfer_len = sg_get_num(optarg);
375 if (-1 == xfer_len) {
376 pr2serr("bad argument to '--xfer_len'\n");
377 return SG_LIB_SYNTAX_ERROR;
378 }
379 break;
380 default:
381 pr2serr("unrecognised option code 0x%x ??\n", c);
382 usage();
383 return SG_LIB_SYNTAX_ERROR;
384 }
385 }
386 if (optind < argc) {
387 if (NULL == device_name) {
388 device_name = argv[optind];
389 ++optind;
390 }
391 if (optind < argc) {
392 for (; optind < argc; ++optind)
393 pr2serr("Unexpected extra argument: %s\n", argv[optind]);
394 usage();
395 return SG_LIB_SYNTAX_ERROR;
396 }
397 }
398
399 #ifdef DEBUG
400 pr2serr("In DEBUG mode, ");
401 if (verbose_given && version_given) {
402 pr2serr("but override: '-vV' given, zero verbose and continue\n");
403 verbose_given = false;
404 version_given = false;
405 verbose = 0;
406 } else if (! verbose_given) {
407 pr2serr("set '-vv'\n");
408 verbose = 2;
409 } else
410 pr2serr("keep verbose=%d\n", verbose);
411 #else
412 if (verbose_given && version_given)
413 pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
414 #endif
415 if (version_given) {
416 pr2serr(ME "version: %s\n", version_str);
417 return 0;
418 }
419
420 if (NULL == device_name) {
421 pr2serr("missing device name!\n\n");
422 usage();
423 return SG_LIB_SYNTAX_ERROR;
424 }
425 if (xfer_len >= MAX_XFER_LEN) {
426 pr2serr("xfer_len (%d) is out of range ( < %d)\n", xfer_len,
427 MAX_XFER_LEN);
428 usage();
429 return SG_LIB_SYNTAX_ERROR;
430 }
431
432 cpResultBuff = (uint8_t *)sg_memalign(xfer_len, 0, &free_cprb,
433 verbose > 3);
434 if (NULL == cpResultBuff) {
435 pr2serr(ME "out of memory\n");
436 return sg_convert_errno(ENOMEM);
437 }
438
439 sg_fd = sg_cmds_open_device(device_name, o_readonly, verbose);
440 if (sg_fd < 0) {
441 if (verbose)
442 pr2serr(ME "open error: %s: %s\n", device_name,
443 safe_strerror(-sg_fd));
444 ret = sg_convert_errno(-sg_fd);
445 goto finish;
446 }
447
448 if ((sa < 0) || (sa >= (int)SG_ARRAY_SIZE(rec_copy_name_arr)))
449 cp = "Out of range service action";
450 else
451 cp = rec_copy_name_arr[sa];
452 if (verbose)
453 pr2serr(ME "issue %s to device %s\n\t\txfer_len= %d (0x%x), list_id=%"
454 PRIu32 "\n", cp, device_name, xfer_len, xfer_len, list_id);
455
456 res = sg_ll_receive_copy_results(sg_fd, sa, list_id, cpResultBuff,
457 xfer_len, true, verbose);
458 ret = res;
459 if (res) {
460 char b[80];
461
462 sg_get_category_sense_str(res, sizeof(b), b, verbose);
463 pr2serr(" SCSI %s failed: %s\n", cp, b);
464 goto finish;
465 }
466 if (do_hex) {
467 hex2stdout(cpResultBuff, xfer_len, 1);
468 goto finish;
469 }
470 switch (sa) {
471 case 4: /* Failed segment details */
472 scsi_failed_segment_details(cpResultBuff, xfer_len);
473 break;
474 case 3: /* Operating parameters */
475 scsi_operating_parameters(cpResultBuff, xfer_len);
476 break;
477 case 0: /* Copy status */
478 scsi_copy_status(cpResultBuff, xfer_len);
479 break;
480 default:
481 hex2stdout(cpResultBuff, xfer_len, 1);
482 break;
483 }
484
485 finish:
486 if (free_cprb)
487 free(free_cprb);
488 if (sg_fd >= 0) {
489 res = sg_cmds_close_device(sg_fd);
490 if (res < 0) {
491 pr2serr(ME "close error: %s\n", safe_strerror(-res));
492 if (0 == ret)
493 ret = sg_convert_errno(-res);
494 }
495 }
496 if (0 == verbose) {
497 if (! sg_if_can2stderr("sg_copy_results failed: ", ret))
498 pr2serr("Some error occurred, try again with '-v' or '-vv' for "
499 "more information\n");
500 }
501 return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
502 }
503