xref: /aosp_15_r20/external/sg3_utils/src/sg_referrals.c (revision 44704f698541f6367e81f991ef8bb54ccbf3fc18)
1*44704f69SBart Van Assche /*
2*44704f69SBart Van Assche  * Copyright (c) 2010-2018 Hannes Reinecke.
3*44704f69SBart Van Assche  * All rights reserved.
4*44704f69SBart Van Assche  * Use of this source code is governed by a BSD-style
5*44704f69SBart Van Assche  * license that can be found in the BSD_LICENSE file.
6*44704f69SBart Van Assche  *
7*44704f69SBart Van Assche  * SPDX-License-Identifier: BSD-2-Clause
8*44704f69SBart Van Assche  */
9*44704f69SBart Van Assche 
10*44704f69SBart Van Assche #include <unistd.h>
11*44704f69SBart Van Assche #include <fcntl.h>
12*44704f69SBart Van Assche #include <stdio.h>
13*44704f69SBart Van Assche #include <stdlib.h>
14*44704f69SBart Van Assche #include <stdarg.h>
15*44704f69SBart Van Assche #include <stdbool.h>
16*44704f69SBart Van Assche #include <string.h>
17*44704f69SBart Van Assche #include <errno.h>
18*44704f69SBart Van Assche #include <getopt.h>
19*44704f69SBart Van Assche #define __STDC_FORMAT_MACROS 1
20*44704f69SBart Van Assche #include <inttypes.h>
21*44704f69SBart Van Assche 
22*44704f69SBart Van Assche #ifdef HAVE_CONFIG_H
23*44704f69SBart Van Assche #include "config.h"
24*44704f69SBart Van Assche #endif
25*44704f69SBart Van Assche 
26*44704f69SBart Van Assche #include "sg_lib.h"
27*44704f69SBart Van Assche #include "sg_cmds_basic.h"
28*44704f69SBart Van Assche #include "sg_cmds_extra.h"
29*44704f69SBart Van Assche #include "sg_unaligned.h"
30*44704f69SBart Van Assche #include "sg_pr2serr.h"
31*44704f69SBart Van Assche 
32*44704f69SBart Van Assche /*
33*44704f69SBart Van Assche  * A utility program originally written for the Linux OS SCSI subsystem.
34*44704f69SBart Van Assche  *
35*44704f69SBart Van Assche  *
36*44704f69SBart Van Assche  * This program issues the SCSI REPORT REFERRALS command to the given
37*44704f69SBart Van Assche  * SCSI device.
38*44704f69SBart Van Assche  */
39*44704f69SBart Van Assche 
40*44704f69SBart Van Assche static const char * version_str = "1.13 20180628";    /* sbc4r10 */
41*44704f69SBart Van Assche 
42*44704f69SBart Van Assche #define MAX_REFER_BUFF_LEN (1024 * 1024)
43*44704f69SBart Van Assche #define DEF_REFER_BUFF_LEN 256
44*44704f69SBart Van Assche 
45*44704f69SBart Van Assche #define TPGS_STATE_OPTIMIZED 0x0
46*44704f69SBart Van Assche #define TPGS_STATE_NONOPTIMIZED 0x1
47*44704f69SBart Van Assche #define TPGS_STATE_STANDBY 0x2
48*44704f69SBart Van Assche #define TPGS_STATE_UNAVAILABLE 0x3
49*44704f69SBart Van Assche #define TPGS_STATE_LB_DEPENDENT 0x4
50*44704f69SBart Van Assche #define TPGS_STATE_OFFLINE 0xe          /* SPC-4 rev 9 */
51*44704f69SBart Van Assche #define TPGS_STATE_TRANSITIONING 0xf
52*44704f69SBart Van Assche 
53*44704f69SBart Van Assche static uint8_t referralBuff[DEF_REFER_BUFF_LEN];
54*44704f69SBart Van Assche 
decode_tpgs_state(const int st)55*44704f69SBart Van Assche static const char *decode_tpgs_state(const int st)
56*44704f69SBart Van Assche {
57*44704f69SBart Van Assche     switch (st) {
58*44704f69SBart Van Assche     case TPGS_STATE_OPTIMIZED:
59*44704f69SBart Van Assche         return "active/optimized";
60*44704f69SBart Van Assche         break;
61*44704f69SBart Van Assche     case TPGS_STATE_NONOPTIMIZED:
62*44704f69SBart Van Assche         return "active/non optimized";
63*44704f69SBart Van Assche         break;
64*44704f69SBart Van Assche     case TPGS_STATE_STANDBY:
65*44704f69SBart Van Assche         return "standby";
66*44704f69SBart Van Assche         break;
67*44704f69SBart Van Assche     case TPGS_STATE_UNAVAILABLE:
68*44704f69SBart Van Assche         return "unavailable";
69*44704f69SBart Van Assche         break;
70*44704f69SBart Van Assche     case TPGS_STATE_LB_DEPENDENT:
71*44704f69SBart Van Assche         return "logical block dependent";
72*44704f69SBart Van Assche         break;
73*44704f69SBart Van Assche     case TPGS_STATE_OFFLINE:
74*44704f69SBart Van Assche         return "offline";
75*44704f69SBart Van Assche         break;
76*44704f69SBart Van Assche     case TPGS_STATE_TRANSITIONING:
77*44704f69SBart Van Assche         return "transitioning between states";
78*44704f69SBart Van Assche         break;
79*44704f69SBart Van Assche     default:
80*44704f69SBart Van Assche         return "unknown";
81*44704f69SBart Van Assche         break;
82*44704f69SBart Van Assche     }
83*44704f69SBart Van Assche }
84*44704f69SBart Van Assche 
85*44704f69SBart Van Assche static struct option long_options[] = {
86*44704f69SBart Van Assche         {"help", no_argument, 0, 'h'},
87*44704f69SBart Van Assche         {"hex", no_argument, 0, 'H'},
88*44704f69SBart Van Assche         {"lba", required_argument, 0, 'l'},
89*44704f69SBart Van Assche         {"maxlen", required_argument, 0, 'm'},
90*44704f69SBart Van Assche         {"one-segment", no_argument, 0, 's'},
91*44704f69SBart Van Assche         {"one_segment", no_argument, 0, 's'},
92*44704f69SBart Van Assche         {"raw", no_argument, 0, 'r'},
93*44704f69SBart Van Assche         {"readonly", no_argument, 0, 'R'},
94*44704f69SBart Van Assche         {"verbose", no_argument, 0, 'v'},
95*44704f69SBart Van Assche         {"version", no_argument, 0, 'V'},
96*44704f69SBart Van Assche         {0, 0, 0, 0},
97*44704f69SBart Van Assche };
98*44704f69SBart Van Assche 
99*44704f69SBart Van Assche static void
usage()100*44704f69SBart Van Assche usage()
101*44704f69SBart Van Assche {
102*44704f69SBart Van Assche     pr2serr("Usage: sg_referrals  [--help] [--hex] [--lba=LBA] "
103*44704f69SBart Van Assche             "[--maxlen=LEN]\n"
104*44704f69SBart Van Assche             "                     [--one-segment] [--raw] [--readonly] "
105*44704f69SBart Van Assche             "[--verbose]\n"
106*44704f69SBart Van Assche             "                     [--version] DEVICE\n"
107*44704f69SBart Van Assche             "  where:\n"
108*44704f69SBart Van Assche             "    --help|-h         print out usage message\n"
109*44704f69SBart Van Assche             "    --hex|-H          output in hexadecimal\n"
110*44704f69SBart Van Assche             "    --lba=LBA|-l LBA    starting LBA (logical block address) "
111*44704f69SBart Van Assche             "(def: 0)\n"
112*44704f69SBart Van Assche             "    --maxlen=LEN|-m LEN    max response length (allocation "
113*44704f69SBart Van Assche             "length in cdb)\n"
114*44704f69SBart Van Assche             "                           (def: 0 -> %d bytes)\n",
115*44704f69SBart Van Assche             DEF_REFER_BUFF_LEN );
116*44704f69SBart Van Assche     pr2serr("    --one-segment|-s    return information about the specified "
117*44704f69SBart Van Assche             "segment only\n"
118*44704f69SBart Van Assche             "    --raw|-r          output in binary\n"
119*44704f69SBart Van Assche             "    --verbose|-v      increase verbosity\n"
120*44704f69SBart Van Assche             "    --version|-V      print version string and exit\n\n"
121*44704f69SBart Van Assche             "Performs a SCSI REPORT REFERRALS command (SBC-3)\n"
122*44704f69SBart Van Assche             );
123*44704f69SBart Van Assche }
124*44704f69SBart Van Assche 
125*44704f69SBart Van Assche static void
dStrRaw(const uint8_t * str,int len)126*44704f69SBart Van Assche dStrRaw(const uint8_t * str, int len)
127*44704f69SBart Van Assche {
128*44704f69SBart Van Assche     int k;
129*44704f69SBart Van Assche 
130*44704f69SBart Van Assche     for (k = 0; k < len; ++k)
131*44704f69SBart Van Assche         printf("%c", str[k]);
132*44704f69SBart Van Assche }
133*44704f69SBart Van Assche 
134*44704f69SBart Van Assche /* Decodes given user data referral segment descriptor
135*44704f69SBart Van Assche  * the number of blocks and returns the number of bytes processed,
136*44704f69SBart Van Assche  * -1 for error.
137*44704f69SBart Van Assche  */
138*44704f69SBart Van Assche static int
decode_referral_desc(const uint8_t * bp,int bytes)139*44704f69SBart Van Assche decode_referral_desc(const uint8_t * bp, int bytes)
140*44704f69SBart Van Assche {
141*44704f69SBart Van Assche     int j, n;
142*44704f69SBart Van Assche     uint64_t first, last;
143*44704f69SBart Van Assche 
144*44704f69SBart Van Assche     if (NULL == bp)
145*44704f69SBart Van Assche         return -1;
146*44704f69SBart Van Assche 
147*44704f69SBart Van Assche     if (bytes < 20)
148*44704f69SBart Van Assche         return -1;
149*44704f69SBart Van Assche 
150*44704f69SBart Van Assche     first = sg_get_unaligned_be64(bp + 4);
151*44704f69SBart Van Assche     last = sg_get_unaligned_be64(bp + 12);
152*44704f69SBart Van Assche 
153*44704f69SBart Van Assche     printf("    target port descriptors: %d\n", bp[3]);
154*44704f69SBart Van Assche     printf("    user data segment: first lba %" PRIu64 ", last lba %"
155*44704f69SBart Van Assche           PRIu64 "\n", first, last);
156*44704f69SBart Van Assche     n = 20;
157*44704f69SBart Van Assche     bytes -= n;
158*44704f69SBart Van Assche     for (j = 0; j < bp[3]; j++) {
159*44704f69SBart Van Assche         if (bytes < 4)
160*44704f69SBart Van Assche             return -1;
161*44704f69SBart Van Assche         printf("      target port descriptor %d:\n", j);
162*44704f69SBart Van Assche         printf("        port group %x state (%s)\n",
163*44704f69SBart Van Assche                sg_get_unaligned_be16(bp + n + 2),
164*44704f69SBart Van Assche                decode_tpgs_state(bp[n] & 0xf));
165*44704f69SBart Van Assche         n += 4;
166*44704f69SBart Van Assche         bytes -= 4;
167*44704f69SBart Van Assche     }
168*44704f69SBart Van Assche     return n;
169*44704f69SBart Van Assche }
170*44704f69SBart Van Assche 
171*44704f69SBart Van Assche 
172*44704f69SBart Van Assche int
main(int argc,char * argv[])173*44704f69SBart Van Assche main(int argc, char * argv[])
174*44704f69SBart Van Assche {
175*44704f69SBart Van Assche     bool do_one_segment = false;
176*44704f69SBart Van Assche     bool o_readonly = false;
177*44704f69SBart Van Assche     bool do_raw = false;
178*44704f69SBart Van Assche     bool verbose_given = false;
179*44704f69SBart Van Assche     bool version_given = false;
180*44704f69SBart Van Assche     int k, res, c, rlen;
181*44704f69SBart Van Assche     int sg_fd = -1;
182*44704f69SBart Van Assche     int do_hex = 0;
183*44704f69SBart Van Assche     int maxlen = DEF_REFER_BUFF_LEN;
184*44704f69SBart Van Assche     int verbose = 0;
185*44704f69SBart Van Assche     int desc = 0;
186*44704f69SBart Van Assche     int ret = 0;
187*44704f69SBart Van Assche     int64_t ll;
188*44704f69SBart Van Assche     uint64_t lba = 0;
189*44704f69SBart Van Assche     const char * device_name = NULL;
190*44704f69SBart Van Assche     const uint8_t * bp;
191*44704f69SBart Van Assche     uint8_t * referralBuffp = referralBuff;
192*44704f69SBart Van Assche     uint8_t * free_referralBuffp = NULL;
193*44704f69SBart Van Assche 
194*44704f69SBart Van Assche     while (1) {
195*44704f69SBart Van Assche         int option_index = 0;
196*44704f69SBart Van Assche 
197*44704f69SBart Van Assche         c = getopt_long(argc, argv, "hHl:m:rRsvV", long_options,
198*44704f69SBart Van Assche                         &option_index);
199*44704f69SBart Van Assche         if (c == -1)
200*44704f69SBart Van Assche             break;
201*44704f69SBart Van Assche 
202*44704f69SBart Van Assche         switch (c) {
203*44704f69SBart Van Assche         case 'h':
204*44704f69SBart Van Assche         case '?':
205*44704f69SBart Van Assche             usage();
206*44704f69SBart Van Assche             return 0;
207*44704f69SBart Van Assche         case 'H':
208*44704f69SBart Van Assche             ++do_hex;
209*44704f69SBart Van Assche             break;
210*44704f69SBart Van Assche         case 'l':
211*44704f69SBart Van Assche             ll = sg_get_llnum(optarg);
212*44704f69SBart Van Assche             if (-1 == ll) {
213*44704f69SBart Van Assche                 pr2serr("bad argument to '--lba'\n");
214*44704f69SBart Van Assche                 return SG_LIB_SYNTAX_ERROR;
215*44704f69SBart Van Assche             }
216*44704f69SBart Van Assche             lba = (uint64_t)ll;
217*44704f69SBart Van Assche             break;
218*44704f69SBart Van Assche         case 'm':
219*44704f69SBart Van Assche             maxlen = sg_get_num(optarg);
220*44704f69SBart Van Assche             if ((maxlen < 0) || (maxlen > MAX_REFER_BUFF_LEN)) {
221*44704f69SBart Van Assche                 pr2serr("argument to '--maxlen' should be %d or less\n",
222*44704f69SBart Van Assche                         MAX_REFER_BUFF_LEN);
223*44704f69SBart Van Assche                 return SG_LIB_SYNTAX_ERROR;
224*44704f69SBart Van Assche             }
225*44704f69SBart Van Assche             break;
226*44704f69SBart Van Assche         case 's':
227*44704f69SBart Van Assche             do_one_segment = true;
228*44704f69SBart Van Assche             break;
229*44704f69SBart Van Assche         case 'r':
230*44704f69SBart Van Assche             do_raw = true;
231*44704f69SBart Van Assche             break;
232*44704f69SBart Van Assche         case 'R':
233*44704f69SBart Van Assche             o_readonly = true;
234*44704f69SBart Van Assche             break;
235*44704f69SBart Van Assche         case 'v':
236*44704f69SBart Van Assche             verbose_given = true;
237*44704f69SBart Van Assche             ++verbose;
238*44704f69SBart Van Assche             break;
239*44704f69SBart Van Assche         case 'V':
240*44704f69SBart Van Assche             version_given = true;
241*44704f69SBart Van Assche             break;
242*44704f69SBart Van Assche         default:
243*44704f69SBart Van Assche             pr2serr("unrecognised option code 0x%x ??\n", c);
244*44704f69SBart Van Assche             usage();
245*44704f69SBart Van Assche             return SG_LIB_SYNTAX_ERROR;
246*44704f69SBart Van Assche         }
247*44704f69SBart Van Assche     }
248*44704f69SBart Van Assche     if (optind < argc) {
249*44704f69SBart Van Assche         if (NULL == device_name) {
250*44704f69SBart Van Assche             device_name = argv[optind];
251*44704f69SBart Van Assche             ++optind;
252*44704f69SBart Van Assche         }
253*44704f69SBart Van Assche         if (optind < argc) {
254*44704f69SBart Van Assche             for (; optind < argc; ++optind)
255*44704f69SBart Van Assche                 pr2serr("Unexpected extra argument: %s\n", argv[optind]);
256*44704f69SBart Van Assche             usage();
257*44704f69SBart Van Assche             return SG_LIB_SYNTAX_ERROR;
258*44704f69SBart Van Assche         }
259*44704f69SBart Van Assche     }
260*44704f69SBart Van Assche #ifdef DEBUG
261*44704f69SBart Van Assche     pr2serr("In DEBUG mode, ");
262*44704f69SBart Van Assche     if (verbose_given && version_given) {
263*44704f69SBart Van Assche         pr2serr("but override: '-vV' given, zero verbose and continue\n");
264*44704f69SBart Van Assche         verbose_given = false;
265*44704f69SBart Van Assche         version_given = false;
266*44704f69SBart Van Assche         verbose = 0;
267*44704f69SBart Van Assche     } else if (! verbose_given) {
268*44704f69SBart Van Assche         pr2serr("set '-vv'\n");
269*44704f69SBart Van Assche         verbose = 2;
270*44704f69SBart Van Assche     } else
271*44704f69SBart Van Assche         pr2serr("keep verbose=%d\n", verbose);
272*44704f69SBart Van Assche #else
273*44704f69SBart Van Assche     if (verbose_given && version_given)
274*44704f69SBart Van Assche         pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
275*44704f69SBart Van Assche #endif
276*44704f69SBart Van Assche     if (version_given) {
277*44704f69SBart Van Assche         pr2serr("version: %s\n", version_str);
278*44704f69SBart Van Assche         return 0;
279*44704f69SBart Van Assche     }
280*44704f69SBart Van Assche 
281*44704f69SBart Van Assche     if (NULL == device_name) {
282*44704f69SBart Van Assche         pr2serr("No DEVICE argument given\n\n");
283*44704f69SBart Van Assche         usage();
284*44704f69SBart Van Assche         return SG_LIB_SYNTAX_ERROR;
285*44704f69SBart Van Assche     }
286*44704f69SBart Van Assche     if (maxlen > DEF_REFER_BUFF_LEN) {
287*44704f69SBart Van Assche         referralBuffp = (uint8_t *)sg_memalign(maxlen, 0,
288*44704f69SBart Van Assche                                                &free_referralBuffp,
289*44704f69SBart Van Assche                                                verbose > 3);
290*44704f69SBart Van Assche         if (NULL == referralBuffp) {
291*44704f69SBart Van Assche             pr2serr("unable to allocate %d bytes on heap\n", maxlen);
292*44704f69SBart Van Assche             return sg_convert_errno(ENOMEM);
293*44704f69SBart Van Assche         }
294*44704f69SBart Van Assche     }
295*44704f69SBart Van Assche     if (do_raw) {
296*44704f69SBart Van Assche         if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
297*44704f69SBart Van Assche             perror("sg_set_binary_mode");
298*44704f69SBart Van Assche             ret = SG_LIB_FILE_ERROR;
299*44704f69SBart Van Assche             goto free_buff;
300*44704f69SBart Van Assche         }
301*44704f69SBart Van Assche     }
302*44704f69SBart Van Assche 
303*44704f69SBart Van Assche     sg_fd = sg_cmds_open_device(device_name, o_readonly, verbose);
304*44704f69SBart Van Assche     if (sg_fd < 0) {
305*44704f69SBart Van Assche         if (verbose)
306*44704f69SBart Van Assche             pr2serr("open error: %s: %s\n", device_name,
307*44704f69SBart Van Assche                     safe_strerror(-sg_fd));
308*44704f69SBart Van Assche         ret = sg_convert_errno(-sg_fd);
309*44704f69SBart Van Assche         goto free_buff;
310*44704f69SBart Van Assche     }
311*44704f69SBart Van Assche 
312*44704f69SBart Van Assche     res = sg_ll_report_referrals(sg_fd, lba, do_one_segment, referralBuffp,
313*44704f69SBart Van Assche                                  maxlen, true, verbose);
314*44704f69SBart Van Assche     ret = res;
315*44704f69SBart Van Assche     if (0 == res) {
316*44704f69SBart Van Assche         if (maxlen >= 4)
317*44704f69SBart Van Assche             /*
318*44704f69SBart Van Assche              * This is strictly speaking incorrect. However, the
319*44704f69SBart Van Assche              * spec reserved bytes 0 and 1, so some implementations
320*44704f69SBart Van Assche              * might want to use them to increase the number of
321*44704f69SBart Van Assche              * possible user segments.
322*44704f69SBart Van Assche              * And maybe someone takes a pity and updates the spec ...
323*44704f69SBart Van Assche              */
324*44704f69SBart Van Assche             rlen = sg_get_unaligned_be32(referralBuffp + 0) + 4;
325*44704f69SBart Van Assche         else
326*44704f69SBart Van Assche             rlen = maxlen;
327*44704f69SBart Van Assche         k = (rlen > maxlen) ? maxlen : rlen;
328*44704f69SBart Van Assche         if (do_raw) {
329*44704f69SBart Van Assche             dStrRaw(referralBuffp, k);
330*44704f69SBart Van Assche             goto the_end;
331*44704f69SBart Van Assche         }
332*44704f69SBart Van Assche         if (do_hex) {
333*44704f69SBart Van Assche             hex2stdout(referralBuffp, k, 1);
334*44704f69SBart Van Assche             goto the_end;
335*44704f69SBart Van Assche         }
336*44704f69SBart Van Assche         if (maxlen < 4) {
337*44704f69SBart Van Assche             if (verbose)
338*44704f69SBart Van Assche                 pr2serr("Exiting because allocation length (maxlen)  less "
339*44704f69SBart Van Assche                         "than 4\n");
340*44704f69SBart Van Assche             goto the_end;
341*44704f69SBart Van Assche         }
342*44704f69SBart Van Assche         if ((verbose > 1) || (verbose && (rlen > maxlen))) {
343*44704f69SBart Van Assche             pr2serr("response length %d bytes\n", rlen);
344*44704f69SBart Van Assche             if (rlen > maxlen)
345*44704f69SBart Van Assche                 pr2serr("  ... which is greater than maxlen (allocation "
346*44704f69SBart Van Assche                         "length %d), truncation\n", maxlen);
347*44704f69SBart Van Assche         }
348*44704f69SBart Van Assche         if (rlen > maxlen)
349*44704f69SBart Van Assche             rlen = maxlen;
350*44704f69SBart Van Assche 
351*44704f69SBart Van Assche         bp = referralBuffp + 4;
352*44704f69SBart Van Assche         k = 0;
353*44704f69SBart Van Assche         printf("Report referrals:\n");
354*44704f69SBart Van Assche         while (k < rlen - 4) {
355*44704f69SBart Van Assche             printf("  descriptor %d:\n", desc);
356*44704f69SBart Van Assche             res = decode_referral_desc(bp + k, rlen - 4 - k);
357*44704f69SBart Van Assche             if (res < 0) {
358*44704f69SBart Van Assche                 pr2serr("bad user data segment referral descriptor\n");
359*44704f69SBart Van Assche                 break;
360*44704f69SBart Van Assche             }
361*44704f69SBart Van Assche             k += res;
362*44704f69SBart Van Assche             desc++;
363*44704f69SBart Van Assche         }
364*44704f69SBart Van Assche     } else {
365*44704f69SBart Van Assche         char b[80];
366*44704f69SBart Van Assche 
367*44704f69SBart Van Assche         sg_get_category_sense_str(res, sizeof(b), b, verbose);
368*44704f69SBart Van Assche         pr2serr("Report Referrals command failed: %s\n", b);
369*44704f69SBart Van Assche     }
370*44704f69SBart Van Assche 
371*44704f69SBart Van Assche the_end:
372*44704f69SBart Van Assche     res = sg_cmds_close_device(sg_fd);
373*44704f69SBart Van Assche     if (res < 0) {
374*44704f69SBart Van Assche         pr2serr("close error: %s\n", safe_strerror(-res));
375*44704f69SBart Van Assche         if (0 == ret)
376*44704f69SBart Van Assche             ret = sg_convert_errno(-res);
377*44704f69SBart Van Assche     }
378*44704f69SBart Van Assche free_buff:
379*44704f69SBart Van Assche     if (free_referralBuffp)
380*44704f69SBart Van Assche         free(free_referralBuffp);
381*44704f69SBart Van Assche     if (0 == verbose) {
382*44704f69SBart Van Assche         if (! sg_if_can2stderr("sg_referrals failed: ", ret))
383*44704f69SBart Van Assche             pr2serr("Some error occurred, try again with '-v' "
384*44704f69SBart Van Assche                     "or '-vv' for more information\n");
385*44704f69SBart Van Assche     }
386*44704f69SBart Van Assche     return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
387*44704f69SBart Van Assche }
388