1 /*
2 * Copyright (c) 2014-2015 Douglas Gilbert.
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
8 #include <unistd.h>
9 #include <fcntl.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <ctype.h>
14 #include <getopt.h>
15 #define __STDC_FORMAT_MACROS 1
16 #include <inttypes.h>
17
18 #ifdef HAVE_CONFIG_H
19 #include "config.h"
20 #endif
21 #include "sg_lib.h"
22 #include "sg_lib_data.h"
23 #include "sg_pt.h"
24 #include "sg_cmds_basic.h"
25 #include "sg_unaligned.h"
26 #include "sg_pr2serr.h"
27
28 /* A utility program originally written for the Linux OS SCSI subsystem.
29 *
30 *
31 * This program issues the SCSI RESET WRITE POINTER command to the given SCSI
32 * device. Based on zbc-r04c.pdf .
33 */
34
35 static const char * version_str = "1.04 20151219";
36
37 #define SG_ZONING_OUT_CMDLEN 16
38 #define RESET_WRITE_POINTER_SA 0x4
39
40 #define SENSE_BUFF_LEN 64 /* Arbitrary, could be larger */
41 #define DEF_PT_TIMEOUT 60 /* 60 seconds */
42
43
44 static struct option long_options[] = {
45 {"all", no_argument, 0, 'a'},
46 {"help", no_argument, 0, 'h'},
47 {"reset-all", no_argument, 0, 'R'},
48 {"reset_all", no_argument, 0, 'R'},
49 {"verbose", no_argument, 0, 'v'},
50 {"version", no_argument, 0, 'V'},
51 {"zone", required_argument, 0, 'z'},
52 {0, 0, 0, 0},
53 };
54
55
56 static void
usage()57 usage()
58 {
59 pr2serr("Usage: "
60 "sg_reset_wp [--all] [--help] [--verbose] [--version]\n"
61 " [--zone=ID] DEVICE\n");
62 pr2serr(" where:\n"
63 " --all|-a sets the ALL flag in the cdb\n"
64 " --help|-h print out usage message\n"
65 " --verbose|-v increase verbosity\n"
66 " --version|-V print version string and exit\n\n"
67 " --zone=ID|-z ID ID is the starting LBA of the zone "
68 "whose\n"
69 " write pointer is to be reset\n"
70 "Performs a SCSI RESET WRITE POINTER command. ID is decimal by "
71 "default,\nfor hex use a leading '0x' or a trailing 'h'. "
72 "Either the --zone=ID\nor --all option needs to be given.\n");
73 }
74
75 /* Invokes a SCSI RESET WRITE POINTER command (ZBC). Return of 0 -> success,
76 * various SG_LIB_CAT_* positive values or -1 -> other errors */
77 static int
sg_ll_reset_write_pointer(int sg_fd,uint64_t zid,int all,int noisy,int verbose)78 sg_ll_reset_write_pointer(int sg_fd, uint64_t zid, int all, int noisy,
79 int verbose)
80 {
81 int k, ret, res, sense_cat;
82 unsigned char rwpCmdBlk[SG_ZONING_OUT_CMDLEN] =
83 {SG_ZONING_OUT, RESET_WRITE_POINTER_SA, 0, 0, 0, 0, 0, 0,
84 0, 0, 0, 0, 0, 0, 0, 0};
85 unsigned char sense_b[SENSE_BUFF_LEN];
86 struct sg_pt_base * ptvp;
87
88 sg_put_unaligned_be64(zid, rwpCmdBlk + 2);
89 if (all)
90 rwpCmdBlk[14] = 0x1;
91 if (verbose) {
92 pr2serr(" Reset write pointer cdb: ");
93 for (k = 0; k < SG_ZONING_OUT_CMDLEN; ++k)
94 pr2serr("%02x ", rwpCmdBlk[k]);
95 pr2serr("\n");
96 }
97
98 ptvp = construct_scsi_pt_obj();
99 if (NULL == ptvp) {
100 pr2serr("Reset write pointer: out of memory\n");
101 return -1;
102 }
103 set_scsi_pt_cdb(ptvp, rwpCmdBlk, sizeof(rwpCmdBlk));
104 set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
105 res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
106 ret = sg_cmds_process_resp(ptvp, "reset write pointer", res, 0, sense_b,
107 noisy, verbose, &sense_cat);
108 if (-1 == ret)
109 ;
110 else if (-2 == ret) {
111 switch (sense_cat) {
112 case SG_LIB_CAT_RECOVERED:
113 case SG_LIB_CAT_NO_SENSE:
114 ret = 0;
115 break;
116 default:
117 ret = sense_cat;
118 break;
119 }
120 } else
121 ret = 0;
122 destruct_scsi_pt_obj(ptvp);
123 return ret;
124 }
125
126
127 int
main(int argc,char * argv[])128 main(int argc, char * argv[])
129 {
130 int sg_fd, res, c;
131 int all = 0;
132 int verbose = 0;
133 int zid_given = 0;
134 uint64_t zid = 0;
135 int64_t ll;
136 const char * device_name = NULL;
137 int ret = 0;
138
139 while (1) {
140 int option_index = 0;
141
142 c = getopt_long(argc, argv, "ahRvVz:", long_options,
143 &option_index);
144 if (c == -1)
145 break;
146
147 switch (c) {
148 case 'a':
149 case 'R':
150 ++all;
151 break;
152 case 'h':
153 case '?':
154 usage();
155 return 0;
156 case 'v':
157 ++verbose;
158 break;
159 case 'V':
160 pr2serr("version: %s\n", version_str);
161 return 0;
162 case 'z':
163 ll = sg_get_llnum(optarg);
164 if (-1 == ll) {
165 pr2serr("bad argument to '--zone=ID'\n");
166 return SG_LIB_SYNTAX_ERROR;
167 }
168 zid = (uint64_t)ll;
169 ++zid_given;
170 break;
171 default:
172 pr2serr("unrecognised option code 0x%x ??\n", c);
173 usage();
174 return SG_LIB_SYNTAX_ERROR;
175 }
176 }
177 if (optind < argc) {
178 if (NULL == device_name) {
179 device_name = argv[optind];
180 ++optind;
181 }
182 if (optind < argc) {
183 for (; optind < argc; ++optind)
184 pr2serr("Unexpected extra argument: %s\n",
185 argv[optind]);
186 usage();
187 return SG_LIB_SYNTAX_ERROR;
188 }
189 }
190
191 if ((! zid_given) && (0 == all)) {
192 pr2serr("either the --zone=ID or --all option is required\n");
193 usage();
194 return SG_LIB_SYNTAX_ERROR;
195 }
196 if (NULL == device_name) {
197 pr2serr("missing device name!\n");
198 usage();
199 return SG_LIB_SYNTAX_ERROR;
200 }
201
202 sg_fd = sg_cmds_open_device(device_name, 0, verbose);
203 if (sg_fd < 0) {
204 pr2serr("open error: %s: %s\n", device_name,
205 safe_strerror(-sg_fd));
206 return SG_LIB_FILE_ERROR;
207 }
208
209 res = sg_ll_reset_write_pointer(sg_fd, zid, all, 1, verbose);
210 ret = res;
211 if (res) {
212 if (SG_LIB_CAT_INVALID_OP == res)
213 pr2serr("Reset write pointer command not supported\n");
214 else {
215 char b[80];
216
217 sg_get_category_sense_str(res, sizeof(b), b, verbose);
218 pr2serr("Reset write pointer command: %s\n", b);
219 }
220 }
221
222 res = sg_cmds_close_device(sg_fd);
223 if (res < 0) {
224 pr2serr("close error: %s\n", safe_strerror(-res));
225 if (0 == ret)
226 return SG_LIB_FILE_ERROR;
227 }
228 return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
229 }
230