]> git.ipfire.org Git - thirdparty/util-linux.git/blob - sys-utils/blkpr.c
Make the ways of using output stream consistent in usage()
[thirdparty/util-linux.git] / 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 fprintf(out, 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 fprintf(out, USAGE_MAN_TAIL("blkpr(8)"));
240 exit(EXIT_SUCCESS);
241 }
242
243 int main(int argc, char **argv)
244 {
245 int 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 }