2 * blkpr.c -- persistent reservations on a block device.
4 * Copyright (C) 2021-2022 zhenwei pi <pizhenwei@bytedance.com>
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.
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.
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/>.
19 * This program uses IOC_PR_XXX ioctl to run persistent reservations
20 * command on a block device if the device supports it.
28 #include <sys/ioctl.h>
33 #include "closestream.h"
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"},
49 {PR_EXCLUSIVE_ACCESS
, "exclusive-access",
50 " * exclusive-access: Only the initiator that owns the reservation can\n"
51 " access the device.\n"},
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"},
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"},
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"},
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"}
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"
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"},
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"},
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"},
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"},
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"},
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"}
115 static void print_type(FILE *out
, const struct type_string
*ts
, size_t nmem
)
119 for (i
= 0; i
< nmem
; i
++) {
120 fprintf(out
, "%s\n", ts
[i
].desc
);
125 static int parse_type_by_str(const struct type_string
*ts
, int nmem
, char *pattern
)
129 for (i
= 0; i
< nmem
; i
++) {
130 if (!strcmp(ts
[i
].str
, pattern
))
138 #define PRINT_SUPPORTED(XX) \
139 static void print_##XX(FILE *out) \
140 { print_type(out, XX, ARRAY_SIZE(XX)); }
143 static int parse_##XX(char *pattern) \
144 { return parse_type_by_str(XX, ARRAY_SIZE(XX), pattern); }
146 PRINT_SUPPORTED(pr_type
)
147 PRINT_SUPPORTED(pr_command
)
148 PRINT_SUPPORTED(pr_flag
)
154 static int do_pr(char *path
, uint64_t key
, uint64_t oldkey
, int op
, int type
, int flag
)
156 struct pr_registration pr_reg
;
157 struct pr_reservation pr_res
;
158 struct pr_preempt pr_prt
;
159 struct pr_clear pr_clr
;
162 fd
= open(path
, O_RDWR
);
164 err(EXIT_FAILURE
, _("cannot open %s"), path
);
167 case IOC_PR_REGISTER
:
168 pr_reg
.old_key
= oldkey
;
169 pr_reg
.new_key
= key
;
171 ret
= ioctl(fd
, op
, &pr_reg
);
178 ret
= ioctl(fd
, op
, &pr_res
);
181 case IOC_PR_PREEMPT_ABORT
:
182 pr_prt
.old_key
= oldkey
;
183 pr_prt
.new_key
= key
;
186 ret
= ioctl(fd
, op
, &pr_prt
);
191 ret
= ioctl(fd
, op
, &pr_clr
);
195 err(EXIT_FAILURE
, _("unknown command"));
200 err(EXIT_FAILURE
, _("pr ioctl failed"));
202 errx(EXIT_FAILURE
, _("error code 0x%x, for more detailed information see specification of device model."), ret
);
207 static void __attribute__((__noreturn__
)) usage(void)
211 fputs(USAGE_HEADER
, out
);
213 _(" %s [options] <device>\n"), program_invocation_short_name
);
215 fputs(USAGE_SEPARATOR
, out
);
216 fputs(_("Persistent reservations on a device.\n"), out
);
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
);
225 fputs(USAGE_SEPARATOR
, out
);
226 fprintf(out
, USAGE_HELP_OPTIONS(26));
228 fputs(USAGE_ARGUMENTS
, out
);
230 fputs(_(" <cmd> is an command, available command:\n"), out
);
231 print_pr_command(out
);
233 fputs(_(" <flag> is a command flag, available flags:\n"), out
);
236 fputs(_(" <type> is a command type, available types:\n"), out
);
239 fprintf(out
, USAGE_MAN_TAIL("blkpr(8)"));
243 int main(int argc
, char **argv
)
247 uint64_t key
= 0, oldkey
= 0;
248 int command
= -1, type
= -1, flag
= 0;
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' },
261 setlocale(LC_ALL
, "");
262 bindtextdomain(PACKAGE
, LOCALEDIR
);
264 close_stdout_atexit();
267 while ((c
= getopt_long(argc
, argv
, "hVc:k:K:f:t:", longopts
, NULL
)) != -1) {
270 key
= strtosize_or_err(optarg
,
271 _("failed to parse key"));
274 oldkey
= strtosize_or_err(optarg
,
275 _("failed to parse old key"));
278 command
= parse_pr_command(optarg
);
280 err(EXIT_FAILURE
, _("unknown command"));
283 type
= parse_pr_type(optarg
);
285 err(EXIT_FAILURE
, _("unknown type"));
288 flag
= parse_pr_flag(optarg
);
290 err(EXIT_FAILURE
, _("unknown flag"));
296 print_version(EXIT_SUCCESS
);
298 errtryhelp(EXIT_FAILURE
);
303 errx(EXIT_FAILURE
, _("no device specified"));
305 path
= argv
[optind
++];
306 if (optind
!= argc
) {
307 warnx(_("unexpected number of arguments"));
308 errtryhelp(EXIT_FAILURE
);
311 do_pr(path
, key
, oldkey
, command
, type
, flag
);