(root)/
util-linux-2.39/
disk-utils/
raw.c
       1  /*
       2   * raw.c: User mode tool to bind and query raw character devices.
       3   *
       4   * Stephen Tweedie, 1999, 2000
       5   *
       6   * This file may be redistributed under the terms of the GNU General
       7   * Public License, version 2.
       8   *
       9   * Copyright Red Hat Software, 1999, 2000
      10   *
      11   */
      12  
      13  #include <errno.h>
      14  #include <fcntl.h>
      15  #include <getopt.h>
      16  #include <linux/major.h>
      17  #include <linux/raw.h>
      18  #include <stdio.h>
      19  #include <stdlib.h>
      20  #include <string.h>
      21  #include <sys/ioctl.h>
      22  #include <sys/stat.h>
      23  #include <unistd.h>
      24  
      25  #include "c.h"
      26  #include "closestream.h"
      27  #include "nls.h"
      28  #include "pathnames.h"
      29  
      30  #define EXIT_RAW_ACCESS 3
      31  #define EXIT_RAW_IOCTL 4
      32  
      33  #define RAW_NR_MINORS 8192
      34  
      35  static int do_query;
      36  static int do_query_all;
      37  
      38  static int master_fd;
      39  static int raw_minor;
      40  
      41  void open_raw_ctl(void);
      42  static int query(int minor_raw, const char *raw_name, int quiet);
      43  static int bind(int minor_raw, int block_major, int block_minor);
      44  
      45  static void __attribute__((__noreturn__)) usage(void)
      46  {
      47  	FILE *out = stdout;
      48  	fputs(USAGE_HEADER, out);
      49  	fprintf(out,
      50  		_(" %1$s %2$srawN <major> <minor>\n"
      51  		  " %1$s %2$srawN /dev/<blockdevice>\n"
      52  		  " %1$s -q %2$srawN\n"
      53  		  " %1$s -qa\n"), program_invocation_short_name,
      54  		_PATH_RAWDEVDIR);
      55  
      56  	fputs(USAGE_SEPARATOR, out);
      57  	fputs(_("Bind a raw character device to a block device.\n"), out);
      58  
      59  	fputs(USAGE_OPTIONS, out);
      60  	fputs(_(" -q, --query    set query mode\n"), out);
      61  	fputs(_(" -a, --all      query all raw devices\n"), out);
      62  	printf(USAGE_HELP_OPTIONS(16));
      63  	printf(USAGE_MAN_TAIL("raw(8)"));
      64  	exit(EXIT_SUCCESS);
      65  }
      66  
      67  static long strtol_octal_or_err(const char *str, const char *errmesg)
      68  {
      69  	long num;
      70  	char *end = NULL;
      71  
      72  	if (str == NULL || *str == '\0')
      73  		goto err;
      74  	errno = 0;
      75  	num = strtol(str, &end, 0);
      76  
      77  	if (errno || str == end || (end && *end))
      78  		goto err;
      79  
      80  	return num;
      81   err:
      82  	if (errno)
      83  		err(EXIT_FAILURE, "%s: '%s'", errmesg, str);
      84  	else
      85  		errx(EXIT_FAILURE, "%s: '%s'", errmesg, str);
      86  	return 0;
      87  }
      88  
      89  int main(int argc, char *argv[])
      90  {
      91  	int c;
      92  	char *raw_name;
      93  	char *block_name;
      94  	int retval;
      95  	int block_major, block_minor;
      96  	int i, rc;
      97  
      98  	struct stat statbuf;
      99  
     100  	static const struct option longopts[] = {
     101  		{"query",   no_argument, NULL, 'q'},
     102  		{"all",     no_argument, NULL, 'a'},
     103  		{"version", no_argument, NULL, 'V'},
     104  		{"help",    no_argument, NULL, 'h'},
     105  		{NULL, 0, NULL, '0'},
     106  	};
     107  
     108  	setlocale(LC_ALL, "");
     109  	bindtextdomain(PACKAGE, LOCALEDIR);
     110  	textdomain(PACKAGE);
     111  	close_stdout_atexit();
     112  
     113  	while ((c = getopt_long(argc, argv, "qaVh", longopts, NULL)) != -1)
     114  		switch (c) {
     115  		case 'q':
     116  			do_query = 1;
     117  			break;
     118  		case 'a':
     119  			do_query_all = 1;
     120  			break;
     121  		case 'V':
     122  			print_version(EXIT_SUCCESS);
     123  		case 'h':
     124  			usage();
     125  		default:
     126  			errtryhelp(EXIT_FAILURE);
     127  		}
     128  
     129  	/*
     130  	 * Check for, and open, the master raw device, /dev/raw
     131  	 */
     132  	open_raw_ctl();
     133  
     134  	if (do_query_all) {
     135  		if (optind < argc) {
     136  			warnx(_("bad usage"));
     137  			errtryhelp(EXIT_FAILURE);
     138  		}
     139  		for (i = 1; i < RAW_NR_MINORS; i++)
     140  			query(i, NULL, 1);
     141  		exit(EXIT_SUCCESS);
     142  	}
     143  
     144  	/*
     145  	 * It's a bind or a single query.  Either way we need a raw device.
     146  	 */
     147  
     148  	if (optind >= argc) {
     149  		warnx(_("bad usage"));
     150  		errtryhelp(EXIT_FAILURE);
     151  	}
     152  	raw_name = argv[optind++];
     153  
     154  	/*
     155  	 * try to check the device name before stat(), because on systems with
     156  	 * udev the raw0 causes a create udev event for char 162/0, which
     157  	 * causes udev to *remove* /dev/rawctl
     158  	 */
     159  	rc = sscanf(raw_name, _PATH_RAWDEVDIR "raw%d", &raw_minor);
     160  	if (rc != 1) {
     161  		warnx(_("bad usage"));
     162  		errtryhelp(EXIT_FAILURE);
     163  	}
     164  	if (raw_minor == 0)
     165  		errx(EXIT_RAW_ACCESS,
     166  		     _("Device '%s' is the control raw device "
     167  		       "(use raw<N> where <N> is greater than zero)"),
     168  		     raw_name);
     169  
     170  	if (do_query)
     171  		return query(raw_minor, raw_name, 0);
     172  
     173  	/*
     174  	 * It's not a query, so we still have some parsing to do.  Have we been
     175  	 * given a block device filename or a major/minor pair?
     176  	 */
     177  	switch (argc - optind) {
     178  	case 1:
     179  		block_name = argv[optind];
     180  		retval = stat(block_name, &statbuf);
     181  		if (retval)
     182  			err(EXIT_RAW_ACCESS,
     183  			    _("Cannot locate block device '%s'"), block_name);
     184  		if (!S_ISBLK(statbuf.st_mode))
     185  			errx(EXIT_RAW_ACCESS,
     186  			     _("Device '%s' is not a block device"),
     187  			     block_name);
     188  		block_major = major(statbuf.st_rdev);
     189  		block_minor = minor(statbuf.st_rdev);
     190  		break;
     191  
     192  	case 2:
     193  		block_major =
     194  		    strtol_octal_or_err(argv[optind],
     195  					_("failed to parse argument"));
     196  		block_minor =
     197  		    strtol_octal_or_err(argv[optind + 1],
     198  					_("failed to parse argument"));
     199  		break;
     200  
     201  	default:
     202  		warnx(_("bad usage"));
     203  		errtryhelp(EXIT_FAILURE);
     204  	}
     205  
     206  	return bind(raw_minor, block_major, block_minor);
     207  }
     208  
     209  void open_raw_ctl(void)
     210  {
     211  	master_fd = open(_PATH_RAWDEVCTL, O_RDWR, 0);
     212  	if (master_fd < 0) {
     213  		master_fd = open(_PATH_RAWDEVCTL_OLD, O_RDWR, 0);
     214  		if (master_fd < 0)
     215  			err(EXIT_RAW_ACCESS,
     216  			    _("Cannot open master raw device '%s'"),
     217  			    _PATH_RAWDEVCTL);
     218  	}
     219  }
     220  
     221  static int query(int minor_raw, const char *raw_name, int quiet)
     222  {
     223  	struct raw_config_request rq;
     224  	static int has_worked = 0;
     225  
     226  	if (raw_name) {
     227  		struct stat statbuf;
     228  
     229  		if (stat(raw_name, &statbuf) != 0)
     230  			err(EXIT_RAW_ACCESS,
     231  			    _("Cannot locate raw device '%s'"), raw_name);
     232  		if (!S_ISCHR(statbuf.st_mode))
     233  			errx(EXIT_RAW_ACCESS,
     234  			     _("Raw device '%s' is not a character dev"),
     235  			     raw_name);
     236  		if (major(statbuf.st_rdev) != RAW_MAJOR)
     237  			errx(EXIT_RAW_ACCESS,
     238  			     _("Device '%s' is not a raw dev"), raw_name);
     239  		minor_raw = minor(statbuf.st_rdev);
     240  	}
     241  
     242  	rq.raw_minor = minor_raw;
     243  	if (ioctl(master_fd, RAW_GETBIND, &rq) < 0) {
     244  		if (quiet && errno == ENODEV)
     245  			return 3;
     246  		if (has_worked && errno == EINVAL)
     247  			return 0;
     248  		err(EXIT_RAW_IOCTL, _("Error querying raw device"));
     249  	}
     250  
     251  	/* If one query has worked, mark that fact so that we don't report
     252  	 * spurious fatal errors if raw(8) has been built to support more raw
     253  	 * minor numbers than the kernel has. */
     254  	has_worked = 1;
     255  	if (quiet && !rq.block_major && !rq.block_minor)
     256  		return 0;
     257  	printf(_("%sraw%d:  bound to major %d, minor %d\n"),
     258  	       _PATH_RAWDEVDIR, minor_raw, (int)rq.block_major,
     259  	       (int)rq.block_minor);
     260  	return 0;
     261  }
     262  
     263  static int bind(int minor_raw, int block_major, int block_minor)
     264  {
     265  	struct raw_config_request rq;
     266  
     267  	rq.raw_minor = minor_raw;
     268  	rq.block_major = block_major;
     269  	rq.block_minor = block_minor;
     270  	if (ioctl(master_fd, RAW_SETBIND, &rq) < 0)
     271  		err(EXIT_RAW_IOCTL, _("Error setting raw device"));
     272  	printf(_("%sraw%d:  bound to major %d, minor %d\n"),
     273  	       _PATH_RAWDEVDIR, raw_minor, (int)rq.block_major,
     274  	       (int)rq.block_minor);
     275  	return 0;
     276  }