/*
* 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
*/
/* Size stored in sectors */
static int _size_arg(struct cmd_context *cmd __attribute((unused)), struct arg *a, int factor)
{
char *ptr;
int i;
static const char *suffixes = "kmgtpebs";
char *val;
double v;
uint64_t v_tmp, adjustment;
a->percent = PERCENT_NONE;
val = a->value;
switch (*val) {
case '+':
a->sign = SIGN_PLUS;
val++;
break;
case '-':
a->sign = SIGN_MINUS;
val++;
break;
default:
a->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;
/*
* 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 *a = _cmdline.the_args + arg;
static struct arg *_find_arg(struct command *com, int opt)
{
struct arg *a;
int i, arg;
for (i = 0; i < com->num_args; i++) {
arg = com->valid_args[i];
a = _cmdline.the_args + 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 a;
}
return 0;
}
static int _process_command_line(struct cmd_context *cmd, int *argc,
char ***argv)
{
int i, opt;
char str[((ARG_COUNT + 1) * 2) + 1], *ptr = str;
struct option opts[ARG_COUNT + 1], *o = opts;
struct arg *a;
for (i = 0; i < ARG_COUNT; i++) {
a = _cmdline.the_args + i;
/* zero the count and arg */
a->count = 0;
a->value = 0;
a->i_value = 0;
a->ui_value = 0;
a->i64_value = 0;
a->ui64_value = 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;
a = _find_arg(cmd->command, opt);
if (!a) {
log_fatal("Unrecognised option.");
return 0;
}
if (a->count && !(a->flags & ARG_REPEATABLE)) {
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;
}
if (arg_count(cmd, partial_ARG)) {
cmd->partial_activation = 1;
log_print("Partial mode. Incomplete volume groups will "
"be activated read-only.");
}
if (arg_count(cmd, ignorelockingfailure_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;
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;
static void _nonroot_warning(void)
{
#ifdef __NetBSD__
gid_t groups_list[NGROUPS_MAX];
int i, group_num, is_operator = 0;
/* Operator group in NetBSD should be able to see lvm status. */
if (getuid() || geteuid()) {
group_num = getgroups(NGROUPS_MAX, groups_list);
for (i = 0; i < group_num; i++) {
if (groups_list[i] == DM_DEVICE_GID) {
is_operator = 1;
init_operator(is_operator);
break;
}
}
if (is_operator)
log_warn("WARNING: Using LVM as operator you have only read access.");
else
log_warn("WARNING: Running as a non-root user and without "
"operator group. Functionality may be unavailable.");
}
#else
if (getuid() || geteuid())
log_warn("WARNING: Running as a non-root user. Functionality may be unavailable.");
#endif
}
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;
if (_lvm1_fallback(cmd)) {
/* Attempt to run equivalent LVM1 tool instead */
if (!alias) {
argv++;
argc--;
alias = 0;
}
if (!argc) {
log_error("Falling back to LVM1 tools, but no "
"command specified.");
return ECMD_FAILED;
}
_exec_lvm1_command(argv);
return ECMD_FAILED;
}
#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);
if (ret == ECMD_PROCESSED)
ret = 0;
return ret;
}