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