| /* |
| * raw.c: User mode tool to bind and query raw character devices. |
| * |
| * Stephen Tweedie, 1999, 2000 |
| * |
| * This file may be redistributed under the terms of the GNU General |
| * Public License, version 2. |
| * |
| * Copyright Red Hat Software, 1999, 2000 |
| * |
| */ |
| |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <getopt.h> |
| #include <linux/major.h> |
| #include <linux/raw.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/ioctl.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| |
| #include "c.h" |
| #include "closestream.h" |
| #include "nls.h" |
| #include "pathnames.h" |
| |
| #define EXIT_RAW_ACCESS 3 |
| #define EXIT_RAW_IOCTL 4 |
| |
| #define RAW_NR_MINORS 8192 |
| |
| static int do_query; |
| static int do_query_all; |
| |
| static int master_fd; |
| static int raw_minor; |
| |
| void open_raw_ctl(void); |
| static int query(int minor_raw, const char *raw_name, int quiet); |
| static int bind(int minor_raw, int block_major, int block_minor); |
| |
| static void __attribute__((__noreturn__)) usage(void) |
| { |
| FILE *out = stdout; |
| fputs(USAGE_HEADER, out); |
| fprintf(out, |
| _(" %1$s %2$srawN <major> <minor>\n" |
| " %1$s %2$srawN /dev/<blockdevice>\n" |
| " %1$s -q %2$srawN\n" |
| " %1$s -qa\n"), program_invocation_short_name, |
| _PATH_RAWDEVDIR); |
| |
| fputs(USAGE_SEPARATOR, out); |
| fputs(_("Bind a raw character device to a block device.\n"), out); |
| |
| fputs(USAGE_OPTIONS, out); |
| fputs(_(" -q, --query set query mode\n"), out); |
| fputs(_(" -a, --all query all raw devices\n"), out); |
| printf(USAGE_HELP_OPTIONS(16)); |
| printf(USAGE_MAN_TAIL("raw(8)")); |
| exit(EXIT_SUCCESS); |
| } |
| |
| static long strtol_octal_or_err(const char *str, const char *errmesg) |
| { |
| long num; |
| char *end = NULL; |
| |
| if (str == NULL || *str == '\0') |
| goto err; |
| errno = 0; |
| num = strtol(str, &end, 0); |
| |
| if (errno || str == end || (end && *end)) |
| goto err; |
| |
| return num; |
| err: |
| if (errno) |
| err(EXIT_FAILURE, "%s: '%s'", errmesg, str); |
| else |
| errx(EXIT_FAILURE, "%s: '%s'", errmesg, str); |
| return 0; |
| } |
| |
| int main(int argc, char *argv[]) |
| { |
| int c; |
| char *raw_name; |
| char *block_name; |
| int retval; |
| int block_major, block_minor; |
| int i, rc; |
| |
| struct stat statbuf; |
| |
| static const struct option longopts[] = { |
| {"query", no_argument, NULL, 'q'}, |
| {"all", no_argument, NULL, 'a'}, |
| {"version", no_argument, NULL, 'V'}, |
| {"help", no_argument, NULL, 'h'}, |
| {NULL, 0, NULL, '0'}, |
| }; |
| |
| setlocale(LC_ALL, ""); |
| bindtextdomain(PACKAGE, LOCALEDIR); |
| textdomain(PACKAGE); |
| atexit(close_stdout); |
| |
| while ((c = getopt_long(argc, argv, "qaVh", longopts, NULL)) != -1) |
| switch (c) { |
| case 'q': |
| do_query = 1; |
| break; |
| case 'a': |
| do_query_all = 1; |
| break; |
| case 'V': |
| printf(UTIL_LINUX_VERSION); |
| return EXIT_SUCCESS; |
| case 'h': |
| usage(); |
| default: |
| errtryhelp(EXIT_FAILURE); |
| } |
| |
| /* |
| * Check for, and open, the master raw device, /dev/raw |
| */ |
| open_raw_ctl(); |
| |
| if (do_query_all) { |
| if (optind < argc) { |
| warnx(_("bad usage")); |
| errtryhelp(EXIT_FAILURE); |
| } |
| for (i = 1; i < RAW_NR_MINORS; i++) |
| query(i, NULL, 1); |
| exit(EXIT_SUCCESS); |
| } |
| |
| /* |
| * It's a bind or a single query. Either way we need a raw device. |
| */ |
| |
| if (optind >= argc) { |
| warnx(_("bad usage")); |
| errtryhelp(EXIT_FAILURE); |
| } |
| raw_name = argv[optind++]; |
| |
| /* |
| * try to check the device name before stat(), because on systems with |
| * udev the raw0 causes a create udev event for char 162/0, which |
| * causes udev to *remove* /dev/rawctl |
| */ |
| rc = sscanf(raw_name, _PATH_RAWDEVDIR "raw%d", &raw_minor); |
| if (rc != 1) { |
| warnx(_("bad usage")); |
| errtryhelp(EXIT_FAILURE); |
| } |
| if (raw_minor == 0) |
| errx(EXIT_RAW_ACCESS, |
| _("Device '%s' is the control raw device " |
| "(use raw<N> where <N> is greater than zero)"), |
| raw_name); |
| |
| if (do_query) |
| return query(raw_minor, raw_name, 0); |
| |
| /* |
| * It's not a query, so we still have some parsing to do. Have we been |
| * given a block device filename or a major/minor pair? |
| */ |
| switch (argc - optind) { |
| case 1: |
| block_name = argv[optind]; |
| retval = stat(block_name, &statbuf); |
| if (retval) |
| err(EXIT_RAW_ACCESS, |
| _("Cannot locate block device '%s'"), block_name); |
| if (!S_ISBLK(statbuf.st_mode)) |
| errx(EXIT_RAW_ACCESS, |
| _("Device '%s' is not a block device"), |
| block_name); |
| block_major = major(statbuf.st_rdev); |
| block_minor = minor(statbuf.st_rdev); |
| break; |
| |
| case 2: |
| block_major = |
| strtol_octal_or_err(argv[optind], |
| _("failed to parse argument")); |
| block_minor = |
| strtol_octal_or_err(argv[optind + 1], |
| _("failed to parse argument")); |
| break; |
| |
| default: |
| warnx(_("bad usage")); |
| errtryhelp(EXIT_FAILURE); |
| } |
| |
| return bind(raw_minor, block_major, block_minor); |
| } |
| |
| void open_raw_ctl(void) |
| { |
| master_fd = open(_PATH_RAWDEVCTL, O_RDWR, 0); |
| if (master_fd < 0) { |
| master_fd = open(_PATH_RAWDEVCTL_OLD, O_RDWR, 0); |
| if (master_fd < 0) |
| err(EXIT_RAW_ACCESS, |
| _("Cannot open master raw device '%s'"), |
| _PATH_RAWDEVCTL); |
| } |
| } |
| |
| static int query(int minor_raw, const char *raw_name, int quiet) |
| { |
| struct raw_config_request rq; |
| static int has_worked = 0; |
| |
| if (raw_name) { |
| struct stat statbuf; |
| |
| if (stat(raw_name, &statbuf) != 0) |
| err(EXIT_RAW_ACCESS, |
| _("Cannot locate raw device '%s'"), raw_name); |
| if (!S_ISCHR(statbuf.st_mode)) |
| errx(EXIT_RAW_ACCESS, |
| _("Raw device '%s' is not a character dev"), |
| raw_name); |
| if (major(statbuf.st_rdev) != RAW_MAJOR) |
| errx(EXIT_RAW_ACCESS, |
| _("Device '%s' is not a raw dev"), raw_name); |
| minor_raw = minor(statbuf.st_rdev); |
| } |
| |
| rq.raw_minor = minor_raw; |
| if (ioctl(master_fd, RAW_GETBIND, &rq) < 0) { |
| if (quiet && errno == ENODEV) |
| return 3; |
| if (has_worked && errno == EINVAL) |
| return 0; |
| err(EXIT_RAW_IOCTL, _("Error querying raw device")); |
| } |
| |
| /* If one query has worked, mark that fact so that we don't report |
| * spurious fatal errors if raw(8) has been built to support more raw |
| * minor numbers than the kernel has. */ |
| has_worked = 1; |
| if (quiet && !rq.block_major && !rq.block_minor) |
| return 0; |
| printf(_("%sraw%d: bound to major %d, minor %d\n"), |
| _PATH_RAWDEVDIR, minor_raw, (int)rq.block_major, |
| (int)rq.block_minor); |
| return 0; |
| } |
| |
| static int bind(int minor_raw, int block_major, int block_minor) |
| { |
| struct raw_config_request rq; |
| |
| rq.raw_minor = minor_raw; |
| rq.block_major = block_major; |
| rq.block_minor = block_minor; |
| if (ioctl(master_fd, RAW_SETBIND, &rq) < 0) |
| err(EXIT_RAW_IOCTL, _("Error setting raw device")); |
| printf(_("%sraw%d: bound to major %d, minor %d\n"), |
| _PATH_RAWDEVDIR, raw_minor, (int)rq.block_major, |
| (int)rq.block_minor); |
| return 0; |
| } |