blob: 288c102dfd1b2c96534fa22e50482c7336fbc38c [file] [log] [blame]
/*
* blockdev.c --- Do various simple block device ioctls from the command line
* aeb, 991028
*/
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <errno.h>
#include "c.h"
#include "nls.h"
#include "blkdev.h"
const char *progname;
struct bdc {
long ioc; /* ioctl code */
const char *iocname; /* ioctl name (e.g. BLKROSET) */
long argval; /* default argument */
const char *name; /* --setfoo */
const char *argname; /* argument name or NULL */
const char *help;
int argtype;
int flags;
};
/* command flags */
enum {
FL_NOPTR = (1 << 1), /* does not assume pointer (ARG_INT only)*/
FL_NORESULT = (1 << 2) /* does not return any data */
};
/* ioctl argument types */
enum {
ARG_NONE,
ARG_USHRT,
ARG_INT,
ARG_UINT,
ARG_LONG,
ARG_ULONG,
ARG_LLONG,
ARG_ULLONG
};
#define IOCTL_ENTRY( io ) .ioc = io, .iocname = # io
struct bdc bdcms[] =
{
{
IOCTL_ENTRY(BLKROSET),
.name = "--setro",
.argtype = ARG_INT,
.argval = 1,
.flags = FL_NORESULT,
.help = N_("set read-only")
},{
IOCTL_ENTRY(BLKROSET),
.name = "--setrw",
.argtype = ARG_INT,
.argval = 0,
.flags = FL_NORESULT,
.help = N_("set read-write")
},{
IOCTL_ENTRY(BLKROGET),
.name = "--getro",
.argtype = ARG_INT,
.argval = -1,
.help = N_("get read-only")
},{
IOCTL_ENTRY(BLKSSZGET),
.name = "--getss",
.argtype = ARG_INT,
.argval = -1,
.help = N_("get logical block (sector) size")
},{
IOCTL_ENTRY(BLKPBSZGET),
.name = "--getpbsz",
.argtype = ARG_UINT,
.argval = -1,
.help = N_("get physical block (sector) size")
},{
IOCTL_ENTRY(BLKIOMIN),
.name = "--getiomin",
.argtype = ARG_UINT,
.argval = -1,
.help = N_("get minimum I/O size")
},{
IOCTL_ENTRY(BLKIOOPT),
.name = "--getioopt",
.argtype = ARG_UINT,
.argval = -1,
.help = N_("get optimal I/O size")
},{
IOCTL_ENTRY(BLKALIGNOFF),
.name = "--getalignoff",
.argtype = ARG_INT,
.argval = -1,
.help = N_("get alignment offset")
},{
IOCTL_ENTRY(BLKSECTGET),
.name = "--getmaxsect",
.argtype = ARG_USHRT,
.argval = -1,
.help = N_("get max sectors per request")
},{
IOCTL_ENTRY(BLKBSZGET),
.name = "--getbsz",
.argtype = ARG_INT,
.argval = -1,
.help = N_("get blocksize")
},{
IOCTL_ENTRY(BLKBSZSET),
.name = "--setbsz",
.argname = "BLOCKSIZE",
.argtype = ARG_INT,
.flags = FL_NORESULT,
.help = N_("set blocksize")
},{
IOCTL_ENTRY(BLKGETSIZE),
.name = "--getsize",
.argtype = ARG_ULONG,
.argval = -1,
.help = N_("get 32-bit sector count")
},{
IOCTL_ENTRY(BLKGETSIZE64),
.name = "--getsize64",
.argtype = ARG_ULLONG,
.argval = -1,
.help = N_("get size in bytes")
},{
IOCTL_ENTRY(BLKRASET),
.name = "--setra",
.argname = "READAHEAD",
.argtype = ARG_INT,
.flags = FL_NOPTR | FL_NORESULT,
.help = N_("set readahead")
},{
IOCTL_ENTRY(BLKRAGET),
.name = "--getra",
.argtype = ARG_LONG,
.argval = -1,
.help = N_("get readahead")
},{
IOCTL_ENTRY(BLKFRASET),
.name = "--setfra",
.argname = "FSREADAHEAD",
.argtype = ARG_INT,
.flags = FL_NOPTR | FL_NORESULT,
.help = N_("set filesystem readahead")
},{
IOCTL_ENTRY(BLKFRAGET),
.name = "--getfra",
.argtype = ARG_LONG,
.argval = -1,
.help = N_("get filesystem readahead")
},{
IOCTL_ENTRY(BLKFLSBUF),
.name = "--flushbufs",
.help = N_("flush buffers")
},{
IOCTL_ENTRY(BLKRRPART),
.name = "--rereadpt",
.help = N_("reread partition table")
}
};
static void
usage(void) {
int i;
fputc('\n', stderr);
fprintf(stderr, _("Usage:\n"));
fprintf(stderr, " %s -V\n", progname);
fprintf(stderr, _(" %s --report [devices]\n"), progname);
fprintf(stderr, _(" %s [-v|-q] commands devices\n"), progname);
fputc('\n', stderr);
fprintf(stderr, _("Available commands:\n"));
fprintf(stderr, "\t%-30s %s\n", "--getsz",
_("get size in 512-byte sectors"));
for (i = 0; i < ARRAY_SIZE(bdcms); i++) {
if (bdcms[i].argname)
fprintf(stderr, "\t%s %-*s %s\n", bdcms[i].name,
(int) (29 - strlen(bdcms[i].name)),
bdcms[i].argname, _(bdcms[i].help));
else
fprintf(stderr, "\t%-30s %s\n", bdcms[i].name,
_(bdcms[i].help));
}
fputc('\n', stderr);
exit(1);
}
static int
find_cmd(char *s) {
int j;
for (j = 0; j < ARRAY_SIZE(bdcms); j++)
if (!strcmp(s, bdcms[j].name))
return j;
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);
int
main(int argc, char **argv) {
int fd, d, j, k;
char *p;
/* egcs-2.91.66 is buggy and says:
blockdev.c:93: warning: `d' might be used uninitialized */
d = 0;
progname = argv[0];
if ((p = strrchr(progname, '/')) != NULL)
progname = p+1;
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
if (argc < 2)
usage();
/* -V not together with commands */
if (!strcmp(argv[1], "-V") || !strcmp(argv[1], "--version")) {
printf("%s (%s)\n", progname, PACKAGE_STRING);
exit(0);
}
/* --report not together with other commands */
if (!strcmp(argv[1], "--report")) {
report_header();
if (argc > 2) {
for (d = 2; d < argc; d++)
report_device(argv[d], 0);
} else {
report_all_devices();
}
exit(0);
}
/* do each of the commands on each of the devices */
/* devices start after last command */
for (d = 1; d < argc; d++) {
j = find_cmd(argv[d]);
if (j >= 0) {
if (bdcms[j].argname)
d++;
continue;
}
if (!strcmp(argv[d], "--getsz"))
continue;
if (!strcmp(argv[d], "--")) {
d++;
break;
}
if (argv[d][0] != '-')
break;
}
if (d >= argc)
usage();
for (k = d; k < argc; k++) {
fd = open(argv[k], O_RDONLY, 0);
if (fd < 0) {
perror(argv[k]);
exit(1);
}
do_commands(fd, argv, d);
close(fd);
}
return 0;
}
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 verbose = 0;
for (i = 1; i < d; i++) {
if (!strcmp(argv[i], "-v")) {
verbose = 1;
continue;
}
if (!strcmp(argv[i], "-q")) {
verbose = 0;
continue;
}
if (!strcmp(argv[i], "--getsz")) {
res = blkdev_get_sectors(fd, &llu);
if (res == 0)
printf("%lld\n", llu);
else
exit(1);
continue;
}
j = find_cmd(argv[i]);
if (j == -1) {
fprintf(stderr, _("%s: Unknown command: %s\n"),
progname, argv[i]);
usage();
}
switch(bdcms[j].argtype) {
default:
case ARG_NONE:
res = ioctl(fd, bdcms[j].ioc, 0);
break;
case ARG_USHRT:
huarg = bdcms[j].argval;
res = ioctl(fd, bdcms[j].ioc, &huarg);
break;
case ARG_INT:
if (bdcms[j].argname) {
if (i == d-1) {
fprintf(stderr, _("%s requires an argument\n"),
bdcms[j].name);
usage();
}
iarg = atoi(argv[++i]);
} else
iarg = bdcms[j].argval;
res = bdcms[j].flags & FL_NOPTR ?
ioctl(fd, bdcms[j].ioc, iarg) :
ioctl(fd, bdcms[j].ioc, &iarg);
break;
case ARG_UINT:
uarg = bdcms[j].argval;
res = ioctl(fd, bdcms[j].ioc, &uarg);
break;
case ARG_LONG:
larg = bdcms[j].argval;
res = ioctl(fd, bdcms[j].ioc, &larg);
break;
case ARG_LLONG:
llarg = bdcms[j].argval;
res = ioctl(fd, bdcms[j].ioc, &llarg);
break;
case ARG_ULONG:
lu = bdcms[j].argval;
res = ioctl(fd, bdcms[j].ioc, &lu);
break;
case ARG_ULLONG:
llu = bdcms[j].argval;
res = ioctl(fd, bdcms[j].ioc, &llu);
break;
}
if (res == -1) {
perror(bdcms[j].iocname);
if (verbose)
printf(_("%s failed.\n"), _(bdcms[j].help));
exit(1);
}
if (bdcms[j].argtype == ARG_NONE ||
(bdcms[j].flags & FL_NORESULT)) {
if (verbose)
printf(_("%s succeeded.\n"), _(bdcms[j].help));
continue;
}
if (verbose)
printf("%s: ", _(bdcms[j].help));
switch(bdcms[j].argtype) {
case ARG_USHRT:
printf("%hu\n", huarg);
break;
case ARG_INT:
printf("%d\n", iarg);
break;
case ARG_UINT:
printf("%u\n", uarg);
break;
case ARG_LONG:
printf("%ld\n", larg);
break;
case ARG_LLONG:
printf("%lld\n", llarg);
break;
case ARG_ULONG:
printf("%lu\n", lu);
break;
case ARG_ULLONG:
printf("%llu\n", llu);
break;
}
}
}
#define PROC_PARTITIONS "/proc/partitions"
void
report_all_devices(void) {
FILE *procpt;
char line[200];
char ptname[200];
char device[210];
int ma, mi, sz;
procpt = fopen(PROC_PARTITIONS, "r");
if (!procpt) {
fprintf(stderr, _("%s: cannot open %s\n"),
progname, PROC_PARTITIONS);
exit(1);
}
while (fgets(line, sizeof(line), procpt)) {
if (sscanf (line, " %d %d %d %200[^\n ]",
&ma, &mi, &sz, ptname) != 4)
continue;
sprintf(device, "/dev/%s", ptname);
report_device(device, 1);
}
fclose(procpt);
}
void
report_device(char *device, int quiet) {
int fd;
int ro, ssz, bsz;
long ra;
unsigned long long bytes;
struct hd_geometry g;
fd = open(device, O_RDONLY | O_NONBLOCK);
if (fd < 0) {
if (!quiet)
fprintf(stderr, _("%s: cannot open %s\n"),
progname, device);
return;
}
ro = ssz = bsz = 0;
g.start = ra = 0;
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);
} else {
if (!quiet)
fprintf(stderr, _("%s: ioctl error on %s\n"),
progname, device);
}
close(fd);
}
void
report_header() {
printf(_("RO RA SSZ BSZ StartSec Size Device\n"));
}