]>
Commit | Line | Data |
---|---|---|
627428a9 | 1 | /* |
2 | * blkpr.c -- persistent reservations on a block device. | |
3 | * | |
2a1203bb | 4 | * Copyright (C) 2021-2022 zhenwei pi <pizhenwei@bytedance.com> |
627428a9 | 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 | * | |
2a1203bb | 19 | * This program uses IOC_PR_XXX ioctl to run persistent reservations |
20 | * command on a block device if the device supports it. | |
627428a9 | 21 | */ |
627428a9 | 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; | |
dfe94106 | 40 | char *desc; |
627428a9 | 41 | }; |
42 | ||
43 | /* This array should keep align with enum pr_type of linux/types.h */ | |
db0a522e | 44 | static const struct type_string pr_type[] = { |
dfe94106 | 45 | {PR_WRITE_EXCLUSIVE, "write-exclusive", |
113cb6a2 KZ |
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"}, | |
dfe94106 | 48 | |
49 | {PR_EXCLUSIVE_ACCESS, "exclusive-access", | |
113cb6a2 KZ |
50 | " * exclusive-access: Only the initiator that owns the reservation can\n" |
51 | " access the device.\n"}, | |
dfe94106 | 52 | |
53 | {PR_WRITE_EXCLUSIVE_REG_ONLY, "write-exclusive-reg-only", | |
113cb6a2 KZ |
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"}, | |
dfe94106 | 56 | |
57 | {PR_EXCLUSIVE_ACCESS_REG_ONLY, "exclusive-access-reg-only", | |
113cb6a2 KZ |
58 | " * exclusive-access-reg-only: Only initiators with a registered key can\n" |
59 | " access the device.\n"}, | |
dfe94106 | 60 | |
61 | {PR_WRITE_EXCLUSIVE_ALL_REGS, "write-exclusive-all-regs", | |
113cb6a2 KZ |
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"}, | |
dfe94106 | 67 | |
68 | {PR_EXCLUSIVE_ACCESS_ALL_REGS, "exclusive-access-all-regs", | |
113cb6a2 KZ |
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"} | |
627428a9 | 73 | }; |
74 | ||
db0a522e | 75 | static const struct type_string pr_command[] = { |
dfe94106 | 76 | {IOC_PR_REGISTER, "register", |
113cb6a2 KZ |
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 | }, | |
dfe94106 | 83 | |
84 | {IOC_PR_RESERVE, "reserve", | |
113cb6a2 KZ |
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"}, | |
dfe94106 | 89 | |
90 | {IOC_PR_RELEASE, "release", | |
113cb6a2 KZ |
91 | " * release: This command releases the reservation specified by key and flags\n" |
92 | " and thus removes any access restriction implied by it.\n"}, | |
dfe94106 | 93 | |
94 | {IOC_PR_PREEMPT, "preempt", | |
113cb6a2 KZ |
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"}, | |
dfe94106 | 98 | |
99 | {IOC_PR_PREEMPT_ABORT, "preempt-abort", | |
113cb6a2 KZ |
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"}, | |
dfe94106 | 102 | |
103 | {IOC_PR_CLEAR, "clear", | |
113cb6a2 KZ |
104 | " * clear: This command unregisters both key and any other reservation\n" |
105 | " key registered with the device and drops any existing reservation.\n"}, | |
627428a9 | 106 | }; |
107 | ||
db0a522e | 108 | static const struct type_string pr_flag[] = { |
dfe94106 | 109 | {PR_FL_IGNORE_KEY, "ignore-key", |
113cb6a2 KZ |
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"} | |
627428a9 | 113 | }; |
114 | ||
db0a522e | 115 | static void print_type(FILE *out, const struct type_string *ts, size_t nmem) |
627428a9 | 116 | { |
3215d510 | 117 | size_t i; |
627428a9 | 118 | |
627428a9 | 119 | for (i = 0; i < nmem; i++) { |
113cb6a2 | 120 | fprintf(out, "%s\n", ts[i].desc); |
627428a9 | 121 | } |
627428a9 | 122 | } |
123 | ||
3215d510 | 124 | |
db0a522e | 125 | static int parse_type_by_str(const struct type_string *ts, int nmem, char *pattern) |
627428a9 | 126 | { |
127 | int i; | |
128 | ||
129 | for (i = 0; i < nmem; i++) { | |
f028fc21 | 130 | if (!strcmp(ts[i].str, pattern)) |
627428a9 | 131 | return ts[i].type; |
627428a9 | 132 | } |
133 | ||
134 | return -1; | |
135 | } | |
136 | ||
3215d510 KZ |
137 | |
138 | #define PRINT_SUPPORTED(XX) \ | |
139 | static void print_##XX(FILE *out) \ | |
140 | { print_type(out, XX, ARRAY_SIZE(XX)); } | |
627428a9 | 141 | |
142 | #define PARSE(XX) \ | |
143 | static int parse_##XX(char *pattern) \ | |
144 | { return parse_type_by_str(XX, ARRAY_SIZE(XX), pattern); } | |
145 | ||
10ddf058 | 146 | PRINT_SUPPORTED(pr_type) |
2a1203bb | 147 | PRINT_SUPPORTED(pr_command) |
10ddf058 | 148 | PRINT_SUPPORTED(pr_flag) |
627428a9 | 149 | |
10ddf058 | 150 | PARSE(pr_type) |
2a1203bb | 151 | PARSE(pr_command) |
10ddf058 | 152 | PARSE(pr_flag) |
627428a9 | 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); | |
f028fc21 | 163 | if (fd < 0) |
627428a9 | 164 | err(EXIT_FAILURE, _("cannot open %s"), path); |
627428a9 | 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; | |
2a1203bb | 195 | err(EXIT_FAILURE, _("unknown command")); |
627428a9 | 196 | } |
197 | ||
198 | close(fd); | |
f028fc21 | 199 | if (ret < 0) |
627428a9 | 200 | err(EXIT_FAILURE, _("pr ioctl failed")); |
f028fc21 | 201 | if (ret > 0) |
627428a9 | 202 | errx(EXIT_FAILURE, _("error code 0x%x, for more detailed information see specification of device model."), ret); |
627428a9 | 203 | |
204 | return ret; | |
205 | } | |
206 | ||
207 | static void __attribute__((__noreturn__)) usage(void) | |
208 | { | |
209 | FILE *out = stdout; | |
3215d510 | 210 | |
627428a9 | 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); | |
2a1203bb | 219 | fputs(_(" -c, --command <cmd> command of persistent reservations\n"), out); |
3215d510 KZ |
220 | fputs(_(" -k, --key <num> key to operate\n"), out); |
221 | fputs(_(" -K, --oldkey <num> old key to operate\n"), out); | |
2a1203bb | 222 | fputs(_(" -f, --flag <flag> command flag\n"), out); |
223 | fputs(_(" -t, --type <type> command type\n"), out); | |
627428a9 | 224 | |
225 | fputs(USAGE_SEPARATOR, out); | |
bad4c729 | 226 | fprintf(out, USAGE_HELP_OPTIONS(26)); |
627428a9 | 227 | |
228 | fputs(USAGE_ARGUMENTS, out); | |
229 | ||
2a1203bb | 230 | fputs(_(" <cmd> is an command, available command:\n"), out); |
2a1203bb | 231 | print_pr_command(out); |
3215d510 | 232 | |
2a1203bb | 233 | fputs(_(" <flag> is a command flag, available flags:\n"), out); |
3215d510 KZ |
234 | print_pr_flag(out); |
235 | ||
2a1203bb | 236 | fputs(_(" <type> is a command type, available types:\n"), out); |
3215d510 KZ |
237 | print_pr_type(out); |
238 | ||
bad4c729 | 239 | fprintf(out, USAGE_MAN_TAIL("blkpr(8)")); |
627428a9 | 240 | exit(EXIT_SUCCESS); |
241 | } | |
242 | ||
243 | int main(int argc, char **argv) | |
244 | { | |
95d33617 | 245 | int c; |
627428a9 | 246 | char *path; |
247 | uint64_t key = 0, oldkey = 0; | |
2a1203bb | 248 | int command = -1, type = -1, flag = 0; |
627428a9 | 249 | |
250 | static const struct option longopts[] = { | |
251 | { "help", no_argument, NULL, 'h' }, | |
252 | { "version", no_argument, NULL, 'V' }, | |
2a1203bb | 253 | { "command", required_argument, NULL, 'c' }, |
627428a9 | 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; | |
2a1203bb | 267 | while ((c = getopt_long(argc, argv, "hVc:k:K:f:t:", longopts, NULL)) != -1) { |
627428a9 | 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; | |
2a1203bb | 277 | case 'c': |
278 | command = parse_pr_command(optarg); | |
279 | if (command < 0) | |
280 | err(EXIT_FAILURE, _("unknown command")); | |
627428a9 | 281 | break; |
282 | case 't': | |
283 | type = parse_pr_type(optarg); | |
f028fc21 | 284 | if (type < 0) |
627428a9 | 285 | err(EXIT_FAILURE, _("unknown type")); |
627428a9 | 286 | break; |
287 | case 'f': | |
288 | flag = parse_pr_flag(optarg); | |
f028fc21 | 289 | if (flag < 0) |
627428a9 | 290 | err(EXIT_FAILURE, _("unknown flag")); |
627428a9 | 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 | ||
f028fc21 | 302 | if (optind == argc) |
627428a9 | 303 | errx(EXIT_FAILURE, _("no device specified")); |
627428a9 | 304 | |
305 | path = argv[optind++]; | |
306 | if (optind != argc) { | |
307 | warnx(_("unexpected number of arguments")); | |
308 | errtryhelp(EXIT_FAILURE); | |
309 | } | |
310 | ||
2a1203bb | 311 | do_pr(path, key, oldkey, command, type, flag); |
627428a9 | 312 | |
313 | return EXIT_SUCCESS; | |
314 | } |