]>
Commit | Line | Data |
---|---|---|
a4b75251 TL |
1 | // SPDX-License-Identifier: BSD-3-Clause |
2 | /* Copyright 2014-2018, Intel Corporation */ | |
3 | ||
4 | /* | |
5 | * rm.c -- pmempool rm command main source file | |
6 | */ | |
7 | ||
8 | #include <stdlib.h> | |
9 | #include <getopt.h> | |
10 | #include <unistd.h> | |
11 | #include <err.h> | |
12 | #include <stdio.h> | |
13 | #include <fcntl.h> | |
14 | ||
15 | #include "os.h" | |
16 | #include "out.h" | |
17 | #include "common.h" | |
18 | #include "output.h" | |
19 | #include "file.h" | |
20 | #include "rm.h" | |
21 | #include "set.h" | |
22 | ||
23 | #ifdef USE_RPMEM | |
24 | #include "librpmem.h" | |
25 | #endif | |
26 | ||
27 | enum ask_type { | |
28 | ASK_SOMETIMES, /* ask before removing write-protected files */ | |
29 | ASK_ALWAYS, /* always ask */ | |
30 | ASK_NEVER, /* never ask */ | |
31 | }; | |
32 | ||
33 | /* verbosity level */ | |
34 | static int vlevel; | |
35 | /* force remove and ignore errors */ | |
36 | static int force; | |
37 | /* poolset files options */ | |
38 | #define RM_POOLSET_NONE (0) | |
39 | #define RM_POOLSET_LOCAL (1 << 0) | |
40 | #define RM_POOLSET_REMOTE (1 << 1) | |
41 | #define RM_POOLSET_ALL (RM_POOLSET_LOCAL | RM_POOLSET_REMOTE) | |
42 | static int rm_poolset_mode; | |
43 | /* mode of interaction */ | |
44 | static enum ask_type ask_mode; | |
45 | /* indicates whether librpmem is available */ | |
46 | static int rpmem_avail; | |
47 | ||
48 | /* help message */ | |
49 | static const char * const help_str = | |
50 | "Remove pool file or all files from poolset\n" | |
51 | "\n" | |
52 | "Available options:\n" | |
53 | " -h, --help Print this help message.\n" | |
54 | " -v, --verbose Be verbose.\n" | |
55 | " -s, --only-pools Remove only pool files (default).\n" | |
56 | " -a, --all Remove all poolset files - local and remote.\n" | |
57 | " -l, --local Remove local poolset files\n" | |
58 | " -r, --remote Remove remote poolset files\n" | |
59 | " -f, --force Ignore nonexisting files.\n" | |
60 | " -i, --interactive Prompt before every single removal.\n" | |
61 | "\n" | |
62 | "For complete documentation see %s-rm(1) manual page.\n"; | |
63 | ||
64 | /* short options string */ | |
65 | static const char *optstr = "hvsfialr"; | |
66 | /* long options */ | |
67 | static const struct option long_options[] = { | |
68 | {"help", no_argument, NULL, 'h'}, | |
69 | {"verbose", no_argument, NULL, 'v'}, | |
70 | {"only-pools", no_argument, NULL, 's'}, | |
71 | {"all", no_argument, NULL, 'a'}, | |
72 | {"local", no_argument, NULL, 'l'}, | |
73 | {"remote", no_argument, NULL, 'r'}, | |
74 | {"force", no_argument, NULL, 'f'}, | |
75 | {"interactive", no_argument, NULL, 'i'}, | |
76 | {NULL, 0, NULL, 0 }, | |
77 | }; | |
78 | ||
79 | /* | |
80 | * print_usage -- print usage message | |
81 | */ | |
82 | static void | |
83 | print_usage(const char *appname) | |
84 | { | |
85 | printf("Usage: %s rm [<args>] <files>\n", appname); | |
86 | } | |
87 | ||
88 | /* | |
89 | * pmempool_rm_help -- print help message | |
90 | */ | |
91 | void | |
92 | pmempool_rm_help(const char *appname) | |
93 | { | |
94 | print_usage(appname); | |
95 | printf(help_str, appname); | |
96 | } | |
97 | ||
98 | /* | |
99 | * rm_file -- remove single file | |
100 | */ | |
101 | static int | |
102 | rm_file(const char *file) | |
103 | { | |
104 | int write_protected = os_access(file, W_OK) != 0; | |
105 | char cask = 'y'; | |
106 | switch (ask_mode) { | |
107 | case ASK_ALWAYS: | |
108 | cask = '?'; | |
109 | break; | |
110 | case ASK_NEVER: | |
111 | cask = 'y'; | |
112 | break; | |
113 | case ASK_SOMETIMES: | |
114 | cask = write_protected ? '?' : 'y'; | |
115 | break; | |
116 | default: | |
117 | outv_err("unknown state"); | |
118 | return 1; | |
119 | } | |
120 | ||
121 | const char *pre_msg = write_protected ? "write-protected " : ""; | |
122 | char ans = ask_Yn(cask, "remove %sfile '%s' ?", pre_msg, file); | |
123 | if (ans == 'y') { | |
124 | if (util_unlink(file)) { | |
125 | outv_err("cannot remove file '%s'", file); | |
126 | return 1; | |
127 | } | |
128 | ||
129 | outv(1, "removed '%s'\n", file); | |
130 | } | |
131 | ||
132 | return 0; | |
133 | } | |
134 | ||
135 | /* | |
136 | * remove_remote -- (internal) remove remote pool | |
137 | */ | |
138 | static int | |
139 | remove_remote(const char *target, const char *pool_set) | |
140 | { | |
141 | #ifdef USE_RPMEM | |
142 | char cask = 'y'; | |
143 | switch (ask_mode) { | |
144 | case ASK_ALWAYS: | |
145 | cask = '?'; | |
146 | break; | |
147 | case ASK_NEVER: | |
148 | case ASK_SOMETIMES: | |
149 | cask = 'y'; | |
150 | break; | |
151 | default: | |
152 | outv_err("unknown state"); | |
153 | return 1; | |
154 | } | |
155 | ||
156 | char ans = ask_Yn(cask, "remove remote pool '%s' on '%s'?", | |
157 | pool_set, target); | |
158 | if (ans == INV_ANS) | |
159 | outv(1, "invalid answer\n"); | |
160 | ||
161 | if (ans != 'y') | |
162 | return 0; | |
163 | ||
164 | if (!rpmem_avail) { | |
165 | if (force) { | |
166 | outv(1, "cannot remove '%s' on '%s' -- " | |
167 | "librpmem not available", pool_set, target); | |
168 | return 0; | |
169 | } | |
170 | ||
171 | outv_err("!cannot remove '%s' on '%s' -- " | |
172 | "librpmem not available", pool_set, target); | |
173 | return 1; | |
174 | } | |
175 | ||
176 | int flags = 0; | |
177 | if (rm_poolset_mode & RM_POOLSET_REMOTE) | |
178 | flags |= RPMEM_REMOVE_POOL_SET; | |
179 | if (force) | |
180 | flags |= RPMEM_REMOVE_FORCE; | |
181 | ||
182 | int ret = Rpmem_remove(target, pool_set, flags); | |
183 | if (ret) { | |
184 | if (force) { | |
185 | ret = 0; | |
186 | outv(1, "cannot remove '%s' on '%s'", | |
187 | pool_set, target); | |
188 | } else { | |
189 | /* | |
190 | * Callback cannot return < 0 value because it | |
191 | * is interpretted as error in parsing poolset file. | |
192 | */ | |
193 | ret = 1; | |
194 | outv_err("!cannot remove '%s' on '%s'", | |
195 | pool_set, target); | |
196 | } | |
197 | } else { | |
198 | outv(1, "removed '%s' on '%s'\n", | |
199 | pool_set, target); | |
200 | } | |
201 | ||
202 | return ret; | |
203 | #else | |
204 | outv_err("remote replication not supported"); | |
205 | return 1; | |
206 | #endif | |
207 | } | |
208 | ||
209 | /* | |
210 | * rm_poolset_cb -- (internal) callback for removing replicas | |
211 | */ | |
212 | static int | |
213 | rm_poolset_cb(struct part_file *pf, void *arg) | |
214 | { | |
215 | int *error = (int *)arg; | |
216 | int ret; | |
217 | if (pf->is_remote) { | |
218 | ret = remove_remote(pf->remote->node_addr, | |
219 | pf->remote->pool_desc); | |
220 | } else { | |
221 | const char *part_file = pf->part->path; | |
222 | ||
223 | outv(2, "part file : %s\n", part_file); | |
224 | ||
225 | int exists = util_file_exists(part_file); | |
226 | if (exists < 0) | |
227 | ret = 1; | |
228 | else if (!exists) { | |
229 | /* | |
230 | * Ignore not accessible file if force | |
231 | * flag is set. | |
232 | */ | |
233 | if (force) | |
234 | return 0; | |
235 | ||
236 | ret = 1; | |
237 | outv_err("!cannot remove file '%s'", part_file); | |
238 | } else { | |
239 | ret = rm_file(part_file); | |
240 | } | |
241 | } | |
242 | ||
243 | if (ret) | |
244 | *error = ret; | |
245 | ||
246 | return 0; | |
247 | } | |
248 | ||
249 | /* | |
250 | * rm_poolset -- remove files parsed from poolset file | |
251 | */ | |
252 | static int | |
253 | rm_poolset(const char *file) | |
254 | { | |
255 | int error = 0; | |
256 | int ret = util_poolset_foreach_part(file, rm_poolset_cb, &error); | |
257 | if (ret == -1) { | |
258 | outv_err("parsing poolset failed: %s\n", | |
259 | out_get_errormsg()); | |
260 | return ret; | |
261 | } | |
262 | ||
263 | if (error && !force) { | |
264 | outv_err("!removing '%s' failed\n", file); | |
265 | return error; | |
266 | } | |
267 | ||
268 | return 0; | |
269 | } | |
270 | ||
271 | /* | |
272 | * pmempool_rm_func -- main function for rm command | |
273 | */ | |
274 | int | |
275 | pmempool_rm_func(const char *appname, int argc, char *argv[]) | |
276 | { | |
277 | /* by default do not remove any poolset files */ | |
278 | rm_poolset_mode = RM_POOLSET_NONE; | |
279 | ||
280 | int opt; | |
281 | while ((opt = getopt_long(argc, argv, optstr, | |
282 | long_options, NULL)) != -1) { | |
283 | switch (opt) { | |
284 | case 'h': | |
285 | pmempool_rm_help(appname); | |
286 | return 0; | |
287 | case 'v': | |
288 | vlevel++; | |
289 | break; | |
290 | case 's': | |
291 | rm_poolset_mode = RM_POOLSET_NONE; | |
292 | break; | |
293 | case 'a': | |
294 | rm_poolset_mode |= RM_POOLSET_ALL; | |
295 | break; | |
296 | case 'l': | |
297 | rm_poolset_mode |= RM_POOLSET_LOCAL; | |
298 | break; | |
299 | case 'r': | |
300 | rm_poolset_mode |= RM_POOLSET_REMOTE; | |
301 | break; | |
302 | case 'f': | |
303 | force = 1; | |
304 | ask_mode = ASK_NEVER; | |
305 | break; | |
306 | case 'i': | |
307 | ask_mode = ASK_ALWAYS; | |
308 | break; | |
309 | default: | |
310 | print_usage(appname); | |
311 | return 1; | |
312 | } | |
313 | } | |
314 | ||
315 | out_set_vlevel(vlevel); | |
316 | ||
317 | if (optind == argc) { | |
318 | print_usage(appname); | |
319 | return 1; | |
320 | } | |
321 | ||
322 | #ifdef USE_RPMEM | |
323 | /* | |
324 | * Try to load librpmem, if loading failed - | |
325 | * assume it is not available. | |
326 | */ | |
327 | util_remote_init(); | |
328 | rpmem_avail = !util_remote_load(); | |
329 | #endif | |
330 | ||
331 | int lret = 0; | |
332 | for (int i = optind; i < argc; i++) { | |
333 | char *file = argv[i]; | |
334 | /* check if file exists and we can read it */ | |
335 | int exists = os_access(file, F_OK | R_OK) == 0; | |
336 | if (!exists) { | |
337 | /* ignore not accessible file if force flag is set */ | |
338 | if (force) | |
339 | continue; | |
340 | ||
341 | outv_err("!cannot remove '%s'", file); | |
342 | lret = 1; | |
343 | continue; | |
344 | } | |
345 | ||
346 | int is_poolset = util_is_poolset_file(file); | |
347 | if (is_poolset < 0) { | |
348 | outv(1, "%s: cannot determine type of file", file); | |
349 | if (force) | |
350 | continue; | |
351 | } | |
352 | ||
353 | if (is_poolset) | |
354 | outv(2, "poolset file: %s\n", file); | |
355 | else | |
356 | outv(2, "pool file : %s\n", file); | |
357 | ||
358 | int ret; | |
359 | if (is_poolset) { | |
360 | ret = rm_poolset(file); | |
361 | if (!ret && (rm_poolset_mode & RM_POOLSET_LOCAL)) | |
362 | ret = rm_file(file); | |
363 | } else { | |
364 | ret = rm_file(file); | |
365 | } | |
366 | ||
367 | if (ret) | |
368 | lret = ret; | |
369 | } | |
370 | ||
371 | return lret; | |
372 | } |