/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
* blockdev.c --- Do various simple block device ioctls from the command line
* aeb, 991028
+ *
+ * Copyright (C) 2007-2023 Karel Zak <kzak@redhat.com>
*/
-
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <errno.h>
+#ifdef HAVE_LINUX_BLKZONED_H
+#include <linux/blkzoned.h>
+#endif
#include "c.h"
#include "nls.h"
#include "blkdev.h"
#include "pathnames.h"
+#include "closestream.h"
+#include "strutils.h"
+#include "sysfs.h"
struct bdc {
long ioc; /* ioctl code */
.argname = "<bytes>",
.argtype = ARG_INT,
.flags = FL_NORESULT,
- .help = N_("set blocksize")
+ .help = N_("set blocksize on file descriptor opening the block device")
},{
IOCTL_ENTRY(BLKGETSIZE),
.name = "--getsize",
.argval = -1,
.help = N_("get filesystem readahead")
},{
+ IOCTL_ENTRY(BLKGETDISKSEQ),
+ .name = "--getdiskseq",
+ .argtype = ARG_ULLONG,
+ .argval = -1,
+ .help = N_("get disk sequence number")
+ },{
+#ifdef BLKGETZONESZ
+ IOCTL_ENTRY(BLKGETZONESZ),
+ .name = "--getzonesz",
+ .argtype = ARG_UINT,
+ .argval = -1,
+ .help = N_("get zone size")
+ },{
+#endif
IOCTL_ENTRY(BLKFLSBUF),
.name = "--flushbufs",
.help = N_("flush buffers")
}
};
-static void __attribute__ ((__noreturn__)) usage(FILE * out)
+static void __attribute__((__noreturn__)) usage(void)
{
size_t i;
- fprintf(out, _("\nUsage:\n"
- " %1$s -V\n"
- " %1$s --report [devices]\n"
- " %1$s [-v|-q] commands devices\n\n"
- "Available commands:\n"), program_invocation_short_name);
- fprintf(out, _(" %-25s get size in 512-byte sectors\n"), "--getsz");
+ fputs(USAGE_HEADER, stdout);
+ fprintf(stdout, _(
+ " %1$s [-v|-q] commands devices\n"
+ " %1$s --report [devices]\n"
+ " %1$s -h|-V\n"
+ ), program_invocation_short_name);
+
+ fputs(USAGE_SEPARATOR, stdout);
+ fputsln( _("Call block device ioctls from the command line."), stdout);
+
+ fputs(USAGE_OPTIONS, stdout);
+ fputsln( _(" -q quiet mode"), stdout);
+ fputsln( _(" -v verbose mode"), stdout);
+ fputsln( _(" --report print report for specified (or all) devices"), stdout);
+ fputs(USAGE_SEPARATOR, stdout);
+ fprintf(stdout, USAGE_HELP_OPTIONS(16));
+
+ fputs(USAGE_SEPARATOR, stdout);
+ fputsln( _("Available commands:"), stdout);
+ fprintf(stdout, _(" %-25s get size in 512-byte sectors\n"), "--getsz");
for (i = 0; i < ARRAY_SIZE(bdcms); i++) {
if (bdcms[i].argname)
- fprintf(out, " %s %-*s %s\n", bdcms[i].name,
+ fprintf(stdout, " %s %-*s %s\n", bdcms[i].name,
(int)(24 - strlen(bdcms[i].name)),
bdcms[i].argname, _(bdcms[i].help));
else
- fprintf(out, " %-25s %s\n", bdcms[i].name,
+ fprintf(stdout, " %-25s %s\n", bdcms[i].name,
_(bdcms[i].help));
}
- fputc('\n', out);
- exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+
+ fprintf(stdout, USAGE_MAN_TAIL("blockdev(8)"));
+ exit(EXIT_SUCCESS);
}
static int find_cmd(char *s)
return -1;
}
-void do_commands(int fd, char **argv, int d);
-void report_header(void);
-void report_device(char *device, int quiet);
-void report_all_devices(void);
+static void do_commands(int fd, char **argv, int d);
+static void report_header(void);
+static int report_device(char *device, int quiet);
+static int report_all_devices(void);
int main(int argc, char **argv)
{
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
+ close_stdout_atexit();
- if (argc < 2)
- usage(stderr);
+ if (argc < 2) {
+ warnx(_("not enough arguments"));
+ errtryhelp(EXIT_FAILURE);
+ }
/* -V not together with commands */
- if (!strcmp(argv[1], "-V") || !strcmp(argv[1], "--version")) {
- printf(_("%s (%s)\n"), program_invocation_short_name,
- PACKAGE_STRING);
- return EXIT_SUCCESS;
- }
+ if (!strcmp(argv[1], "-V") || !strcmp(argv[1], "--version"))
+ print_version(EXIT_SUCCESS);
if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
- usage(stdout);
+ usage();
/* --report not together with other commands */
if (!strcmp(argv[1], "--report")) {
+ int rc = 0;
report_header();
if (argc > 2) {
for (d = 2; d < argc; d++)
- report_device(argv[d], 0);
+ rc += report_device(argv[d], 0);
} else {
- report_all_devices();
+ rc = report_all_devices();
}
- return EXIT_SUCCESS;
+ return rc ? EXIT_FAILURE : EXIT_SUCCESS;
}
/* do each of the commands on each of the devices */
break;
}
- if (d >= argc)
- usage(stderr);
+ if (d >= argc) {
+ warnx(_("no device specified"));
+ errtryhelp(EXIT_FAILURE);
+ }
for (k = d; k < argc; k++) {
fd = open(argv[k], O_RDONLY, 0);
return EXIT_SUCCESS;
}
-void do_commands(int fd, char **argv, int d)
+static void do_commands(int fd, char **argv, int d)
{
int res, i, j;
- int iarg;
- unsigned int uarg;
- unsigned short huarg;
- long larg;
- long long llarg;
- unsigned long lu;
- unsigned long long llu;
+ int iarg = 0;
+ unsigned int uarg = 0;
+ unsigned short huarg = 0;
+ long larg = 0;
+ long long llarg = 0;
+ unsigned long lu = 0;
+ unsigned long long llu = 0;
int verbose = 0;
for (i = 1; i < d; i++) {
if (!strcmp(argv[i], "--getsz")) {
res = blkdev_get_sectors(fd, &llu);
- if (res == 0)
+ if (res == 0) {
+ if (verbose)
+ printf(_("get size in 512-byte sectors: "));
printf("%lld\n", llu);
+ }
else
errx(EXIT_FAILURE,
_("could not get device size"));
j = find_cmd(argv[i]);
if (j == -1) {
warnx(_("Unknown command: %s"), argv[i]);
- usage(stderr);
+ errtryhelp(EXIT_FAILURE);
}
switch (bdcms[j].argtype) {
if (i == d - 1) {
warnx(_("%s requires an argument"),
bdcms[j].name);
- usage(stderr);
+ errtryhelp(EXIT_FAILURE);
}
- iarg = atoi(argv[++i]);
+ iarg = strtos32_or_err(argv[++i], _("failed to parse command argument"));
} else
iarg = bdcms[j].argval;
}
if (res == -1) {
- perror(bdcms[j].iocname);
+ warn(_("ioctl error on %s"), bdcms[j].iocname);
if (verbose)
printf(_("%s failed.\n"), _(bdcms[j].help));
exit(EXIT_FAILURE);
}
}
-void report_all_devices(void)
+static int report_all_devices(void)
{
FILE *procpt;
char line[200];
- char ptname[200];
+ char ptname[200 + 1];
char device[210];
int ma, mi, sz;
+ int rc = 0;
procpt = fopen(_PATH_PROC_PARTITIONS, "r");
if (!procpt)
&ma, &mi, &sz, ptname) != 4)
continue;
- sprintf(device, "/dev/%s", ptname);
- report_device(device, 1);
+ snprintf(device, sizeof(device), "/dev/%s", ptname);
+ rc += report_device(device, 1);
}
fclose(procpt);
+ return rc;
}
-void report_device(char *device, int quiet)
+static int report_device(char *device, int quiet)
{
int fd;
int ro, ssz, bsz;
+ int rc = 0;
long ra;
unsigned long long bytes;
- struct hd_geometry g;
+ uint64_t start = 0;
+ char start_str[16] = { "\0" };
+ struct stat st;
fd = open(device, O_RDONLY | O_NONBLOCK);
if (fd < 0) {
if (!quiet)
warn(_("cannot open %s"), device);
- return;
+ return 1;
}
ro = ssz = bsz = 0;
- g.start = ra = 0;
+ ra = 0;
+ if (fstat(fd, &st) == 0) {
+ dev_t disk;
+ struct path_cxt *pc;
+
+ pc = ul_new_sysfs_path(st.st_rdev, NULL, NULL);
+ if (pc &&
+ sysfs_blkdev_get_wholedisk(pc, NULL, 0, &disk) == 0 &&
+ disk != st.st_rdev) {
+
+ if (ul_path_read_u64(pc, &start, "start") != 0)
+ /* TRANSLATORS: Start sector not available. Max. 15 letters. */
+ snprintf(start_str, sizeof(start_str), "%15s", _("N/A"));
+ }
+ ul_unref_path(pc);
+ }
+ if (!*start_str)
+ snprintf(start_str, sizeof(start_str), "%15ju", start);
+
if (ioctl(fd, BLKROGET, &ro) == 0 &&
ioctl(fd, BLKRAGET, &ra) == 0 &&
ioctl(fd, BLKSSZGET, &ssz) == 0 &&
ioctl(fd, BLKBSZGET, &bsz) == 0 &&
- ioctl(fd, HDIO_GETGEO, &g) == 0 &&
blkdev_get_size(fd, &bytes) == 0) {
- printf("%s %5ld %5d %5d %10ld %15lld %s\n",
- ro ? "ro" : "rw", ra, ssz, bsz, g.start, bytes, device);
+ printf("%s %5ld %5d %5d %s %15lld %s\n",
+ ro ? "ro" : "rw", ra, ssz, bsz, start_str, bytes, device);
} else {
if (!quiet)
warnx(_("ioctl error on %s"), device);
+ rc = 1;
}
close(fd);
+ return rc;
}
-void report_header()
+static void report_header(void)
{
- printf(_("RO RA SSZ BSZ StartSec Size Device\n"));
+ printf(_("RO RA SSZ BSZ StartSec Size Device\n"));
}