1 /* A utility program originally written for the Linux OS SCSI subsystem.
2 * Copyright (C) 1999-2022 D. Gilbert
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2, or (at your option)
6 * any later version.
7 *
8 * SPDX-License-Identifier: GPL-2.0-or-later
9 *
10 * This program send either device, bus or host resets to device,
11 * or bus or host associated with the given sg device. This is a Linux
12 * only utility (perhaps Android as well).
13 */
14
15 #include <unistd.h>
16 #include <fcntl.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <stdarg.h>
20 #include <stdbool.h>
21 #include <string.h>
22 #include <errno.h>
23 #include <sys/ioctl.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <getopt.h>
27
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31 #include "sg_io_linux.h"
32
33
34 #define ME "sg_reset: "
35
36 static const char * version_str = "0.68 20220308";
37
38 #ifndef SG_SCSI_RESET
39 #define SG_SCSI_RESET 0x2284
40 #endif
41
42 #ifndef SG_SCSI_RESET_NOTHING
43 #define SG_SCSI_RESET_NOTHING 0
44 #define SG_SCSI_RESET_DEVICE 1
45 #define SG_SCSI_RESET_BUS 2
46 #define SG_SCSI_RESET_HOST 3
47 #endif
48
49 #ifndef SG_SCSI_RESET_TARGET
50 #define SG_SCSI_RESET_TARGET 4
51 #endif
52
53 #ifndef SG_SCSI_RESET_NO_ESCALATE
54 #define SG_SCSI_RESET_NO_ESCALATE 0x100
55 #endif
56
57 static struct option long_options[] = {
58 {"bus", no_argument, 0, 'b'},
59 {"device", no_argument, 0, 'd'},
60 {"help", no_argument, 0, 'z'},
61 {"host", no_argument, 0, 'H'},
62 {"no-esc", no_argument, 0, 'N'},
63 {"no_esc", no_argument, 0, 'N'},
64 {"no-escalate", no_argument, 0, 'N'},
65 {"target", no_argument, 0, 't'},
66 {"verbose", no_argument, 0, 'v'},
67 {"version", no_argument, 0, 'V'},
68 {0, 0, 0, 0},
69 };
70
71 #if defined(__GNUC__) || defined(__clang__)
72 static int pr2serr(const char * fmt, ...)
73 __attribute__ ((format (printf, 1, 2)));
74 #else
75 static int pr2serr(const char * fmt, ...);
76 #endif
77
78
79 static int
pr2serr(const char * fmt,...)80 pr2serr(const char * fmt, ...)
81 {
82 va_list args;
83 int n;
84
85 va_start(args, fmt);
86 n = vfprintf(stderr, fmt, args);
87 va_end(args);
88 return n;
89 }
90
91 static void
usage(int compat_mode)92 usage(int compat_mode)
93 {
94 pr2serr("Usage: sg_reset [--bus] [--device] [--help] [--host] [--no-esc] "
95 "[--no-escalate] [--target]\n"
96 " [--verbose] [--version] DEVICE\n"
97 " where:\n"
98 " --bus|-b SCSI bus reset (SPI concept), might be all "
99 "targets\n"
100 " --device|-d device (logical unit) reset\n");
101 if (compat_mode) {
102 pr2serr(" --help|-z print usage information then exit\n"
103 " --host|-h|-H host (bus adapter: HBA) reset\n");
104 } else {
105 pr2serr(" --help|-h print usage information then exit\n"
106 " --host|-H host (bus adapter: HBA) reset\n");
107 }
108 pr2serr(" --no-esc|-N overrides default action and only does "
109 "reset requested\n"
110 " --no-escalate The same as --no-esc|-N\n"
111 " --target|-t target reset. The target holds the DEVICE "
112 "and perhaps\n"
113 " other LUs\n"
114 " --verbose|-v increase the level of verbosity\n"
115 " --version|-V print version number then exit\n\n"
116 "Use SG_SCSI_RESET ioctl to send a reset to the "
117 "host/bus/target/device\nalong the DEVICE path. The DEVICE "
118 "itself is known as a logical unit (LU)\nin SCSI terminology.\n"
119 "Be warned: if the '-N' option is not given then if '-d' "
120 "fails then a\ntarget reset ('-t') is instigated. And it "
121 "'-t' fails then a bus reset\n('-b') is instigated. And if "
122 "'-b' fails then a host reset ('h') is\ninstigated. It is "
123 "recommended to use '-N' to stop the reset escalation.\n"
124 );
125 }
126
127
main(int argc,char * argv[])128 int main(int argc, char * argv[])
129 {
130 bool do_device_reset = false;
131 bool do_bus_reset = false;
132 bool do_host_reset = false;
133 bool no_escalate = false;
134 bool do_target_reset = false;
135 int c, sg_fd, res, k, hold_errno;
136 int verbose = 0;
137 char * device_name = NULL;
138 char * cp = NULL;
139
140 cp = getenv("SG3_UTILS_OLD_OPTS");
141 if (NULL == cp)
142 cp = getenv("SG_RESET_OLD_OPTS");
143
144 while (1) {
145 int option_index = 0;
146
147 c = getopt_long(argc, argv, "bdhHNtvVz", long_options,
148 &option_index);
149 if (c == -1)
150 break;
151
152 switch (c) {
153 case 'b':
154 do_bus_reset = true;
155 break;
156 case 'd':
157 do_device_reset = true;
158 break;
159 case 'h':
160 if (cp) {
161 do_host_reset = true;
162 break;
163 } else {
164 usage(!!cp);
165 return 0;
166 }
167 case 'H':
168 do_host_reset = true;
169 break;
170 case 'N':
171 no_escalate = true;
172 break;
173 case 't':
174 do_target_reset = true;
175 break;
176 case 'v':
177 ++verbose;
178 break;
179 case 'V':
180 pr2serr(ME "version: %s\n", version_str);
181 return 0;
182 case 'z':
183 usage(!!cp);
184 return 0;
185 default:
186 pr2serr("unrecognised option code 0x%x ??\n", c);
187 usage(!!cp);
188 return SG_LIB_SYNTAX_ERROR;
189 }
190 }
191
192 if (optind < argc) {
193 if (NULL == device_name) {
194 device_name = argv[optind];
195 ++optind;
196 }
197 if (optind < argc) {
198 for (; optind < argc; ++optind)
199 pr2serr("Unexpected extra argument: %s\n", argv[optind]);
200 usage(!!cp);
201 return SG_LIB_SYNTAX_ERROR;
202 }
203 }
204 if (NULL == device_name) {
205 pr2serr("Missing DEVICE name. Use '--help' to see usage.\n");
206 return SG_LIB_SYNTAX_ERROR;
207 }
208
209 if (cp && (0 == verbose))
210 ++verbose; // older behaviour was more verbose
211
212 if (((int)do_device_reset + (int)do_target_reset + (int)do_bus_reset +
213 (int)do_host_reset) > 1) {
214 pr2serr("Can only request one type of reset per invocation\n");
215 return 1;
216 }
217
218 sg_fd = open(device_name, O_RDWR | O_NONBLOCK);
219 if (sg_fd < 0) {
220 pr2serr(ME "open error: %s: ", device_name);
221 perror("");
222 return 1;
223 }
224
225 k = SG_SCSI_RESET_NOTHING;
226 if (do_device_reset) {
227 if (verbose)
228 printf(ME "starting device reset\n");
229 k = SG_SCSI_RESET_DEVICE;
230 }
231 else if (do_target_reset) {
232 if (verbose)
233 printf(ME "starting target reset\n");
234 k = SG_SCSI_RESET_TARGET;
235 }
236 else if (do_bus_reset) {
237 if (verbose)
238 printf(ME "starting bus reset\n");
239 k = SG_SCSI_RESET_BUS;
240 }
241 else if (do_host_reset) {
242 if (verbose)
243 printf(ME "starting host reset\n");
244 k = SG_SCSI_RESET_HOST;
245 }
246 if (no_escalate)
247 k += SG_SCSI_RESET_NO_ESCALATE;
248 if (verbose > 2)
249 pr2serr(" third argument to ioctl(SG_SCSI_RESET) is 0x%x\n", k);
250
251 res = ioctl(sg_fd, SG_SCSI_RESET, &k);
252 if (res < 0) {
253 hold_errno = errno;
254 switch (errno) {
255 case EBUSY:
256 pr2serr(ME "BUSY, may be resetting now\n");
257 break;
258 case ENODEV:
259 pr2serr(ME "'no device' error, may be temporary while device is "
260 "resetting\n");
261 break;
262 case EAGAIN:
263 pr2serr(ME "try again later, may be resetting now\n");
264 break;
265 case EIO:
266 pr2serr(ME "reset (for value=0x%x) may not be available\n", k);
267 break;
268 case EPERM:
269 case EACCES:
270 pr2serr(ME "reset requires CAP_SYS_ADMIN (root) permission\n");
271 break;
272 case EINVAL:
273 pr2serr(ME "SG_SCSI_RESET not supported (for value=0x%x)\n", k);
274 #if defined(__GNUC__)
275 #if (__GNUC__ >= 7)
276 __attribute__((fallthrough));
277 /* FALL THROUGH */
278 #endif
279 #endif
280 default:
281 perror(ME "SG_SCSI_RESET failed");
282 break;
283 }
284 if (verbose > 1)
285 pr2serr(ME "ioctl(SG_SCSI_RESET) returned %d, errno=%d\n", res,
286 hold_errno);
287 close(sg_fd);
288 return 1;
289 }
290
291 if (no_escalate)
292 k -= SG_SCSI_RESET_NO_ESCALATE;
293 if (verbose) {
294 if (SG_SCSI_RESET_NOTHING == k)
295 printf(ME "did nothing, device is normal mode\n");
296 else if (SG_SCSI_RESET_DEVICE == k)
297 printf(ME "completed device %sreset\n", (no_escalate ?
298 "" : "(or target or bus or host) "));
299 else if (SG_SCSI_RESET_TARGET == k)
300 printf(ME "completed target %sreset\n", (no_escalate ?
301 "" : "(or bus or host) "));
302 else if (SG_SCSI_RESET_BUS == k)
303 printf(ME "completed bus %sreset\n", (no_escalate ?
304 "" : "(or host) "));
305 else if (SG_SCSI_RESET_HOST == k)
306 printf(ME "completed host reset\n");
307 }
308
309 if (close(sg_fd) < 0) {
310 perror(ME "close error");
311 return 1;
312 }
313 return 0;
314 }
315