]>
Commit | Line | Data |
---|---|---|
eb63b9b8 KZ |
1 | /* |
2 | * blockdev.c --- Do various simple block device ioctls from the command line | |
3 | * aeb, 991028 | |
4 | */ | |
5 | ||
6 | #include <stdio.h> | |
7 | #include <fcntl.h> | |
eb63b9b8 KZ |
8 | #include <stdlib.h> |
9 | #include <string.h> | |
10 | #include <unistd.h> | |
11 | #include <sys/ioctl.h> | |
3281d426 | 12 | #include <errno.h> |
eb63b9b8 | 13 | |
515422fd | 14 | #include "c.h" |
eb63b9b8 | 15 | #include "nls.h" |
bc8b6383 | 16 | #include "blkdev.h" |
14c76d8b | 17 | #include "pathnames.h" |
45ca68ec | 18 | #include "closestream.h" |
40775268 | 19 | #include "strutils.h" |
569d1dac | 20 | #include "sysfs.h" |
612721db | 21 | |
eb63b9b8 | 22 | struct bdc { |
c3f62be7 KZ |
23 | long ioc; /* ioctl code */ |
24 | const char *iocname; /* ioctl name (e.g. BLKROSET) */ | |
25 | long argval; /* default argument */ | |
26 | ||
27 | const char *name; /* --setfoo */ | |
28 | const char *argname; /* argument name or NULL */ | |
29 | ||
30 | const char *help; | |
31 | ||
32 | int argtype; | |
33 | int flags; | |
34 | }; | |
35 | ||
36 | /* command flags */ | |
37 | enum { | |
38 | FL_NOPTR = (1 << 1), /* does not assume pointer (ARG_INT only)*/ | |
39 | FL_NORESULT = (1 << 2) /* does not return any data */ | |
40 | }; | |
41 | ||
42 | /* ioctl argument types */ | |
43 | enum { | |
44 | ARG_NONE, | |
726fa24e | 45 | ARG_USHRT, |
c3f62be7 | 46 | ARG_INT, |
726fa24e | 47 | ARG_UINT, |
c3f62be7 KZ |
48 | ARG_LONG, |
49 | ARG_ULONG, | |
50 | ARG_LLONG, | |
51 | ARG_ULLONG | |
52 | }; | |
53 | ||
54 | #define IOCTL_ENTRY( io ) .ioc = io, .iocname = # io | |
55 | ||
00239f33 | 56 | static const struct bdc bdcms[] = |
c3f62be7 KZ |
57 | { |
58 | { | |
59 | IOCTL_ENTRY(BLKROSET), | |
60 | .name = "--setro", | |
61 | .argtype = ARG_INT, | |
62 | .argval = 1, | |
63 | .flags = FL_NORESULT, | |
64 | .help = N_("set read-only") | |
65 | },{ | |
66 | IOCTL_ENTRY(BLKROSET), | |
67 | .name = "--setrw", | |
68 | .argtype = ARG_INT, | |
69 | .argval = 0, | |
70 | .flags = FL_NORESULT, | |
71 | .help = N_("set read-write") | |
72 | },{ | |
73 | IOCTL_ENTRY(BLKROGET), | |
74 | .name = "--getro", | |
75 | .argtype = ARG_INT, | |
76 | .argval = -1, | |
77 | .help = N_("get read-only") | |
1519ab5f KZ |
78 | },{ |
79 | IOCTL_ENTRY(BLKDISCARDZEROES), | |
80 | .name = "--getdiscardzeroes", | |
81 | .argtype = ARG_UINT, | |
82 | .argval = -1, | |
83 | .help = N_("get discard zeroes support status") | |
c3f62be7 KZ |
84 | },{ |
85 | IOCTL_ENTRY(BLKSSZGET), | |
86 | .name = "--getss", | |
87 | .argtype = ARG_INT, | |
88 | .argval = -1, | |
d965d63f KZ |
89 | .help = N_("get logical block (sector) size") |
90 | },{ | |
91 | IOCTL_ENTRY(BLKPBSZGET), | |
92 | .name = "--getpbsz", | |
93 | .argtype = ARG_UINT, | |
94 | .argval = -1, | |
95 | .help = N_("get physical block (sector) size") | |
96 | },{ | |
97 | IOCTL_ENTRY(BLKIOMIN), | |
98 | .name = "--getiomin", | |
99 | .argtype = ARG_UINT, | |
100 | .argval = -1, | |
101 | .help = N_("get minimum I/O size") | |
102 | },{ | |
103 | IOCTL_ENTRY(BLKIOOPT), | |
104 | .name = "--getioopt", | |
105 | .argtype = ARG_UINT, | |
106 | .argval = -1, | |
107 | .help = N_("get optimal I/O size") | |
108 | },{ | |
109 | IOCTL_ENTRY(BLKALIGNOFF), | |
110 | .name = "--getalignoff", | |
111 | .argtype = ARG_INT, | |
ed98508a | 112 | .argval = -1, |
455fdf4a | 113 | .help = N_("get alignment offset in bytes") |
d965d63f KZ |
114 | },{ |
115 | IOCTL_ENTRY(BLKSECTGET), | |
116 | .name = "--getmaxsect", | |
117 | .argtype = ARG_USHRT, | |
118 | .argval = -1, | |
119 | .help = N_("get max sectors per request") | |
c3f62be7 KZ |
120 | },{ |
121 | IOCTL_ENTRY(BLKBSZGET), | |
122 | .name = "--getbsz", | |
123 | .argtype = ARG_INT, | |
124 | .argval = -1, | |
125 | .help = N_("get blocksize") | |
39d2e706 KZ |
126 | },{ |
127 | IOCTL_ENTRY(BLKBSZSET), | |
128 | .name = "--setbsz", | |
129 | .argname = "<bytes>", | |
130 | .argtype = ARG_INT, | |
131 | .flags = FL_NORESULT, | |
7ab32ae6 | 132 | .help = N_("set blocksize on file descriptor opening the block device") |
c3f62be7 KZ |
133 | },{ |
134 | IOCTL_ENTRY(BLKGETSIZE), | |
135 | .name = "--getsize", | |
136 | .argtype = ARG_ULONG, | |
137 | .argval = -1, | |
455fdf4a | 138 | .help = N_("get 32-bit sector count (deprecated, use --getsz)") |
c3f62be7 KZ |
139 | },{ |
140 | IOCTL_ENTRY(BLKGETSIZE64), | |
141 | .name = "--getsize64", | |
142 | .argtype = ARG_ULLONG, | |
143 | .argval = -1, | |
144 | .help = N_("get size in bytes") | |
145 | },{ | |
146 | IOCTL_ENTRY(BLKRASET), | |
147 | .name = "--setra", | |
455fdf4a | 148 | .argname = "<sectors>", |
c3f62be7 KZ |
149 | .argtype = ARG_INT, |
150 | .flags = FL_NOPTR | FL_NORESULT, | |
151 | .help = N_("set readahead") | |
152 | },{ | |
153 | IOCTL_ENTRY(BLKRAGET), | |
154 | .name = "--getra", | |
155 | .argtype = ARG_LONG, | |
156 | .argval = -1, | |
157 | .help = N_("get readahead") | |
158 | },{ | |
159 | IOCTL_ENTRY(BLKFRASET), | |
160 | .name = "--setfra", | |
455fdf4a | 161 | .argname = "<sectors>", |
c3f62be7 KZ |
162 | .argtype = ARG_INT, |
163 | .flags = FL_NOPTR | FL_NORESULT, | |
164 | .help = N_("set filesystem readahead") | |
165 | },{ | |
166 | IOCTL_ENTRY(BLKFRAGET), | |
167 | .name = "--getfra", | |
168 | .argtype = ARG_LONG, | |
169 | .argval = -1, | |
170 | .help = N_("get filesystem readahead") | |
c10adc20 TW |
171 | },{ |
172 | IOCTL_ENTRY(BLKGETDISKSEQ), | |
173 | .name = "--getdiskseq", | |
174 | .argtype = ARG_ULLONG, | |
175 | .argval = -1, | |
176 | .help = N_("get disk sequence number") | |
c3f62be7 KZ |
177 | },{ |
178 | IOCTL_ENTRY(BLKFLSBUF), | |
179 | .name = "--flushbufs", | |
180 | .help = N_("flush buffers") | |
181 | },{ | |
182 | IOCTL_ENTRY(BLKRRPART), | |
183 | .name = "--rereadpt", | |
184 | .help = N_("reread partition table") | |
185 | } | |
eb63b9b8 KZ |
186 | }; |
187 | ||
9325dbfd | 188 | static void __attribute__((__noreturn__)) usage(void) |
bded204c | 189 | { |
75737ad6 | 190 | size_t i; |
9325dbfd | 191 | |
a861538c RM |
192 | fputs(USAGE_HEADER, stdout); |
193 | printf(_( | |
194 | " %1$s [-v|-q] commands devices\n" | |
195 | " %1$s --report [devices]\n" | |
196 | " %1$s -h|-V\n" | |
197 | ), program_invocation_short_name); | |
a424171c | 198 | |
a861538c RM |
199 | fputs(USAGE_SEPARATOR, stdout); |
200 | puts( _("Call block device ioctls from the command line.")); | |
201 | ||
202 | fputs(USAGE_OPTIONS, stdout); | |
203 | puts( _(" -q quiet mode")); | |
204 | puts( _(" -v verbose mode")); | |
205 | puts( _(" --report print report for specified (or all) devices")); | |
206 | fputs(USAGE_SEPARATOR, stdout); | |
f45f3ec3 | 207 | printf(USAGE_HELP_OPTIONS(16)); |
a861538c RM |
208 | |
209 | fputs(USAGE_SEPARATOR, stdout); | |
210 | puts( _("Available commands:")); | |
211 | printf(_(" %-25s get size in 512-byte sectors\n"), "--getsz"); | |
515422fd | 212 | for (i = 0; i < ARRAY_SIZE(bdcms); i++) { |
eb63b9b8 | 213 | if (bdcms[i].argname) |
a861538c | 214 | printf(" %s %-*s %s\n", bdcms[i].name, |
bded204c SK |
215 | (int)(24 - strlen(bdcms[i].name)), |
216 | bdcms[i].argname, _(bdcms[i].help)); | |
a424171c | 217 | else |
a861538c | 218 | printf(" %-25s %s\n", bdcms[i].name, |
bded204c | 219 | _(bdcms[i].help)); |
eb63b9b8 | 220 | } |
9325dbfd | 221 | |
43c960c8 | 222 | printf(USAGE_MAN_TAIL("blockdev(8)")); |
9325dbfd | 223 | exit(EXIT_SUCCESS); |
eb63b9b8 KZ |
224 | } |
225 | ||
baaa4479 SK |
226 | static int find_cmd(char *s) |
227 | { | |
75737ad6 | 228 | size_t j; |
eb63b9b8 | 229 | |
515422fd | 230 | for (j = 0; j < ARRAY_SIZE(bdcms); j++) |
eb63b9b8 KZ |
231 | if (!strcmp(s, bdcms[j].name)) |
232 | return j; | |
233 | return -1; | |
234 | } | |
235 | ||
f61a097b WG |
236 | static void do_commands(int fd, char **argv, int d); |
237 | static void report_header(void); | |
238 | static void report_device(char *device, int quiet); | |
239 | static void report_all_devices(void); | |
eb63b9b8 | 240 | |
baaa4479 SK |
241 | int main(int argc, char **argv) |
242 | { | |
eb63b9b8 | 243 | int fd, d, j, k; |
eb63b9b8 | 244 | |
eb63b9b8 KZ |
245 | setlocale(LC_ALL, ""); |
246 | bindtextdomain(PACKAGE, LOCALEDIR); | |
247 | textdomain(PACKAGE); | |
2c308875 | 248 | close_stdout_atexit(); |
eb63b9b8 | 249 | |
9325dbfd RM |
250 | if (argc < 2) { |
251 | warnx(_("not enough arguments")); | |
252 | errtryhelp(EXIT_FAILURE); | |
253 | } | |
eb63b9b8 KZ |
254 | |
255 | /* -V not together with commands */ | |
2c308875 KZ |
256 | if (!strcmp(argv[1], "-V") || !strcmp(argv[1], "--version")) |
257 | print_version(EXIT_SUCCESS); | |
bded204c | 258 | if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) |
9325dbfd | 259 | usage(); |
eb63b9b8 | 260 | |
612721db KZ |
261 | /* --report not together with other commands */ |
262 | if (!strcmp(argv[1], "--report")) { | |
263 | report_header(); | |
264 | if (argc > 2) { | |
265 | for (d = 2; d < argc; d++) | |
266 | report_device(argv[d], 0); | |
267 | } else { | |
268 | report_all_devices(); | |
269 | } | |
6b515d0b | 270 | return EXIT_SUCCESS; |
612721db KZ |
271 | } |
272 | ||
eb63b9b8 KZ |
273 | /* do each of the commands on each of the devices */ |
274 | /* devices start after last command */ | |
275 | for (d = 1; d < argc; d++) { | |
276 | j = find_cmd(argv[d]); | |
277 | if (j >= 0) { | |
c3f62be7 | 278 | if (bdcms[j].argname) |
eb63b9b8 KZ |
279 | d++; |
280 | continue; | |
281 | } | |
c129767e KZ |
282 | if (!strcmp(argv[d], "--getsz")) |
283 | continue; | |
eb63b9b8 KZ |
284 | if (!strcmp(argv[d], "--")) { |
285 | d++; | |
286 | break; | |
287 | } | |
288 | if (argv[d][0] != '-') | |
289 | break; | |
290 | } | |
291 | ||
9325dbfd RM |
292 | if (d >= argc) { |
293 | warnx(_("no device specified")); | |
294 | errtryhelp(EXIT_FAILURE); | |
295 | } | |
eb63b9b8 KZ |
296 | |
297 | for (k = d; k < argc; k++) { | |
298 | fd = open(argv[k], O_RDONLY, 0); | |
6b515d0b SK |
299 | if (fd < 0) |
300 | err(EXIT_FAILURE, _("cannot open %s"), argv[k]); | |
eb63b9b8 | 301 | do_commands(fd, argv, d); |
e8f26419 | 302 | close(fd); |
eb63b9b8 | 303 | } |
6b515d0b | 304 | return EXIT_SUCCESS; |
eb63b9b8 KZ |
305 | } |
306 | ||
f61a097b | 307 | static void do_commands(int fd, char **argv, int d) |
baaa4479 | 308 | { |
eb63b9b8 | 309 | int res, i, j; |
15705de0 KZ |
310 | int iarg = 0; |
311 | unsigned int uarg = 0; | |
312 | unsigned short huarg = 0; | |
313 | long larg = 0; | |
314 | long long llarg = 0; | |
315 | unsigned long lu = 0; | |
316 | unsigned long long llu = 0; | |
eb63b9b8 KZ |
317 | int verbose = 0; |
318 | ||
319 | for (i = 1; i < d; i++) { | |
320 | if (!strcmp(argv[i], "-v")) { | |
321 | verbose = 1; | |
322 | continue; | |
baaa4479 | 323 | } |
eb63b9b8 KZ |
324 | if (!strcmp(argv[i], "-q")) { |
325 | verbose = 0; | |
326 | continue; | |
327 | } | |
328 | ||
c129767e | 329 | if (!strcmp(argv[i], "--getsz")) { |
bc8b6383 | 330 | res = blkdev_get_sectors(fd, &llu); |
c129767e | 331 | if (res == 0) |
bc8b6383 | 332 | printf("%lld\n", llu); |
c129767e | 333 | else |
baaa4479 SK |
334 | errx(EXIT_FAILURE, |
335 | _("could not get device size")); | |
c129767e KZ |
336 | continue; |
337 | } | |
338 | ||
eb63b9b8 KZ |
339 | j = find_cmd(argv[i]); |
340 | if (j == -1) { | |
cc4d8d72 | 341 | warnx(_("Unknown command: %s"), argv[i]); |
9325dbfd | 342 | errtryhelp(EXIT_FAILURE); |
eb63b9b8 KZ |
343 | } |
344 | ||
baaa4479 | 345 | switch (bdcms[j].argtype) { |
eb63b9b8 | 346 | default: |
c3f62be7 | 347 | case ARG_NONE: |
eb63b9b8 KZ |
348 | res = ioctl(fd, bdcms[j].ioc, 0); |
349 | break; | |
726fa24e KZ |
350 | case ARG_USHRT: |
351 | huarg = bdcms[j].argval; | |
352 | res = ioctl(fd, bdcms[j].ioc, &huarg); | |
353 | break; | |
c3f62be7 KZ |
354 | case ARG_INT: |
355 | if (bdcms[j].argname) { | |
baaa4479 | 356 | if (i == d - 1) { |
cc4d8d72 | 357 | warnx(_("%s requires an argument"), |
baaa4479 | 358 | bdcms[j].name); |
9325dbfd | 359 | errtryhelp(EXIT_FAILURE); |
c3f62be7 | 360 | } |
40775268 | 361 | iarg = strtos32_or_err(argv[++i], _("failed to parse command argument")); |
c3f62be7 KZ |
362 | } else |
363 | iarg = bdcms[j].argval; | |
364 | ||
365 | res = bdcms[j].flags & FL_NOPTR ? | |
baaa4479 SK |
366 | ioctl(fd, bdcms[j].ioc, iarg) : |
367 | ioctl(fd, bdcms[j].ioc, &iarg); | |
eb63b9b8 | 368 | break; |
726fa24e KZ |
369 | case ARG_UINT: |
370 | uarg = bdcms[j].argval; | |
371 | res = ioctl(fd, bdcms[j].ioc, &uarg); | |
372 | break; | |
c3f62be7 | 373 | case ARG_LONG: |
eb63b9b8 KZ |
374 | larg = bdcms[j].argval; |
375 | res = ioctl(fd, bdcms[j].ioc, &larg); | |
376 | break; | |
c3f62be7 | 377 | case ARG_LLONG: |
c129767e KZ |
378 | llarg = bdcms[j].argval; |
379 | res = ioctl(fd, bdcms[j].ioc, &llarg); | |
380 | break; | |
c3f62be7 | 381 | case ARG_ULONG: |
1dea05a8 KZ |
382 | lu = bdcms[j].argval; |
383 | res = ioctl(fd, bdcms[j].ioc, &lu); | |
384 | break; | |
c3f62be7 | 385 | case ARG_ULLONG: |
1dea05a8 KZ |
386 | llu = bdcms[j].argval; |
387 | res = ioctl(fd, bdcms[j].ioc, &llu); | |
388 | break; | |
eb63b9b8 | 389 | } |
c3f62be7 | 390 | |
eb63b9b8 | 391 | if (res == -1) { |
338a6bc5 | 392 | warn(_("ioctl error on %s"), bdcms[j].iocname); |
eb63b9b8 | 393 | if (verbose) |
65b27d36 | 394 | printf(_("%s failed.\n"), _(bdcms[j].help)); |
6b515d0b | 395 | exit(EXIT_FAILURE); |
eb63b9b8 | 396 | } |
c3f62be7 KZ |
397 | |
398 | if (bdcms[j].argtype == ARG_NONE || | |
399 | (bdcms[j].flags & FL_NORESULT)) { | |
eb63b9b8 | 400 | if (verbose) |
c3f62be7 KZ |
401 | printf(_("%s succeeded.\n"), _(bdcms[j].help)); |
402 | continue; | |
403 | } | |
404 | ||
405 | if (verbose) | |
406 | printf("%s: ", _(bdcms[j].help)); | |
407 | ||
baaa4479 | 408 | switch (bdcms[j].argtype) { |
726fa24e KZ |
409 | case ARG_USHRT: |
410 | printf("%hu\n", huarg); | |
411 | break; | |
c3f62be7 KZ |
412 | case ARG_INT: |
413 | printf("%d\n", iarg); | |
eb63b9b8 | 414 | break; |
726fa24e KZ |
415 | case ARG_UINT: |
416 | printf("%u\n", uarg); | |
417 | break; | |
c3f62be7 KZ |
418 | case ARG_LONG: |
419 | printf("%ld\n", larg); | |
c129767e | 420 | break; |
c3f62be7 KZ |
421 | case ARG_LLONG: |
422 | printf("%lld\n", llarg); | |
1dea05a8 | 423 | break; |
c3f62be7 KZ |
424 | case ARG_ULONG: |
425 | printf("%lu\n", lu); | |
1dea05a8 | 426 | break; |
c3f62be7 KZ |
427 | case ARG_ULLONG: |
428 | printf("%llu\n", llu); | |
eb63b9b8 KZ |
429 | break; |
430 | } | |
431 | } | |
432 | } | |
612721db | 433 | |
f61a097b | 434 | static void report_all_devices(void) |
baaa4479 | 435 | { |
612721db KZ |
436 | FILE *procpt; |
437 | char line[200]; | |
657d9adb | 438 | char ptname[200 + 1]; |
612721db KZ |
439 | char device[210]; |
440 | int ma, mi, sz; | |
441 | ||
14c76d8b | 442 | procpt = fopen(_PATH_PROC_PARTITIONS, "r"); |
cc4d8d72 | 443 | if (!procpt) |
14c76d8b | 444 | err(EXIT_FAILURE, _("cannot open %s"), _PATH_PROC_PARTITIONS); |
612721db KZ |
445 | |
446 | while (fgets(line, sizeof(line), procpt)) { | |
baaa4479 SK |
447 | if (sscanf(line, " %d %d %d %200[^\n ]", |
448 | &ma, &mi, &sz, ptname) != 4) | |
612721db KZ |
449 | continue; |
450 | ||
26266436 | 451 | snprintf(device, sizeof(device), "/dev/%s", ptname); |
612721db KZ |
452 | report_device(device, 1); |
453 | } | |
122db55d | 454 | |
455 | fclose(procpt); | |
612721db KZ |
456 | } |
457 | ||
f61a097b | 458 | static void report_device(char *device, int quiet) |
baaa4479 | 459 | { |
612721db KZ |
460 | int fd; |
461 | int ro, ssz, bsz; | |
bc8b6383 KZ |
462 | long ra; |
463 | unsigned long long bytes; | |
569d1dac | 464 | uint64_t start = 0; |
d73a7184 | 465 | char start_str[16] = { "\0" }; |
569d1dac | 466 | struct stat st; |
612721db KZ |
467 | |
468 | fd = open(device, O_RDONLY | O_NONBLOCK); | |
469 | if (fd < 0) { | |
470 | if (!quiet) | |
6b515d0b | 471 | warn(_("cannot open %s"), device); |
612721db KZ |
472 | return; |
473 | } | |
474 | ||
475 | ro = ssz = bsz = 0; | |
569d1dac | 476 | ra = 0; |
345ddd28 KZ |
477 | if (fstat(fd, &st) == 0) { |
478 | dev_t disk; | |
479 | struct path_cxt *pc; | |
480 | ||
481 | pc = ul_new_sysfs_path(st.st_rdev, NULL, NULL); | |
482 | if (pc && | |
483 | sysfs_blkdev_get_wholedisk(pc, NULL, 0, &disk) == 0 && | |
484 | disk != st.st_rdev) { | |
485 | ||
486 | if (ul_path_read_u64(pc, &start, "start") != 0) | |
d73a7184 | 487 | /* TRANSLATORS: Start sector not available. Max. 15 letters. */ |
26266436 | 488 | snprintf(start_str, sizeof(start_str), "%15s", _("N/A")); |
345ddd28 KZ |
489 | } |
490 | ul_unref_path(pc); | |
569d1dac | 491 | } |
9147d2ad | 492 | if (!*start_str) |
26266436 | 493 | snprintf(start_str, sizeof(start_str), "%15ju", start); |
9147d2ad | 494 | |
baaa4479 SK |
495 | if (ioctl(fd, BLKROGET, &ro) == 0 && |
496 | ioctl(fd, BLKRAGET, &ra) == 0 && | |
497 | ioctl(fd, BLKSSZGET, &ssz) == 0 && | |
498 | ioctl(fd, BLKBSZGET, &bsz) == 0 && | |
baaa4479 | 499 | blkdev_get_size(fd, &bytes) == 0) { |
c4a543ff | 500 | printf("%s %5ld %5d %5d %s %15lld %s\n", |
9147d2ad | 501 | ro ? "ro" : "rw", ra, ssz, bsz, start_str, bytes, device); |
612721db KZ |
502 | } else { |
503 | if (!quiet) | |
cc4d8d72 | 504 | warnx(_("ioctl error on %s"), device); |
612721db | 505 | } |
122db55d | 506 | |
507 | close(fd); | |
612721db KZ |
508 | } |
509 | ||
16bd8025 | 510 | static void report_header(void) |
baaa4479 | 511 | { |
d73a7184 | 512 | printf(_("RO RA SSZ BSZ StartSec Size Device\n")); |
612721db | 513 | } |