xref: /aosp_15_r20/external/sg3_utils/src/sg_seek.c (revision 44704f698541f6367e81f991ef8bb54ccbf3fc18)
1*44704f69SBart Van Assche /*
2*44704f69SBart Van Assche  * Copyright (c) 2018-2020 Douglas Gilbert.
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 <stdbool.h>
15*44704f69SBart Van Assche #include <string.h>
16*44704f69SBart Van Assche #include <ctype.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 #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
27*44704f69SBart Van Assche #include <time.h>
28*44704f69SBart Van Assche #elif defined(HAVE_GETTIMEOFDAY)
29*44704f69SBart Van Assche #include <time.h>
30*44704f69SBart Van Assche #include <sys/time.h>
31*44704f69SBart Van Assche #endif
32*44704f69SBart Van Assche 
33*44704f69SBart Van Assche #include "sg_lib.h"
34*44704f69SBart Van Assche #include "sg_lib_data.h"
35*44704f69SBart Van Assche #include "sg_cmds_basic.h"
36*44704f69SBart Van Assche #include "sg_cmds_extra.h"
37*44704f69SBart Van Assche #include "sg_unaligned.h"
38*44704f69SBart Van Assche #include "sg_pr2serr.h"
39*44704f69SBart Van Assche 
40*44704f69SBart Van Assche /*
41*44704f69SBart Van Assche  * This program issues one or more SCSI SEEK(10), PRE-FETCH(10) or
42*44704f69SBart Van Assche  * PRE-FETCH(16) commands. Both PRE-FETCH commands are current and appear
43*44704f69SBart Van Assche  * in the most recent SBC-4 draft (sbc4r15.pdf at time of writing) while
44*44704f69SBart Van Assche  * SEEK(10) has been obsolete since SBC-2 (2004). Currently more hard disks
45*44704f69SBart Van Assche  * and SSDs support SEEK(10) than PRE-FETCH. It is even unclear what
46*44704f69SBart Van Assche  * SEEK(10) means (defined in SBC-1 as moving the hard disk heads to the
47*44704f69SBart Van Assche  * track containing the given LBA) for a SSD. But if the manufacturers'
48*44704f69SBart Van Assche  * support it, then it must have a use, presumably to speed the next access
49*44704f69SBart Van Assche  * to that LBA ...
50*44704f69SBart Van Assche  */
51*44704f69SBart Van Assche 
52*44704f69SBart Van Assche static const char * version_str = "1.08 20200115";
53*44704f69SBart Van Assche 
54*44704f69SBart Van Assche #define BACKGROUND_CONTROL_SA 0x15
55*44704f69SBart Van Assche 
56*44704f69SBart Van Assche #define CMD_ABORT_TIMEOUT  60      /* 60 seconds */
57*44704f69SBart Van Assche 
58*44704f69SBart Van Assche 
59*44704f69SBart Van Assche static struct option long_options[] = {
60*44704f69SBart Van Assche         {"10", no_argument, 0, 'T'},
61*44704f69SBart Van Assche         {"count", required_argument, 0, 'c'},
62*44704f69SBart Van Assche         {"grpnum", required_argument, 0, 'g'},
63*44704f69SBart Van Assche         {"help", no_argument, 0, 'h'},
64*44704f69SBart Van Assche         {"immed", no_argument, 0, 'i'},
65*44704f69SBart Van Assche         {"lba", required_argument, 0, 'l'},
66*44704f69SBart Van Assche         {"num-blocks", required_argument, 0, 'n'},
67*44704f69SBart Van Assche         {"num_blocks", required_argument, 0, 'n'},
68*44704f69SBart Van Assche         {"pre-fetch", no_argument, 0, 'p'},
69*44704f69SBart Van Assche         {"pre_fetch", no_argument, 0, 'p'},
70*44704f69SBart Van Assche         {"readonly", no_argument, 0, 'r'},
71*44704f69SBart Van Assche         {"skip", required_argument, 0, 's'},
72*44704f69SBart Van Assche         {"time", required_argument, 0, 't'},
73*44704f69SBart Van Assche         {"verbose", no_argument, 0, 'v'},
74*44704f69SBart Van Assche         {"version", no_argument, 0, 'V'},
75*44704f69SBart Van Assche         {"wrap-offset", required_argument, 0, 'w'},
76*44704f69SBart Van Assche         {"wrap_offset", required_argument, 0, 'w'},
77*44704f69SBart Van Assche         {0, 0, 0, 0},
78*44704f69SBart Van Assche };
79*44704f69SBart Van Assche 
80*44704f69SBart Van Assche 
81*44704f69SBart Van Assche static void
usage()82*44704f69SBart Van Assche usage()
83*44704f69SBart Van Assche {
84*44704f69SBart Van Assche     pr2serr("Usage: "
85*44704f69SBart Van Assche             "sg_seek  [--10] [--count=NC] [--grpnum=GN] [--help] [--immed]\n"
86*44704f69SBart Van Assche             "                [--lba=LBA] [--num-blocks=NUM] [--pre-fetch] "
87*44704f69SBart Van Assche             "[--readonly]\n"
88*44704f69SBart Van Assche             "                [--skip=SB] [--time] [--verbose] [--version]\n"
89*44704f69SBart Van Assche             "                [--wrap-offset=WO] DEVICE\n");
90*44704f69SBart Van Assche     pr2serr("  where:\n"
91*44704f69SBart Van Assche             "    --10|-T             do PRE-FETCH(10) command (def: "
92*44704f69SBart Van Assche             "SEEK(10), or\n"
93*44704f69SBart Van Assche             "                        PRE-FETCH(16) if --pre-fetch also "
94*44704f69SBart Van Assche             "given)\n"
95*44704f69SBart Van Assche             "    --count=NC|-c NC    NC is number of commands to execute "
96*44704f69SBart Van Assche             "(def: 1)\n"
97*44704f69SBart Van Assche             "    --grpnum=GN|-g GN    GN is group number to place in "
98*44704f69SBart Van Assche             "PRE-FETCH\n"
99*44704f69SBart Van Assche             "                         cdb; 0 to 63 (def: 0)\n"
100*44704f69SBart Van Assche             "    --help|-h           print out usage message\n"
101*44704f69SBart Van Assche             "    --immed|-i          set IMMED bit in PRE-FETCH command\n"
102*44704f69SBart Van Assche             "    --lba=LBA|-l LBA    starting Logical Block Address (LBA) "
103*44704f69SBart Van Assche             "(def: 0)\n"
104*44704f69SBart Van Assche             "    --num-blocks=NUM|-n NUM    number of blocks to cache (for "
105*44704f69SBart Van Assche             "PRE-FETCH)\n"
106*44704f69SBart Van Assche             "                               (def: 1). Ignored by "
107*44704f69SBart Van Assche             "SEEK(10)\n");
108*44704f69SBart Van Assche     pr2serr("    --pre-fetch|-p     do PRE-FETCH command, 16 byte variant if "
109*44704f69SBart Van Assche             "--10 not\n"
110*44704f69SBart Van Assche             "                       given (def: do SEEK(10))\n"
111*44704f69SBart Van Assche             "    --readonly|-r      open DEVICE read-only (if supported)\n"
112*44704f69SBart Van Assche             "    --skip=SB|-s SB    when NC>1 skip SB blocks to next LBA "
113*44704f69SBart Van Assche             "(def: 1)\n"
114*44704f69SBart Van Assche             "    --time|-t          time the command(s) and if NC>1 show "
115*44704f69SBart Van Assche             "usecs/command\n"
116*44704f69SBart Van Assche             "                       (def: don't time)\n"
117*44704f69SBart Van Assche             "    --verbose|-v       increase verbosity\n"
118*44704f69SBart Van Assche             "    --version|-V       print version string and exit\n"
119*44704f69SBart Van Assche             "    --wrap-offset=WO|-w WO    if SB>0 and WO>0 then if "
120*44704f69SBart Van Assche             "LBAn>LBA+WO\n"
121*44704f69SBart Van Assche             "                       then reset LBAn back to LBA (def: 0)\n\n"
122*44704f69SBart Van Assche             "Performs SCSI SEEK(10), PRE-FETCH(10) or PRE-FETCH(16) "
123*44704f69SBart Van Assche             "command(s).If no\noptions are given does one SEEK(10) command "
124*44704f69SBart Van Assche             "with an LBA of 0 . If NC>1\nthen a tally is kept of successes, "
125*44704f69SBart Van Assche             "'condition-met's and errors that is\nprinted on completion. "
126*44704f69SBart Van Assche             "'condition-met' is from PRE-FETCH when NUM blocks\nfit in "
127*44704f69SBart Van Assche             "the DEVICE's cache.\n"
128*44704f69SBart Van Assche            );
129*44704f69SBart Van Assche }
130*44704f69SBart Van Assche 
131*44704f69SBart Van Assche 
132*44704f69SBart Van Assche int
main(int argc,char * argv[])133*44704f69SBart Van Assche main(int argc, char * argv[])
134*44704f69SBart Van Assche {
135*44704f69SBart Van Assche     bool cdb10 = false;
136*44704f69SBart Van Assche     bool count_given = false;
137*44704f69SBart Van Assche     bool do_time = false;
138*44704f69SBart Van Assche     bool immed = false;
139*44704f69SBart Van Assche     bool prefetch = false;
140*44704f69SBart Van Assche     bool readonly = false;
141*44704f69SBart Van Assche     bool start_tm_valid = false;
142*44704f69SBart Van Assche     bool verbose_given = false;
143*44704f69SBart Van Assche     bool version_given = false;
144*44704f69SBart Van Assche     int res, c;
145*44704f69SBart Van Assche     int sg_fd = -1;
146*44704f69SBart Van Assche     int first_err = 0;
147*44704f69SBart Van Assche     int last_err = 0;
148*44704f69SBart Van Assche     int ret = 0;
149*44704f69SBart Van Assche     int verbose = 0;
150*44704f69SBart Van Assche     uint32_t count = 1;
151*44704f69SBart Van Assche     int32_t l;
152*44704f69SBart Van Assche     uint32_t grpnum = 0;
153*44704f69SBart Van Assche     uint32_t k;
154*44704f69SBart Van Assche     uint32_t num_cond_met = 0;
155*44704f69SBart Van Assche     uint32_t num_err = 0;
156*44704f69SBart Van Assche     uint32_t num_good = 0;
157*44704f69SBart Van Assche     uint32_t numblocks = 1;
158*44704f69SBart Van Assche     uint32_t skip = 1;
159*44704f69SBart Van Assche     uint32_t wrap_offs = 0;
160*44704f69SBart Van Assche     int64_t ll;
161*44704f69SBart Van Assche     int64_t elapsed_usecs = 0;
162*44704f69SBart Van Assche     uint64_t lba = 0;
163*44704f69SBart Van Assche     uint64_t lba_n;
164*44704f69SBart Van Assche     const char * device_name = NULL;
165*44704f69SBart Van Assche     const char * cdb_name = NULL;
166*44704f69SBart Van Assche     char b[64];
167*44704f69SBart Van Assche #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
168*44704f69SBart Van Assche     struct timespec start_tm, end_tm;
169*44704f69SBart Van Assche #elif defined(HAVE_GETTIMEOFDAY)
170*44704f69SBart Van Assche     struct timeval start_tm, end_tm;
171*44704f69SBart Van Assche #endif
172*44704f69SBart Van Assche 
173*44704f69SBart Van Assche     while (1) {
174*44704f69SBart Van Assche         int option_index = 0;
175*44704f69SBart Van Assche 
176*44704f69SBart Van Assche         c = getopt_long(argc, argv, "c:g:hil:n:prs:tTvVw:", long_options,
177*44704f69SBart Van Assche                         &option_index);
178*44704f69SBart Van Assche         if (c == -1)
179*44704f69SBart Van Assche             break;
180*44704f69SBart Van Assche 
181*44704f69SBart Van Assche         switch (c) {
182*44704f69SBart Van Assche         case 'c':
183*44704f69SBart Van Assche             l = sg_get_num(optarg);
184*44704f69SBart Van Assche             if (l < 0) {
185*44704f69SBart Van Assche                 pr2serr("--count= unable to decode argument, want 0 or "
186*44704f69SBart Van Assche                         "higher\n");
187*44704f69SBart Van Assche                 return SG_LIB_SYNTAX_ERROR;
188*44704f69SBart Van Assche             }
189*44704f69SBart Van Assche             count = (uint32_t)l;
190*44704f69SBart Van Assche             count_given = true;
191*44704f69SBart Van Assche             break;
192*44704f69SBart Van Assche         case 'g':
193*44704f69SBart Van Assche             l = sg_get_num(optarg);
194*44704f69SBart Van Assche             if ((l > 63) || (l < 0)) {
195*44704f69SBart Van Assche                 pr2serr("--grpnum= expect argument in range 0 to 63\n");
196*44704f69SBart Van Assche                 return SG_LIB_SYNTAX_ERROR;
197*44704f69SBart Van Assche             }
198*44704f69SBart Van Assche             grpnum = (uint32_t)l;
199*44704f69SBart Van Assche             break;
200*44704f69SBart Van Assche         case 'h':
201*44704f69SBart Van Assche         case '?':
202*44704f69SBart Van Assche             usage();
203*44704f69SBart Van Assche             return 0;
204*44704f69SBart Van Assche         case 'i':
205*44704f69SBart Van Assche             immed = true;
206*44704f69SBart Van Assche             break;
207*44704f69SBart Van Assche         case 'l':
208*44704f69SBart Van Assche             ll = sg_get_llnum(optarg);
209*44704f69SBart Van Assche             if (-1 == ll) {
210*44704f69SBart Van Assche                 pr2serr("--lba= unable to decode argument\n");
211*44704f69SBart Van Assche                 return SG_LIB_SYNTAX_ERROR;
212*44704f69SBart Van Assche             }
213*44704f69SBart Van Assche             lba = (uint64_t)ll;
214*44704f69SBart Van Assche             break;
215*44704f69SBart Van Assche         case 'n':
216*44704f69SBart Van Assche             l = sg_get_num(optarg);
217*44704f69SBart Van Assche             if (-1 == l) {
218*44704f69SBart Van Assche                 pr2serr("--num= unable to decode argument\n");
219*44704f69SBart Van Assche                 return SG_LIB_SYNTAX_ERROR;
220*44704f69SBart Van Assche             }
221*44704f69SBart Van Assche             numblocks = (uint32_t)l;
222*44704f69SBart Van Assche             break;
223*44704f69SBart Van Assche         case 'p':
224*44704f69SBart Van Assche             prefetch = true;
225*44704f69SBart Van Assche             break;
226*44704f69SBart Van Assche         case 'r':
227*44704f69SBart Van Assche             readonly = true;
228*44704f69SBart Van Assche             break;
229*44704f69SBart Van Assche         case 's':
230*44704f69SBart Van Assche             l = sg_get_num(optarg);
231*44704f69SBart Van Assche             if (-1 == l) {
232*44704f69SBart Van Assche                 pr2serr("--skip= unable to decode argument\n");
233*44704f69SBart Van Assche                 return SG_LIB_SYNTAX_ERROR;
234*44704f69SBart Van Assche             }
235*44704f69SBart Van Assche             skip = (uint32_t)l;
236*44704f69SBart Van Assche             break;
237*44704f69SBart Van Assche         case 't':
238*44704f69SBart Van Assche             do_time = true;
239*44704f69SBart Van Assche             break;
240*44704f69SBart Van Assche         case 'T':
241*44704f69SBart Van Assche             cdb10 = true;
242*44704f69SBart Van Assche             break;
243*44704f69SBart Van Assche         case 'v':
244*44704f69SBart Van Assche             verbose_given = true;
245*44704f69SBart Van Assche             ++verbose;
246*44704f69SBart Van Assche             break;
247*44704f69SBart Van Assche         case 'V':
248*44704f69SBart Van Assche             version_given = true;
249*44704f69SBart Van Assche             break;
250*44704f69SBart Van Assche         case 'w':
251*44704f69SBart Van Assche             l = sg_get_num(optarg);
252*44704f69SBart Van Assche             if (-1 == l) {
253*44704f69SBart Van Assche                 pr2serr("--wrap-offset= unable to decode argument\n");
254*44704f69SBart Van Assche                 return SG_LIB_SYNTAX_ERROR;
255*44704f69SBart Van Assche             }
256*44704f69SBart Van Assche             wrap_offs = (uint32_t)l;
257*44704f69SBart Van Assche             break;
258*44704f69SBart Van Assche         default:
259*44704f69SBart Van Assche             pr2serr("unrecognised option code 0x%x ??\n", c);
260*44704f69SBart Van Assche             usage();
261*44704f69SBart Van Assche             return SG_LIB_SYNTAX_ERROR;
262*44704f69SBart Van Assche         }
263*44704f69SBart Van Assche     }
264*44704f69SBart Van Assche     if (optind < argc) {
265*44704f69SBart Van Assche         if (NULL == device_name) {
266*44704f69SBart Van Assche             device_name = argv[optind];
267*44704f69SBart Van Assche             ++optind;
268*44704f69SBart Van Assche         }
269*44704f69SBart Van Assche         if (optind < argc) {
270*44704f69SBart Van Assche             for (; optind < argc; ++optind)
271*44704f69SBart Van Assche                 pr2serr("Unexpected extra argument: %s\n",
272*44704f69SBart Van Assche                         argv[optind]);
273*44704f69SBart Van Assche             usage();
274*44704f69SBart Van Assche             return SG_LIB_SYNTAX_ERROR;
275*44704f69SBart Van Assche         }
276*44704f69SBart Van Assche     }
277*44704f69SBart Van Assche 
278*44704f69SBart Van Assche #ifdef DEBUG
279*44704f69SBart Van Assche     pr2serr("In DEBUG mode, ");
280*44704f69SBart Van Assche     if (verbose_given && version_given) {
281*44704f69SBart Van Assche         pr2serr("but override: '-vV' given, zero verbose and continue\n");
282*44704f69SBart Van Assche         verbose_given = false;
283*44704f69SBart Van Assche         version_given = false;
284*44704f69SBart Van Assche         verbose = 0;
285*44704f69SBart Van Assche     } else if (! verbose_given) {
286*44704f69SBart Van Assche         pr2serr("set '-vv'\n");
287*44704f69SBart Van Assche         verbose = 2;
288*44704f69SBart Van Assche     } else
289*44704f69SBart Van Assche         pr2serr("keep verbose=%d\n", verbose);
290*44704f69SBart Van Assche #else
291*44704f69SBart Van Assche     if (verbose_given && version_given)
292*44704f69SBart Van Assche         pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
293*44704f69SBart Van Assche #endif
294*44704f69SBart Van Assche     if (version_given) {
295*44704f69SBart Van Assche         pr2serr("version: %s\n", version_str);
296*44704f69SBart Van Assche         return 0;
297*44704f69SBart Van Assche     }
298*44704f69SBart Van Assche 
299*44704f69SBart Van Assche     if (NULL == device_name) {
300*44704f69SBart Van Assche         pr2serr("Missing device name!\n\n");
301*44704f69SBart Van Assche         usage();
302*44704f69SBart Van Assche         return SG_LIB_SYNTAX_ERROR;
303*44704f69SBart Van Assche     }
304*44704f69SBart Van Assche 
305*44704f69SBart Van Assche     if (prefetch) {
306*44704f69SBart Van Assche         if (cdb10)
307*44704f69SBart Van Assche             cdb_name = "Pre-fetch(10)";
308*44704f69SBart Van Assche         else
309*44704f69SBart Van Assche             cdb_name = "Pre-fetch(16)";
310*44704f69SBart Van Assche     } else
311*44704f69SBart Van Assche         cdb_name = "Seek(10)";
312*44704f69SBart Van Assche 
313*44704f69SBart Van Assche     sg_fd = sg_cmds_open_device(device_name, readonly, verbose);
314*44704f69SBart Van Assche     if (sg_fd < 0) {
315*44704f69SBart Van Assche         if (verbose)
316*44704f69SBart Van Assche             pr2serr("open error: %s: %s %s\n", device_name, cdb_name,
317*44704f69SBart Van Assche                     safe_strerror(-sg_fd));
318*44704f69SBart Van Assche         ret = sg_convert_errno(-sg_fd);
319*44704f69SBart Van Assche         goto fini;
320*44704f69SBart Van Assche     }
321*44704f69SBart Van Assche #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
322*44704f69SBart Van Assche     if (do_time) {
323*44704f69SBart Van Assche         start_tm.tv_sec = 0;
324*44704f69SBart Van Assche         start_tm.tv_nsec = 0;
325*44704f69SBart Van Assche         if (0 == clock_gettime(CLOCK_MONOTONIC, &start_tm))
326*44704f69SBart Van Assche             start_tm_valid = true;
327*44704f69SBart Van Assche         else
328*44704f69SBart Van Assche             perror("clock_gettime(CLOCK_MONOTONIC)\n");
329*44704f69SBart Van Assche     }
330*44704f69SBart Van Assche #elif defined(HAVE_GETTIMEOFDAY)
331*44704f69SBart Van Assche     if (do_time) {
332*44704f69SBart Van Assche         start_tm.tv_sec = 0;
333*44704f69SBart Van Assche         start_tm.tv_usec = 0;
334*44704f69SBart Van Assche         gettimeofday(&start_tm, NULL);
335*44704f69SBart Van Assche         start_tm_valid = true;
336*44704f69SBart Van Assche     }
337*44704f69SBart Van Assche #else
338*44704f69SBart Van Assche     start_tm_valid = false;
339*44704f69SBart Van Assche #endif
340*44704f69SBart Van Assche 
341*44704f69SBart Van Assche     for (k = 0, lba_n = lba; k < count; ++k, lba_n += skip) {
342*44704f69SBart Van Assche         if (wrap_offs && (lba_n > lba) && ((lba_n - lba) > wrap_offs))
343*44704f69SBart Van Assche             lba_n = lba;
344*44704f69SBart Van Assche         res = sg_ll_pre_fetch_x(sg_fd, ! prefetch, ! cdb10, immed, lba_n,
345*44704f69SBart Van Assche                                 numblocks, grpnum, 0, (verbose > 0), verbose);
346*44704f69SBart Van Assche         ret = res;      /* last command executed sets exit status */
347*44704f69SBart Van Assche         if (SG_LIB_CAT_CONDITION_MET == res)
348*44704f69SBart Van Assche             ++num_cond_met;
349*44704f69SBart Van Assche         else if (res) {
350*44704f69SBart Van Assche             ++num_err;
351*44704f69SBart Van Assche             if (0 == first_err)
352*44704f69SBart Van Assche                 first_err = res;
353*44704f69SBart Van Assche             last_err = res;
354*44704f69SBart Van Assche         } else
355*44704f69SBart Van Assche             ++num_good;
356*44704f69SBart Van Assche     }
357*44704f69SBart Van Assche 
358*44704f69SBart Van Assche #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
359*44704f69SBart Van Assche     if ((count > 0) && start_tm_valid &&
360*44704f69SBart Van Assche         (start_tm.tv_sec || start_tm.tv_nsec)) {
361*44704f69SBart Van Assche         int err;
362*44704f69SBart Van Assche 
363*44704f69SBart Van Assche         res = clock_gettime(CLOCK_MONOTONIC, &end_tm);
364*44704f69SBart Van Assche         if (res < 0) {
365*44704f69SBart Van Assche             err = errno;
366*44704f69SBart Van Assche             perror("clock_gettime");
367*44704f69SBart Van Assche             if (EINVAL == err)
368*44704f69SBart Van Assche                 pr2serr("clock_gettime(CLOCK_MONOTONIC) not supported\n");
369*44704f69SBart Van Assche         }
370*44704f69SBart Van Assche         elapsed_usecs = (end_tm.tv_sec - start_tm.tv_sec) * 1000000;
371*44704f69SBart Van Assche         /* Note that (end_tm.tv_nsec - start_tm.tv_nsec) may be negative */
372*44704f69SBart Van Assche         elapsed_usecs += (end_tm.tv_nsec - start_tm.tv_nsec) / 1000;
373*44704f69SBart Van Assche     }
374*44704f69SBart Van Assche #elif defined(HAVE_GETTIMEOFDAY)
375*44704f69SBart Van Assche     if ((count > 0) && start_tm_valid &&
376*44704f69SBart Van Assche         (start_tm.tv_sec || start_tm.tv_usec)) {
377*44704f69SBart Van Assche         gettimeofday(&end_tm, NULL);
378*44704f69SBart Van Assche         elapsed_usecs = (end_tm.tv_sec - start_tm.tv_sec) * 1000000;
379*44704f69SBart Van Assche         elapsed_usecs += (end_tm.tv_usec - start_tm.tv_usec);
380*44704f69SBart Van Assche     }
381*44704f69SBart Van Assche #endif
382*44704f69SBart Van Assche 
383*44704f69SBart Van Assche     if (elapsed_usecs > 0) {
384*44704f69SBart Van Assche         if (elapsed_usecs > 1000000)
385*44704f69SBart Van Assche             snprintf(b, sizeof(b), " (over %d seconds)",
386*44704f69SBart Van Assche                     (int)elapsed_usecs / 1000000);
387*44704f69SBart Van Assche         else
388*44704f69SBart Van Assche             b[0] = '\0';
389*44704f69SBart Van Assche         printf("Elapsed time: %" PRId64 " microseconds%s, per command time: "
390*44704f69SBart Van Assche                "%" PRId64 "\n", elapsed_usecs, b, elapsed_usecs / count);
391*44704f69SBart Van Assche     }
392*44704f69SBart Van Assche 
393*44704f69SBart Van Assche     if (count_given && verbose_given)
394*44704f69SBart Van Assche         printf("Command count=%u, number of condition_mets=%u, number of "
395*44704f69SBart Van Assche                "goods=%u\n", count, num_cond_met, num_good);
396*44704f69SBart Van Assche     if (first_err) {
397*44704f69SBart Van Assche         bool printed;
398*44704f69SBart Van Assche 
399*44704f69SBart Van Assche         printf(" number of errors=%d\n", num_err);
400*44704f69SBart Van Assche         printf("    first error");
401*44704f69SBart Van Assche         printed = sg_if_can2stdout(": ", first_err);
402*44704f69SBart Van Assche         if (! printed)
403*44704f69SBart Van Assche             printf(" code: %d\n", first_err);
404*44704f69SBart Van Assche         if (num_err > 1) {
405*44704f69SBart Van Assche             printf("    last error");
406*44704f69SBart Van Assche             printed = sg_if_can2stdout(": ", last_err);
407*44704f69SBart Van Assche             if (! printed)
408*44704f69SBart Van Assche                 printf(" code: %d\n", last_err);
409*44704f69SBart Van Assche         }
410*44704f69SBart Van Assche     }
411*44704f69SBart Van Assche fini:
412*44704f69SBart Van Assche     if (sg_fd >= 0) {
413*44704f69SBart Van Assche         res = sg_cmds_close_device(sg_fd);
414*44704f69SBart Van Assche         if (res < 0) {
415*44704f69SBart Van Assche             pr2serr("close error: %s\n", safe_strerror(-res));
416*44704f69SBart Van Assche             if (0 == ret)
417*44704f69SBart Van Assche                 ret = sg_convert_errno(-res);
418*44704f69SBart Van Assche         }
419*44704f69SBart Van Assche     }
420*44704f69SBart Van Assche     if (0 == verbose) {
421*44704f69SBart Van Assche         const char * e_str = (SG_LIB_CAT_CONDITION_MET == ret) ?
422*44704f69SBart Van Assche                              "sg_seek: " : "sg_seek: failed";
423*44704f69SBart Van Assche 
424*44704f69SBart Van Assche         if (! sg_if_can2stderr(e_str, ret))
425*44704f69SBart Van Assche             pr2serr("Some error occurred, try again with '-v' "
426*44704f69SBart Van Assche                     "or '-vv' for more information\n");
427*44704f69SBart Van Assche     }
428*44704f69SBart Van Assche     return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
429*44704f69SBart Van Assche }
430