(root)/
util-linux-2.39/
sys-utils/
blkpr.c
       1  /*
       2   * blkpr.c -- persistent reservations on a block device.
       3   *
       4   * Copyright (C) 2021-2022 zhenwei pi <pizhenwei@bytedance.com>
       5   *
       6   * This program is free software: you can redistribute it and/or modify
       7   * it under the terms of the GNU General Public License as published by
       8   * the Free Software Foundation, either version 2 of the License, or
       9   * (at your option) any later version.
      10   *
      11   * This program is distributed in the hope that it will be useful,
      12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
      13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14   * GNU General Public License for more details.
      15   *
      16   * You should have received a copy of the GNU General Public License
      17   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
      18   *
      19   * This program uses IOC_PR_XXX ioctl to run persistent reservations
      20   * command on a block device if the device supports it.
      21   */
      22  #include <string.h>
      23  #include <unistd.h>
      24  #include <stdlib.h>
      25  #include <stdio.h>
      26  #include <getopt.h>
      27  #include <locale.h>
      28  #include <sys/ioctl.h>
      29  #include <linux/pr.h>
      30  
      31  #include "nls.h"
      32  #include "c.h"
      33  #include "closestream.h"
      34  #include "strutils.h"
      35  #include "xalloc.h"
      36  
      37  struct type_string {
      38  	int type;
      39  	char *str;
      40  	char *desc;
      41  };
      42  
      43  /* This array should keep align with enum pr_type of linux/types.h */
      44  static const struct type_string pr_type[] = {
      45  	{PR_WRITE_EXCLUSIVE,           "write-exclusive",
      46  	"  * write-exclusive: Only the initiator that owns the reservation can\n"
      47  	"    write to the device. Any initiator can read from the device.\n"},
      48  
      49  	{PR_EXCLUSIVE_ACCESS,          "exclusive-access",
      50  	"  * exclusive-access: Only the initiator that owns the reservation can\n"
      51  	"    access the device.\n"},
      52  
      53  	{PR_WRITE_EXCLUSIVE_REG_ONLY,  "write-exclusive-reg-only",
      54  	"  * write-exclusive-reg-only: Only initiators with a registered key can\n"
      55  	"    write to the device, any initiator can read from the device.\n"},
      56  
      57  	{PR_EXCLUSIVE_ACCESS_REG_ONLY, "exclusive-access-reg-only",
      58  	"  * exclusive-access-reg-only: Only initiators with a registered key can\n"
      59  	"    access the device.\n"},
      60  
      61  	{PR_WRITE_EXCLUSIVE_ALL_REGS,  "write-exclusive-all-regs",
      62  	"  * write-exclusive-all-regs: Only initiators with a registered key can\n"
      63  	"    write to the device.  Any initiator can read from the device.  All\n"
      64  	"    initiators with a registered key are considered reservation holders.\n"
      65  	"    Please reference the SPC spec on the meaning of a reservation holder\n"
      66  	"    if you want to use this type.\n"},
      67  
      68  	{PR_EXCLUSIVE_ACCESS_ALL_REGS, "exclusive-access-all-regs",
      69  	"  * exclusive-access-all-regs: Only initiators with a registered key can\n"
      70  	"    access the device.  All initiators with a registered key are considered\n"
      71  	"    reservation holders. Please reference the SPC spec on the meaning of\n"
      72  	"    a reservation holder if you want to use this type.\n"}
      73  };
      74  
      75  static const struct type_string pr_command[] = {
      76  	{IOC_PR_REGISTER,      "register",
      77  	"  * register: This command registers a new reservation if the key argument\n"
      78  	"    is non-null. If no existing reservation exists oldkey must be zero, if\n"
      79  	"    an existing reservation should be replaced oldkey must contain the old\n"
      80  	"    reservation key. If the key argument is 0 it unregisters the existing\n"
      81  	"    reservation passed in oldkey.\n"
      82  	},
      83  
      84  	{IOC_PR_RESERVE,       "reserve",
      85  	"  * reserve: This command reserves the device and thus restricts access for\n"
      86  	"    other devices based on the type argument.  The key argument must be\n"
      87  	"    the existing reservation key for the device as acquired by the register,\n"
      88  	"    preempt, preempt-abort commands.\n"},
      89  
      90  	{IOC_PR_RELEASE,       "release",
      91  	"  * release: This command releases the reservation specified by key and flags\n"
      92  	"    and thus removes any access restriction implied by it.\n"},
      93  
      94  	{IOC_PR_PREEMPT,       "preempt",
      95  	"  * preempt: This command releases the existing reservation referred to by\n"
      96  	"    old_key and replaces it with a new reservation of type for the\n"
      97  	"    reservation key key.\n"},
      98  
      99  	{IOC_PR_PREEMPT_ABORT, "preempt-abort",
     100  	"  * preempt-abort: This command works like preempt except that it also aborts\n"
     101  	"    any outstanding command sent over a connection identified by oldkey.\n"},
     102  
     103  	{IOC_PR_CLEAR,         "clear",
     104  	"  * clear: This command unregisters both key and any other reservation\n"
     105  	"    key registered with the device and drops any existing reservation.\n"},
     106  };
     107  
     108  static const struct type_string pr_flag[] = {
     109  	{PR_FL_IGNORE_KEY, "ignore-key",
     110  	"  * ignore-key: Ignore the existing reservation key.  This is commonly\n"
     111  	"    supported for register command, and some implementation may support\n"
     112  	"    the flag for reserve command.\n"}
     113  };
     114  
     115  static void print_type(FILE *out, const struct type_string *ts, size_t nmem)
     116  {
     117  	size_t i;
     118  
     119  	for (i = 0; i < nmem; i++) {
     120  		fprintf(out, "%s\n", ts[i].desc);
     121  	}
     122  }
     123  
     124  
     125  static int parse_type_by_str(const struct type_string *ts, int nmem, char *pattern)
     126  {
     127  	int i;
     128  
     129  	for (i = 0; i < nmem; i++) {
     130  		if (!strcmp(ts[i].str, pattern))
     131  			return ts[i].type;
     132  	}
     133  
     134  	return -1;
     135  }
     136  
     137  
     138  #define PRINT_SUPPORTED(XX) \
     139  	static void print_##XX(FILE *out) \
     140  	{ print_type(out, XX, ARRAY_SIZE(XX)); }
     141  
     142  #define PARSE(XX) \
     143  	static int parse_##XX(char *pattern) \
     144  	{ return parse_type_by_str(XX, ARRAY_SIZE(XX), pattern); }
     145  
     146  PRINT_SUPPORTED(pr_type)
     147  PRINT_SUPPORTED(pr_command)
     148  PRINT_SUPPORTED(pr_flag)
     149  
     150  PARSE(pr_type)
     151  PARSE(pr_command)
     152  PARSE(pr_flag)
     153  
     154  static int do_pr(char *path, uint64_t key, uint64_t oldkey, int op, int type, int flag)
     155  {
     156  	struct pr_registration pr_reg;
     157  	struct pr_reservation pr_res;
     158  	struct pr_preempt pr_prt;
     159  	struct pr_clear pr_clr;
     160  	int fd, ret;
     161  
     162  	fd = open(path, O_RDWR);
     163  	if (fd < 0)
     164  		err(EXIT_FAILURE, _("cannot open %s"), path);
     165  
     166  	switch (op) {
     167  	case IOC_PR_REGISTER:
     168  		pr_reg.old_key = oldkey;
     169  		pr_reg.new_key = key;
     170  		pr_reg.flags = flag;
     171  		ret = ioctl(fd, op, &pr_reg);
     172  		break;
     173  	case IOC_PR_RESERVE:
     174  	case IOC_PR_RELEASE:
     175  		pr_res.key = key;
     176  		pr_res.type = type;
     177  		pr_res.flags = flag;
     178  		ret = ioctl(fd, op, &pr_res);
     179  		break;
     180  	case IOC_PR_PREEMPT:
     181  	case IOC_PR_PREEMPT_ABORT:
     182  		pr_prt.old_key = oldkey;
     183  		pr_prt.new_key = key;
     184  		pr_prt.type = type;
     185  		pr_prt.flags = flag;
     186  		ret = ioctl(fd, op, &pr_prt);
     187  		break;
     188  	case IOC_PR_CLEAR:
     189  		pr_clr.key = key;
     190  		pr_clr.flags = flag;
     191  		ret = ioctl(fd, op, &pr_clr);
     192  		break;
     193  	default:
     194  		errno = EINVAL;
     195  		err(EXIT_FAILURE, _("unknown command"));
     196  	}
     197  
     198  	close(fd);
     199  	if (ret < 0)
     200  		err(EXIT_FAILURE, _("pr ioctl failed"));
     201  	if (ret > 0)
     202  		errx(EXIT_FAILURE, _("error code 0x%x, for more detailed information see specification of device model."), ret);
     203  
     204  	return ret;
     205  }
     206  
     207  static void __attribute__((__noreturn__)) usage(void)
     208  {
     209  	FILE *out = stdout;
     210  
     211  	fputs(USAGE_HEADER, out);
     212  	fprintf(out,
     213  	      _(" %s [options] <device>\n"), program_invocation_short_name);
     214  
     215  	fputs(USAGE_SEPARATOR, out);
     216  	fputs(_("Persistent reservations on a device.\n"), out);
     217  
     218  	fputs(USAGE_OPTIONS, out);
     219  	fputs(_(" -c, --command <cmd>      command of persistent reservations\n"), out);
     220  	fputs(_(" -k, --key <num>          key to operate\n"), out);
     221  	fputs(_(" -K, --oldkey <num>       old key to operate\n"), out);
     222  	fputs(_(" -f, --flag <flag>        command flag\n"), out);
     223  	fputs(_(" -t, --type <type>        command type\n"), out);
     224  
     225  	fputs(USAGE_SEPARATOR, out);
     226  	printf(USAGE_HELP_OPTIONS(26));
     227  
     228  	fputs(USAGE_ARGUMENTS, out);
     229  
     230  	fputs(_(" <cmd> is an command, available command:\n"), out);
     231  	print_pr_command(out);
     232  
     233  	fputs(_(" <flag> is a command flag, available flags:\n"), out);
     234  	print_pr_flag(out);
     235  
     236  	fputs(_(" <type> is a command type, available types:\n"), out);
     237  	print_pr_type(out);
     238  
     239  	printf(USAGE_MAN_TAIL("blkpr(8)"));
     240  	exit(EXIT_SUCCESS);
     241  }
     242  
     243  int main(int argc, char **argv)
     244  {
     245  	char c;
     246  	char *path;
     247  	uint64_t key = 0, oldkey = 0;
     248  	int command = -1, type = -1, flag = 0;
     249  
     250  	static const struct option longopts[] = {
     251  	    { "help",            no_argument,       NULL, 'h' },
     252  	    { "version",         no_argument,       NULL, 'V' },
     253  	    { "command",         required_argument, NULL, 'c' },
     254  	    { "key",             required_argument, NULL, 'k' },
     255  	    { "oldkey",          required_argument, NULL, 'K' },
     256  	    { "flag",            required_argument, NULL, 'f' },
     257  	    { "type",            required_argument, NULL, 't' },
     258  	    { NULL, 0, NULL, 0 }
     259  	};
     260  
     261  	setlocale(LC_ALL, "");
     262  	bindtextdomain(PACKAGE, LOCALEDIR);
     263  	textdomain(PACKAGE);
     264  	close_stdout_atexit();
     265  
     266  	errno = EINVAL;
     267  	while ((c = getopt_long(argc, argv, "hVc:k:K:f:t:", longopts, NULL)) != -1) {
     268  		switch(c) {
     269  		case 'k':
     270  			key = strtosize_or_err(optarg,
     271  					_("failed to parse key"));
     272  			break;
     273  		case 'K':
     274  			oldkey = strtosize_or_err(optarg,
     275  					_("failed to parse old key"));
     276  			break;
     277  		case 'c':
     278  			command = parse_pr_command(optarg);
     279  			if (command < 0)
     280  				err(EXIT_FAILURE, _("unknown command"));
     281  			break;
     282  		case 't':
     283  			type = parse_pr_type(optarg);
     284  			if (type < 0)
     285  				err(EXIT_FAILURE, _("unknown type"));
     286  			break;
     287  		case 'f':
     288  			flag = parse_pr_flag(optarg);
     289  			if (flag < 0)
     290  				err(EXIT_FAILURE, _("unknown flag"));
     291  			break;
     292  
     293  		case 'h':
     294  			usage();
     295  		case 'V':
     296  			print_version(EXIT_SUCCESS);
     297  		default:
     298  			errtryhelp(EXIT_FAILURE);
     299  		}
     300  	}
     301  
     302  	if (optind == argc)
     303  		errx(EXIT_FAILURE, _("no device specified"));
     304  
     305  	path = argv[optind++];
     306  	if (optind != argc) {
     307  		warnx(_("unexpected number of arguments"));
     308  		errtryhelp(EXIT_FAILURE);
     309  	}
     310  
     311  	do_pr(path, key, oldkey, command, type, flag);
     312  
     313  	return EXIT_SUCCESS;
     314  }