#include <stdlib.h>
#include <getopt.h>
#include <unistd.h>
#include <err.h>
#include <stdio.h>
#include <fcntl.h>
#include "os.h"
#include "out.h"
#include "common.h"
#include "output.h"
#include "file.h"
#include "rm.h"
#include "set.h"
#ifdef USE_RPMEM
#include "librpmem.h"
#endif
enum ask_type {
ASK_SOMETIMES,
ASK_ALWAYS,
ASK_NEVER,
};
static int vlevel;
static int force;
#define RM_POOLSET_NONE (0)
#define RM_POOLSET_LOCAL (1 << 0)
#define RM_POOLSET_REMOTE (1 << 1)
#define RM_POOLSET_ALL (RM_POOLSET_LOCAL | RM_POOLSET_REMOTE)
static int rm_poolset_mode;
static enum ask_type ask_mode;
static int rpmem_avail;
static const char *help_str =
"Remove pool file or all files from poolset\n"
"\n"
"Available options:\n"
" -h, --help Print this help message.\n"
" -v, --verbose Be verbose.\n"
" -s, --only-pools Remove only pool files (default).\n"
" -a, --all Remove all poolset files - local and remote.\n"
" -l, --local Remove local poolset files\n"
" -r, --remote Remove remote poolset files\n"
" -f, --force Ignore nonexisting files.\n"
" -i, --interactive Prompt before every single removal.\n"
"\n"
"For complete documentation see %s-rm(1) manual page.\n";
static const char *optstr = "hvsfialr";
static const struct option long_options[] = {
{"help", no_argument, NULL, 'h'},
{"verbose", no_argument, NULL, 'v'},
{"only-pools", no_argument, NULL, 's'},
{"all", no_argument, NULL, 'a'},
{"local", no_argument, NULL, 'l'},
{"remote", no_argument, NULL, 'r'},
{"force", no_argument, NULL, 'f'},
{"interactive", no_argument, NULL, 'i'},
{NULL, 0, NULL, 0 },
};
static void
print_usage(const char *appname)
{
printf("Usage: %s rm [<args>] <files>\n", appname);
}
void
pmempool_rm_help(char *appname)
{
print_usage(appname);
printf(help_str, appname);
}
static int
rm_file(const char *file)
{
int write_protected = os_access(file, W_OK) != 0;
char cask = 'y';
switch (ask_mode) {
case ASK_ALWAYS:
cask = '?';
break;
case ASK_NEVER:
cask = 'y';
break;
case ASK_SOMETIMES:
cask = write_protected ? '?' : 'y';
break;
}
const char *pre_msg = write_protected ? "write-protected " : "";
char ans = ask_Yn(cask, "remove %sfile '%s' ?", pre_msg, file);
if (ans == INV_ANS)
outv(1, "invalid answer\n");
if (ans == 'y') {
if (util_unlink(file)) {
outv_err("cannot remove file '%s'", file);
return 1;
}
outv(1, "removed '%s'\n", file);
}
return 0;
}
static int
remove_remote(const char *target, const char *pool_set)
{
#ifdef USE_RPMEM
char cask = 'y';
switch (ask_mode) {
case ASK_ALWAYS:
cask = '?';
break;
case ASK_NEVER:
case ASK_SOMETIMES:
cask = 'y';
break;
}
char ans = ask_Yn(cask, "remove remote pool '%s' on '%s'?",
pool_set, target);
if (ans == INV_ANS)
outv(1, "invalid answer\n");
if (ans != 'y')
return 0;
if (!rpmem_avail) {
if (force) {
outv(1, "cannot remove '%s' on '%s' -- "
"librpmem not available", pool_set, target);
return 0;
}
outv_err("!cannot remove '%s' on '%s' -- "
"librpmem not available", pool_set, target);
return 1;
}
int flags = 0;
if (rm_poolset_mode & RM_POOLSET_REMOTE)
flags |= RPMEM_REMOVE_POOL_SET;
if (force)
flags |= RPMEM_REMOVE_FORCE;
int ret = Rpmem_remove(target, pool_set, flags);
if (ret) {
if (force) {
ret = 0;
outv(1, "cannot remove '%s' on '%s'",
pool_set, target);
} else {
ret = 1;
outv_err("!cannot remove '%s' on '%s'",
pool_set, target);
}
} else {
outv(1, "removed '%s' on '%s'\n",
pool_set, target);
}
return ret;
#else
outv_err("remote replication not supported");
return 1;
#endif
}
static int
rm_poolset_cb(struct part_file *pf, void *arg)
{
int *error = (int *)arg;
int ret;
if (pf->is_remote) {
ret = remove_remote(pf->node_addr, pf->pool_desc);
} else {
const char *part_file = pf->path;
outv(2, "part file : %s\n", part_file);
int exists = os_access(part_file, F_OK) == 0;
if (!exists) {
if (force)
return 0;
ret = 1;
outv_err("!cannot remove file '%s'", part_file);
} else {
ret = rm_file(part_file);
}
}
if (ret)
*error = ret;
return 0;
}
static int
rm_poolset(const char *file)
{
int error = 0;
int ret = util_poolset_foreach_part(file, rm_poolset_cb, &error);
if (ret == -1) {
outv_err("parsing poolset failed: %s\n",
out_get_errormsg());
return ret;
}
if (error && !force) {
outv_err("!removing '%s' failed\n", file);
return error;
}
return 0;
}
int
pmempool_rm_func(char *appname, int argc, char *argv[])
{
rm_poolset_mode = RM_POOLSET_NONE;
int opt;
while ((opt = getopt_long(argc, argv, optstr,
long_options, NULL)) != -1) {
switch (opt) {
case 'h':
pmempool_rm_help(appname);
return 0;
case 'v':
vlevel++;
break;
case 's':
rm_poolset_mode = RM_POOLSET_NONE;
break;
case 'a':
rm_poolset_mode |= RM_POOLSET_ALL;
break;
case 'l':
rm_poolset_mode |= RM_POOLSET_LOCAL;
break;
case 'r':
rm_poolset_mode |= RM_POOLSET_REMOTE;
break;
case 'f':
force = 1;
ask_mode = ASK_NEVER;
break;
case 'i':
ask_mode = ASK_ALWAYS;
break;
default:
print_usage(appname);
return 1;
}
}
out_set_vlevel(vlevel);
if (optind == argc) {
print_usage(appname);
return 1;
}
#ifdef USE_RPMEM
util_remote_init();
rpmem_avail = !util_remote_load();
#endif
int lret = 0;
for (int i = optind; i < argc; i++) {
char *file = argv[i];
int exists = os_access(file, F_OK | R_OK) == 0;
if (!exists) {
if (force)
continue;
outv_err("!cannot remove '%s'", file);
lret = 1;
continue;
}
int is_poolset = util_is_poolset_file(file);
if (is_poolset < 0) {
outv(1, "%s: cannot determine type of file", file);
if (force)
continue;
}
if (is_poolset)
outv(2, "poolset file: %s\n", file);
else
outv(2, "pool file : %s\n", file);
int ret;
if (is_poolset) {
ret = rm_poolset(file);
if (!ret && (rm_poolset_mode & RM_POOLSET_LOCAL))
ret = rm_file(file);
} else {
ret = rm_file(file);
}
if (ret)
lret = ret;
}
return lret;
}