mirror of
git://sourceware.org/git/lvm2.git
synced 2024-12-22 17:35:59 +03:00
1bc531301e
We've used udev fallback code till now to check whether udev created/removed the entries in /dev correctly and if not, a repair was done (giving a warning messagea about that). This patch adds a possibility to enable this additional check and subsequent fallback only when required (debugging purposes mostly) and trust udev completely. So let's disable the fallback code by default and add a new configuration option "activation/udev_fallback". (The original code for creating the nodes will still be used in case the device directory that is set in lvm.conf differs from the one that udev uses and also when activation/udev_rules is set to 0 - otherwise we would end up with no nodes/symlinks at all)
1502 lines
33 KiB
C
1502 lines
33 KiB
C
/*
|
|
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
|
* Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
|
|
*
|
|
* This file is part of LVM2.
|
|
*
|
|
* This copyrighted material is made available to anyone wishing to use,
|
|
* modify, copy, or redistribute it subject to the terms and conditions
|
|
* of the GNU Lesser General Public License v.2.1.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include "tools.h"
|
|
#include "lvm2cmdline.h"
|
|
#include "label.h"
|
|
#include "lvm-version.h"
|
|
|
|
#include "stub.h"
|
|
#include "lvm2cmd.h"
|
|
#include "last-path-component.h"
|
|
|
|
#include <signal.h>
|
|
#include <syslog.h>
|
|
#include <libgen.h>
|
|
#include <sys/stat.h>
|
|
#include <time.h>
|
|
#include <sys/resource.h>
|
|
|
|
#ifdef HAVE_GETOPTLONG
|
|
# include <getopt.h>
|
|
# define GETOPTLONG_FN(a, b, c, d, e) getopt_long((a), (b), (c), (d), (e))
|
|
# define OPTIND_INIT 0
|
|
#else
|
|
struct option {
|
|
};
|
|
extern int optind;
|
|
extern char *optarg;
|
|
# define GETOPTLONG_FN(a, b, c, d, e) getopt((a), (b), (c))
|
|
# define OPTIND_INIT 1
|
|
#endif
|
|
|
|
#ifdef UDEV_SYNC_SUPPORT
|
|
# include <libudev.h>
|
|
#endif
|
|
|
|
/*
|
|
* Table of valid switches
|
|
*/
|
|
static struct arg_props _arg_props[ARG_COUNT + 1] = {
|
|
#define arg(a, b, c, d, e) {b, "", "--" c, d, e},
|
|
#include "args.h"
|
|
#undef arg
|
|
};
|
|
|
|
static struct cmdline_context _cmdline;
|
|
|
|
/* Command line args */
|
|
unsigned arg_count(const struct cmd_context *cmd, int a)
|
|
{
|
|
return cmd->arg_values[a].count;
|
|
}
|
|
|
|
unsigned grouped_arg_count(const struct arg_values *av, int a)
|
|
{
|
|
return av[a].count;
|
|
}
|
|
|
|
unsigned arg_is_set(const struct cmd_context *cmd, int a)
|
|
{
|
|
return arg_count(cmd, a) ? 1 : 0;
|
|
}
|
|
|
|
unsigned grouped_arg_is_set(const struct arg_values *av, int a)
|
|
{
|
|
return grouped_arg_count(av, a) ? 1 : 0;
|
|
}
|
|
|
|
const char *arg_value(struct cmd_context *cmd, int a)
|
|
{
|
|
return cmd->arg_values[a].value;
|
|
}
|
|
|
|
const char *arg_str_value(struct cmd_context *cmd, int a, const char *def)
|
|
{
|
|
return arg_count(cmd, a) ? cmd->arg_values[a].value : def;
|
|
}
|
|
|
|
const char *grouped_arg_str_value(const struct arg_values *av, int a, const char *def)
|
|
{
|
|
return grouped_arg_count(av, a) ? av[a].value : def;
|
|
}
|
|
|
|
int32_t arg_int_value(struct cmd_context *cmd, int a, const int32_t def)
|
|
{
|
|
return arg_count(cmd, a) ? cmd->arg_values[a].i_value : def;
|
|
}
|
|
|
|
uint32_t arg_uint_value(struct cmd_context *cmd, int a, const uint32_t def)
|
|
{
|
|
return arg_count(cmd, a) ? cmd->arg_values[a].ui_value : def;
|
|
}
|
|
|
|
int64_t arg_int64_value(struct cmd_context *cmd, int a, const int64_t def)
|
|
{
|
|
return arg_count(cmd, a) ? cmd->arg_values[a].i64_value : def;
|
|
}
|
|
|
|
uint64_t arg_uint64_value(struct cmd_context *cmd, int a, const uint64_t def)
|
|
{
|
|
return arg_count(cmd, a) ? cmd->arg_values[a].ui64_value : def;
|
|
}
|
|
|
|
/* No longer used.
|
|
const void *arg_ptr_value(struct cmd_context *cmd, int a, const void *def)
|
|
{
|
|
return arg_count(cmd, a) ? cmd->arg_values[a].ptr : def;
|
|
}
|
|
*/
|
|
|
|
sign_t arg_sign_value(struct cmd_context *cmd, int a, const sign_t def)
|
|
{
|
|
return arg_count(cmd, a) ? cmd->arg_values[a].sign : def;
|
|
}
|
|
|
|
percent_type_t arg_percent_value(struct cmd_context *cmd, int a, const percent_type_t def)
|
|
{
|
|
return arg_count(cmd, a) ? cmd->arg_values[a].percent : def;
|
|
}
|
|
|
|
int arg_count_increment(struct cmd_context *cmd, int a)
|
|
{
|
|
return cmd->arg_values[a].count++;
|
|
}
|
|
|
|
int yes_no_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av)
|
|
{
|
|
av->sign = SIGN_NONE;
|
|
av->percent = PERCENT_NONE;
|
|
|
|
if (!strcmp(av->value, "y")) {
|
|
av->i_value = 1;
|
|
av->ui_value = 1;
|
|
}
|
|
|
|
else if (!strcmp(av->value, "n")) {
|
|
av->i_value = 0;
|
|
av->ui_value = 0;
|
|
}
|
|
|
|
else
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
int yes_no_excl_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av)
|
|
{
|
|
av->sign = SIGN_NONE;
|
|
av->percent = PERCENT_NONE;
|
|
|
|
if (!strcmp(av->value, "e") || !strcmp(av->value, "ey") ||
|
|
!strcmp(av->value, "ye")) {
|
|
av->i_value = CHANGE_AE;
|
|
av->ui_value = CHANGE_AE;
|
|
}
|
|
|
|
else if (!strcmp(av->value, "y")) {
|
|
av->i_value = CHANGE_AY;
|
|
av->ui_value = CHANGE_AY;
|
|
}
|
|
|
|
else if (!strcmp(av->value, "n") || !strcmp(av->value, "en") ||
|
|
!strcmp(av->value, "ne")) {
|
|
av->i_value = CHANGE_AN;
|
|
av->ui_value = CHANGE_AN;
|
|
}
|
|
|
|
else if (!strcmp(av->value, "ln") || !strcmp(av->value, "nl")) {
|
|
av->i_value = CHANGE_ALN;
|
|
av->ui_value = CHANGE_ALN;
|
|
}
|
|
|
|
else if (!strcmp(av->value, "ly") || !strcmp(av->value, "yl")) {
|
|
av->i_value = CHANGE_ALY;
|
|
av->ui_value = CHANGE_ALY;
|
|
}
|
|
|
|
else
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
int metadatatype_arg(struct cmd_context *cmd, struct arg_values *av)
|
|
{
|
|
return get_format_by_name(cmd, av->value) ? 1 : 0;
|
|
}
|
|
|
|
static int _get_int_arg(struct arg_values *av, char **ptr)
|
|
{
|
|
char *val;
|
|
long v;
|
|
|
|
av->percent = PERCENT_NONE;
|
|
|
|
val = av->value;
|
|
switch (*val) {
|
|
case '+':
|
|
av->sign = SIGN_PLUS;
|
|
val++;
|
|
break;
|
|
case '-':
|
|
av->sign = SIGN_MINUS;
|
|
val++;
|
|
break;
|
|
default:
|
|
av->sign = SIGN_NONE;
|
|
}
|
|
|
|
if (!isdigit(*val))
|
|
return 0;
|
|
|
|
v = strtol(val, ptr, 10);
|
|
|
|
if (*ptr == val)
|
|
return 0;
|
|
|
|
av->i_value = (int32_t) v;
|
|
av->ui_value = (uint32_t) v;
|
|
av->i64_value = (int64_t) v;
|
|
av->ui64_value = (uint64_t) v;
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* Size stored in sectors */
|
|
static int _size_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av, int factor)
|
|
{
|
|
char *ptr;
|
|
int i;
|
|
static const char *suffixes = "kmgtpebs";
|
|
char *val;
|
|
double v;
|
|
uint64_t v_tmp, adjustment;
|
|
|
|
av->percent = PERCENT_NONE;
|
|
|
|
val = av->value;
|
|
switch (*val) {
|
|
case '+':
|
|
av->sign = SIGN_PLUS;
|
|
val++;
|
|
break;
|
|
case '-':
|
|
av->sign = SIGN_MINUS;
|
|
val++;
|
|
break;
|
|
default:
|
|
av->sign = SIGN_NONE;
|
|
}
|
|
|
|
if (!isdigit(*val))
|
|
return 0;
|
|
|
|
v = strtod(val, &ptr);
|
|
|
|
if (ptr == val)
|
|
return 0;
|
|
|
|
if (*ptr) {
|
|
for (i = strlen(suffixes) - 1; i >= 0; i--)
|
|
if (suffixes[i] == tolower((int) *ptr))
|
|
break;
|
|
|
|
if (i < 0) {
|
|
return 0;
|
|
} else if (i == 7) {
|
|
/* sectors */
|
|
v = v;
|
|
} else if (i == 6) {
|
|
/* bytes */
|
|
v_tmp = (uint64_t) v;
|
|
adjustment = v_tmp % 512;
|
|
if (adjustment) {
|
|
v_tmp += (512 - adjustment);
|
|
log_error("Size is not a multiple of 512. "
|
|
"Try using %"PRIu64" or %"PRIu64".",
|
|
v_tmp - 512, v_tmp);
|
|
return 0;
|
|
}
|
|
v /= 512;
|
|
} else {
|
|
/* all other units: kmgtpe */
|
|
while (i-- > 0)
|
|
v *= 1024;
|
|
v *= 2;
|
|
}
|
|
} else
|
|
v *= factor;
|
|
|
|
av->i_value = (int32_t) v;
|
|
av->ui_value = (uint32_t) v;
|
|
av->i64_value = (int64_t) v;
|
|
av->ui64_value = (uint64_t) v;
|
|
|
|
return 1;
|
|
}
|
|
|
|
int size_kb_arg(struct cmd_context *cmd, struct arg_values *av)
|
|
{
|
|
return _size_arg(cmd, av, 2);
|
|
}
|
|
|
|
int size_mb_arg(struct cmd_context *cmd, struct arg_values *av)
|
|
{
|
|
return _size_arg(cmd, av, 2048);
|
|
}
|
|
|
|
int int_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av)
|
|
{
|
|
char *ptr;
|
|
|
|
if (!_get_int_arg(av, &ptr) || (*ptr) || (av->sign == SIGN_MINUS))
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
int int_arg_with_sign(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av)
|
|
{
|
|
char *ptr;
|
|
|
|
if (!_get_int_arg(av, &ptr) || (*ptr))
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
int int_arg_with_sign_and_percent(struct cmd_context *cmd __attribute__((unused)),
|
|
struct arg_values *av)
|
|
{
|
|
char *ptr;
|
|
|
|
if (!_get_int_arg(av, &ptr))
|
|
return 0;
|
|
|
|
if (!*ptr)
|
|
return 1;
|
|
|
|
if (*ptr++ != '%')
|
|
return 0;
|
|
|
|
if (!strcasecmp(ptr, "V") || !strcasecmp(ptr, "VG"))
|
|
av->percent = PERCENT_VG;
|
|
else if (!strcasecmp(ptr, "L") || !strcasecmp(ptr, "LV"))
|
|
av->percent = PERCENT_LV;
|
|
else if (!strcasecmp(ptr, "P") || !strcasecmp(ptr, "PV") ||
|
|
!strcasecmp(ptr, "PVS"))
|
|
av->percent = PERCENT_PVS;
|
|
else if (!strcasecmp(ptr, "F") || !strcasecmp(ptr, "FR") ||
|
|
!strcasecmp(ptr, "FREE"))
|
|
av->percent = PERCENT_FREE;
|
|
else if (!strcasecmp(ptr, "O") || !strcasecmp(ptr, "OR") ||
|
|
!strcasecmp(ptr, "ORIGIN"))
|
|
av->percent = PERCENT_ORIGIN;
|
|
else
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
int minor_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av)
|
|
{
|
|
char *ptr;
|
|
|
|
if (!_get_int_arg(av, &ptr) || (*ptr) || (av->sign == SIGN_MINUS))
|
|
return 0;
|
|
|
|
if (av->i_value > 255) {
|
|
log_error("Minor number outside range 0-255");
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int major_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av)
|
|
{
|
|
char *ptr;
|
|
|
|
if (!_get_int_arg(av, &ptr) || (*ptr) || (av->sign == SIGN_MINUS))
|
|
return 0;
|
|
|
|
if (av->i_value > 255) {
|
|
log_error("Major number outside range 0-255");
|
|
return 0;
|
|
}
|
|
|
|
/* FIXME Also Check against /proc/devices */
|
|
|
|
return 1;
|
|
}
|
|
|
|
int string_arg(struct cmd_context *cmd __attribute__((unused)),
|
|
struct arg_values *av __attribute__((unused)))
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
int tag_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av)
|
|
{
|
|
char *pos = av->value;
|
|
|
|
if (*pos == '@')
|
|
pos++;
|
|
|
|
if (!validate_tag(pos))
|
|
return 0;
|
|
|
|
av->value = pos;
|
|
|
|
return 1;
|
|
}
|
|
|
|
int permission_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av)
|
|
{
|
|
av->sign = SIGN_NONE;
|
|
|
|
if ((!strcmp(av->value, "rw")) || (!strcmp(av->value, "wr")))
|
|
av->ui_value = LVM_READ | LVM_WRITE;
|
|
|
|
else if (!strcmp(av->value, "r"))
|
|
av->ui_value = LVM_READ;
|
|
|
|
else
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
int alloc_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av)
|
|
{
|
|
alloc_policy_t alloc;
|
|
|
|
av->sign = SIGN_NONE;
|
|
|
|
alloc = get_alloc_from_string(av->value);
|
|
if (alloc == ALLOC_INVALID)
|
|
return 0;
|
|
|
|
av->ui_value = (uint32_t) alloc;
|
|
|
|
return 1;
|
|
}
|
|
|
|
int segtype_arg(struct cmd_context *cmd, struct arg_values *av)
|
|
{
|
|
return get_segtype_from_string(cmd, av->value) ? 1 : 0;
|
|
}
|
|
|
|
/*
|
|
* Positive integer, zero or "auto".
|
|
*/
|
|
int readahead_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av)
|
|
{
|
|
if (!strcasecmp(av->value, "auto")) {
|
|
av->ui_value = DM_READ_AHEAD_AUTO;
|
|
return 1;
|
|
}
|
|
|
|
if (!strcasecmp(av->value, "none")) {
|
|
av->ui_value = DM_READ_AHEAD_NONE;
|
|
return 1;
|
|
}
|
|
|
|
if (!_size_arg(cmd, av, 1))
|
|
return 0;
|
|
|
|
if (av->sign == SIGN_MINUS)
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Non-zero, positive integer, "all", or "unmanaged"
|
|
*/
|
|
int metadatacopies_arg(struct cmd_context *cmd, struct arg_values *av)
|
|
{
|
|
if (!strncmp(cmd->command->name, "vg", 2)) {
|
|
if (!strcasecmp(av->value, "all")) {
|
|
av->ui_value = VGMETADATACOPIES_ALL;
|
|
return 1;
|
|
}
|
|
|
|
if (!strcasecmp(av->value, "unmanaged")) {
|
|
av->ui_value = VGMETADATACOPIES_UNMANAGED;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return int_arg(cmd, av);
|
|
}
|
|
|
|
static void __alloc(int size)
|
|
{
|
|
if (!(_cmdline.commands = dm_realloc(_cmdline.commands, sizeof(*_cmdline.commands) * size))) {
|
|
log_fatal("Couldn't allocate memory.");
|
|
exit(ECMD_FAILED);
|
|
}
|
|
|
|
_cmdline.commands_size = size;
|
|
}
|
|
|
|
static void _alloc_command(void)
|
|
{
|
|
if (!_cmdline.commands_size)
|
|
__alloc(32);
|
|
|
|
if (_cmdline.commands_size <= _cmdline.num_commands)
|
|
__alloc(2 * _cmdline.commands_size);
|
|
}
|
|
|
|
static void _create_new_command(const char *name, command_fn command,
|
|
unsigned flags,
|
|
const char *desc, const char *usagestr,
|
|
int nargs, int *args)
|
|
{
|
|
struct command *nc;
|
|
|
|
_alloc_command();
|
|
|
|
nc = _cmdline.commands + _cmdline.num_commands++;
|
|
|
|
nc->name = name;
|
|
nc->desc = desc;
|
|
nc->usage = usagestr;
|
|
nc->fn = command;
|
|
nc->flags = flags;
|
|
nc->num_args = nargs;
|
|
nc->valid_args = args;
|
|
}
|
|
|
|
static void _register_command(const char *name, command_fn fn, const char *desc,
|
|
unsigned flags, const char *usagestr, ...)
|
|
{
|
|
int nargs = 0, i;
|
|
int *args;
|
|
va_list ap;
|
|
|
|
/* count how many arguments we have */
|
|
va_start(ap, usagestr);
|
|
while (va_arg(ap, int) >= 0)
|
|
nargs++;
|
|
va_end(ap);
|
|
|
|
/* allocate space for them */
|
|
if (!(args = dm_malloc(sizeof(*args) * nargs))) {
|
|
log_fatal("Out of memory.");
|
|
exit(ECMD_FAILED);
|
|
}
|
|
|
|
/* fill them in */
|
|
va_start(ap, usagestr);
|
|
for (i = 0; i < nargs; i++)
|
|
args[i] = va_arg(ap, int);
|
|
va_end(ap);
|
|
|
|
/* enter the command in the register */
|
|
_create_new_command(name, fn, flags, desc, usagestr, nargs, args);
|
|
}
|
|
|
|
void lvm_register_commands(void)
|
|
{
|
|
#define xx(a, b, c, d...) _register_command(# a, a, b, c, ## d, \
|
|
driverloaded_ARG, \
|
|
debug_ARG, help_ARG, help2_ARG, \
|
|
version_ARG, verbose_ARG, \
|
|
quiet_ARG, config_ARG, -1);
|
|
#include "commands.h"
|
|
#undef xx
|
|
}
|
|
|
|
static struct command *_find_command(const char *name)
|
|
{
|
|
int i;
|
|
const char *base;
|
|
|
|
base = last_path_component(name);
|
|
|
|
for (i = 0; i < _cmdline.num_commands; i++) {
|
|
if (!strcmp(base, _cmdline.commands[i].name))
|
|
break;
|
|
}
|
|
|
|
if (i >= _cmdline.num_commands)
|
|
return 0;
|
|
|
|
return _cmdline.commands + i;
|
|
}
|
|
|
|
static void _short_usage(const char *name)
|
|
{
|
|
log_error("Run `%s --help' for more information.", name);
|
|
}
|
|
|
|
static int _usage(const char *name)
|
|
{
|
|
struct command *com = _find_command(name);
|
|
|
|
if (!com) {
|
|
log_print("%s: no such command.", name);
|
|
return 0;
|
|
}
|
|
|
|
log_print("%s: %s\n\n%s", com->name, com->desc, com->usage);
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Sets up the short and long argument. If there
|
|
* is no short argument then the index of the
|
|
* argument in the the_args array is set as the
|
|
* long opt value. Yuck. Of course this means we
|
|
* can't have more than 'a' long arguments.
|
|
*/
|
|
static void _add_getopt_arg(int arg, char **ptr, struct option **o)
|
|
{
|
|
struct arg_props *a = _cmdline.arg_props + arg;
|
|
|
|
if (a->short_arg) {
|
|
*(*ptr)++ = a->short_arg;
|
|
|
|
if (a->fn)
|
|
*(*ptr)++ = ':';
|
|
}
|
|
#ifdef HAVE_GETOPTLONG
|
|
if (*(a->long_arg + 2)) {
|
|
(*o)->name = a->long_arg + 2;
|
|
(*o)->has_arg = a->fn ? 1 : 0;
|
|
(*o)->flag = NULL;
|
|
if (a->short_arg)
|
|
(*o)->val = a->short_arg;
|
|
else
|
|
(*o)->val = arg;
|
|
(*o)++;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static int _find_arg(struct command *com, int opt)
|
|
{
|
|
struct arg_props *a;
|
|
int i, arg;
|
|
|
|
for (i = 0; i < com->num_args; i++) {
|
|
arg = com->valid_args[i];
|
|
a = _cmdline.arg_props + arg;
|
|
|
|
/*
|
|
* opt should equal either the
|
|
* short arg, or the index into
|
|
* the_args.
|
|
*/
|
|
if ((a->short_arg && (opt == a->short_arg)) ||
|
|
(!a->short_arg && (opt == arg)))
|
|
return arg;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int _process_command_line(struct cmd_context *cmd, int *argc,
|
|
char ***argv)
|
|
{
|
|
int i, opt, arg;
|
|
char str[((ARG_COUNT + 1) * 2) + 1], *ptr = str;
|
|
struct option opts[ARG_COUNT + 1], *o = opts;
|
|
struct arg_props *a;
|
|
struct arg_values *av;
|
|
struct arg_value_group_list *current_group = NULL;
|
|
|
|
if (!(cmd->arg_values = dm_pool_zalloc(cmd->mem, sizeof(*cmd->arg_values) * ARG_COUNT))) {
|
|
log_fatal("Unable to allocate memory for command line arguments.");
|
|
return 0;
|
|
}
|
|
|
|
/* fill in the short and long opts */
|
|
for (i = 0; i < cmd->command->num_args; i++)
|
|
_add_getopt_arg(cmd->command->valid_args[i], &ptr, &o);
|
|
|
|
*ptr = '\0';
|
|
memset(o, 0, sizeof(*o));
|
|
|
|
/* initialise getopt_long & scan for command line switches */
|
|
optarg = 0;
|
|
optind = OPTIND_INIT;
|
|
while ((opt = GETOPTLONG_FN(*argc, *argv, str, opts, NULL)) >= 0) {
|
|
|
|
if (opt == '?')
|
|
return 0;
|
|
|
|
if ((arg = _find_arg(cmd->command, opt)) < 0) {
|
|
log_fatal("Unrecognised option.");
|
|
return 0;
|
|
}
|
|
|
|
a = _cmdline.arg_props + arg;
|
|
|
|
av = &cmd->arg_values[arg];
|
|
|
|
if (a->flags & ARG_GROUPABLE) {
|
|
/* Start a new group of arguments the first time or if a non-countable argument is repeated. */
|
|
if (!current_group || (current_group->arg_values[arg].count && !(a->flags & ARG_COUNTABLE))) {
|
|
/* FIXME Reduce size including only groupable args */
|
|
if (!(current_group = dm_pool_zalloc(cmd->mem, sizeof(struct arg_value_group_list) + sizeof(*cmd->arg_values) * ARG_COUNT))) {
|
|
log_fatal("Unable to allocate memory for command line arguments.");
|
|
return 0;
|
|
}
|
|
|
|
dm_list_add(&cmd->arg_value_groups, ¤t_group->list);
|
|
}
|
|
/* Maintain total argument count as well as count within each group */
|
|
av->count++;
|
|
av = ¤t_group->arg_values[arg];
|
|
}
|
|
|
|
if (av->count && !(a->flags & ARG_COUNTABLE)) {
|
|
log_error("Option%s%c%s%s may not be repeated.",
|
|
a->short_arg ? " -" : "",
|
|
a->short_arg ? : ' ',
|
|
(a->short_arg && a->long_arg) ?
|
|
"/" : "", a->long_arg ? : "");
|
|
return 0;
|
|
}
|
|
|
|
if (a->fn) {
|
|
if (!optarg) {
|
|
log_error("Option requires argument.");
|
|
return 0;
|
|
}
|
|
|
|
av->value = optarg;
|
|
|
|
if (!a->fn(cmd, av)) {
|
|
log_error("Invalid argument for %s: %s", a->long_arg, optarg);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
av->count++;
|
|
}
|
|
|
|
*argc -= optind;
|
|
*argv += optind;
|
|
return 1;
|
|
}
|
|
|
|
static int _merge_synonym(struct cmd_context *cmd, int oldarg, int newarg)
|
|
{
|
|
const struct arg_values *old;
|
|
struct arg_values *new;
|
|
|
|
if (arg_count(cmd, oldarg) && arg_count(cmd, newarg)) {
|
|
log_error("%s and %s are synonyms. Please only supply one.",
|
|
_cmdline.arg_props[oldarg].long_arg, _cmdline.arg_props[newarg].long_arg);
|
|
return 0;
|
|
}
|
|
|
|
if (!arg_count(cmd, oldarg))
|
|
return 1;
|
|
|
|
old = cmd->arg_values + oldarg;
|
|
new = cmd->arg_values + newarg;
|
|
|
|
new->count = old->count;
|
|
new->value = old->value;
|
|
new->i_value = old->i_value;
|
|
new->ui_value = old->ui_value;
|
|
new->i64_value = old->i64_value;
|
|
new->ui64_value = old->ui64_value;
|
|
new->sign = old->sign;
|
|
|
|
return 1;
|
|
}
|
|
|
|
int version(struct cmd_context *cmd __attribute__((unused)),
|
|
int argc __attribute__((unused)),
|
|
char **argv __attribute__((unused)))
|
|
{
|
|
char vsn[80];
|
|
|
|
log_print("LVM version: %s", LVM_VERSION);
|
|
if (library_version(vsn, sizeof(vsn)))
|
|
log_print("Library version: %s", vsn);
|
|
if (driver_version(vsn, sizeof(vsn)))
|
|
log_print("Driver version: %s", vsn);
|
|
|
|
return ECMD_PROCESSED;
|
|
}
|
|
|
|
static int _get_settings(struct cmd_context *cmd)
|
|
{
|
|
cmd->current_settings = cmd->default_settings;
|
|
|
|
if (arg_count(cmd, debug_ARG))
|
|
cmd->current_settings.debug = _LOG_FATAL +
|
|
(arg_count(cmd, debug_ARG) - 1);
|
|
|
|
if (arg_count(cmd, verbose_ARG))
|
|
cmd->current_settings.verbose = arg_count(cmd, verbose_ARG);
|
|
|
|
if (arg_count(cmd, quiet_ARG)) {
|
|
cmd->current_settings.debug = 0;
|
|
cmd->current_settings.verbose = 0;
|
|
}
|
|
|
|
if (arg_count(cmd, test_ARG))
|
|
cmd->current_settings.test = arg_count(cmd, test_ARG);
|
|
|
|
if (arg_count(cmd, driverloaded_ARG)) {
|
|
cmd->current_settings.activation =
|
|
arg_int_value(cmd, driverloaded_ARG,
|
|
cmd->default_settings.activation);
|
|
}
|
|
|
|
cmd->current_settings.archive = arg_int_value(cmd, autobackup_ARG, cmd->current_settings.archive);
|
|
cmd->current_settings.backup = arg_int_value(cmd, autobackup_ARG, cmd->current_settings.backup);
|
|
cmd->current_settings.cache_vgmetadata = cmd->command->flags & CACHE_VGMETADATA ? 1 : 0;
|
|
cmd->partial_activation = 0;
|
|
|
|
if (arg_count(cmd, partial_ARG)) {
|
|
cmd->partial_activation = 1;
|
|
log_print("Partial mode. Incomplete logical volumes will be processed.");
|
|
}
|
|
|
|
if (arg_count(cmd, ignorelockingfailure_ARG) || arg_count(cmd, sysinit_ARG))
|
|
init_ignorelockingfailure(1);
|
|
else
|
|
init_ignorelockingfailure(0);
|
|
|
|
if (arg_count(cmd, nosuffix_ARG))
|
|
cmd->current_settings.suffix = 0;
|
|
|
|
if (arg_count(cmd, units_ARG))
|
|
if (!(cmd->current_settings.unit_factor =
|
|
units_to_bytes(arg_str_value(cmd, units_ARG, ""),
|
|
&cmd->current_settings.unit_type))) {
|
|
log_error("Invalid units specification");
|
|
return EINVALID_CMD_LINE;
|
|
}
|
|
|
|
if (arg_count(cmd, trustcache_ARG)) {
|
|
if (arg_count(cmd, all_ARG)) {
|
|
log_error("--trustcache is incompatible with --all");
|
|
return EINVALID_CMD_LINE;
|
|
}
|
|
init_trust_cache(1);
|
|
log_warn("WARNING: Cache file of PVs will be trusted. "
|
|
"New devices holding PVs may get ignored.");
|
|
} else
|
|
init_trust_cache(0);
|
|
|
|
if (arg_count(cmd, noudevsync_ARG))
|
|
cmd->current_settings.udev_sync = 0;
|
|
|
|
/* Handle synonyms */
|
|
if (!_merge_synonym(cmd, resizable_ARG, resizeable_ARG) ||
|
|
!_merge_synonym(cmd, allocation_ARG, allocatable_ARG) ||
|
|
!_merge_synonym(cmd, allocation_ARG, resizeable_ARG) ||
|
|
!_merge_synonym(cmd, virtualoriginsize_ARG, virtualsize_ARG))
|
|
return EINVALID_CMD_LINE;
|
|
|
|
if ((!strncmp(cmd->command->name, "pv", 2) &&
|
|
!_merge_synonym(cmd, metadatacopies_ARG, pvmetadatacopies_ARG)) ||
|
|
(!strncmp(cmd->command->name, "vg", 2) &&
|
|
!_merge_synonym(cmd, metadatacopies_ARG, vgmetadatacopies_ARG)))
|
|
return EINVALID_CMD_LINE;
|
|
|
|
/* Zero indicates success */
|
|
return 0;
|
|
}
|
|
|
|
static int _process_common_commands(struct cmd_context *cmd)
|
|
{
|
|
if (arg_count(cmd, help_ARG) || arg_count(cmd, help2_ARG)) {
|
|
_usage(cmd->command->name);
|
|
return ECMD_PROCESSED;
|
|
}
|
|
|
|
if (arg_count(cmd, version_ARG)) {
|
|
return version(cmd, 0, (char **) NULL);
|
|
}
|
|
|
|
/* Zero indicates it's OK to continue processing this command */
|
|
return 0;
|
|
}
|
|
|
|
static void _display_help(void)
|
|
{
|
|
int i;
|
|
|
|
log_error("Available lvm commands:");
|
|
log_error("Use 'lvm help <command>' for more information");
|
|
log_error(" ");
|
|
|
|
for (i = 0; i < _cmdline.num_commands; i++) {
|
|
struct command *com = _cmdline.commands + i;
|
|
|
|
log_error("%-16.16s%s", com->name, com->desc);
|
|
}
|
|
}
|
|
|
|
int help(struct cmd_context *cmd __attribute__((unused)), int argc, char **argv)
|
|
{
|
|
int ret = ECMD_PROCESSED;
|
|
|
|
if (!argc)
|
|
_display_help();
|
|
else {
|
|
int i;
|
|
for (i = 0; i < argc; i++)
|
|
if (!_usage(argv[i]))
|
|
ret = EINVALID_CMD_LINE;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void _apply_settings(struct cmd_context *cmd)
|
|
{
|
|
init_debug(cmd->current_settings.debug);
|
|
init_verbose(cmd->current_settings.verbose + VERBOSE_BASE_LEVEL);
|
|
init_test(cmd->current_settings.test);
|
|
init_full_scan_done(0);
|
|
init_mirror_in_sync(0);
|
|
|
|
init_msg_prefix(cmd->default_settings.msg_prefix);
|
|
init_cmd_name(cmd->default_settings.cmd_name);
|
|
|
|
archive_enable(cmd, cmd->current_settings.archive);
|
|
backup_enable(cmd, cmd->current_settings.backup);
|
|
|
|
set_activation(cmd->current_settings.activation);
|
|
|
|
cmd->fmt = get_format_by_name(cmd, arg_str_value(cmd, metadatatype_ARG,
|
|
cmd->current_settings.fmt_name));
|
|
|
|
cmd->handles_missing_pvs = 0;
|
|
}
|
|
|
|
static int _set_udev_fallback(struct cmd_context *cmd)
|
|
{
|
|
#ifdef UDEV_SYNC_SUPPORT
|
|
const char *udev_dev_dir;
|
|
size_t udev_dev_dir_len;
|
|
int dirs_diff;
|
|
|
|
if (!(udev_dev_dir = udev_get_dev_dir()) ||
|
|
!*udev_dev_dir) {
|
|
log_error("Could not get udev dev path.");
|
|
return 0;
|
|
}
|
|
udev_dev_dir_len = strlen(udev_dev_dir);
|
|
|
|
/* There's always a slash at the end of dev_dir. But check udev_dev_dir! */
|
|
if (udev_dev_dir[udev_dev_dir_len - 1] != '/')
|
|
dirs_diff = strncmp(cmd->dev_dir, udev_dev_dir,
|
|
udev_dev_dir_len);
|
|
else
|
|
dirs_diff = strcmp(cmd->dev_dir, udev_dev_dir);
|
|
|
|
if (dirs_diff) {
|
|
log_debug("The path %s used for creating device nodes and "
|
|
"symlinks that is set in the configuration differs "
|
|
"from the path %s that is used by udev. All warnings "
|
|
"about udev not working correctly while processing "
|
|
"particular nodes and symlinks will be suppressed. "
|
|
"These nodes and symlinks will be managed in each "
|
|
"directory separately.",
|
|
cmd->dev_dir, udev_dev_dir);
|
|
dm_udev_set_checking(0);
|
|
init_udev_checking(0);
|
|
|
|
/* Device directories differ - we must use the fallback code! */
|
|
cmd->current_settings.udev_fallback = 1;
|
|
}
|
|
|
|
#endif
|
|
return 1;
|
|
}
|
|
|
|
static const char *_copy_command_line(struct cmd_context *cmd, int argc, char **argv)
|
|
{
|
|
int i, space;
|
|
|
|
/*
|
|
* Build up the complete command line, used as a
|
|
* description for backups.
|
|
*/
|
|
if (!dm_pool_begin_object(cmd->mem, 128))
|
|
goto_bad;
|
|
|
|
for (i = 0; i < argc; i++) {
|
|
space = strchr(argv[i], ' ') ? 1 : 0;
|
|
|
|
if (space && !dm_pool_grow_object(cmd->mem, "'", 1))
|
|
goto_bad;
|
|
|
|
if (!dm_pool_grow_object(cmd->mem, argv[i], strlen(argv[i])))
|
|
goto_bad;
|
|
|
|
if (space && !dm_pool_grow_object(cmd->mem, "'", 1))
|
|
goto_bad;
|
|
|
|
if (i < (argc - 1))
|
|
if (!dm_pool_grow_object(cmd->mem, " ", 1))
|
|
goto_bad;
|
|
}
|
|
|
|
/*
|
|
* Terminate.
|
|
*/
|
|
if (!dm_pool_grow_object(cmd->mem, "\0", 1))
|
|
goto_bad;
|
|
|
|
return dm_pool_end_object(cmd->mem);
|
|
|
|
bad:
|
|
log_error("Couldn't copy command line.");
|
|
dm_pool_abandon_object(cmd->mem);
|
|
return NULL;
|
|
}
|
|
|
|
int lvm_run_command(struct cmd_context *cmd, int argc, char **argv)
|
|
{
|
|
int ret = 0;
|
|
int locking_type;
|
|
|
|
init_error_message_produced(0);
|
|
|
|
/* each command should start out with sigint flag cleared */
|
|
sigint_clear();
|
|
|
|
if (!(cmd->cmd_line = _copy_command_line(cmd, argc, argv))) {
|
|
stack;
|
|
return ECMD_FAILED;
|
|
}
|
|
|
|
log_debug("Parsing: %s", cmd->cmd_line);
|
|
|
|
if (!(cmd->command = _find_command(argv[0])))
|
|
return ENO_SUCH_CMD;
|
|
|
|
if (!_process_command_line(cmd, &argc, &argv)) {
|
|
log_error("Error during parsing of command line.");
|
|
return EINVALID_CMD_LINE;
|
|
}
|
|
|
|
set_cmd_name(cmd->command->name);
|
|
|
|
if (arg_count(cmd, config_ARG))
|
|
if (override_config_tree_from_string(cmd,
|
|
arg_str_value(cmd, config_ARG, ""))) {
|
|
ret = EINVALID_CMD_LINE;
|
|
goto_out;
|
|
}
|
|
|
|
if (arg_count(cmd, config_ARG) || !cmd->config_valid || config_files_changed(cmd)) {
|
|
/* Reinitialise various settings inc. logging, filters */
|
|
if (!refresh_toolcontext(cmd)) {
|
|
if (cmd->cft_override) {
|
|
destroy_config_tree(cmd->cft_override);
|
|
cmd->cft_override = NULL;
|
|
}
|
|
log_error("Updated config file invalid. Aborting.");
|
|
return ECMD_FAILED;
|
|
}
|
|
}
|
|
|
|
if ((ret = _get_settings(cmd)))
|
|
goto_out;
|
|
_apply_settings(cmd);
|
|
|
|
log_debug("Processing: %s", cmd->cmd_line);
|
|
|
|
#ifdef O_DIRECT_SUPPORT
|
|
log_debug("O_DIRECT will be used");
|
|
#endif
|
|
|
|
if (!_set_udev_fallback(cmd))
|
|
goto_out;
|
|
|
|
if ((ret = _process_common_commands(cmd))) {
|
|
if (ret != ECMD_PROCESSED)
|
|
stack;
|
|
goto out;
|
|
}
|
|
|
|
if (cmd->metadata_read_only &&
|
|
!(cmd->command->flags & PERMITTED_READ_ONLY)) {
|
|
log_error("%s: Command not permitted while global/metadata_read_only "
|
|
"is set.", cmd->cmd_line);
|
|
goto out;
|
|
}
|
|
|
|
if (arg_count(cmd, nolocking_ARG))
|
|
locking_type = 0;
|
|
else
|
|
locking_type = -1;
|
|
|
|
if (!init_locking(locking_type, cmd, arg_count(cmd, sysinit_ARG))) {
|
|
ret = ECMD_FAILED;
|
|
goto out;
|
|
}
|
|
|
|
ret = cmd->command->fn(cmd, argc, argv);
|
|
|
|
fin_locking();
|
|
|
|
out:
|
|
if (test_mode()) {
|
|
log_verbose("Test mode: Wiping internal cache");
|
|
lvmcache_destroy(cmd, 1);
|
|
}
|
|
|
|
if (cmd->cft_override) {
|
|
destroy_config_tree(cmd->cft_override);
|
|
cmd->cft_override = NULL;
|
|
/* Move this? */
|
|
if (!refresh_toolcontext(cmd))
|
|
stack;
|
|
}
|
|
|
|
/* FIXME Move this? */
|
|
cmd->current_settings = cmd->default_settings;
|
|
_apply_settings(cmd);
|
|
|
|
if (ret == EINVALID_CMD_LINE && !_cmdline.interactive)
|
|
_short_usage(cmd->command->name);
|
|
|
|
log_debug("Completed: %s", cmd->cmd_line);
|
|
|
|
/*
|
|
* free off any memory the command used.
|
|
*/
|
|
dm_list_init(&cmd->arg_value_groups);
|
|
dm_pool_empty(cmd->mem);
|
|
|
|
reset_lvm_errno(1);
|
|
reset_log_duplicated();
|
|
|
|
return ret;
|
|
}
|
|
|
|
int lvm_return_code(int ret)
|
|
{
|
|
return (ret == ECMD_PROCESSED ? 0 : ret);
|
|
}
|
|
|
|
int lvm_split(char *str, int *argc, char **argv, int max)
|
|
{
|
|
char *b = str, *e;
|
|
*argc = 0;
|
|
|
|
while (*b) {
|
|
while (*b && isspace(*b))
|
|
b++;
|
|
|
|
if ((!*b) || (*b == '#'))
|
|
break;
|
|
|
|
e = b;
|
|
while (*e && !isspace(*e))
|
|
e++;
|
|
|
|
argv[(*argc)++] = b;
|
|
if (!*e)
|
|
break;
|
|
*e++ = '\0';
|
|
b = e;
|
|
if (*argc == max)
|
|
break;
|
|
}
|
|
|
|
return *argc;
|
|
}
|
|
|
|
static const char *_get_cmdline(pid_t pid)
|
|
{
|
|
static char _proc_cmdline[32];
|
|
char buf[256];
|
|
int fd, n = 0;
|
|
|
|
snprintf(buf, sizeof(buf), DEFAULT_PROC_DIR "/%u/cmdline", pid);
|
|
/* FIXME Use generic read code. */
|
|
if ((fd = open(buf, O_RDONLY)) > 0) {
|
|
if ((n = read(fd, _proc_cmdline, sizeof(_proc_cmdline) - 1)) < 0) {
|
|
log_sys_error("read", buf);
|
|
n = 0;
|
|
}
|
|
if (close(fd))
|
|
log_sys_error("close", buf);
|
|
}
|
|
_proc_cmdline[n] = '\0';
|
|
|
|
return _proc_cmdline;
|
|
}
|
|
|
|
static const char *_get_filename(int fd)
|
|
{
|
|
static char filename[PATH_MAX];
|
|
char buf[32]; /* Assumes short DEFAULT_PROC_DIR */
|
|
int size;
|
|
|
|
snprintf(buf, sizeof(buf), DEFAULT_PROC_DIR "/self/fd/%u", fd);
|
|
|
|
if ((size = readlink(buf, filename, sizeof(filename) - 1)) == -1)
|
|
filename[0] = '\0';
|
|
else
|
|
filename[size] = '\0';
|
|
|
|
return filename;
|
|
}
|
|
|
|
static void _close_descriptor(int fd, unsigned suppress_warnings,
|
|
const char *command, pid_t ppid,
|
|
const char *parent_cmdline)
|
|
{
|
|
int r;
|
|
const char *filename;
|
|
|
|
/* Ignore bad file descriptors */
|
|
if (fcntl(fd, F_GETFD) == -1 && errno == EBADF)
|
|
return;
|
|
|
|
if (!suppress_warnings)
|
|
filename = _get_filename(fd);
|
|
|
|
r = close(fd);
|
|
if (suppress_warnings)
|
|
return;
|
|
|
|
if (!r)
|
|
fprintf(stderr, "File descriptor %d (%s) leaked on "
|
|
"%s invocation.", fd, filename, command);
|
|
else if (errno == EBADF)
|
|
return;
|
|
else
|
|
fprintf(stderr, "Close failed on stray file descriptor "
|
|
"%d (%s): %s", fd, filename, strerror(errno));
|
|
|
|
fprintf(stderr, " Parent PID %" PRIpid_t ": %s\n", ppid, parent_cmdline);
|
|
}
|
|
|
|
static void _close_stray_fds(const char *command)
|
|
{
|
|
struct rlimit rlim;
|
|
int fd;
|
|
unsigned suppress_warnings = 0;
|
|
pid_t ppid = getppid();
|
|
const char *parent_cmdline = _get_cmdline(ppid);
|
|
|
|
if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) {
|
|
fprintf(stderr, "getrlimit(RLIMIT_NOFILE) failed: %s\n",
|
|
strerror(errno));
|
|
return;
|
|
}
|
|
|
|
if (getenv("LVM_SUPPRESS_FD_WARNINGS"))
|
|
suppress_warnings = 1;
|
|
|
|
for (fd = 3; fd < (int)rlim.rlim_cur; fd++)
|
|
_close_descriptor(fd, suppress_warnings, command, ppid,
|
|
parent_cmdline);
|
|
}
|
|
|
|
struct cmd_context *init_lvm(void)
|
|
{
|
|
struct cmd_context *cmd;
|
|
|
|
if (!udev_init_library_context())
|
|
stack;
|
|
|
|
if (!(cmd = create_toolcontext(0, NULL, 1)))
|
|
return_NULL;
|
|
|
|
_cmdline.arg_props = &_arg_props[0];
|
|
|
|
if (stored_errno()) {
|
|
destroy_toolcontext(cmd);
|
|
return_NULL;
|
|
}
|
|
|
|
return cmd;
|
|
}
|
|
|
|
static void _fin_commands(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < _cmdline.num_commands; i++)
|
|
dm_free(_cmdline.commands[i].valid_args);
|
|
|
|
dm_free(_cmdline.commands);
|
|
|
|
_cmdline.commands = NULL;
|
|
_cmdline.num_commands = 0;
|
|
_cmdline.commands_size = 0;
|
|
}
|
|
|
|
void lvm_fin(struct cmd_context *cmd)
|
|
{
|
|
_fin_commands();
|
|
destroy_toolcontext(cmd);
|
|
udev_fin_library_context();
|
|
}
|
|
|
|
static int _run_script(struct cmd_context *cmd, int argc, char **argv)
|
|
{
|
|
FILE *script;
|
|
|
|
char buffer[CMD_LEN];
|
|
int ret = 0;
|
|
int magic_number = 0;
|
|
char *script_file = argv[0];
|
|
|
|
if ((script = fopen(script_file, "r")) == NULL)
|
|
return ENO_SUCH_CMD;
|
|
|
|
while (fgets(buffer, sizeof(buffer), script) != NULL) {
|
|
if (!magic_number) {
|
|
if (buffer[0] == '#' && buffer[1] == '!')
|
|
magic_number = 1;
|
|
else {
|
|
ret = ENO_SUCH_CMD;
|
|
break;
|
|
}
|
|
}
|
|
if ((strlen(buffer) == sizeof(buffer) - 1)
|
|
&& (buffer[sizeof(buffer) - 1] - 2 != '\n')) {
|
|
buffer[50] = '\0';
|
|
log_error("Line too long (max 255) beginning: %s",
|
|
buffer);
|
|
ret = EINVALID_CMD_LINE;
|
|
break;
|
|
}
|
|
if (lvm_split(buffer, &argc, argv, MAX_ARGS) == MAX_ARGS) {
|
|
buffer[50] = '\0';
|
|
log_error("Too many arguments: %s", buffer);
|
|
ret = EINVALID_CMD_LINE;
|
|
break;
|
|
}
|
|
if (!argc)
|
|
continue;
|
|
if (!strcmp(argv[0], "quit") || !strcmp(argv[0], "exit"))
|
|
break;
|
|
ret = lvm_run_command(cmd, argc, argv);
|
|
if (ret != ECMD_PROCESSED) {
|
|
if (!error_message_produced()) {
|
|
log_debug(INTERNAL_ERROR "Failed command did not use log_error");
|
|
log_error("Command failed with status code %d.", ret);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (fclose(script))
|
|
log_sys_error("fclose", script_file);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Determine whether we should fall back and exec the equivalent LVM1 tool
|
|
*/
|
|
static int _lvm1_fallback(struct cmd_context *cmd)
|
|
{
|
|
char vsn[80];
|
|
int dm_present;
|
|
|
|
if (!find_config_tree_int(cmd, "global/fallback_to_lvm1",
|
|
DEFAULT_FALLBACK_TO_LVM1) ||
|
|
strncmp(cmd->kernel_vsn, "2.4.", 4))
|
|
return 0;
|
|
|
|
log_suppress(1);
|
|
dm_present = driver_version(vsn, sizeof(vsn));
|
|
log_suppress(0);
|
|
|
|
if (dm_present || !lvm1_present(cmd))
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void _exec_lvm1_command(char **argv)
|
|
{
|
|
char path[PATH_MAX];
|
|
|
|
if (dm_snprintf(path, sizeof(path), "%s.lvm1", argv[0]) < 0) {
|
|
log_error("Failed to create LVM1 tool pathname");
|
|
return;
|
|
}
|
|
|
|
execvp(path, argv);
|
|
log_sys_error("execvp", path);
|
|
}
|
|
|
|
static void _nonroot_warning(void)
|
|
{
|
|
if (getuid() || geteuid())
|
|
log_warn("WARNING: Running as a non-root user. Functionality may be unavailable.");
|
|
}
|
|
|
|
int lvm2_main(int argc, char **argv)
|
|
{
|
|
const char *base;
|
|
int ret, alias = 0;
|
|
struct cmd_context *cmd;
|
|
|
|
base = last_path_component(argv[0]);
|
|
if (strcmp(base, "lvm") && strcmp(base, "lvm.static") &&
|
|
strcmp(base, "initrd-lvm"))
|
|
alias = 1;
|
|
|
|
_close_stray_fds(base);
|
|
|
|
if (is_static() && strcmp(base, "lvm.static") &&
|
|
path_exists(LVM_SHARED_PATH) &&
|
|
!getenv("LVM_DID_EXEC")) {
|
|
setenv("LVM_DID_EXEC", base, 1);
|
|
execvp(LVM_SHARED_PATH, argv);
|
|
unsetenv("LVM_DID_EXEC");
|
|
}
|
|
|
|
/* "version" command is simple enough so it doesn't need any complex init */
|
|
if (!alias && argc > 1 && !strcmp(argv[1], "version"))
|
|
return lvm_return_code(version(NULL, argc, argv));
|
|
|
|
if (!(cmd = init_lvm()))
|
|
return -1;
|
|
|
|
cmd->argv = argv;
|
|
lvm_register_commands();
|
|
|
|
if (_lvm1_fallback(cmd)) {
|
|
/* Attempt to run equivalent LVM1 tool instead */
|
|
if (!alias) {
|
|
argv++;
|
|
argc--;
|
|
}
|
|
if (!argc) {
|
|
log_error("Falling back to LVM1 tools, but no "
|
|
"command specified.");
|
|
ret = ECMD_FAILED;
|
|
goto out;
|
|
}
|
|
_exec_lvm1_command(argv);
|
|
ret = ECMD_FAILED;
|
|
goto out;
|
|
}
|
|
#ifdef READLINE_SUPPORT
|
|
if (!alias && argc == 1) {
|
|
_nonroot_warning();
|
|
ret = lvm_shell(cmd, &_cmdline);
|
|
goto out;
|
|
}
|
|
#endif
|
|
|
|
if (!alias) {
|
|
if (argc < 2) {
|
|
log_fatal("Please supply an LVM command.");
|
|
_display_help();
|
|
ret = EINVALID_CMD_LINE;
|
|
goto out;
|
|
}
|
|
|
|
argc--;
|
|
argv++;
|
|
}
|
|
|
|
_nonroot_warning();
|
|
ret = lvm_run_command(cmd, argc, argv);
|
|
if ((ret == ENO_SUCH_CMD) && (!alias))
|
|
ret = _run_script(cmd, argc, argv);
|
|
if (ret == ENO_SUCH_CMD)
|
|
log_error("No such command. Try 'help'.");
|
|
|
|
if ((ret != ECMD_PROCESSED) && !error_message_produced()) {
|
|
log_debug(INTERNAL_ERROR "Failed command did not use log_error");
|
|
log_error("Command failed with status code %d.", ret);
|
|
}
|
|
|
|
out:
|
|
lvm_fin(cmd);
|
|
return lvm_return_code(ret);
|
|
}
|