mirror of
git://sourceware.org/git/lvm2.git
synced 2026-01-25 00:32:58 +03:00
Compare commits
41 Commits
dm_v1_02_1
...
v2_02_24
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
154e9a2c47 | ||
|
|
84574a1257 | ||
|
|
bf83527b64 | ||
|
|
12c53622a0 | ||
|
|
d8f54cf891 | ||
|
|
625a671189 | ||
|
|
6ebdad3102 | ||
|
|
9a8f21aa03 | ||
|
|
291dd8edc2 | ||
|
|
de4c1daf29 | ||
|
|
0b55d7d0d8 | ||
|
|
5d86fd8fdb | ||
|
|
1b76eb1f59 | ||
|
|
b05678d8bf | ||
|
|
781f4971c6 | ||
|
|
d02ac7b99a | ||
|
|
b1b6c97f7c | ||
|
|
baee28ab5c | ||
|
|
83edf68ff9 | ||
|
|
c7588f91dd | ||
|
|
30b432adc5 | ||
|
|
7c9733eb5d | ||
|
|
a223c3fea3 | ||
|
|
d5250f4901 | ||
|
|
25f29f4712 | ||
|
|
382af5563d | ||
|
|
8cf3d165d3 | ||
|
|
0fd6ce546f | ||
|
|
b881c372bc | ||
|
|
94c5e7deb0 | ||
|
|
c344766f3c | ||
|
|
67895de0bc | ||
|
|
ff00cb6990 | ||
|
|
2cc75c11ed | ||
|
|
cd79e58eda | ||
|
|
6fa801f3d8 | ||
|
|
684eecba1d | ||
|
|
da9cf7e5de | ||
|
|
f57e7445fd | ||
|
|
fba1388719 | ||
|
|
80ed029c17 |
37
WHATS_NEW
37
WHATS_NEW
@@ -1,3 +1,40 @@
|
||||
Version 2.02.24 - 19th March 2007
|
||||
=================================
|
||||
Fix processing of exit status in init scripts
|
||||
Fix vgremove to require at least one vg argument.
|
||||
Fix reading of striped LVs in LVM1 format.
|
||||
Flag nolocking as clustered so clvmd startup sees clustered LVs. (2.02.10)
|
||||
Add a few missing pieces of vgname command line validation.
|
||||
Support the /dev/mapper prefix on most command lines.
|
||||
|
||||
Version 2.02.23 - 8th March 2007
|
||||
================================
|
||||
Fix vgrename active LV check to ignore differing vgids.
|
||||
Remove no-longer-used uuid_out parameter from activation info functions.
|
||||
Fix two more segfaults if an empty config file section encountered.
|
||||
Move .cache file into a new /etc/lvm/cache directory by default.
|
||||
Add devices/cache_dir & devices/cache_file_prefix, deprecating devices/cache.
|
||||
Create directory in fcntl_lock_file() if required.
|
||||
Exclude readline support from lvm.static.
|
||||
Fix a leak in a reporting error path (2.02.19).
|
||||
|
||||
Version 2.02.22 - 13th February 2007
|
||||
====================================
|
||||
Correct -b and -P on a couple of man pages.
|
||||
Add global/units to example.conf.
|
||||
Fix loading of segment_libraries.
|
||||
If a PV reappears after it was removed from its VG, make it an orphan.
|
||||
Don't update metadata automatically if VGIDs don't match.
|
||||
Fix some vgreduce --removemissing command line validation.
|
||||
|
||||
Version 2.02.21 - 30th January 2007
|
||||
===================================
|
||||
Add warning to lvm2_monitoring_init_rhel4 if attempting to stop monitoring.
|
||||
Fix vgsplit to handle mirrors.
|
||||
Reorder fields in reporting field definitions.
|
||||
Fix vgs to treat args as VGs even when PV fields are displayed.
|
||||
Fix md signature check to handle both endiannesses.
|
||||
|
||||
Version 2.02.20 - 25th January 2007
|
||||
===================================
|
||||
dmeventd mirror sets ignore_suspended_devices and avoids scanning mirrors.
|
||||
|
||||
16
WHATS_NEW_DM
16
WHATS_NEW_DM
@@ -1,3 +1,19 @@
|
||||
Version 1.02.19 -
|
||||
====================================
|
||||
Remove unnecessary memset() return value checks.
|
||||
Fix a few leaks in reporting error paths. [1.02.15+]
|
||||
|
||||
Version 1.02.18 - 13th February 2007
|
||||
====================================
|
||||
Improve dmeventd messaging protocol: drain pipe and tag messages.
|
||||
|
||||
Version 1.02.17 - 29th January 2007
|
||||
===================================
|
||||
Add recent reporting options to dmsetup man page.
|
||||
Revise some report fields names.
|
||||
Add dmsetup 'help' command and update usage text.
|
||||
Use fixed-size fields in report interface and reorder.
|
||||
|
||||
Version 1.02.16 - 25th January 2007
|
||||
===================================
|
||||
Add some missing close() and fclose() return value checks.
|
||||
|
||||
@@ -146,6 +146,7 @@ static LIST_INIT(_dso_registry);
|
||||
|
||||
/* Structure to keep parsed register variables from client message. */
|
||||
struct message_data {
|
||||
char *id;
|
||||
char *dso_name; /* Name of DSO. */
|
||||
char *device_uuid; /* Mapped device path. */
|
||||
union {
|
||||
@@ -225,8 +226,8 @@ static struct thread_status *_alloc_thread_status(struct message_data *data,
|
||||
if (!ret)
|
||||
return NULL;
|
||||
|
||||
if (!memset(ret, 0, sizeof(*ret)) ||
|
||||
!(ret->device.uuid = dm_strdup(data->device_uuid))) {
|
||||
memset(ret, 0, sizeof(*ret));
|
||||
if (!(ret->device.uuid = dm_strdup(data->device_uuid))) {
|
||||
dm_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
@@ -257,8 +258,8 @@ static struct dso_data *_alloc_dso_data(struct message_data *data)
|
||||
if (!ret)
|
||||
return NULL;
|
||||
|
||||
if (!memset(ret, 0, sizeof(*ret)) ||
|
||||
!(ret->dso_name = dm_strdup(data->dso_name))) {
|
||||
memset(ret, 0, sizeof(*ret));
|
||||
if (!(ret->dso_name = dm_strdup(data->dso_name))) {
|
||||
dm_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
@@ -320,6 +321,8 @@ static int _fetch_string(char **ptr, char **src, const int delimiter)
|
||||
/* Free message memory. */
|
||||
static void _free_message(struct message_data *message_data)
|
||||
{
|
||||
if (message_data->id)
|
||||
dm_free(message_data->id);
|
||||
if (message_data->dso_name)
|
||||
dm_free(message_data->dso_name);
|
||||
|
||||
@@ -342,7 +345,8 @@ static int _parse_message(struct message_data *message_data)
|
||||
* Retrieve application identifier, mapped device
|
||||
* path and events # string from message.
|
||||
*/
|
||||
if (_fetch_string(&message_data->dso_name, &p, ' ') &&
|
||||
if (_fetch_string(&message_data->id, &p, ' ') &&
|
||||
_fetch_string(&message_data->dso_name, &p, ' ') &&
|
||||
_fetch_string(&message_data->device_uuid, &p, ' ') &&
|
||||
_fetch_string(&message_data->events.str, &p, ' ') &&
|
||||
_fetch_string(&message_data->timeout.str, &p, ' ')) {
|
||||
@@ -875,8 +879,8 @@ static struct dso_data *_load_dso(struct message_data *data)
|
||||
syslog(LOG_ERR, "dmeventd %s dlopen failed: %s", data->dso_name,
|
||||
dlerr);
|
||||
data->msg->size =
|
||||
dm_asprintf(&(data->msg->data), "%s dlopen failed: %s",
|
||||
data->dso_name, dlerr);
|
||||
dm_asprintf(&(data->msg->data), "%s %s dlopen failed: %s",
|
||||
data->id, data->dso_name, dlerr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -1056,7 +1060,8 @@ static int _registered_device(struct message_data *message_data,
|
||||
{
|
||||
struct dm_event_daemon_message *msg = message_data->msg;
|
||||
|
||||
const char *fmt = "%s %s %u";
|
||||
const char *fmt = "%s %s %s %u";
|
||||
const char *id = message_data->id;
|
||||
const char *dso = thread->dso_data->dso_name;
|
||||
const char *dev = thread->device.uuid;
|
||||
unsigned events = ((thread->status == DM_THREAD_RUNNING)
|
||||
@@ -1066,7 +1071,7 @@ static int _registered_device(struct message_data *message_data,
|
||||
if (msg->data)
|
||||
dm_free(msg->data);
|
||||
|
||||
msg->size = dm_asprintf(&(msg->data), fmt, dso, dev, events);
|
||||
msg->size = dm_asprintf(&(msg->data), fmt, id, dso, dev, events);
|
||||
|
||||
_unlock_mutex();
|
||||
|
||||
@@ -1180,7 +1185,8 @@ static int _get_timeout(struct message_data *message_data)
|
||||
_lock_mutex();
|
||||
if ((thread = _lookup_thread_status(message_data))) {
|
||||
msg->size =
|
||||
dm_asprintf(&(msg->data), "%" PRIu32, thread->timeout);
|
||||
dm_asprintf(&(msg->data), "%s %" PRIu32, message_data->id,
|
||||
thread->timeout);
|
||||
} else {
|
||||
msg->data = NULL;
|
||||
msg->size = 0;
|
||||
@@ -1375,17 +1381,32 @@ static int _handle_request(struct dm_event_daemon_message *msg,
|
||||
static int _do_process_request(struct dm_event_daemon_message *msg)
|
||||
{
|
||||
int ret;
|
||||
char *answer;
|
||||
static struct message_data message_data;
|
||||
|
||||
/* Parse the message. */
|
||||
memset(&message_data, 0, sizeof(message_data));
|
||||
message_data.msg = msg;
|
||||
if (msg->cmd != DM_EVENT_CMD_ACTIVE && !_parse_message(&message_data)) {
|
||||
if (msg->cmd == DM_EVENT_CMD_HELLO) {
|
||||
ret = 0;
|
||||
answer = dm_strdup(msg->data);
|
||||
if (answer) {
|
||||
msg->size = dm_asprintf(&(msg->data), "%s HELLO", answer);
|
||||
dm_free(answer);
|
||||
} else {
|
||||
msg->size = 0;
|
||||
msg->data = NULL;
|
||||
}
|
||||
} else if (msg->cmd != DM_EVENT_CMD_ACTIVE && !_parse_message(&message_data)) {
|
||||
stack;
|
||||
ret = -EINVAL;
|
||||
} else
|
||||
ret = _handle_request(msg, &message_data);
|
||||
|
||||
msg->cmd = ret;
|
||||
if (!msg->data)
|
||||
msg->size = dm_asprintf(&(msg->data), "%s %s", message_data.id, strerror(-ret));
|
||||
|
||||
_free_message(&message_data);
|
||||
|
||||
return ret;
|
||||
@@ -1405,16 +1426,9 @@ static void _process_request(struct dm_event_fifos *fifos)
|
||||
if (!_client_read(fifos, &msg))
|
||||
return;
|
||||
|
||||
msg.cmd = _do_process_request(&msg);
|
||||
if (!msg.data) {
|
||||
msg.data = dm_strdup(strerror(-msg.cmd));
|
||||
if (msg.data)
|
||||
msg.size = strlen(msg.data) + 1;
|
||||
else {
|
||||
msg.size = 0;
|
||||
stack;
|
||||
}
|
||||
}
|
||||
/* _do_process_request fills in msg (if memory allows for
|
||||
data, otherwise just cmd and size = 0) */
|
||||
_do_process_request(&msg);
|
||||
|
||||
if (!_client_write(fifos, &msg))
|
||||
stack;
|
||||
|
||||
@@ -20,6 +20,7 @@ enum dm_event_command {
|
||||
DM_EVENT_CMD_GET_NEXT_REGISTERED_DEVICE,
|
||||
DM_EVENT_CMD_SET_TIMEOUT,
|
||||
DM_EVENT_CMD_GET_TIMEOUT,
|
||||
DM_EVENT_CMD_HELLO,
|
||||
};
|
||||
|
||||
/* Message passed between client and daemon. */
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
#include <sys/wait.h>
|
||||
#include <arpa/inet.h> /* for htonl, ntohl */
|
||||
|
||||
static int _sequence_nr = 0;
|
||||
|
||||
struct dm_event_handler {
|
||||
char *dso;
|
||||
|
||||
@@ -182,6 +184,21 @@ enum dm_event_mask dm_event_handler_get_event_mask(const struct dm_event_handler
|
||||
return dmevh->mask;
|
||||
}
|
||||
|
||||
static int _check_message_id(struct dm_event_daemon_message *msg)
|
||||
{
|
||||
int pid, seq_nr;
|
||||
|
||||
if ((sscanf(msg->data, "%d:%d", &pid, &seq_nr) != 2) ||
|
||||
(pid != getpid()) || (seq_nr != _sequence_nr)) {
|
||||
log_error("Ignoring out-of-sequence reply from dmeventd. "
|
||||
"Expected %d:%d but received %s", getpid(),
|
||||
_sequence_nr, msg->data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* daemon_read
|
||||
* @fifos
|
||||
@@ -260,11 +277,28 @@ static int _daemon_write(struct dm_event_fifos *fifos,
|
||||
|
||||
size_t size = 2 * sizeof(uint32_t) + msg->size;
|
||||
char *buf = alloca(size);
|
||||
char drainbuf[128];
|
||||
struct timeval tval = { 0, 0 };
|
||||
|
||||
*((uint32_t *)buf) = htonl(msg->cmd);
|
||||
*((uint32_t *)buf + 1) = htonl(msg->size);
|
||||
memcpy(buf + 2 * sizeof(uint32_t), msg->data, msg->size);
|
||||
|
||||
/* drain the answer fifo */
|
||||
while (1) {
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(fifos->server, &fds);
|
||||
tval.tv_usec = 100;
|
||||
ret = select(fifos->server + 1, &fds, NULL, NULL, &tval);
|
||||
if ((ret < 0) && (errno != EINTR)) {
|
||||
log_error("Unable to talk to event daemon");
|
||||
return 0;
|
||||
}
|
||||
if (ret == 0)
|
||||
break;
|
||||
read(fifos->server, drainbuf, 127);
|
||||
}
|
||||
|
||||
while (bytes < size) {
|
||||
do {
|
||||
/* Watch daemon write FIFO to be ready for output. */
|
||||
@@ -301,7 +335,7 @@ static int _daemon_talk(struct dm_event_fifos *fifos,
|
||||
{
|
||||
const char *dso = dso_name ? dso_name : "";
|
||||
const char *dev = dev_name ? dev_name : "";
|
||||
const char *fmt = "%s %s %u %" PRIu32;
|
||||
const char *fmt = "%d:%d %s %s %u %" PRIu32;
|
||||
int msg_size;
|
||||
memset(msg, 0, sizeof(*msg));
|
||||
|
||||
@@ -310,8 +344,10 @@ static int _daemon_talk(struct dm_event_fifos *fifos,
|
||||
* into ASCII message string.
|
||||
*/
|
||||
msg->cmd = cmd;
|
||||
if ((msg_size = dm_asprintf(&(msg->data), fmt, dso, dev, evmask,
|
||||
timeout)) < 0) {
|
||||
if (cmd == DM_EVENT_CMD_HELLO)
|
||||
fmt = "%d:%d HELLO";
|
||||
if ((msg_size = dm_asprintf(&(msg->data), fmt, getpid(), _sequence_nr,
|
||||
dso, dev, evmask, timeout)) < 0) {
|
||||
log_error("_daemon_talk: message allocation failed");
|
||||
return -ENOMEM;
|
||||
}
|
||||
@@ -326,10 +362,14 @@ static int _daemon_talk(struct dm_event_fifos *fifos,
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (!_daemon_read(fifos, msg)) {
|
||||
stack;
|
||||
return -EIO;
|
||||
}
|
||||
do {
|
||||
if (!_daemon_read(fifos, msg)) {
|
||||
stack;
|
||||
return -EIO;
|
||||
}
|
||||
} while (!_check_message_id(msg));
|
||||
|
||||
_sequence_nr++;
|
||||
|
||||
return (int32_t) msg->cmd;
|
||||
}
|
||||
@@ -507,7 +547,9 @@ static int _do_event(int cmd, struct dm_event_daemon_message *msg,
|
||||
return -ESRCH;
|
||||
}
|
||||
|
||||
ret = _daemon_talk(&fifos, msg, cmd, dso_name, dev_name, evmask, timeout);
|
||||
ret = _daemon_talk(&fifos, msg, DM_EVENT_CMD_HELLO, 0, 0, 0, 0);
|
||||
if (!ret)
|
||||
ret = _daemon_talk(&fifos, msg, cmd, dso_name, dev_name, evmask, timeout);
|
||||
|
||||
/* what is the opposite of init? */
|
||||
_dtr_client(&fifos);
|
||||
@@ -521,7 +563,7 @@ int dm_event_register_handler(const struct dm_event_handler *dmevh)
|
||||
int ret = 1, err;
|
||||
const char *uuid;
|
||||
struct dm_task *dmt;
|
||||
struct dm_event_daemon_message msg;
|
||||
struct dm_event_daemon_message msg = { 0, 0, NULL };
|
||||
|
||||
if (!(dmt = _get_device_info(dmevh))) {
|
||||
stack;
|
||||
@@ -551,7 +593,7 @@ int dm_event_unregister_handler(const struct dm_event_handler *dmevh)
|
||||
int ret = 1, err;
|
||||
const char *uuid;
|
||||
struct dm_task *dmt;
|
||||
struct dm_event_daemon_message msg;
|
||||
struct dm_event_daemon_message msg = { 0, 0, NULL };
|
||||
|
||||
if (!(dmt = _get_device_info(dmevh))) {
|
||||
stack;
|
||||
@@ -598,15 +640,20 @@ static char *_fetch_string(char **src, const int delimiter)
|
||||
static int _parse_message(struct dm_event_daemon_message *msg, char **dso_name,
|
||||
char **uuid, enum dm_event_mask *evmask)
|
||||
{
|
||||
char *id = NULL;
|
||||
char *p = msg->data;
|
||||
|
||||
if ((*dso_name = _fetch_string(&p, ' ')) &&
|
||||
if ((id = _fetch_string(&p, ' ')) &&
|
||||
(*dso_name = _fetch_string(&p, ' ')) &&
|
||||
(*uuid = _fetch_string(&p, ' '))) {
|
||||
*evmask = atoi(p);
|
||||
|
||||
dm_free(id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (id)
|
||||
dm_free(id);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
@@ -621,12 +668,12 @@ static int _parse_message(struct dm_event_daemon_message *msg, char **dso_name,
|
||||
*/
|
||||
int dm_event_get_registered_device(struct dm_event_handler *dmevh, int next)
|
||||
{
|
||||
int ret;
|
||||
int ret = 0;
|
||||
const char *uuid = NULL;
|
||||
char *reply_dso = NULL, *reply_uuid = NULL;
|
||||
enum dm_event_mask reply_mask;
|
||||
struct dm_task *dmt;
|
||||
struct dm_event_daemon_message msg;
|
||||
enum dm_event_mask reply_mask = 0;
|
||||
struct dm_task *dmt = NULL;
|
||||
struct dm_event_daemon_message msg = { 0, 0, NULL };
|
||||
|
||||
if (!(dmt = _get_device_info(dmevh))) {
|
||||
stack;
|
||||
@@ -696,9 +743,17 @@ int dm_event_get_registered_device(struct dm_event_handler *dmevh, int next)
|
||||
|
||||
#if 0 /* left out for now */
|
||||
|
||||
static char *_skip_string(char *src, const int delimiter)
|
||||
{
|
||||
src = srtchr(src, delimiter);
|
||||
if (src && *(src + 1))
|
||||
return src + 1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int dm_event_set_timeout(const char *device_path, uint32_t timeout)
|
||||
{
|
||||
struct dm_event_daemon_message msg;
|
||||
struct dm_event_daemon_message msg = { 0, 0, NULL };
|
||||
|
||||
if (!device_exists(device_path))
|
||||
return -ENODEV;
|
||||
@@ -710,13 +765,20 @@ int dm_event_set_timeout(const char *device_path, uint32_t timeout)
|
||||
int dm_event_get_timeout(const char *device_path, uint32_t *timeout)
|
||||
{
|
||||
int ret;
|
||||
struct dm_event_daemon_message msg;
|
||||
struct dm_event_daemon_message msg = { 0, 0, NULL };
|
||||
|
||||
if (!device_exists(device_path))
|
||||
return -ENODEV;
|
||||
if (!(ret = _do_event(DM_EVENT_CMD_GET_TIMEOUT, &msg, NULL, device_path,
|
||||
0, 0)))
|
||||
*timeout = atoi(msg.data);
|
||||
0, 0))) {
|
||||
char *p = _skip_string(msg.data, ' ');
|
||||
if (!p) {
|
||||
log_error("malformed reply from dmeventd '%s'\n",
|
||||
msg.data);
|
||||
return -EIO;
|
||||
}
|
||||
*timeout = atoi(p);
|
||||
}
|
||||
if (msg.data)
|
||||
dm_free(msg.data);
|
||||
return ret;
|
||||
|
||||
@@ -56,10 +56,14 @@ devices {
|
||||
# filter = [ "a|^/dev/hda8$|", "r/.*/" ]
|
||||
|
||||
# The results of the filtering are cached on disk to avoid
|
||||
# rescanning dud devices (which can take a very long time). By
|
||||
# default this cache file is hidden in the /etc/lvm directory.
|
||||
# It is safe to delete this file: the tools regenerate it.
|
||||
cache = "/etc/lvm/.cache"
|
||||
# rescanning dud devices (which can take a very long time).
|
||||
# By default this cache is stored in the /etc/lvm/cache directory
|
||||
# in a file called '.cache'.
|
||||
# It is safe to delete the contents: the tools regenerate it.
|
||||
# (The old setting 'cache' is still respected if neither of
|
||||
# these new ones is present.)
|
||||
cache_dir = "/etc/lvm/cache"
|
||||
cache_file_prefix = ""
|
||||
|
||||
# You can turn off writing this cache file by setting this to 0.
|
||||
write_cache_state = 1
|
||||
@@ -188,6 +192,9 @@ global {
|
||||
# command. Defaults to off.
|
||||
test = 0
|
||||
|
||||
# Default value for --units argument
|
||||
units = "h"
|
||||
|
||||
# Whether or not to communicate with the kernel device-mapper.
|
||||
# Set to 0 if you want to use the tools to manipulate LVM metadata
|
||||
# without activating any logical volumes.
|
||||
|
||||
@@ -168,6 +168,10 @@ int lvs_in_vg_activated(struct volume_group *vg)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
int lvs_in_vg_activated_by_uuid_only(struct volume_group *vg);
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
int lvs_in_vg_opened(struct volume_group *vg)
|
||||
{
|
||||
return 0;
|
||||
@@ -421,21 +425,23 @@ int target_present(const char *target_name, int use_modprobe)
|
||||
* Returns 1 if info structure populated, else 0 on failure.
|
||||
*/
|
||||
static int _lv_info(struct cmd_context *cmd, const struct logical_volume *lv, int with_mknodes,
|
||||
struct lvinfo *info, int with_open_count)
|
||||
struct lvinfo *info, int with_open_count, unsigned by_uuid_only)
|
||||
{
|
||||
struct dm_info dminfo;
|
||||
char *name;
|
||||
char *name = NULL;
|
||||
|
||||
if (!activation())
|
||||
return 0;
|
||||
|
||||
if (!(name = build_dm_name(cmd->mem, lv->vg->name, lv->name, NULL)))
|
||||
if (!by_uuid_only &&
|
||||
!(name = build_dm_name(cmd->mem, lv->vg->name, lv->name, NULL)))
|
||||
return_0;
|
||||
|
||||
log_debug("Getting device info for %s", name);
|
||||
if (!dev_manager_info(lv->vg->cmd->mem, name, lv, with_mknodes,
|
||||
with_open_count, &dminfo)) {
|
||||
dm_pool_free(cmd->mem, name);
|
||||
if (name)
|
||||
dm_pool_free(cmd->mem, name);
|
||||
return_0;
|
||||
}
|
||||
|
||||
@@ -448,14 +454,16 @@ static int _lv_info(struct cmd_context *cmd, const struct logical_volume *lv, in
|
||||
info->live_table = dminfo.live_table;
|
||||
info->inactive_table = dminfo.inactive_table;
|
||||
|
||||
dm_pool_free(cmd->mem, name);
|
||||
if (name)
|
||||
dm_pool_free(cmd->mem, name);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int lv_info(struct cmd_context *cmd, const struct logical_volume *lv, struct lvinfo *info,
|
||||
int with_open_count)
|
||||
{
|
||||
return _lv_info(cmd, lv, 0, info, with_open_count);
|
||||
return _lv_info(cmd, lv, 0, info, with_open_count, 0);
|
||||
}
|
||||
|
||||
int lv_info_by_lvid(struct cmd_context *cmd, const char *lvid_s,
|
||||
@@ -466,7 +474,7 @@ int lv_info_by_lvid(struct cmd_context *cmd, const char *lvid_s,
|
||||
if (!(lv = lv_from_lvid(cmd, lvid_s, 0)))
|
||||
return 0;
|
||||
|
||||
return _lv_info(cmd, lv, 0, info, with_open_count);
|
||||
return _lv_info(cmd, lv, 0, info, with_open_count, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -519,11 +527,12 @@ int lv_mirror_percent(struct cmd_context *cmd, struct logical_volume *lv,
|
||||
return r;
|
||||
}
|
||||
|
||||
static int _lv_active(struct cmd_context *cmd, struct logical_volume *lv)
|
||||
static int _lv_active(struct cmd_context *cmd, struct logical_volume *lv,
|
||||
unsigned by_uuid_only)
|
||||
{
|
||||
struct lvinfo info;
|
||||
|
||||
if (!lv_info(cmd, lv, &info, 0)) {
|
||||
if (!_lv_info(cmd, lv, 0, &info, 0, by_uuid_only)) {
|
||||
stack;
|
||||
return -1;
|
||||
}
|
||||
@@ -607,7 +616,7 @@ static int _lv_suspend_lv(struct logical_volume *lv, int lockfs)
|
||||
* These two functions return the number of visible LVs in the state,
|
||||
* or -1 on error.
|
||||
*/
|
||||
int lvs_in_vg_activated(struct volume_group *vg)
|
||||
static int _lvs_in_vg_activated(struct volume_group *vg, unsigned by_uuid_only)
|
||||
{
|
||||
struct lv_list *lvl;
|
||||
int count = 0;
|
||||
@@ -617,12 +626,22 @@ int lvs_in_vg_activated(struct volume_group *vg)
|
||||
|
||||
list_iterate_items(lvl, &vg->lvs) {
|
||||
if (lvl->lv->status & VISIBLE_LV)
|
||||
count += (_lv_active(vg->cmd, lvl->lv) == 1);
|
||||
count += (_lv_active(vg->cmd, lvl->lv, by_uuid_only) == 1);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int lvs_in_vg_activated_by_uuid_only(struct volume_group *vg)
|
||||
{
|
||||
return _lvs_in_vg_activated(vg, 1);
|
||||
}
|
||||
|
||||
int lvs_in_vg_activated(struct volume_group *vg)
|
||||
{
|
||||
return _lvs_in_vg_activated(vg, 0);
|
||||
}
|
||||
|
||||
int lvs_in_vg_opened(struct volume_group *vg)
|
||||
{
|
||||
struct lv_list *lvl;
|
||||
@@ -973,7 +992,7 @@ int lv_mknodes(struct cmd_context *cmd, const struct logical_volume *lv)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (!_lv_info(cmd, lv, 1, &info, 0))
|
||||
if (!_lv_info(cmd, lv, 1, &info, 0, 0))
|
||||
return_0;
|
||||
|
||||
if (info.exists)
|
||||
|
||||
@@ -83,6 +83,7 @@ int lv_mirror_percent(struct cmd_context *cmd, struct logical_volume *lv,
|
||||
* Return number of LVs in the VG that are active.
|
||||
*/
|
||||
int lvs_in_vg_activated(struct volume_group *vg);
|
||||
int lvs_in_vg_activated_by_uuid_only(struct volume_group *vg);
|
||||
int lvs_in_vg_opened(struct volume_group *vg);
|
||||
|
||||
|
||||
|
||||
@@ -118,12 +118,10 @@ static struct dm_task *_setup_task(const char *name, const char *uuid,
|
||||
}
|
||||
|
||||
static int _info_run(const char *name, const char *dlid, struct dm_info *info,
|
||||
int mknodes, int with_open_count, struct dm_pool *mem,
|
||||
char **uuid_out)
|
||||
int mknodes, int with_open_count)
|
||||
{
|
||||
int r = 0;
|
||||
struct dm_task *dmt;
|
||||
const char *u;
|
||||
int dmtask;
|
||||
|
||||
dmtask = mknodes ? DM_DEVICE_MKNODES : DM_DEVICE_INFO;
|
||||
@@ -143,11 +141,6 @@ static int _info_run(const char *name, const char *dlid, struct dm_info *info,
|
||||
if (!dm_task_get_info(dmt, info))
|
||||
goto_out;
|
||||
|
||||
if (info->exists && uuid_out) {
|
||||
if (!(u = dm_task_get_uuid(dmt)))
|
||||
goto_out;
|
||||
*uuid_out = dm_pool_strdup(mem, u);
|
||||
}
|
||||
r = 1;
|
||||
|
||||
out:
|
||||
@@ -208,23 +201,20 @@ int device_is_usable(dev_t dev)
|
||||
}
|
||||
|
||||
static int _info(const char *name, const char *dlid, int mknodes,
|
||||
int with_open_count, struct dm_info *info,
|
||||
struct dm_pool *mem, char **uuid_out)
|
||||
int with_open_count, struct dm_info *info)
|
||||
{
|
||||
if (!mknodes && dlid && *dlid) {
|
||||
if (_info_run(NULL, dlid, info, 0, with_open_count, mem,
|
||||
uuid_out) &&
|
||||
if (_info_run(NULL, dlid, info, 0, with_open_count) &&
|
||||
info->exists)
|
||||
return 1;
|
||||
else if (_info_run(NULL, dlid + sizeof(UUID_PREFIX) - 1, info,
|
||||
0, with_open_count, mem, uuid_out) &&
|
||||
0, with_open_count) &&
|
||||
info->exists)
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (name)
|
||||
return _info_run(name, NULL, info, mknodes, with_open_count,
|
||||
mem, uuid_out);
|
||||
return _info_run(name, NULL, info, mknodes, with_open_count);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -240,8 +230,7 @@ int dev_manager_info(struct dm_pool *mem, const char *name,
|
||||
return 0;
|
||||
}
|
||||
|
||||
return _info(name, dlid, with_mknodes, with_open_count, info,
|
||||
NULL, NULL);
|
||||
return _info(name, dlid, with_mknodes, with_open_count, info);
|
||||
}
|
||||
|
||||
/* FIXME Interface must cope with multiple targets */
|
||||
@@ -646,7 +635,7 @@ static int _add_dev_to_dtree(struct dev_manager *dm, struct dm_tree *dtree,
|
||||
return_0;
|
||||
|
||||
log_debug("Getting device info for %s [%s]", name, dlid);
|
||||
if (!_info(name, dlid, 0, 1, &info, dm->mem, NULL)) {
|
||||
if (!_info(name, dlid, 0, 1, &info)) {
|
||||
log_error("Failed to get info for %s [%s].", name, dlid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -575,7 +575,7 @@ static struct dev_filter *_init_filter_components(struct cmd_context *cmd)
|
||||
|
||||
static int _init_filters(struct cmd_context *cmd, unsigned load_persistent_cache)
|
||||
{
|
||||
const char *dev_cache;
|
||||
const char *dev_cache = NULL, *cache_dir, *cache_file_prefix;
|
||||
struct dev_filter *f3, *f4;
|
||||
struct stat st;
|
||||
char cache_file[PATH_MAX];
|
||||
@@ -585,19 +585,35 @@ static int _init_filters(struct cmd_context *cmd, unsigned load_persistent_cache
|
||||
if (!(f3 = _init_filter_components(cmd)))
|
||||
return 0;
|
||||
|
||||
if (dm_snprintf(cache_file, sizeof(cache_file),
|
||||
"%s/.cache", cmd->sys_dir) < 0) {
|
||||
log_error("Persistent cache filename too long ('%s/.cache').",
|
||||
cmd->sys_dir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
init_ignore_suspended_devices(find_config_tree_int(cmd,
|
||||
"devices/ignore_suspended_devices", DEFAULT_IGNORE_SUSPENDED_DEVICES));
|
||||
|
||||
dev_cache = find_config_tree_str(cmd, "devices/cache",
|
||||
cache_file);
|
||||
if (!(f4 = persistent_filter_create(f3, dev_cache))) {
|
||||
/*
|
||||
* If 'cache_dir' or 'cache_file_prefix' is set, ignore 'cache'.
|
||||
*/
|
||||
cache_dir = find_config_tree_str(cmd, "devices/cache_dir", NULL);
|
||||
cache_file_prefix = find_config_tree_str(cmd, "devices/cache_file_prefix", NULL);
|
||||
|
||||
if (cache_dir || cache_file_prefix) {
|
||||
if (dm_snprintf(cache_file, sizeof(cache_file),
|
||||
"%s%s%s/%s.cache",
|
||||
cache_dir ? "" : cmd->sys_dir,
|
||||
cache_dir ? "" : "/",
|
||||
cache_dir ? : DEFAULT_CACHE_SUBDIR,
|
||||
cache_file_prefix ? : DEFAULT_CACHE_FILE_PREFIX) < 0) {
|
||||
log_error("Persistent cache filename too long.");
|
||||
return 0;
|
||||
}
|
||||
} else if (!(dev_cache = find_config_tree_str(cmd, "devices/cache", NULL)) &&
|
||||
(dm_snprintf(cache_file, sizeof(cache_file),
|
||||
"%s/%s/%s.cache",
|
||||
cmd->sys_dir, DEFAULT_CACHE_SUBDIR,
|
||||
DEFAULT_CACHE_FILE_PREFIX) < 0)) {
|
||||
log_error("Persistent cache filename too long.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(f4 = persistent_filter_create(f3, dev_cache ? : cache_file))) {
|
||||
log_error("Failed to create persistent device filter");
|
||||
return 0;
|
||||
}
|
||||
@@ -754,7 +770,6 @@ static int _init_segtypes(struct cmd_context *cmd)
|
||||
struct config_value *cv;
|
||||
struct segment_type *(*init_segtype_fn) (struct cmd_context *);
|
||||
void *lib;
|
||||
struct list *sgtl, *tmp;
|
||||
struct segment_type *segtype2;
|
||||
|
||||
for (cv = cn->v; cv; cv = cv->next) {
|
||||
@@ -781,18 +796,16 @@ static int _init_segtypes(struct cmd_context *cmd)
|
||||
segtype->library = lib;
|
||||
list_add(&cmd->segtypes, &segtype->list);
|
||||
|
||||
list_iterate_safe(sgtl, tmp, &cmd->segtypes) {
|
||||
segtype2 = list_item(sgtl, struct segment_type);
|
||||
if (!strcmp(segtype2->name, segtype->name)) {
|
||||
log_error("Duplicate segment type %s: "
|
||||
"unloading shared library %s",
|
||||
segtype->name, cv->v.str);
|
||||
list_del(&segtype->list);
|
||||
segtype->ops->destroy(segtype);
|
||||
dlclose(lib);
|
||||
break;
|
||||
}
|
||||
|
||||
list_iterate_items(segtype2, &cmd->segtypes) {
|
||||
if ((segtype == segtype2) ||
|
||||
strcmp(segtype2->name, segtype->name))
|
||||
continue;
|
||||
log_error("Duplicate segment type %s: "
|
||||
"unloading shared library %s",
|
||||
segtype->name, cv->v.str);
|
||||
list_del(&segtype->list);
|
||||
segtype->ops->destroy(segtype);
|
||||
dlclose(lib);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -878,7 +878,7 @@ static int _find_config_int(const struct config_node *cn1,
|
||||
{
|
||||
const struct config_node *n = _find_first_config_node(cn1, cn2, path);
|
||||
|
||||
if (n && n->v->type == CFG_INT) {
|
||||
if (n && n->v && n->v->type == CFG_INT) {
|
||||
log_very_verbose("Setting %s to %d", path, n->v->v.i);
|
||||
return n->v->v.i;
|
||||
}
|
||||
@@ -899,7 +899,7 @@ static float _find_config_float(const struct config_node *cn1,
|
||||
{
|
||||
const struct config_node *n = _find_first_config_node(cn1, cn2, path);
|
||||
|
||||
if (n && n->v->type == CFG_FLOAT) {
|
||||
if (n && n->v && n->v->type == CFG_FLOAT) {
|
||||
log_very_verbose("Setting %s to %f", path, n->v->v.r);
|
||||
return n->v->v.r;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
|
||||
#define DEFAULT_ARCHIVE_SUBDIR "archive"
|
||||
#define DEFAULT_BACKUP_SUBDIR "backup"
|
||||
#define DEFAULT_CACHE_SUBDIR "cache"
|
||||
#define DEFAULT_CACHE_FILE_PREFIX ""
|
||||
|
||||
#define DEFAULT_ARCHIVE_DAYS 30
|
||||
#define DEFAULT_ARCHIVE_NUMBER 10
|
||||
|
||||
@@ -53,8 +53,10 @@ int dev_is_md(struct device *dev, uint64_t *sb)
|
||||
sb_offset = MD_NEW_SIZE_SECTORS(size) << SECTOR_SHIFT;
|
||||
|
||||
/* Check if it is an md component device. */
|
||||
/* Version 1 is little endian; version 0.90.0 is machine endian */
|
||||
if (dev_read(dev, sb_offset, sizeof(uint32_t), &md_magic) &&
|
||||
(md_magic == xlate32(MD_SB_MAGIC))) {
|
||||
((md_magic == xlate32(MD_SB_MAGIC)) ||
|
||||
(md_magic == MD_SB_MAGIC))) {
|
||||
if (sb)
|
||||
*sb = sb_offset;
|
||||
ret = 1;
|
||||
|
||||
@@ -203,6 +203,19 @@ static int _check_maps_are_complete(struct dm_hash_table *maps)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static uint32_t _area_length(struct lv_map *lvm, uint32_t le)
|
||||
{
|
||||
uint32_t len = 0;
|
||||
|
||||
do
|
||||
len++;
|
||||
while ((lvm->map[le + len].pv == lvm->map[le].pv) &&
|
||||
(lvm->map[le].pv &&
|
||||
lvm->map[le + len].pe == lvm->map[le].pe + len));
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int _read_linear(struct cmd_context *cmd, struct lv_map *lvm)
|
||||
{
|
||||
uint32_t le = 0, len;
|
||||
@@ -215,13 +228,7 @@ static int _read_linear(struct cmd_context *cmd, struct lv_map *lvm)
|
||||
}
|
||||
|
||||
while (le < lvm->lv->le_count) {
|
||||
len = 0;
|
||||
|
||||
do
|
||||
len++;
|
||||
while ((lvm->map[le + len].pv == lvm->map[le].pv) &&
|
||||
(lvm->map[le].pv &&
|
||||
lvm->map[le + len].pe == lvm->map[le].pe + len));
|
||||
len = _area_length(lvm, le);
|
||||
|
||||
if (!(seg = alloc_lv_segment(cmd->mem, segtype, lvm->lv, le,
|
||||
len, 0, 0, NULL, 1, len, 0, 0, 0))) {
|
||||
@@ -230,10 +237,8 @@ static int _read_linear(struct cmd_context *cmd, struct lv_map *lvm)
|
||||
}
|
||||
|
||||
if (!set_lv_segment_area_pv(seg, 0, lvm->map[le].pv,
|
||||
lvm->map[le].pe)) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
lvm->map[le].pe))
|
||||
return_0;
|
||||
|
||||
list_add(&lvm->lv->segments, &seg->list);
|
||||
|
||||
@@ -244,7 +249,8 @@ static int _read_linear(struct cmd_context *cmd, struct lv_map *lvm)
|
||||
}
|
||||
|
||||
static int _check_stripe(struct lv_map *lvm, uint32_t area_count,
|
||||
uint32_t seg_len, uint32_t base_le, uint32_t len)
|
||||
uint32_t area_len, uint32_t base_le,
|
||||
uint32_t total_area_len)
|
||||
{
|
||||
uint32_t st;
|
||||
|
||||
@@ -252,11 +258,11 @@ static int _check_stripe(struct lv_map *lvm, uint32_t area_count,
|
||||
* Is the next physical extent in every stripe adjacent to the last?
|
||||
*/
|
||||
for (st = 0; st < area_count; st++)
|
||||
if ((lvm->map[base_le + st * len + seg_len].pv !=
|
||||
lvm->map[base_le + st * len].pv) ||
|
||||
(lvm->map[base_le + st * len].pv &&
|
||||
lvm->map[base_le + st * len + seg_len].pe !=
|
||||
lvm->map[base_le + st * len].pe + seg_len))
|
||||
if ((lvm->map[base_le + st * total_area_len + area_len].pv !=
|
||||
lvm->map[base_le + st * total_area_len].pv) ||
|
||||
(lvm->map[base_le + st * total_area_len].pv &&
|
||||
lvm->map[base_le + st * total_area_len + area_len].pe !=
|
||||
lvm->map[base_le + st * total_area_len].pe + area_len))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
@@ -264,7 +270,7 @@ static int _check_stripe(struct lv_map *lvm, uint32_t area_count,
|
||||
|
||||
static int _read_stripes(struct cmd_context *cmd, struct lv_map *lvm)
|
||||
{
|
||||
uint32_t st, le = 0, len;
|
||||
uint32_t st, first_area_le = 0, total_area_len;
|
||||
uint32_t area_len;
|
||||
struct lv_segment *seg;
|
||||
struct segment_type *segtype;
|
||||
@@ -277,26 +283,25 @@ static int _read_stripes(struct cmd_context *cmd, struct lv_map *lvm)
|
||||
"with logical extent count (%u) for %s",
|
||||
lvm->stripes, lvm->lv->le_count, lvm->lv->name);
|
||||
}
|
||||
len = lvm->lv->le_count / lvm->stripes;
|
||||
|
||||
if (!(segtype = get_segtype_from_string(cmd, "striped"))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
total_area_len = lvm->lv->le_count / lvm->stripes;
|
||||
|
||||
while (le < len) {
|
||||
if (!(segtype = get_segtype_from_string(cmd, "striped")))
|
||||
return_0;
|
||||
|
||||
while (first_area_le < total_area_len) {
|
||||
area_len = 1;
|
||||
|
||||
/*
|
||||
* Find how many blocks are contiguous in all stripes
|
||||
* Find how many extents are contiguous in all stripes
|
||||
* and so can form part of this segment
|
||||
*/
|
||||
while (_check_stripe(lvm, lvm->stripes,
|
||||
area_len * lvm->stripes, le, len))
|
||||
area_len, first_area_le, total_area_len))
|
||||
area_len++;
|
||||
|
||||
if (!(seg = alloc_lv_segment(cmd->mem, segtype, lvm->lv,
|
||||
lvm->stripes * le,
|
||||
lvm->stripes * first_area_le,
|
||||
lvm->stripes * area_len,
|
||||
0, lvm->stripe_size, NULL,
|
||||
lvm->stripes,
|
||||
@@ -310,15 +315,13 @@ static int _read_stripes(struct cmd_context *cmd, struct lv_map *lvm)
|
||||
*/
|
||||
for (st = 0; st < seg->area_count; st++)
|
||||
if (!set_lv_segment_area_pv(seg, st,
|
||||
lvm->map[le + st * len].pv,
|
||||
lvm->map[le + st * len].pe)) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
lvm->map[first_area_le + st * total_area_len].pv,
|
||||
lvm->map[first_area_le + st * total_area_len].pe))
|
||||
return_0;
|
||||
|
||||
list_add(&lvm->lv->segments, &seg->list);
|
||||
|
||||
le += seg->len;
|
||||
first_area_le += area_len;
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
@@ -81,7 +81,7 @@ int init_no_locking(struct locking_type *locking, struct cmd_context *cmd)
|
||||
locking->lock_resource = _no_lock_resource;
|
||||
locking->reset_locking = _no_reset_locking;
|
||||
locking->fin_locking = _no_fin_locking;
|
||||
locking->flags = 0;
|
||||
locking->flags = LCK_CLUSTERED;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -963,6 +963,30 @@ static struct volume_group *_vg_read_orphans(struct cmd_context *cmd)
|
||||
return vg;
|
||||
}
|
||||
|
||||
static int _update_pv_list(struct list *all_pvs, struct volume_group *vg)
|
||||
{
|
||||
struct pv_list *pvl, *pvl2;
|
||||
|
||||
list_iterate_items(pvl, &vg->pvs) {
|
||||
list_iterate_items(pvl2, all_pvs) {
|
||||
if (pvl->pv->dev == pvl2->pv->dev)
|
||||
goto next_pv;
|
||||
}
|
||||
/* PV is not on list so add it. Note that we don't copy it. */
|
||||
if (!(pvl2 = dm_pool_zalloc(vg->cmd->mem, sizeof(*pvl2)))) {
|
||||
log_error("pv_list allocation for '%s' failed",
|
||||
dev_name(pvl->pv->dev));
|
||||
return 0;
|
||||
}
|
||||
pvl2->pv = pvl->pv;
|
||||
list_add(all_pvs, &pvl2->list);
|
||||
next_pv:
|
||||
;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Caller sets consistent to 1 if it's safe for vg_read to correct
|
||||
* inconsistent metadata on disk (i.e. the VG write lock is held).
|
||||
* This guarantees only consistent metadata is returned unless PARTIAL_VG.
|
||||
@@ -982,9 +1006,12 @@ static struct volume_group *_vg_read(struct cmd_context *cmd,
|
||||
struct volume_group *vg, *correct_vg = NULL;
|
||||
struct metadata_area *mda;
|
||||
int inconsistent = 0;
|
||||
int inconsistent_vgid = 0;
|
||||
int use_precommitted = precommitted;
|
||||
struct list *pvids;
|
||||
struct pv_list *pvl;
|
||||
struct pv_list *pvl, *pvl2;
|
||||
struct list all_pvs;
|
||||
char uuid[64] __attribute((aligned(8)));
|
||||
|
||||
if (!*vgname) {
|
||||
if (use_precommitted) {
|
||||
@@ -1069,6 +1096,8 @@ static struct volume_group *_vg_read(struct cmd_context *cmd,
|
||||
}
|
||||
}
|
||||
|
||||
list_init(&all_pvs);
|
||||
|
||||
/* Failed to find VG where we expected it - full scan and retry */
|
||||
if (!correct_vg) {
|
||||
inconsistent = 0;
|
||||
@@ -1104,13 +1133,25 @@ static struct volume_group *_vg_read(struct cmd_context *cmd,
|
||||
}
|
||||
if (!correct_vg) {
|
||||
correct_vg = vg;
|
||||
if (!_update_pv_list(&all_pvs, correct_vg))
|
||||
return_NULL;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strncmp((char *)vg->id.uuid,
|
||||
(char *)correct_vg->id.uuid, ID_LEN)) {
|
||||
inconsistent = 1;
|
||||
inconsistent_vgid = 1;
|
||||
}
|
||||
|
||||
/* FIXME Also ensure contents same - checksums same? */
|
||||
if (correct_vg->seqno != vg->seqno) {
|
||||
inconsistent = 1;
|
||||
if (vg->seqno > correct_vg->seqno)
|
||||
if (vg->seqno > correct_vg->seqno) {
|
||||
if (!_update_pv_list(&all_pvs, vg))
|
||||
return_NULL;
|
||||
correct_vg = vg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1143,17 +1184,42 @@ static struct volume_group *_vg_read(struct cmd_context *cmd,
|
||||
return correct_vg;
|
||||
}
|
||||
|
||||
log_print("Inconsistent metadata copies found - updating "
|
||||
"to use version %u", correct_vg->seqno);
|
||||
/* Don't touch if vgids didn't match */
|
||||
if (inconsistent_vgid) {
|
||||
log_error("Inconsistent metadata UUIDs found for "
|
||||
"volume group %s", vgname);
|
||||
*consistent = 0;
|
||||
return correct_vg;
|
||||
}
|
||||
|
||||
log_print("Inconsistent metadata found for VG %s - updating "
|
||||
"to use version %u", vgname, correct_vg->seqno);
|
||||
|
||||
if (!vg_write(correct_vg)) {
|
||||
log_error("Automatic metadata correction failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!vg_commit(correct_vg)) {
|
||||
log_error("Automatic metadata correction commit "
|
||||
"failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
list_iterate_items(pvl, &all_pvs) {
|
||||
list_iterate_items(pvl2, &correct_vg->pvs) {
|
||||
if (pvl->pv->dev == pvl2->pv->dev)
|
||||
goto next_pv;
|
||||
}
|
||||
if (!id_write_format(&pvl->pv->id, uuid, sizeof(uuid)))
|
||||
return_NULL;
|
||||
log_error("Removing PV %s (%s) that no longer belongs to VG %s",
|
||||
dev_name(pvl->pv->dev), uuid, correct_vg->name);
|
||||
if (!pv_write_orphan(cmd, pvl->pv))
|
||||
return_NULL;
|
||||
next_pv:
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
if ((correct_vg->status & PVMOVE) && !pvmove_mode()) {
|
||||
@@ -1433,3 +1499,25 @@ int pv_write(struct cmd_context *cmd __attribute((unused)), struct physical_volu
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int pv_write_orphan(struct cmd_context *cmd, struct physical_volume *pv)
|
||||
{
|
||||
const char *old_vg_name = pv->vg_name;
|
||||
|
||||
pv->vg_name = ORPHAN;
|
||||
pv->status = ALLOCATABLE_PV;
|
||||
|
||||
if (!dev_get_size(pv->dev, &pv->size)) {
|
||||
log_error("%s: Couldn't get size.", dev_name(pv->dev));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!pv_write(cmd, pv, NULL, INT64_C(-1))) {
|
||||
log_error("Failed to clear metadata from physical "
|
||||
"volume \"%s\" after removal from \"%s\"",
|
||||
dev_name(pv->dev), old_vg_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -423,6 +423,7 @@ struct list *get_vgids(struct cmd_context *cmd, int full_scan);
|
||||
|
||||
int pv_write(struct cmd_context *cmd, struct physical_volume *pv,
|
||||
struct list *mdas, int64_t label_sector);
|
||||
int pv_write_orphan(struct cmd_context *cmd, struct physical_volume *pv);
|
||||
|
||||
/* pe_start and pe_end relate to any existing data so that new metadata
|
||||
* areas can avoid overlap */
|
||||
|
||||
@@ -256,6 +256,8 @@ void sync_dir(const char *file)
|
||||
int fcntl_lock_file(const char *file, short lock_type, int warn_if_read_only)
|
||||
{
|
||||
int lockfd;
|
||||
char *dir;
|
||||
char *c;
|
||||
struct flock lock = {
|
||||
.l_type = lock_type,
|
||||
.l_whence = 0,
|
||||
@@ -263,6 +265,17 @@ int fcntl_lock_file(const char *file, short lock_type, int warn_if_read_only)
|
||||
.l_len = 0
|
||||
};
|
||||
|
||||
if (!(dir = dm_strdup(file))) {
|
||||
log_error("fcntl_lock_file failed in strdup.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((c = strrchr(dir, '/')))
|
||||
*c = '\0';
|
||||
|
||||
if (!create_dir(dir))
|
||||
return -1;
|
||||
|
||||
log_very_verbose("Locking %s (%s, %hd)", file,
|
||||
(lock_type == F_WRLCK) ? "F_WRLCK" : "F_RDLCK",
|
||||
lock_type);
|
||||
|
||||
@@ -868,11 +868,11 @@ static const struct dm_report_object_type _report_types[] = {
|
||||
|
||||
#define STR DM_REPORT_FIELD_TYPE_STRING
|
||||
#define NUM DM_REPORT_FIELD_TYPE_NUMBER
|
||||
#define FIELD(type, strct, sorttype, head, field, width, func, id, desc) {type, id, (off_t)((void *)&_dummy._ ## strct.field - (void *)&_dummy._ ## strct), head, width, sorttype, &_ ## func ## _disp, desc},
|
||||
#define FIELD(type, strct, sorttype, head, field, width, func, id, desc) {type, sorttype, (off_t)((void *)&_dummy._ ## strct.field - (void *)&_dummy._ ## strct), width, id, head, &_ ## func ## _disp, desc},
|
||||
|
||||
static struct dm_report_field_type _fields[] = {
|
||||
#include "columns.h"
|
||||
{0, "", 0, "", 0, 0, NULL, NULL},
|
||||
{0, 0, 0, 0, "", "", NULL, NULL},
|
||||
};
|
||||
|
||||
#undef STR
|
||||
|
||||
@@ -657,11 +657,11 @@ struct dm_report_field;
|
||||
struct dm_report;
|
||||
struct dm_report_field_type {
|
||||
uint32_t type; /* object type id */
|
||||
const char id[32]; /* string used to specify the field */
|
||||
unsigned int offset; /* byte offset in the object */
|
||||
const char heading[32]; /* string printed in header */
|
||||
int width; /* default width */
|
||||
uint32_t flags; /* DM_REPORT_FIELD_* */
|
||||
uint32_t offset; /* byte offset in the object */
|
||||
int32_t width; /* default width */
|
||||
const char id[32]; /* string used to specify the field */
|
||||
const char heading[32]; /* string printed in header */
|
||||
int (*report_fn)(struct dm_report *rh, struct dm_pool *mem,
|
||||
struct dm_report_field *field, const void *data,
|
||||
void *private);
|
||||
|
||||
@@ -60,7 +60,7 @@ struct field_properties {
|
||||
struct list list;
|
||||
uint32_t field_num;
|
||||
uint32_t sort_posn;
|
||||
unsigned width;
|
||||
int32_t width;
|
||||
const struct dm_report_object_type *type;
|
||||
uint32_t flags;
|
||||
};
|
||||
@@ -504,15 +504,20 @@ struct dm_report *dm_report_init(uint32_t *report_types,
|
||||
|
||||
if (!(rh->mem = dm_pool_create("report", 10 * 1024))) {
|
||||
log_error("dm_report_init: allocation of memory pool failed");
|
||||
dm_free(rh);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Generate list of fields for output based on format string & flags */
|
||||
if (!_parse_options(rh, output_fields))
|
||||
if (!_parse_options(rh, output_fields)) {
|
||||
dm_report_free(rh);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!_parse_keys(rh, sort_keys))
|
||||
if (!_parse_keys(rh, sort_keys)) {
|
||||
dm_report_free(rh);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Return updated types value for further compatility check by caller */
|
||||
if (report_types)
|
||||
@@ -750,7 +755,7 @@ int dm_report_output(struct dm_report *rh)
|
||||
struct dm_report_field *field;
|
||||
const char *repstr;
|
||||
char buf[4096];
|
||||
unsigned width;
|
||||
int32_t width;
|
||||
uint32_t align;
|
||||
|
||||
if (list_empty(&rh->rows))
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
dmsetup \- low level logical volume management
|
||||
.SH SYNOPSIS
|
||||
.ad l
|
||||
.B dmsetup help
|
||||
.I [-c|-C|--columns]
|
||||
.br
|
||||
.B dmsetup create
|
||||
.I device_name [-u uuid] [--notable | --table <table> | table_file]
|
||||
.br
|
||||
@@ -33,21 +36,25 @@ dmsetup \- low level logical volume management
|
||||
.B dmsetup message
|
||||
.I device_name sector message
|
||||
.br
|
||||
.B dmsetup ls [--target target_type] [--exec command] [--tree [-o options]]
|
||||
.B dmsetup ls
|
||||
.I [--target target_type] [--exec command] [--tree [-o options]]
|
||||
.br
|
||||
.B dmsetup info
|
||||
.I [device_name]
|
||||
.br
|
||||
.B dmsetup info -c|-C|--columns [--noheadings] [-o name]
|
||||
.B dmsetup info -c|-C|--columns
|
||||
.I [--noheadings] [--separator separator] [-o fields] [-O|--sort sort_fields]
|
||||
.I [device_name]
|
||||
.br
|
||||
.B dmsetup deps
|
||||
.I [device_name]
|
||||
.br
|
||||
.B dmsetup status [--target target_type]
|
||||
.B dmsetup status
|
||||
.I [--target target_type]
|
||||
.I [device_name]
|
||||
.br
|
||||
.B dmsetup table [--target target_type]
|
||||
.B dmsetup table
|
||||
.I [--target target_type]
|
||||
.I [device_name]
|
||||
.br
|
||||
.B dmsetup wait
|
||||
@@ -103,7 +110,7 @@ Tell the kernel not to supply the open reference count for the device.
|
||||
When creating a device, don't load any table.
|
||||
.IP \fB-o|--options
|
||||
.br
|
||||
Specify which fields to display. Only \fB-o\ name\fP is supported.
|
||||
Specify which fields to display.
|
||||
.IP \fB-r|--readonly
|
||||
.br
|
||||
Set the table being loaded read-only.
|
||||
@@ -136,6 +143,11 @@ See below for information on the table format.
|
||||
.br
|
||||
Outputs a list of (major, minor) pairs for devices referenced by the
|
||||
live table for the specified device.
|
||||
.IP \fBhelp
|
||||
.I [-c|-C|--columns]
|
||||
.br
|
||||
Outputs a summary of the commands available, optionally including
|
||||
the list of report fields.
|
||||
.IP \fBinfo
|
||||
.I [device_name]
|
||||
.br
|
||||
@@ -154,6 +166,17 @@ Outputs some brief information about the device in the form:
|
||||
Number of targets in the live table
|
||||
.br
|
||||
UUID
|
||||
.IP \fBinfo -c|-C|--columns
|
||||
.I [--noheadings] [--separator separator] [-o fields] [-O|--sort sort_fields]
|
||||
.I [device_name]
|
||||
.br
|
||||
Output you can customise.
|
||||
Fields are comma-separated and chosen from the following list:
|
||||
name, major, minor, attr, open, segments, events, uuid.
|
||||
Attributes are: (L)ive, (I)nactive, (s)uspended, (r)ead-only, read-(w)rite.
|
||||
Precede the list with '+' to append
|
||||
to the default selection of columns instead of replacing it.
|
||||
Precede any sort_field with - for a reverse sort on that column.
|
||||
.IP \fBls
|
||||
.I [--target target_type]
|
||||
.I [--exec command]
|
||||
|
||||
@@ -12,7 +12,7 @@ lvchange \- change attributes of a logical volume
|
||||
[\-\-ignorelockingfailure]
|
||||
[\-\-monitor {y|n}]
|
||||
[\-M/\-\-persistent y/n] [\-\-minor minor]
|
||||
[\-P/\-\-partial y/n]
|
||||
[\-P/\-\-partial]
|
||||
[\-p/\-\-permission r/w] [\-r/\-\-readahead ReadAheadSectors]
|
||||
[\-\-refresh]
|
||||
[\-t/\-\-test]
|
||||
|
||||
@@ -95,8 +95,8 @@ pattern it is rejected; otherwise it is accepted.
|
||||
As an example, to ignore /dev/cdrom you could use:
|
||||
\fBdevices { filter=["r|cdrom|"] }\fP
|
||||
.IP
|
||||
\fBcache\fP \(em Persistent filter cache file.
|
||||
Defaults to "/etc/lvm/.cache".
|
||||
\fBcache_dir\fP \(em Persistent filter cache file directory.
|
||||
Defaults to "/etc/lvm/cache".
|
||||
.IP
|
||||
\fBwrite_cache_state\fP \(em Set to 0 to disable the writing out of the
|
||||
persistent filter cache file when \fBlvm\fP exits.
|
||||
@@ -364,9 +364,9 @@ understand how things work: to make changes you should always use
|
||||
the tools as normal, or else vgcfgbackup, edit backup, vgcfgrestore.
|
||||
.SH FILES
|
||||
.I /etc/lvm/lvm.conf
|
||||
.I /etc/lvm/.cache
|
||||
.I /etc/lvm/archive
|
||||
.I /etc/lvm/backup
|
||||
.I /etc/lvm/cache/.cache
|
||||
.I /var/lock/lvm
|
||||
.SH SEE ALSO
|
||||
.BR lvm (8),
|
||||
|
||||
@@ -5,7 +5,7 @@ pvmove \- move physical extents
|
||||
.B pvmove
|
||||
[\-\-abort]
|
||||
[\-\-alloc AllocationPolicy]
|
||||
[\-\-background]
|
||||
[\-b/\-\-background]
|
||||
[\-d/\-\-debug] [\-h/\-\-help] [\-i/\-\-interval Seconds] [\-v/\-\-verbose]
|
||||
[\-n/\-\-name LogicalVolume]
|
||||
[SourcePhysicalVolume[:PE[-PE]...] [DestinationPhysicalVolume[:PE[-PE]...]...]]
|
||||
@@ -73,7 +73,7 @@ type of on-disk metadata. Metadata can be converted using \fBvgconvert\fP(8).
|
||||
.I \-\-abort
|
||||
Abort any moves in progress.
|
||||
.TP
|
||||
.I \-\-background
|
||||
.I \-b, \-\-background
|
||||
Run the daemon in the background.
|
||||
.TP
|
||||
.I \-i, \-\-interval Seconds
|
||||
|
||||
@@ -43,16 +43,10 @@ start()
|
||||
then
|
||||
for vg in $LVM_VGS
|
||||
do
|
||||
if ! action "Activating VG $vg:" $VGCHANGE -ayl $vg
|
||||
then
|
||||
rtrn=$?
|
||||
fi
|
||||
action "Activating VG $vg:" $VGCHANGE -ayl $vg || rtrn=$?
|
||||
done
|
||||
else
|
||||
if ! action "Activating VGs:" $VGCHANGE -ayl
|
||||
then
|
||||
rtrn=$?
|
||||
fi
|
||||
action "Activating VGs:" $VGCHANGE -ayl || rtrn=$?
|
||||
fi
|
||||
done
|
||||
|
||||
@@ -67,19 +61,13 @@ stop()
|
||||
then
|
||||
for vg in $LVM_VGS
|
||||
do
|
||||
if ! action "Deactivating VG $vg:" $VGCHANGE -anl $vg
|
||||
then
|
||||
rtrn=$?
|
||||
fi
|
||||
action "Deactivating VG $vg:" $VGCHANGE -anl $vg || rtrn=$?
|
||||
done
|
||||
else
|
||||
# Hack to only deactivate clustered volumes
|
||||
clustervgs=`$VGDISPLAY 2> /dev/null | awk 'BEGIN {RS="VG Name"} {if (/Clustered/) print $1;}'`
|
||||
for vg in $clustervgs; do
|
||||
if ! action "Deactivating VG $vg:" $VGCHANGE -anl $vg
|
||||
then
|
||||
rtrn=$?
|
||||
fi
|
||||
action "Deactivating VG $vg:" $VGCHANGE -anl $vg || rtrn=$?
|
||||
done
|
||||
fi
|
||||
|
||||
|
||||
@@ -25,65 +25,67 @@
|
||||
. /etc/init.d/functions
|
||||
|
||||
VGCHANGE="/usr/sbin/vgchange"
|
||||
WARN=1
|
||||
|
||||
start()
|
||||
{
|
||||
for ret in 0
|
||||
do
|
||||
ret=0
|
||||
# TODO do we want to separate out already active groups only?
|
||||
VGS=`vgs --noheadings -o name`
|
||||
for vg in $VGS
|
||||
do
|
||||
if ! action "Starting monitoring for VG $vg:" $VGCHANGE --monitor y $vg
|
||||
then
|
||||
ret=$?
|
||||
fi
|
||||
action "Starting monitoring for VG $vg:" $VGCHANGE --monitor y $vg || ret=$?
|
||||
done
|
||||
|
||||
done
|
||||
|
||||
return $ret
|
||||
return $ret
|
||||
}
|
||||
|
||||
|
||||
stop()
|
||||
{
|
||||
for ret in 0
|
||||
do
|
||||
ret=0
|
||||
# TODO do we want to separate out already active groups only?
|
||||
if test "$WARN" = "1"; then
|
||||
echo "Not stopping monitoring, this is a dangerous operation. Please use force-stop to override."
|
||||
return 1
|
||||
fi
|
||||
VGS=`vgs --noheadings -o name`
|
||||
for vg in $VGS
|
||||
do
|
||||
if ! action "Starting monitoring for VG $vg:" $VGCHANGE --monitor n $vg
|
||||
then
|
||||
ret=$?
|
||||
fi
|
||||
action "Stopping monitoring for VG $vg:" $VGCHANGE --monitor n $vg || ret=$?
|
||||
done
|
||||
|
||||
done
|
||||
|
||||
return $ret
|
||||
}
|
||||
|
||||
ret=1
|
||||
result=1
|
||||
|
||||
# See how we were called.
|
||||
case "$1" in
|
||||
start)
|
||||
start
|
||||
ret=$?
|
||||
result=$?
|
||||
;;
|
||||
|
||||
force-stop)
|
||||
WARN=0
|
||||
stop
|
||||
result=$?
|
||||
;;
|
||||
|
||||
stop)
|
||||
test "$runlevel" = "0" && WARN=0
|
||||
test "$runlevel" = "6" && WARN=0
|
||||
stop
|
||||
ret=$?
|
||||
result=$?
|
||||
;;
|
||||
|
||||
restart)
|
||||
WARN=0
|
||||
if stop
|
||||
then
|
||||
start
|
||||
fi
|
||||
ret=$?
|
||||
result=$?
|
||||
;;
|
||||
|
||||
status)
|
||||
@@ -91,8 +93,8 @@ case "$1" in
|
||||
;;
|
||||
|
||||
*)
|
||||
echo $"Usage: $0 {start|stop|restart|status}"
|
||||
echo $"Usage: $0 {start|stop|restart|status|force-stop}"
|
||||
;;
|
||||
esac
|
||||
|
||||
exit $ret
|
||||
exit $result
|
||||
|
||||
@@ -1561,20 +1561,20 @@ static const struct dm_report_object_type _report_types[] = {
|
||||
#define OFFSET_OF(strct, field) ((unsigned int) &((struct strct *)NULL)->field)
|
||||
#define STR (DM_REPORT_FIELD_TYPE_STRING)
|
||||
#define NUM (DM_REPORT_FIELD_TYPE_NUMBER)
|
||||
#define FIELD_O(type, strct, sorttype, head, field, width, func, id, desc) {DR_ ## type, id, OFFSET_OF(strct, field), head, width, sorttype, &_ ## func ## _disp, desc},
|
||||
#define FIELD_F(type, sorttype, head, width, func, id, desc) {DR_ ## type, id, 0, head, width, sorttype, &_ ## func ## _disp, desc},
|
||||
#define FIELD_O(type, strct, sorttype, head, field, width, func, id, desc) {DR_ ## type, sorttype, OFFSET_OF(strct, field), width, id, head, &_ ## func ## _disp, desc},
|
||||
#define FIELD_F(type, sorttype, head, width, func, id, desc) {DR_ ## type, sorttype, 0, width, id, head, &_ ## func ## _disp, desc},
|
||||
|
||||
static const struct dm_report_field_type _report_fields[] = {
|
||||
/* *INDENT-OFF* */
|
||||
FIELD_F(TASK, STR, "Name", 16, dm_name, "name", "Name of mapped device.")
|
||||
FIELD_F(TASK, STR, "UUID", 32, dm_uuid, "uuid", "Unique identifier for mapped device (optional).")
|
||||
FIELD_F(INFO, STR, "Stat", 4, dm_info_status, "status", "Attributes.")
|
||||
FIELD_O(INFO, dm_info, NUM, "Maj", major, 3, int32, "major", "Major number.")
|
||||
FIELD_O(INFO, dm_info, NUM, "Min", minor, 3, int32, "minor", "Minor number.")
|
||||
FIELD_O(INFO, dm_info, NUM, "Open", open_count, 4, int32, "open_count", "Number of references to open device, if requested.")
|
||||
FIELD_O(INFO, dm_info, NUM, "Targ", target_count, 4, int32, "target_count", "Number of segments in live table, if present.")
|
||||
FIELD_O(INFO, dm_info, NUM, "Event", event_nr, 6, uint32, "event_nr", "Current event number.")
|
||||
{0, "", 0, "", 0, 0, NULL, NULL},
|
||||
FIELD_F(TASK, STR, "UUID", 32, dm_uuid, "uuid", "Unique (optional) identifier for mapped device.")
|
||||
FIELD_F(INFO, STR, "Stat", 4, dm_info_status, "attr", "(L)ive, (I)nactive, (s)uspended, (r)ead-only, read-(w)rite.")
|
||||
FIELD_O(INFO, dm_info, NUM, "Maj", major, 3, int32, "major", "Block device major number.")
|
||||
FIELD_O(INFO, dm_info, NUM, "Min", minor, 3, int32, "minor", "Block device minor number.")
|
||||
FIELD_O(INFO, dm_info, NUM, "Open", open_count, 4, int32, "open", "Number of references to open device, if requested.")
|
||||
FIELD_O(INFO, dm_info, NUM, "Targ", target_count, 4, int32, "segments", "Number of segments in live table, if present.")
|
||||
FIELD_O(INFO, dm_info, NUM, "Event", event_nr, 6, uint32, "events", "Number of most recent event.")
|
||||
{0, 0, 0, 0, "", "", NULL, NULL},
|
||||
/* *INDENT-ON* */
|
||||
};
|
||||
|
||||
@@ -1583,7 +1583,7 @@ FIELD_O(INFO, dm_info, NUM, "Event", event_nr, 6, uint32, "event_nr", "Current e
|
||||
#undef FIELD_O
|
||||
#undef FIELD_F
|
||||
|
||||
static const char *default_report_options = "name,major,minor,status,open_count,target_count,event_nr,uuid";
|
||||
static const char *default_report_options = "name,major,minor,attr,open,segments,events,uuid";
|
||||
|
||||
static int _report_init(struct command *c)
|
||||
{
|
||||
@@ -1625,7 +1625,7 @@ static int _report_init(struct command *c)
|
||||
if (_switches[SORT_ARG] && _string_args[SORT_ARG]) {
|
||||
keys = _string_args[SORT_ARG];
|
||||
buffered = 1;
|
||||
if (!strcmp(c->name, "status") || !strcmp(c->name, "table")) {
|
||||
if (c && (!strcmp(c->name, "status") || !strcmp(c->name, "table"))) {
|
||||
err("--sort is not yet supported with status and table");
|
||||
goto out;
|
||||
}
|
||||
@@ -1673,10 +1673,13 @@ static int _ls(int argc, char **argv, void *data)
|
||||
return _process_all(argc, argv, 0, _display_name);
|
||||
}
|
||||
|
||||
static int _help(int argc, char **argv, void *data);
|
||||
|
||||
/*
|
||||
* Dispatch table
|
||||
*/
|
||||
static struct command _commands[] = {
|
||||
{"help", "[-c|-C|--columns]", 0, 0, _help},
|
||||
{"create", "<dev_name> [-j|--major <major> -m|--minor <minor>]\n"
|
||||
"\t [-U|--uid <uid>] [-G|--gid <gid>] [-M|--mode <octal_mode>]\n"
|
||||
"\t [-u|uuid <uuid>]\n"
|
||||
@@ -1710,14 +1713,18 @@ static void _usage(FILE *out)
|
||||
|
||||
fprintf(out, "Usage:\n\n");
|
||||
fprintf(out, "dmsetup [--version] [-v|--verbose [-v|--verbose ...]]\n"
|
||||
" [-r|--readonly] [--noopencount] [--nolockfs]\n\n");
|
||||
" [-r|--readonly] [--noopencount] [--nolockfs]\n"
|
||||
" [-c|-C|--columns] [-o <fields>] [-O|--sort <sort_fields>]\n"
|
||||
" [--noheadings] [--separator <separator>]\n\n");
|
||||
for (i = 0; _commands[i].name; i++)
|
||||
fprintf(out, "\t%s %s\n", _commands[i].name, _commands[i].help);
|
||||
fprintf(out, "\n<device> may be device name or -u <uuid> or "
|
||||
"-j <major> -m <minor>\n");
|
||||
fprintf(out, "<fields> are comma-separated. Use 'help -c' for list.\n");
|
||||
fprintf(out, "Table_file contents may be supplied on stdin.\n");
|
||||
fprintf(out, "Tree options are: ascii, utf, vt100; compact, inverted, notrunc;\n"
|
||||
" [no]device, active, open, rw and uuid.\n\n");
|
||||
" [no]device, active, open, rw and uuid.\n");
|
||||
fprintf(out, "\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1728,6 +1735,23 @@ static void _losetup_usage(FILE *out)
|
||||
"[-o offset] [-f|loop_device] [file]\n\n");
|
||||
}
|
||||
|
||||
static int _help(int argc __attribute((unused)),
|
||||
char **argv __attribute((unused)),
|
||||
void *data __attribute((unused)))
|
||||
{
|
||||
_usage(stderr);
|
||||
|
||||
if (_switches[COLS_ARG]) {
|
||||
_switches[OPTIONS_ARG] = 1;
|
||||
_string_args[OPTIONS_ARG] = (char *) "help";
|
||||
_switches[SORT_ARG] = 0;
|
||||
|
||||
(void) _report_init(NULL);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct command *_find_command(const char *name)
|
||||
{
|
||||
int i;
|
||||
@@ -1995,6 +2019,7 @@ static int _process_losetup_switches(const char *base, int *argc, char ***argv)
|
||||
if (*argc != 2) {
|
||||
fprintf(stderr, "%s: Too few arguments\n", base);
|
||||
_losetup_usage(stderr);
|
||||
dm_free(device_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2003,6 +2028,7 @@ static int _process_losetup_switches(const char *base, int *argc, char ***argv)
|
||||
fprintf(stderr, "%s: Could not parse loop file name %s\n",
|
||||
base, (*argv)[1]);
|
||||
_losetup_usage(stderr);
|
||||
dm_free(device_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2010,6 +2036,7 @@ static int _process_losetup_switches(const char *base, int *argc, char ***argv)
|
||||
_table = dm_malloc(LOOP_TABLE_SIZE);
|
||||
if (!_loop_table(_table, LOOP_TABLE_SIZE, loop_file, device_name, offset)) {
|
||||
fprintf(stderr, "Could not build device-mapper table for %s\n", (*argv)[0]);
|
||||
dm_free(device_name);
|
||||
return 0;
|
||||
}
|
||||
_switches[TABLE_ARG]++;
|
||||
@@ -2096,7 +2123,7 @@ static int _process_switches(int *argc, char ***argv)
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(!strcmp(base, "losetup") || !strcmp(base, "dmlosetup")){
|
||||
if (!strcmp(base, "losetup") || !strcmp(base, "dmlosetup")){
|
||||
r = _process_losetup_switches(base, argc, argv);
|
||||
free(namebase);
|
||||
return r;
|
||||
|
||||
@@ -95,7 +95,7 @@ static int _lvcreate_name_params(struct lvcreate_params *lp,
|
||||
}
|
||||
|
||||
} else {
|
||||
vg_name = skip_dev_dir(cmd, argv[0]);
|
||||
vg_name = skip_dev_dir(cmd, argv[0], NULL);
|
||||
if (strrchr(vg_name, '/')) {
|
||||
log_error("Volume group name expected "
|
||||
"(no slash)");
|
||||
|
||||
@@ -19,3 +19,8 @@ int main(int argc, char **argv)
|
||||
{
|
||||
return lvm2_main(argc, argv, 1);
|
||||
}
|
||||
|
||||
int lvm_shell(struct cmd_context *cmd, struct cmdline_context *cmdline)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
224
tools/lvm.c
224
tools/lvm.c
@@ -13,9 +13,233 @@
|
||||
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "tools.h"
|
||||
#include "lvm2cmdline.h"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
return lvm2_main(argc, argv, 0);
|
||||
}
|
||||
|
||||
#ifdef READLINE_SUPPORT
|
||||
|
||||
# include <readline/readline.h>
|
||||
# include <readline/history.h>
|
||||
# ifndef HAVE_RL_COMPLETION_MATCHES
|
||||
# define rl_completion_matches(a, b) completion_matches((char *)a, b)
|
||||
# endif
|
||||
|
||||
static struct cmdline_context *_cmdline;
|
||||
|
||||
/* List matching commands */
|
||||
static char *_list_cmds(const char *text, int state)
|
||||
{
|
||||
static int i = 0;
|
||||
static size_t len = 0;
|
||||
|
||||
/* Initialise if this is a new completion attempt */
|
||||
if (!state) {
|
||||
i = 0;
|
||||
len = strlen(text);
|
||||
}
|
||||
|
||||
while (i < _cmdline->num_commands)
|
||||
if (!strncmp(text, _cmdline->commands[i++].name, len))
|
||||
return strdup(_cmdline->commands[i - 1].name);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* List matching arguments */
|
||||
static char *_list_args(const char *text, int state)
|
||||
{
|
||||
static int match_no = 0;
|
||||
static size_t len = 0;
|
||||
static struct command *com;
|
||||
|
||||
/* Initialise if this is a new completion attempt */
|
||||
if (!state) {
|
||||
char *s = rl_line_buffer;
|
||||
int j = 0;
|
||||
|
||||
match_no = 0;
|
||||
com = NULL;
|
||||
len = strlen(text);
|
||||
|
||||
/* Find start of first word in line buffer */
|
||||
while (isspace(*s))
|
||||
s++;
|
||||
|
||||
/* Look for word in list of commands */
|
||||
for (j = 0; j < _cmdline->num_commands; j++) {
|
||||
const char *p;
|
||||
char *q = s;
|
||||
|
||||
p = _cmdline->commands[j].name;
|
||||
while (*p == *q) {
|
||||
p++;
|
||||
q++;
|
||||
}
|
||||
if ((!*p) && *q == ' ') {
|
||||
com = _cmdline->commands + j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!com)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Short form arguments */
|
||||
if (len < 3) {
|
||||
while (match_no < com->num_args) {
|
||||
char s[3];
|
||||
char c;
|
||||
if (!(c = (_cmdline->the_args +
|
||||
com->valid_args[match_no++])->short_arg))
|
||||
continue;
|
||||
|
||||
sprintf(s, "-%c", c);
|
||||
if (!strncmp(text, s, len))
|
||||
return strdup(s);
|
||||
}
|
||||
}
|
||||
|
||||
/* Long form arguments */
|
||||
if (match_no < com->num_args)
|
||||
match_no = com->num_args;
|
||||
|
||||
while (match_no - com->num_args < com->num_args) {
|
||||
const char *l;
|
||||
l = (_cmdline->the_args +
|
||||
com->valid_args[match_no++ - com->num_args])->long_arg;
|
||||
if (*(l + 2) && !strncmp(text, l, len))
|
||||
return strdup(l);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Custom completion function */
|
||||
static char **_completion(const char *text, int start_pos, int end_pos)
|
||||
{
|
||||
char **match_list = NULL;
|
||||
int p = 0;
|
||||
|
||||
while (isspace((int) *(rl_line_buffer + p)))
|
||||
p++;
|
||||
|
||||
/* First word should be one of our commands */
|
||||
if (start_pos == p)
|
||||
match_list = rl_completion_matches(text, _list_cmds);
|
||||
|
||||
else if (*text == '-')
|
||||
match_list = rl_completion_matches(text, _list_args);
|
||||
/* else other args */
|
||||
|
||||
/* No further completion */
|
||||
rl_attempted_completion_over = 1;
|
||||
return match_list;
|
||||
}
|
||||
|
||||
static int _hist_file(char *buffer, size_t size)
|
||||
{
|
||||
char *e = getenv("HOME");
|
||||
|
||||
if (dm_snprintf(buffer, size, "%s/.lvm_history", e) < 0) {
|
||||
log_error("$HOME/.lvm_history: path too long");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void _read_history(struct cmd_context *cmd)
|
||||
{
|
||||
char hist_file[PATH_MAX];
|
||||
|
||||
if (!_hist_file(hist_file, sizeof(hist_file)))
|
||||
return;
|
||||
|
||||
if (read_history(hist_file))
|
||||
log_very_verbose("Couldn't read history from %s.", hist_file);
|
||||
|
||||
stifle_history(find_config_tree_int(cmd, "shell/history_size",
|
||||
DEFAULT_MAX_HISTORY));
|
||||
|
||||
}
|
||||
|
||||
static void _write_history(void)
|
||||
{
|
||||
char hist_file[PATH_MAX];
|
||||
|
||||
if (!_hist_file(hist_file, sizeof(hist_file)))
|
||||
return;
|
||||
|
||||
if (write_history(hist_file))
|
||||
log_very_verbose("Couldn't write history to %s.", hist_file);
|
||||
}
|
||||
|
||||
int lvm_shell(struct cmd_context *cmd, struct cmdline_context *cmdline)
|
||||
{
|
||||
int argc, ret;
|
||||
char *input = NULL, *args[MAX_ARGS], **argv;
|
||||
|
||||
rl_readline_name = "lvm";
|
||||
rl_attempted_completion_function = (CPPFunction *) _completion;
|
||||
|
||||
_read_history(cmd);
|
||||
|
||||
_cmdline = cmdline;
|
||||
|
||||
_cmdline->interactive = 1;
|
||||
while (1) {
|
||||
free(input);
|
||||
input = readline("lvm> ");
|
||||
|
||||
/* EOF */
|
||||
if (!input) {
|
||||
printf("\n");
|
||||
break;
|
||||
}
|
||||
|
||||
/* empty line */
|
||||
if (!*input)
|
||||
continue;
|
||||
|
||||
add_history(input);
|
||||
|
||||
argv = args;
|
||||
|
||||
if (lvm_split(input, &argc, argv, MAX_ARGS) == MAX_ARGS) {
|
||||
log_error("Too many arguments, sorry.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(argv[0], "lvm")) {
|
||||
argv++;
|
||||
argc--;
|
||||
}
|
||||
|
||||
if (!argc)
|
||||
continue;
|
||||
|
||||
if (!strcmp(argv[0], "quit") || !strcmp(argv[0], "exit")) {
|
||||
remove_history(history_length - 1);
|
||||
log_error("Exiting.");
|
||||
break;
|
||||
}
|
||||
|
||||
ret = lvm_run_command(cmd, argc, argv);
|
||||
if (ret == ENO_SUCH_CMD)
|
||||
log_error("No such command '%s'. Try 'help'.",
|
||||
argv[0]);
|
||||
|
||||
_write_history();
|
||||
}
|
||||
|
||||
free(input);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* READLINE_SUPPORT */
|
||||
|
||||
@@ -18,6 +18,14 @@
|
||||
|
||||
struct cmd_context;
|
||||
|
||||
struct cmdline_context {
|
||||
struct arg *the_args;
|
||||
struct command *commands;
|
||||
int num_commands;
|
||||
int commands_size;
|
||||
int interactive;
|
||||
};
|
||||
|
||||
int lvm2_main(int argc, char **argv, unsigned is_static);
|
||||
|
||||
void *cmdlib_lvm2_init(unsigned is_static);
|
||||
@@ -27,5 +35,6 @@ struct cmd_context *init_lvm(unsigned is_static);
|
||||
void lvm_register_commands(void);
|
||||
int lvm_split(char *str, int *argc, char **argv, int max);
|
||||
int lvm_run_command(struct cmd_context *cmd, int argc, char **argv);
|
||||
int lvm_shell(struct cmd_context *cmd, struct cmdline_context *cmdline);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -41,30 +41,16 @@ extern char *optarg;
|
||||
# define OPTIND_INIT 1
|
||||
#endif
|
||||
|
||||
#ifdef READLINE_SUPPORT
|
||||
# include <readline/readline.h>
|
||||
# include <readline/history.h>
|
||||
# ifndef HAVE_RL_COMPLETION_MATCHES
|
||||
# define rl_completion_matches(a, b) completion_matches((char *)a, b)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Exported table of valid switches
|
||||
* Table of valid switches
|
||||
*/
|
||||
struct arg the_args[ARG_COUNT + 1] = {
|
||||
|
||||
static struct arg _the_args[ARG_COUNT + 1] = {
|
||||
#define arg(a, b, c, d) {b, "", "--" c, d, 0, NULL, 0, 0, INT64_C(0), UINT64_C(0), SIGN_NONE, PERCENT_NONE, NULL},
|
||||
#include "args.h"
|
||||
#undef arg
|
||||
|
||||
};
|
||||
|
||||
static int _array_size;
|
||||
static int _num_commands;
|
||||
static struct command *_commands;
|
||||
|
||||
static int _interactive;
|
||||
static struct cmdline_context _cmdline;
|
||||
|
||||
int yes_no_arg(struct cmd_context *cmd __attribute((unused)), struct arg *a)
|
||||
{
|
||||
@@ -412,21 +398,21 @@ char yes_no_prompt(const char *prompt, ...)
|
||||
|
||||
static void __alloc(int size)
|
||||
{
|
||||
if (!(_commands = dm_realloc(_commands, sizeof(*_commands) * size))) {
|
||||
if (!(_cmdline.commands = dm_realloc(_cmdline.commands, sizeof(*_cmdline.commands) * size))) {
|
||||
log_fatal("Couldn't allocate memory.");
|
||||
exit(ECMD_FAILED);
|
||||
}
|
||||
|
||||
_array_size = size;
|
||||
_cmdline.commands_size = size;
|
||||
}
|
||||
|
||||
static void _alloc_command(void)
|
||||
{
|
||||
if (!_array_size)
|
||||
if (!_cmdline.commands_size)
|
||||
__alloc(32);
|
||||
|
||||
if (_array_size <= _num_commands)
|
||||
__alloc(2 * _array_size);
|
||||
if (_cmdline.commands_size <= _cmdline.num_commands)
|
||||
__alloc(2 * _cmdline.commands_size);
|
||||
}
|
||||
|
||||
static void _create_new_command(const char *name, command_fn command,
|
||||
@@ -437,7 +423,7 @@ static void _create_new_command(const char *name, command_fn command,
|
||||
|
||||
_alloc_command();
|
||||
|
||||
nc = _commands + _num_commands++;
|
||||
nc = _cmdline.commands + _cmdline.num_commands++;
|
||||
|
||||
nc->name = name;
|
||||
nc->desc = desc;
|
||||
@@ -495,17 +481,17 @@ static struct command *_find_command(const char *name)
|
||||
namebase = strdup(name);
|
||||
base = basename(namebase);
|
||||
|
||||
for (i = 0; i < _num_commands; i++) {
|
||||
if (!strcmp(base, _commands[i].name))
|
||||
for (i = 0; i < _cmdline.num_commands; i++) {
|
||||
if (!strcmp(base, _cmdline.commands[i].name))
|
||||
break;
|
||||
}
|
||||
|
||||
free(namebase);
|
||||
|
||||
if (i >= _num_commands)
|
||||
if (i >= _cmdline.num_commands)
|
||||
return 0;
|
||||
|
||||
return _commands + i;
|
||||
return _cmdline.commands + i;
|
||||
}
|
||||
|
||||
static void _usage(const char *name)
|
||||
@@ -527,7 +513,7 @@ static void _usage(const char *name)
|
||||
*/
|
||||
static void _add_getopt_arg(int arg, char **ptr, struct option **o)
|
||||
{
|
||||
struct arg *a = the_args + arg;
|
||||
struct arg *a = _cmdline.the_args + arg;
|
||||
|
||||
if (a->short_arg) {
|
||||
*(*ptr)++ = a->short_arg;
|
||||
@@ -556,12 +542,12 @@ static struct arg *_find_arg(struct command *com, int opt)
|
||||
|
||||
for (i = 0; i < com->num_args; i++) {
|
||||
arg = com->valid_args[i];
|
||||
a = the_args + arg;
|
||||
a = _cmdline.the_args + arg;
|
||||
|
||||
/*
|
||||
* opt should equal either the
|
||||
* short arg, or the index into
|
||||
* 'the_args'.
|
||||
* the_args.
|
||||
*/
|
||||
if ((a->short_arg && (opt == a->short_arg)) ||
|
||||
(!a->short_arg && (opt == arg)))
|
||||
@@ -580,7 +566,7 @@ static int _process_command_line(struct cmd_context *cmd, int *argc,
|
||||
struct arg *a;
|
||||
|
||||
for (i = 0; i < ARG_COUNT; i++) {
|
||||
a = the_args + i;
|
||||
a = _cmdline.the_args + i;
|
||||
|
||||
/* zero the count and arg */
|
||||
a->count = 0;
|
||||
@@ -651,15 +637,15 @@ static int _merge_synonym(struct cmd_context *cmd, int oldarg, int newarg)
|
||||
|
||||
if (arg_count(cmd, oldarg) && arg_count(cmd, newarg)) {
|
||||
log_error("%s and %s are synonyms. Please only supply one.",
|
||||
the_args[oldarg].long_arg, the_args[newarg].long_arg);
|
||||
_cmdline.the_args[oldarg].long_arg, _cmdline.the_args[newarg].long_arg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!arg_count(cmd, oldarg))
|
||||
return 1;
|
||||
|
||||
old = the_args + oldarg;
|
||||
new = the_args + newarg;
|
||||
old = _cmdline.the_args + oldarg;
|
||||
new = _cmdline.the_args + newarg;
|
||||
|
||||
new->count = old->count;
|
||||
new->value = old->value;
|
||||
@@ -782,8 +768,8 @@ static void _display_help(void)
|
||||
log_error("Use 'lvm help <command>' for more information");
|
||||
log_error(" ");
|
||||
|
||||
for (i = 0; i < _num_commands; i++) {
|
||||
struct command *com = _commands + i;
|
||||
for (i = 0; i < _cmdline.num_commands; i++) {
|
||||
struct command *com = _cmdline.commands + i;
|
||||
|
||||
log_error("%-16.16s%s", com->name, com->desc);
|
||||
}
|
||||
@@ -959,7 +945,7 @@ int lvm_run_command(struct cmd_context *cmd, int argc, char **argv)
|
||||
*/
|
||||
dm_pool_empty(cmd->mem);
|
||||
|
||||
if (ret == EINVALID_CMD_LINE && !_interactive)
|
||||
if (ret == EINVALID_CMD_LINE && !_cmdline.interactive)
|
||||
_usage(cmd->command->name);
|
||||
|
||||
log_debug("Completed: %s", cmd->cmd_line);
|
||||
@@ -1030,7 +1016,9 @@ struct cmd_context *init_lvm(unsigned is_static)
|
||||
{
|
||||
struct cmd_context *cmd;
|
||||
|
||||
if (!(cmd = create_toolcontext(&the_args[0], is_static, 0))) {
|
||||
_cmdline.the_args = &_the_args[0];
|
||||
|
||||
if (!(cmd = create_toolcontext(_cmdline.the_args, is_static, 0))) {
|
||||
stack;
|
||||
return NULL;
|
||||
}
|
||||
@@ -1046,10 +1034,10 @@ static void _fin_commands(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < _num_commands; i++)
|
||||
dm_free(_commands[i].valid_args);
|
||||
for (i = 0; i < _cmdline.num_commands; i++)
|
||||
dm_free(_cmdline.commands[i].valid_args);
|
||||
|
||||
dm_free(_commands);
|
||||
dm_free(_cmdline.commands);
|
||||
}
|
||||
|
||||
void lvm_fin(struct cmd_context *cmd)
|
||||
@@ -1106,218 +1094,6 @@ static int _run_script(struct cmd_context *cmd, int argc, char **argv)
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef READLINE_SUPPORT
|
||||
/* List matching commands */
|
||||
static char *_list_cmds(const char *text, int state)
|
||||
{
|
||||
static int i = 0;
|
||||
static size_t len = 0;
|
||||
|
||||
/* Initialise if this is a new completion attempt */
|
||||
if (!state) {
|
||||
i = 0;
|
||||
len = strlen(text);
|
||||
}
|
||||
|
||||
while (i < _num_commands)
|
||||
if (!strncmp(text, _commands[i++].name, len))
|
||||
return strdup(_commands[i - 1].name);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* List matching arguments */
|
||||
static char *_list_args(const char *text, int state)
|
||||
{
|
||||
static int match_no = 0;
|
||||
static size_t len = 0;
|
||||
static struct command *com;
|
||||
|
||||
/* Initialise if this is a new completion attempt */
|
||||
if (!state) {
|
||||
char *s = rl_line_buffer;
|
||||
int j = 0;
|
||||
|
||||
match_no = 0;
|
||||
com = NULL;
|
||||
len = strlen(text);
|
||||
|
||||
/* Find start of first word in line buffer */
|
||||
while (isspace(*s))
|
||||
s++;
|
||||
|
||||
/* Look for word in list of commands */
|
||||
for (j = 0; j < _num_commands; j++) {
|
||||
const char *p;
|
||||
char *q = s;
|
||||
|
||||
p = _commands[j].name;
|
||||
while (*p == *q) {
|
||||
p++;
|
||||
q++;
|
||||
}
|
||||
if ((!*p) && *q == ' ') {
|
||||
com = _commands + j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!com)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Short form arguments */
|
||||
if (len < 3) {
|
||||
while (match_no < com->num_args) {
|
||||
char s[3];
|
||||
char c;
|
||||
if (!(c = (the_args +
|
||||
com->valid_args[match_no++])->short_arg))
|
||||
continue;
|
||||
|
||||
sprintf(s, "-%c", c);
|
||||
if (!strncmp(text, s, len))
|
||||
return strdup(s);
|
||||
}
|
||||
}
|
||||
|
||||
/* Long form arguments */
|
||||
if (match_no < com->num_args)
|
||||
match_no = com->num_args;
|
||||
|
||||
while (match_no - com->num_args < com->num_args) {
|
||||
const char *l;
|
||||
l = (the_args +
|
||||
com->valid_args[match_no++ - com->num_args])->long_arg;
|
||||
if (*(l + 2) && !strncmp(text, l, len))
|
||||
return strdup(l);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Custom completion function */
|
||||
static char **_completion(const char *text, int start_pos, int end_pos)
|
||||
{
|
||||
char **match_list = NULL;
|
||||
int p = 0;
|
||||
|
||||
while (isspace((int) *(rl_line_buffer + p)))
|
||||
p++;
|
||||
|
||||
/* First word should be one of our commands */
|
||||
if (start_pos == p)
|
||||
match_list = rl_completion_matches(text, _list_cmds);
|
||||
|
||||
else if (*text == '-')
|
||||
match_list = rl_completion_matches(text, _list_args);
|
||||
/* else other args */
|
||||
|
||||
/* No further completion */
|
||||
rl_attempted_completion_over = 1;
|
||||
return match_list;
|
||||
}
|
||||
|
||||
static int _hist_file(char *buffer, size_t size)
|
||||
{
|
||||
char *e = getenv("HOME");
|
||||
|
||||
if (dm_snprintf(buffer, size, "%s/.lvm_history", e) < 0) {
|
||||
log_error("$HOME/.lvm_history: path too long");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void _read_history(struct cmd_context *cmd)
|
||||
{
|
||||
char hist_file[PATH_MAX];
|
||||
|
||||
if (!_hist_file(hist_file, sizeof(hist_file)))
|
||||
return;
|
||||
|
||||
if (read_history(hist_file))
|
||||
log_very_verbose("Couldn't read history from %s.", hist_file);
|
||||
|
||||
stifle_history(find_config_tree_int(cmd, "shell/history_size",
|
||||
DEFAULT_MAX_HISTORY));
|
||||
|
||||
}
|
||||
|
||||
static void _write_history(void)
|
||||
{
|
||||
char hist_file[PATH_MAX];
|
||||
|
||||
if (!_hist_file(hist_file, sizeof(hist_file)))
|
||||
return;
|
||||
|
||||
if (write_history(hist_file))
|
||||
log_very_verbose("Couldn't write history to %s.", hist_file);
|
||||
}
|
||||
|
||||
static int _shell(struct cmd_context *cmd)
|
||||
{
|
||||
int argc, ret;
|
||||
char *input = NULL, *args[MAX_ARGS], **argv;
|
||||
|
||||
rl_readline_name = "lvm";
|
||||
rl_attempted_completion_function = (CPPFunction *) _completion;
|
||||
|
||||
_read_history(cmd);
|
||||
|
||||
_interactive = 1;
|
||||
while (1) {
|
||||
free(input);
|
||||
input = readline("lvm> ");
|
||||
|
||||
/* EOF */
|
||||
if (!input) {
|
||||
printf("\n");
|
||||
break;
|
||||
}
|
||||
|
||||
/* empty line */
|
||||
if (!*input)
|
||||
continue;
|
||||
|
||||
add_history(input);
|
||||
|
||||
argv = args;
|
||||
|
||||
if (lvm_split(input, &argc, argv, MAX_ARGS) == MAX_ARGS) {
|
||||
log_error("Too many arguments, sorry.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(argv[0], "lvm")) {
|
||||
argv++;
|
||||
argc--;
|
||||
}
|
||||
|
||||
if (!argc)
|
||||
continue;
|
||||
|
||||
if (!strcmp(argv[0], "quit") || !strcmp(argv[0], "exit")) {
|
||||
remove_history(history_length - 1);
|
||||
log_error("Exiting.");
|
||||
break;
|
||||
}
|
||||
|
||||
ret = lvm_run_command(cmd, argc, argv);
|
||||
if (ret == ENO_SUCH_CMD)
|
||||
log_error("No such command '%s'. Try 'help'.",
|
||||
argv[0]);
|
||||
|
||||
_write_history();
|
||||
}
|
||||
|
||||
free(input);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Determine whether we should fall back and exec the equivalent LVM1 tool
|
||||
*/
|
||||
@@ -1403,7 +1179,7 @@ int lvm2_main(int argc, char **argv, unsigned is_static)
|
||||
}
|
||||
#ifdef READLINE_SUPPORT
|
||||
if (!alias && argc == 1) {
|
||||
ret = _shell(cmd);
|
||||
ret = lvm_shell(cmd, &_cmdline);
|
||||
goto out;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -29,7 +29,7 @@ int lvrename(struct cmd_context *cmd, int argc, char **argv)
|
||||
struct lv_list *lvl;
|
||||
|
||||
if (argc == 3) {
|
||||
vg_name = skip_dev_dir(cmd, argv[0]);
|
||||
vg_name = skip_dev_dir(cmd, argv[0], NULL);
|
||||
lv_name_old = argv[1];
|
||||
lv_name_new = argv[2];
|
||||
if (strchr(lv_name_old, '/') &&
|
||||
|
||||
@@ -27,7 +27,7 @@ static const char *_extract_lvname(struct cmd_context *cmd, const char *vgname,
|
||||
if (!strchr(arg, '/'))
|
||||
return arg;
|
||||
|
||||
lvname = skip_dev_dir(cmd, arg);
|
||||
lvname = skip_dev_dir(cmd, arg, NULL);
|
||||
while (*lvname == '/')
|
||||
lvname++;
|
||||
if (!strchr(lvname, '/')) {
|
||||
|
||||
@@ -138,6 +138,30 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int _pvs_in_vg(struct cmd_context *cmd, const char *vg_name,
|
||||
struct volume_group *vg, int consistent,
|
||||
void *handle)
|
||||
{
|
||||
if (!vg) {
|
||||
log_error("Volume group %s not found", vg_name);
|
||||
return ECMD_FAILED;
|
||||
}
|
||||
|
||||
return process_each_pv_in_vg(cmd, vg, NULL, handle, &_pvs_single);
|
||||
}
|
||||
|
||||
static int _pvsegs_in_vg(struct cmd_context *cmd, const char *vg_name,
|
||||
struct volume_group *vg, int consistent,
|
||||
void *handle)
|
||||
{
|
||||
if (!vg) {
|
||||
log_error("Volume group %s not found", vg_name);
|
||||
return ECMD_FAILED;
|
||||
}
|
||||
|
||||
return process_each_pv_in_vg(cmd, vg, NULL, handle, &_pvsegs_single);
|
||||
}
|
||||
|
||||
static int _report(struct cmd_context *cmd, int argc, char **argv,
|
||||
report_type_t report_type)
|
||||
{
|
||||
@@ -146,8 +170,8 @@ static int _report(struct cmd_context *cmd, int argc, char **argv,
|
||||
char *str;
|
||||
const char *keys = NULL, *options = NULL, *separator;
|
||||
int r = ECMD_PROCESSED;
|
||||
|
||||
int aligned, buffered, headings;
|
||||
unsigned args_are_pvs;
|
||||
|
||||
aligned = find_config_tree_int(cmd, "report/aligned",
|
||||
DEFAULT_REP_ALIGNED);
|
||||
@@ -158,6 +182,8 @@ static int _report(struct cmd_context *cmd, int argc, char **argv,
|
||||
separator = find_config_tree_str(cmd, "report/separator",
|
||||
DEFAULT_REP_SEPARATOR);
|
||||
|
||||
args_are_pvs = (report_type == PVS || report_type == PVSEGS) ? 1 : 0;
|
||||
|
||||
switch (report_type) {
|
||||
case LVS:
|
||||
keys = find_config_tree_str(cmd, "report/lvs_sort",
|
||||
@@ -260,7 +286,7 @@ static int _report(struct cmd_context *cmd, int argc, char **argv,
|
||||
if (!(report_handle = report_init(cmd, options, keys, &report_type,
|
||||
separator, aligned, buffered,
|
||||
headings)))
|
||||
return 0;
|
||||
return_0;
|
||||
|
||||
/* Ensure options selected are compatible */
|
||||
if (report_type & SEGS)
|
||||
@@ -269,6 +295,7 @@ static int _report(struct cmd_context *cmd, int argc, char **argv,
|
||||
report_type |= PVS;
|
||||
if ((report_type & LVS) && (report_type & PVS)) {
|
||||
log_error("Can't report LV and PV fields at the same time");
|
||||
dm_report_free(report_handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -292,16 +319,24 @@ static int _report(struct cmd_context *cmd, int argc, char **argv,
|
||||
report_handle, &_vgs_single);
|
||||
break;
|
||||
case PVS:
|
||||
r = process_each_pv(cmd, argc, argv, NULL, report_handle,
|
||||
&_pvs_single);
|
||||
if (args_are_pvs)
|
||||
r = process_each_pv(cmd, argc, argv, NULL,
|
||||
report_handle, &_pvs_single);
|
||||
else
|
||||
r = process_each_vg(cmd, argc, argv, LCK_VG_READ, 0,
|
||||
report_handle, &_pvs_in_vg);
|
||||
break;
|
||||
case SEGS:
|
||||
r = process_each_lv(cmd, argc, argv, LCK_VG_READ, report_handle,
|
||||
&_lvsegs_single);
|
||||
break;
|
||||
case PVSEGS:
|
||||
r = process_each_pv(cmd, argc, argv, NULL, report_handle,
|
||||
&_pvsegs_single);
|
||||
if (args_are_pvs)
|
||||
r = process_each_pv(cmd, argc, argv, NULL,
|
||||
report_handle, &_pvsegs_single);
|
||||
else
|
||||
r = process_each_vg(cmd, argc, argv, LCK_VG_READ, 0,
|
||||
report_handle, &_pvsegs_in_vg);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -88,21 +88,53 @@ const char *command_name(struct cmd_context *cmd)
|
||||
/*
|
||||
* Strip dev_dir if present
|
||||
*/
|
||||
char *skip_dev_dir(struct cmd_context *cmd, const char *vg_name)
|
||||
char *skip_dev_dir(struct cmd_context *cmd, const char *vg_name,
|
||||
unsigned *dev_dir_found)
|
||||
{
|
||||
/* FIXME Do this properly */
|
||||
const char *dmdir = dm_dir();
|
||||
size_t dmdir_len = strlen(dmdir), vglv_sz;
|
||||
char *vgname, *lvname, *layer, *vglv;
|
||||
|
||||
/* FIXME Do this properly */
|
||||
if (*vg_name == '/') {
|
||||
while (*vg_name == '/')
|
||||
vg_name++;
|
||||
vg_name--;
|
||||
}
|
||||
|
||||
/* Reformat string if /dev/mapper found */
|
||||
if (!strncmp(vg_name, dmdir, dmdir_len) && vg_name[dmdir_len] == '/') {
|
||||
if (dev_dir_found)
|
||||
*dev_dir_found = 1;
|
||||
vg_name += dmdir_len;
|
||||
while (*vg_name == '/')
|
||||
vg_name++;
|
||||
|
||||
if (!dm_split_lvm_name(cmd->mem, vg_name, &vgname, &lvname, &layer) ||
|
||||
*layer) {
|
||||
log_error("skip_dev_dir: Couldn't split up device name %s",
|
||||
vg_name);
|
||||
return (char *) vg_name;
|
||||
}
|
||||
vglv_sz = strlen(vgname) + strlen(lvname) + 2;
|
||||
if (!(vglv = dm_pool_alloc(cmd->mem, vglv_sz)) ||
|
||||
dm_snprintf(vglv, vglv_sz, "%s%s%s", vgname,
|
||||
*lvname ? "/" : "",
|
||||
lvname) < 0) {
|
||||
log_error("vg/lv string alloc failed");
|
||||
return (char *) vg_name;
|
||||
}
|
||||
return vglv;
|
||||
}
|
||||
|
||||
if (!strncmp(vg_name, cmd->dev_dir, strlen(cmd->dev_dir))) {
|
||||
if (dev_dir_found)
|
||||
*dev_dir_found = 1;
|
||||
vg_name += strlen(cmd->dev_dir);
|
||||
while (*vg_name == '/')
|
||||
vg_name++;
|
||||
}
|
||||
} else if (dev_dir_found)
|
||||
*dev_dir_found = 0;
|
||||
|
||||
return (char *) vg_name;
|
||||
}
|
||||
@@ -222,7 +254,7 @@ int process_each_lv(struct cmd_context *cmd, int argc, char **argv,
|
||||
for (; opt < argc; opt++) {
|
||||
const char *lv_name = argv[opt];
|
||||
char *vgname_def;
|
||||
int dev_dir_found = 0;
|
||||
unsigned dev_dir_found = 0;
|
||||
|
||||
/* Do we have a tag or vgname or lvname? */
|
||||
vgname = lv_name;
|
||||
@@ -243,18 +275,8 @@ int process_each_lv(struct cmd_context *cmd, int argc, char **argv,
|
||||
}
|
||||
|
||||
/* FIXME Jumbled parsing */
|
||||
if (*vgname == '/') {
|
||||
while (*vgname == '/')
|
||||
vgname++;
|
||||
vgname--;
|
||||
}
|
||||
if (!strncmp(vgname, cmd->dev_dir,
|
||||
strlen(cmd->dev_dir))) {
|
||||
vgname += strlen(cmd->dev_dir);
|
||||
dev_dir_found = 1;
|
||||
while (*vgname == '/')
|
||||
vgname++;
|
||||
}
|
||||
vgname = skip_dev_dir(cmd, vgname, &dev_dir_found);
|
||||
|
||||
if (*vgname == '/') {
|
||||
log_error("\"%s\": Invalid path for Logical "
|
||||
"Volume", argv[opt]);
|
||||
@@ -528,7 +550,7 @@ int process_each_vg(struct cmd_context *cmd, int argc, char **argv,
|
||||
continue;
|
||||
}
|
||||
|
||||
vg_name = skip_dev_dir(cmd, vg_name);
|
||||
vg_name = skip_dev_dir(cmd, vg_name, NULL);
|
||||
if (strchr(vg_name, '/')) {
|
||||
log_error("Invalid volume group name: %s",
|
||||
vg_name);
|
||||
@@ -830,7 +852,7 @@ char *default_vgname(struct cmd_context *cmd)
|
||||
if (!vg_path)
|
||||
return 0;
|
||||
|
||||
vg_path = skip_dev_dir(cmd, vg_path);
|
||||
vg_path = skip_dev_dir(cmd, vg_path, NULL);
|
||||
|
||||
if (strchr(vg_path, '/')) {
|
||||
log_error("Environment Volume Group in LVM_VG_NAME invalid: "
|
||||
|
||||
@@ -76,7 +76,8 @@ int process_each_lv_in_vg(struct cmd_context *cmd, struct volume_group *vg,
|
||||
|
||||
char *default_vgname(struct cmd_context *cmd);
|
||||
const char *extract_vgname(struct cmd_context *cmd, const char *lv_name);
|
||||
char *skip_dev_dir(struct cmd_context *cmd, const char *vg_name);
|
||||
char *skip_dev_dir(struct cmd_context *cmd, const char *vg_name,
|
||||
unsigned *dev_dir_found);
|
||||
|
||||
/*
|
||||
* Builds a list of pv's from the names in argv. Used in
|
||||
|
||||
@@ -24,7 +24,7 @@ int vgcfgrestore(struct cmd_context *cmd, int argc, char **argv)
|
||||
return ECMD_FAILED;
|
||||
}
|
||||
|
||||
vg_name = skip_dev_dir(cmd, argv[0]);
|
||||
vg_name = skip_dev_dir(cmd, argv[0], NULL);
|
||||
|
||||
if (!validate_name(vg_name)) {
|
||||
log_error("Volume group name \"%s\" is invalid", vg_name);
|
||||
|
||||
@@ -38,7 +38,7 @@ int vgcreate(struct cmd_context *cmd, int argc, char **argv)
|
||||
return EINVALID_CMD_LINE;
|
||||
}
|
||||
|
||||
vg_name = skip_dev_dir(cmd, argv[0]);
|
||||
vg_name = skip_dev_dir(cmd, argv[0], NULL);
|
||||
max_lv = arg_uint_value(cmd, maxlogicalvolumes_ARG, 0);
|
||||
max_pv = arg_uint_value(cmd, maxphysicalvolumes_ARG, 0);
|
||||
alloc = arg_uint_value(cmd, alloc_ARG, ALLOC_NORMAL);
|
||||
|
||||
@@ -32,7 +32,7 @@ int vgextend(struct cmd_context *cmd, int argc, char **argv)
|
||||
return EINVALID_CMD_LINE;
|
||||
}
|
||||
|
||||
vg_name = skip_dev_dir(cmd, argv[0]);
|
||||
vg_name = skip_dev_dir(cmd, argv[0], NULL);
|
||||
argc--;
|
||||
argv++;
|
||||
|
||||
@@ -41,6 +41,12 @@ int vgextend(struct cmd_context *cmd, int argc, char **argv)
|
||||
return ECMD_FAILED;
|
||||
}
|
||||
|
||||
if (!validate_name(vg_name)) {
|
||||
log_error("Volume group name \"%s\" is invalid",
|
||||
vg_name);
|
||||
return ECMD_FAILED;
|
||||
}
|
||||
|
||||
log_verbose("Checking for volume group \"%s\"", vg_name);
|
||||
if (!lock_vol(cmd, vg_name, LCK_VG_WRITE | LCK_NONBLOCK)) {
|
||||
unlock_vg(cmd, ORPHAN);
|
||||
|
||||
@@ -253,12 +253,12 @@ int vgmerge(struct cmd_context *cmd, int argc, char **argv)
|
||||
return EINVALID_CMD_LINE;
|
||||
}
|
||||
|
||||
vg_name_to = skip_dev_dir(cmd, argv[0]);
|
||||
vg_name_to = skip_dev_dir(cmd, argv[0], NULL);
|
||||
argc--;
|
||||
argv++;
|
||||
|
||||
for (; opt < argc; opt++) {
|
||||
vg_name_from = skip_dev_dir(cmd, argv[opt]);
|
||||
vg_name_from = skip_dev_dir(cmd, argv[opt], NULL);
|
||||
|
||||
ret = _vgmerge_single(cmd, vg_name_to, vg_name_from);
|
||||
if (ret > ret_max)
|
||||
|
||||
@@ -427,13 +427,13 @@ int vgreduce(struct cmd_context *cmd, int argc, char **argv)
|
||||
int ret = 1;
|
||||
int consistent = 1;
|
||||
|
||||
if (!argc & !arg_count(cmd, removemissing_ARG)) {
|
||||
if (!argc && !arg_count(cmd, removemissing_ARG)) {
|
||||
log_error("Please give volume group name and "
|
||||
"physical volume paths");
|
||||
return EINVALID_CMD_LINE;
|
||||
}
|
||||
|
||||
if (!argc & arg_count(cmd, removemissing_ARG)) {
|
||||
if (!argc && arg_count(cmd, removemissing_ARG)) {
|
||||
log_error("Please give volume group name");
|
||||
return EINVALID_CMD_LINE;
|
||||
}
|
||||
@@ -461,10 +461,16 @@ int vgreduce(struct cmd_context *cmd, int argc, char **argv)
|
||||
return EINVALID_CMD_LINE;
|
||||
}
|
||||
|
||||
vg_name = skip_dev_dir(cmd, argv[0]);
|
||||
vg_name = skip_dev_dir(cmd, argv[0], NULL);
|
||||
argv++;
|
||||
argc--;
|
||||
|
||||
if (!validate_name(vg_name)) {
|
||||
log_error("Volume group name \"%s\" is invalid",
|
||||
vg_name);
|
||||
return ECMD_FAILED;
|
||||
}
|
||||
|
||||
log_verbose("Finding volume group \"%s\"", vg_name);
|
||||
if (!lock_vol(cmd, vg_name, LCK_VG_WRITE)) {
|
||||
log_error("Can't get lock for %s", vg_name);
|
||||
|
||||
@@ -87,6 +87,11 @@ int vgremove(struct cmd_context *cmd, int argc, char **argv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!argc) {
|
||||
log_error("Please enter one or more volume group paths");
|
||||
return EINVALID_CMD_LINE;
|
||||
}
|
||||
|
||||
if (!lock_vol(cmd, ORPHAN, LCK_VG_WRITE)) {
|
||||
log_error("Can't get lock for orphan PVs");
|
||||
return ECMD_FAILED;
|
||||
|
||||
@@ -35,8 +35,8 @@ int vgrename(struct cmd_context *cmd, int argc, char **argv)
|
||||
return EINVALID_CMD_LINE;
|
||||
}
|
||||
|
||||
vg_name_old = skip_dev_dir(cmd, argv[0]);
|
||||
vg_name_new = skip_dev_dir(cmd, argv[1]);
|
||||
vg_name_old = skip_dev_dir(cmd, argv[0], NULL);
|
||||
vg_name_new = skip_dev_dir(cmd, argv[1], NULL);
|
||||
|
||||
dev_dir = cmd->dev_dir;
|
||||
length = strlen(dev_dir);
|
||||
@@ -118,7 +118,7 @@ int vgrename(struct cmd_context *cmd, int argc, char **argv)
|
||||
return ECMD_FAILED;
|
||||
}
|
||||
|
||||
if (lvs_in_vg_activated(vg_old)) {
|
||||
if (lvs_in_vg_activated_by_uuid_only(vg_old)) {
|
||||
unlock_vg(cmd, vg_name_old);
|
||||
log_error("Volume group \"%s\" still has active LVs",
|
||||
vg_name_old);
|
||||
|
||||
@@ -72,6 +72,9 @@ static int _move_lvs(struct volume_group *vg_from, struct volume_group *vg_to)
|
||||
if ((lv->status & SNAPSHOT))
|
||||
continue;
|
||||
|
||||
if ((lv->status & MIRRORED))
|
||||
continue;
|
||||
|
||||
/* Ensure all the PVs used by this LV remain in the same */
|
||||
/* VG as each other */
|
||||
vg_with = NULL;
|
||||
@@ -161,6 +164,48 @@ static int _move_snapshots(struct volume_group *vg_from,
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _move_mirrors(struct volume_group *vg_from,
|
||||
struct volume_group *vg_to)
|
||||
{
|
||||
struct list *lvh, *lvht;
|
||||
struct logical_volume *lv;
|
||||
struct lv_segment *seg;
|
||||
int i, seg_in, log_in;
|
||||
|
||||
list_iterate_safe(lvh, lvht, &vg_from->lvs) {
|
||||
lv = list_item(lvh, struct lv_list)->lv;
|
||||
|
||||
if (!(lv->status & MIRRORED))
|
||||
continue;
|
||||
|
||||
seg = first_seg(lv);
|
||||
|
||||
seg_in = 0;
|
||||
for (i = 0; i < seg->area_count; i++)
|
||||
if (_lv_is_in_vg(vg_to, seg_lv(seg, i)))
|
||||
seg_in++;
|
||||
|
||||
log_in = (!seg->log_lv || _lv_is_in_vg(vg_to, seg->log_lv));
|
||||
|
||||
if ((seg_in && seg_in < seg->area_count) ||
|
||||
(seg_in && seg->log_lv && !log_in) ||
|
||||
(!seg_in && seg->log_lv && log_in)) {
|
||||
log_error("Mirror %s split", lv->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (seg_in == seg->area_count && log_in) {
|
||||
list_del(lvh);
|
||||
list_add(&vg_to->lvs, lvh);
|
||||
|
||||
vg_from->lv_count--;
|
||||
vg_to->lv_count++;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int vgsplit(struct cmd_context *cmd, int argc, char **argv)
|
||||
{
|
||||
char *vg_name_from, *vg_name_to;
|
||||
@@ -174,11 +219,17 @@ int vgsplit(struct cmd_context *cmd, int argc, char **argv)
|
||||
return EINVALID_CMD_LINE;
|
||||
}
|
||||
|
||||
vg_name_from = argv[0];
|
||||
vg_name_to = argv[1];
|
||||
vg_name_from = skip_dev_dir(cmd, argv[0], NULL);
|
||||
vg_name_to = skip_dev_dir(cmd, argv[1], NULL);
|
||||
argc -= 2;
|
||||
argv += 2;
|
||||
|
||||
if (!validate_name(vg_name_from)) {
|
||||
log_error("Volume group name \"%s\" is invalid",
|
||||
vg_name_from);
|
||||
return ECMD_FAILED;
|
||||
}
|
||||
|
||||
if (!strcmp(vg_name_to, vg_name_from)) {
|
||||
log_error("Duplicate volume group name \"%s\"", vg_name_from);
|
||||
return ECMD_FAILED;
|
||||
@@ -275,6 +326,10 @@ int vgsplit(struct cmd_context *cmd, int argc, char **argv)
|
||||
if (!(_move_snapshots(vg_from, vg_to)))
|
||||
goto error;
|
||||
|
||||
/* Move required mirrors across */
|
||||
if (!(_move_mirrors(vg_from, vg_to)))
|
||||
goto error;
|
||||
|
||||
/* FIXME Split mdas properly somehow too! */
|
||||
/* Currently we cheat by sharing the format instance and relying on
|
||||
* vg_write to ignore mdas outside the VG! Done this way, with text
|
||||
|
||||
Reference in New Issue
Block a user