1
0
mirror of git://sourceware.org/git/lvm2.git synced 2024-12-22 17:35:59 +03:00
lvm2/lib/commands/toolcontext.c

2080 lines
53 KiB
C
Raw Normal View History

/*
2008-01-30 17:00:02 +03:00
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
* Copyright (C) 2004-2014 Red Hat, Inc. All rights reserved.
2002-11-18 17:01:16 +03:00
*
2004-03-30 23:35:44 +04:00
* This file is part of LVM2.
*
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License v.2.1.
2004-03-30 23:35:44 +04:00
*
* You should have received a copy of the GNU Lesser General Public License
2004-03-30 23:35:44 +04:00
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
2002-11-18 17:01:16 +03:00
*/
#include "base/memory/zalloc.h"
#include "lib/misc/lib.h"
#include "lib/commands/toolcontext.h"
#include "lib/metadata/metadata.h"
#include "lib/config/defaults.h"
#include "lib/misc/lvm-string.h"
#include "lib/activate/activate.h"
#include "lib/filters/filter.h"
#include "lib/label/label.h"
#include "lib/label/hints.h"
#include "lib/misc/lvm-file.h"
#include "lib/format_text/format-text.h"
#include "lib/display/display.h"
#include "lib/mm/memlock.h"
#include "lib/datastruct/str_list.h"
#include "lib/metadata/segtype.h"
#include "lib/cache/lvmcache.h"
#include "lib/format_text/archiver.h"
#include "lib/lvmpolld/lvmpolld-client.h"
device usage based on devices file The LVM devices file lists devices that lvm can use. The default file is /etc/lvm/devices/system.devices, and the lvmdevices(8) command is used to add or remove device entries. If the file does not exist, or if lvm.conf includes use_devicesfile=0, then lvm will not use a devices file. When the devices file is in use, the regex filter is not used, and the filter settings in lvm.conf or on the command line are ignored. LVM records devices in the devices file using hardware-specific IDs, such as the WWID, and attempts to use subsystem-specific IDs for virtual device types. These device IDs are also written in the VG metadata. When no hardware or virtual ID is available, lvm falls back using the unstable device name as the device ID. When devnames are used, lvm performs extra scanning to find devices if their devname changes, e.g. after reboot. When proper device IDs are used, an lvm command will not look at devices outside the devices file, but when devnames are used as a fallback, lvm will scan devices outside the devices file to locate PVs on renamed devices. A config setting search_for_devnames can be used to control the scanning for renamed devname entries. Related to the devices file, the new command option --devices <devnames> allows a list of devices to be specified for the command to use, overriding the devices file. The listed devices act as a sort of devices file in terms of limiting which devices lvm will see and use. Devices that are not listed will appear to be missing to the lvm command. Multiple devices files can be kept in /etc/lvm/devices, which allows lvm to be used with different sets of devices, e.g. system devices do not need to be exposed to a specific application, and the application can use lvm on its own set of devices that are not exposed to the system. The option --devicesfile <filename> is used to select the devices file to use with the command. Without the option set, the default system devices file is used. Setting --devicesfile "" causes lvm to not use a devices file. An existing, empty devices file means lvm will see no devices. The new command vgimportdevices adds PVs from a VG to the devices file and updates the VG metadata to include the device IDs. vgimportdevices -a will import all VGs into the system devices file. LVM commands run by dmeventd not use a devices file by default, and will look at all devices on the system. A devices file can be created for dmeventd (/etc/lvm/devices/dmeventd.devices) If this file exists, lvm commands run by dmeventd will use it. Internal implementaion: - device_ids_read - read the devices file . add struct dev_use (du) to cmd->use_devices for each devices file entry - dev_cache_scan - get /dev entries . add struct device (dev) to dev_cache for each device on the system - device_ids_match - match devices file entries to /dev entries . match each du on cmd->use_devices to a dev in dev_cache, using device ID . on match, set du->dev, dev->id, dev->flags MATCHED_USE_ID - label_scan - read lvm headers and metadata from devices . filters are applied, those that do not need data from the device . filter-deviceid skips devs without MATCHED_USE_ID, i.e. skips /dev entries that are not listed in the devices file . read lvm label from dev . filters are applied, those that use data from the device . read lvm metadata from dev . add info/vginfo structs for PVs/VGs (info is "lvmcache") - device_ids_find_renamed_devs - handle devices with unstable devname ID where devname changed . this step only needed when devs do not have proper device IDs, and their dev names change, e.g. after reboot sdb becomes sdc. . detect incorrect match because PVID in the devices file entry does not match the PVID found when the device was read above . undo incorrect match between du and dev above . search system devices for new location of PVID . update devices file with new devnames for PVIDs on renamed devices . label_scan the renamed devs - continue with command processing
2020-06-23 21:25:41 +03:00
#include "lib/device/device_id.h"
2002-11-18 17:01:16 +03:00
#include <locale.h>
#include <sys/stat.h>
toolcontext: do not change stream for pthreaded programs With pthreaded daemons like 'dmeventd' using liblvm via plugin, lvm2 actually should not 'play' with streams at all - as there could be parallel outputs running. As a current quick workaround just disable change for pthreaded program (gettid() != getpid()). TODO: it's possible the change of buffering actually doesn't serve us any measurable benefit and could be dropped as whole later... Meanwhile this patch is fixing this occasional valgrind race report: Invalid read of size 4 at 0x571892C: vfprintf (in /usr/lib64/libc-2.26.9000.so) by 0x57216B3: fprintf (in /usr/lib64/libc-2.26.9000.so) by 0x5042886: dm_event_log (libdevmapper-event.c:925) by 0x10B015: _dmeventd_log (dmeventd.c:125) by 0x10D289: _unregister_for_event (dmeventd.c:1146) by 0x10E52E: _handle_request (dmeventd.c:1583) by 0x10E6D7: _do_process_request (dmeventd.c:1631) by 0x10E7C6: _process_request (dmeventd.c:1660) by 0x1101A4: main (dmeventd.c:2285) Address 0x6264d30 is 192 bytes inside a block of size 552 free'd at 0x4C2ED68: free (vg_replace_malloc.c:530) by 0x573907D: fclose@@GLIBC_2.2.5 (in /usr/lib64/libc-2.26.9000.so) by 0x6AC5C00: reopen_standard_stream (log.c:189) by 0x6A8E62C: destroy_toolcontext (toolcontext.c:2271) by 0x6BA5C22: lvm_fin (lvmcmdline.c:3339) by 0x6BD5EF3: lvm2_exit (lvmcmdlib.c:123) by 0x6856013: dmeventd_lvm2_exit (dmeventd_lvm.c:103) by 0x66535B8: unregister_device (dmeventd_thin.c:432) by 0x10CBBC: _do_unregister_device (dmeventd.c:926) by 0x10CD74: _monitor_unregister (dmeventd.c:979) by 0x10D094: _monitor_thread (dmeventd.c:1066) by 0x54B35E0: start_thread (in /usr/lib64/libpthread-2.26.9000.so) by 0x57C30EE: clone (in /usr/lib64/libc-2.26.9000.so) Block was alloc'd at at 0x4C2DBBB: malloc (vg_replace_malloc.c:299) by 0x573932B: fdopen@@GLIBC_2.2.5 (in /usr/lib64/libc-2.26.9000.so) by 0x6AC5DC2: reopen_standard_stream (log.c:200) by 0x6A8D11D: create_toolcontext (toolcontext.c:1898) by 0x6BA5B6B: init_lvm (lvmcmdline.c:3319) by 0x6BD5BC8: cmdlib_lvm2_init (lvmcmdlib.c:34) by 0x6BD5F04: lvm2_init (lvm2cmd.c:20) by 0x6855EA7: dmeventd_lvm2_init (dmeventd_lvm.c:67) by 0x665305F: register_device (dmeventd_thin.c:352) by 0x10CB7A: _do_register_device (dmeventd.c:916) by 0x10CEE4: _monitor_thread (dmeventd.c:1006) by 0x54B35E0: start_thread (in /usr/lib64/libpthread-2.26.9000.so) by 0x57C30EE: clone (in /usr/lib64/libc-2.26.9000.so) .... Process terminating with default action of signal 6 (SIGABRT): dumping core at 0x570016B: raise (in /usr/lib64/libc-2.26.9000.so) by 0x5701520: abort (in /usr/lib64/libc-2.26.9000.so) by 0x57437D8: __libc_message (in /usr/lib64/libc-2.26.9000.so) by 0x5743831: __libc_fatal (in /usr/lib64/libc-2.26.9000.so) by 0x5744056: _IO_vtable_check (in /usr/lib64/libc-2.26.9000.so) by 0x574751C: __overflow (in /usr/lib64/libc-2.26.9000.so) by 0x574191A: fputc (in /usr/lib64/libc-2.26.9000.so) by 0x50428E3: dm_event_log (libdevmapper-event.c:934) by 0x10B015: _dmeventd_log (dmeventd.c:125) by 0x10D289: _unregister_for_event (dmeventd.c:1146) by 0x10E52E: _handle_request (dmeventd.c:1583) by 0x10E6D7: _do_process_request (dmeventd.c:1631) by 0x10E7C6: _process_request (dmeventd.c:1660) by 0x1101A4: main (dmeventd.c:2285)
2018-02-09 02:26:22 +03:00
#include <sys/syscall.h>
2004-03-08 20:25:59 +03:00
#include <sys/utsname.h>
2002-11-18 17:01:16 +03:00
#include <syslog.h>
#include <time.h>
#ifdef APP_MACHINEID_SUPPORT
#include <systemd/sd-id128.h>
#endif
#ifdef __linux__
# include <malloc.h>
#endif
static const size_t _linebuffer_size = 4096;
/*
* Copy the input string, removing invalid characters.
*/
const char *system_id_from_string(struct cmd_context *cmd, const char *str)
{
char *system_id;
if (!str || !*str) {
log_warn("WARNING: Empty system ID supplied.");
return "";
}
if (!(system_id = dm_pool_zalloc(cmd->libmem, strlen(str) + 1))) {
log_warn("WARNING: Failed to allocate system ID.");
return NULL;
}
copy_systemid_chars(str, system_id);
if (!*system_id) {
log_warn("WARNING: Invalid system ID format: %s", str);
return NULL;
}
if (!strncmp(system_id, "localhost", 9)) {
log_warn("WARNING: system ID may not begin with the string \"localhost\".");
return NULL;
}
return system_id;
}
static const char *_read_system_id_from_file(struct cmd_context *cmd, const char *file)
{
char *line = NULL;
size_t line_size;
char *start, *end;
const char *system_id = NULL;
FILE *fp;
if (!file || !strlen(file) || !file[0])
return_NULL;
if (!(fp = fopen(file, "r"))) {
log_warn("WARNING: %s: fopen failed: %s", file, strerror(errno));
return NULL;
}
while (getline(&line, &line_size, fp) > 0) {
start = line;
/* Ignore leading whitespace */
while (*start && isspace(*start))
start++;
/* Ignore rest of line after # */
if (!*start || *start == '#')
continue;
if (system_id && *system_id) {
log_warn("WARNING: Ignoring extra line(s) in system ID file %s.", file);
break;
}
/* Remove any comments from end of line */
for (end = start; *end; end++)
if (*end == '#') {
*end = '\0';
break;
}
system_id = system_id_from_string(cmd, start);
}
free(line);
if (fclose(fp))
stack;
return system_id;
}
/* systemd-id128 new produced: f64406832c2140e8ac5422d1089aae03 */
#define LVM_APPLICATION_ID SD_ID128_MAKE(f6,44,06,83,2c,21,40,e8,ac,54,22,d1,08,9a,ae,03)
static const char *_system_id_from_source(struct cmd_context *cmd, const char *source)
{
char buf[PATH_MAX];
const char *file;
const char *etc_str;
const char *str;
const char *system_id = NULL;
if (!strcasecmp(source, "uname")) {
if (cmd->hostname)
system_id = system_id_from_string(cmd, cmd->hostname);
goto out;
}
/* lvm.conf and lvmlocal.conf are merged into one config tree */
if (!strcasecmp(source, "lvmlocal")) {
if ((str = find_config_tree_str(cmd, local_system_id_CFG, NULL)))
system_id = system_id_from_string(cmd, str);
goto out;
}
#ifdef APP_MACHINEID_SUPPORT
if (!strcasecmp(source, "appmachineid")) {
sd_id128_t id = { 0 };
if (sd_id128_get_machine_app_specific(LVM_APPLICATION_ID, &id) != 0)
log_warn("WARNING: sd_id128_get_machine_app_specific() failed %s (%d).",
strerror(errno), errno);
if (dm_snprintf(buf, PATH_MAX, SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(id)) < 0)
stack;
system_id = system_id_from_string(cmd, buf);
goto out;
}
#endif
if (!strcasecmp(source, "machineid") || !strcasecmp(source, "machine-id")) {
etc_str = find_config_tree_str(cmd, global_etc_CFG, NULL);
if (dm_snprintf(buf, sizeof(buf), "%s/machine-id", etc_str) != -1)
system_id = _read_system_id_from_file(cmd, buf);
goto out;
}
if (!strcasecmp(source, "file")) {
file = find_config_tree_str(cmd, global_system_id_file_CFG, NULL);
system_id = _read_system_id_from_file(cmd, file);
goto out;
}
log_warn("WARNING: Unrecognised system_id_source \"%s\".", source);
out:
return system_id;
}
2002-11-18 17:01:16 +03:00
static int _get_env_vars(struct cmd_context *cmd)
{
const char *e;
/* Set to "" to avoid using any system directory */
if ((e = getenv("LVM_SYSTEM_DIR"))) {
if (dm_snprintf(cmd->system_dir, sizeof(cmd->system_dir),
2002-11-18 17:01:16 +03:00
"%s", e) < 0) {
log_error("LVM_SYSTEM_DIR environment variable "
"is too long.");
return 0;
}
}
if (strcmp((getenv("LVM_RUN_BY_DMEVENTD") ? : "0"), "1") == 0)
init_run_by_dmeventd(cmd);
2002-11-18 17:01:16 +03:00
return 1;
}
static void _get_sysfs_dir(struct cmd_context *cmd, char *buf, size_t buf_size)
{
static char proc_mounts[PATH_MAX];
static char *split[4], buffer[PATH_MAX + 16];
FILE *fp;
char *sys_mnt = NULL;
*buf = '\0';
if (!*cmd->proc_dir) {
log_debug("No proc filesystem found: skipping sysfs detection");
return;
}
if (dm_snprintf(proc_mounts, sizeof(proc_mounts),
"%s/mounts", cmd->proc_dir) < 0) {
log_error("Failed to create /proc/mounts string for sysfs detection");
return;
}
if (!(fp = fopen(proc_mounts, "r"))) {
log_sys_error("_get_sysfs_dir fopen", proc_mounts);
return;
}
while (fgets(buffer, sizeof(buffer), fp)) {
if (dm_split_words(buffer, 4, 0, split) == 4 &&
!strcmp(split[2], "sysfs")) {
sys_mnt = split[1];
break;
}
}
if (fclose(fp))
log_sys_error("fclose", proc_mounts);
if (!sys_mnt) {
log_error("Failed to find sysfs mount point");
return;
}
2021-03-19 01:08:08 +03:00
(void) dm_strncpy(buf, sys_mnt, buf_size);
}
static uint32_t _parse_debug_fields(struct cmd_context *cmd, int cfg, const char *cfgname)
{
const struct dm_config_node *cn;
const struct dm_config_value *cv;
uint32_t debug_fields = 0;
if (!(cn = find_config_tree_array(cmd, cfg, NULL))) {
log_error(INTERNAL_ERROR "Unable to find configuration for log/%s.", cfgname);
return 0;
}
for (cv = cn->v; cv; cv = cv->next) {
if (cv->type != DM_CFG_STRING) {
log_verbose("log/%s contains a value which is not a string. Ignoring.", cfgname);
continue;
}
if (!strcasecmp(cv->v.str, "all"))
return 0;
if (!strcasecmp(cv->v.str, "time"))
debug_fields |= LOG_DEBUG_FIELD_TIME;
else if (!strcasecmp(cv->v.str, "command"))
debug_fields |= LOG_DEBUG_FIELD_COMMAND;
else if (!strcasecmp(cv->v.str, "fileline"))
debug_fields |= LOG_DEBUG_FIELD_FILELINE;
else if (!strcasecmp(cv->v.str, "message"))
debug_fields |= LOG_DEBUG_FIELD_MESSAGE;
else
log_verbose("Unrecognised value for log/%s: %s", cfgname, cv->v.str);
}
return debug_fields;
}
static int _parse_debug_classes(struct cmd_context *cmd)
{
const struct dm_config_node *cn;
const struct dm_config_value *cv;
int debug_classes = 0;
if (!(cn = find_config_tree_array(cmd, log_debug_classes_CFG, NULL))) {
log_error(INTERNAL_ERROR "Unable to find configuration for log/debug_classes.");
return -1;
}
for (cv = cn->v; cv; cv = cv->next) {
if (cv->type != DM_CFG_STRING) {
log_verbose("log/debug_classes contains a value "
"which is not a string. Ignoring.");
continue;
}
if (!strcasecmp(cv->v.str, "all"))
return -1;
if (!strcasecmp(cv->v.str, "memory"))
debug_classes |= LOG_CLASS_MEM;
else if (!strcasecmp(cv->v.str, "devices"))
debug_classes |= LOG_CLASS_DEVS;
else if (!strcasecmp(cv->v.str, "activation"))
debug_classes |= LOG_CLASS_ACTIVATION;
else if (!strcasecmp(cv->v.str, "allocation"))
debug_classes |= LOG_CLASS_ALLOC;
else if (!strcasecmp(cv->v.str, "metadata"))
debug_classes |= LOG_CLASS_METADATA;
else if (!strcasecmp(cv->v.str, "cache"))
debug_classes |= LOG_CLASS_CACHE;
else if (!strcasecmp(cv->v.str, "locking"))
debug_classes |= LOG_CLASS_LOCKING;
else if (!strcasecmp(cv->v.str, "lvmpolld"))
debug_classes |= LOG_CLASS_LVMPOLLD;
else if (!strcasecmp(cv->v.str, "dbus"))
debug_classes |= LOG_CLASS_DBUS;
2017-11-15 03:59:30 +03:00
else if (!strcasecmp(cv->v.str, "io"))
debug_classes |= LOG_CLASS_IO;
else
log_verbose("Unrecognised value for log/debug_classes: %s", cv->v.str);
}
return debug_classes;
}
static uint32_t _parse_log_journal(struct cmd_context *cmd, int cfg, const char *cfgname)
{
const struct dm_config_node *cn;
const struct dm_config_value *cv;
uint32_t fields = 0;
uint32_t val;
if (!(cn = find_config_tree_array(cmd, cfg, NULL))) {
log_debug("Unable to find configuration for log/%s.", cfgname);
return 0;
}
for (cv = cn->v; cv; cv = cv->next) {
if (cv->type != DM_CFG_STRING) {
log_verbose("log/%s contains a value which is not a string. Ignoring.", cfgname);
continue;
}
if ((val = log_journal_str_to_val(cv->v.str)))
fields |= val;
else
log_verbose("Unrecognised value for log/%s: %s", cfgname, cv->v.str);
}
return fields;
}
2002-11-18 17:01:16 +03:00
static void _init_logging(struct cmd_context *cmd)
{
int append = 1;
2002-11-18 17:01:16 +03:00
time_t t;
const char *log_file;
char timebuf[26];
2002-11-18 17:01:16 +03:00
/* Syslog */
cmd->default_settings.syslog = find_config_tree_bool(cmd, log_syslog_CFG, NULL);
if (cmd->default_settings.syslog)
init_syslog(1, DEFAULT_LOG_FACILITY);
else
2002-11-18 17:01:16 +03:00
fin_syslog();
/* Debug level for log file output */
cmd->default_settings.debug = find_config_tree_int(cmd, log_level_CFG, NULL);
2002-11-18 17:01:16 +03:00
init_debug(cmd->default_settings.debug);
/*
* Suppress all non-essential stdout?
* -qq can override the default of 0 to 1 later.
* Once set to 1, there is no facility to change it back to 0.
*/
cmd->default_settings.silent = silent_mode() ? :
find_config_tree_bool(cmd, log_silent_CFG, NULL);
init_silent(cmd->default_settings.silent);
2002-11-18 17:01:16 +03:00
/* Verbose level for tty output */
cmd->default_settings.verbose = find_config_tree_int(cmd, log_verbose_CFG, NULL);
2004-03-26 14:45:01 +03:00
init_verbose(cmd->default_settings.verbose + VERBOSE_BASE_LEVEL);
2002-11-18 17:01:16 +03:00
/* Log message formatting */
init_indent(find_config_tree_bool(cmd, log_indent_CFG, NULL));
init_abort_on_internal_errors(find_config_tree_bool(cmd, global_abort_on_internal_errors_CFG, NULL));
cmd->default_settings.msg_prefix = find_config_tree_str_allow_empty(cmd, log_prefix_CFG, NULL);
2002-11-18 17:01:16 +03:00
init_msg_prefix(cmd->default_settings.msg_prefix);
/* so that file and verbose output have a command prefix */
init_log_command(0, 0);
2002-11-18 17:01:16 +03:00
/* Test mode */
cmd->default_settings.test =
find_config_tree_bool(cmd, global_test_CFG, NULL);
init_test(cmd->default_settings.test);
2002-11-18 17:01:16 +03:00
/* Settings for logging to file */
if (find_config_tree_bool(cmd, log_overwrite_CFG, NULL))
append = 0;
2002-11-18 17:01:16 +03:00
log_file = find_config_tree_str(cmd, log_file_CFG, NULL);
2004-03-08 21:13:22 +03:00
if (log_file) {
fin_log();
init_log_file(log_file, append);
2004-03-08 21:13:22 +03:00
}
init_log_while_suspended(find_config_tree_bool(cmd, log_activation_CFG, NULL));
2002-11-18 17:01:16 +03:00
cmd->default_settings.debug_classes = _parse_debug_classes(cmd);
log_debug("Setting log debug classes to %d", cmd->default_settings.debug_classes);
init_debug_classes_logged(cmd->default_settings.debug_classes);
init_debug_file_fields(_parse_debug_fields(cmd, log_debug_file_fields_CFG, "debug_file_fields"));
init_debug_output_fields(_parse_debug_fields(cmd, log_debug_output_fields_CFG, "debug_output_fields"));
cmd->default_settings.journal = _parse_log_journal(cmd, log_journal_CFG, "journal");
init_log_journal(cmd->default_settings.journal);
2002-11-18 17:01:16 +03:00
t = time(NULL);
ctime_r(&t, &timebuf[0]);
timebuf[24] = '\0';
log_verbose("Logging initialised at %s", timebuf);
2002-11-18 17:01:16 +03:00
/* Tell device-mapper about our logging */
#ifdef DEVMAPPER_SUPPORT
if (!dm_log_is_non_default())
dm_log_with_errno_init(print_log_libdm);
#endif
reset_log_duplicated();
reset_lvm_errno(1);
2002-11-18 17:01:16 +03:00
}
devices: rework libudev usage related to config settings: obtain_device_info_from_udev (controls if lvm gets a list of devices from readdir /dev or from libudev) external_device_info_source (controls if lvm asks libudev for device information) . Make the obtain_device_list_from_udev setting affect only the choice of readdir /dev vs libudev. The setting no longer controls if udev is used for device type checks. . Change obtain_device_list_from_udev default to 0. This helps avoid boot timeouts due to slow libudev queries, avoids reported failures from udev_enumerate_scan_devices, and avoids delays from "device not initialized in udev database" errors. Even without errors, for a system booting with 1024 PVs, lvm2-pvscan times improve from about 100 sec to 15 sec, and the pvscan command from about 64 sec to about 4 sec. . For external_device_info_source="none", remove all libudev device info queries, and use only lvm native device info. . For external_device_info_source="udev", first check lvm native device info, then check libudev info. . Remove sleep/retry loop when attempting libudev queries for device info. udev info will simply be skipped if it's not immediately available. . Only set up a libdev connection if it will be used by obtain_device_list_from_udev/external_device_info_source. . For native multipath component detection, use /etc/multipath/wwids. If a device has a wwid matching an entry in the wwids file, then it's considered a multipath component. This is necessary to natively detect multipath components when the mpath device is not set up.
2021-06-09 01:12:09 +03:00
static int _check_disable_udev(const char *msg)
{
if (getenv("DM_DISABLE_UDEV")) {
devices: rework libudev usage related to config settings: obtain_device_info_from_udev (controls if lvm gets a list of devices from readdir /dev or from libudev) external_device_info_source (controls if lvm asks libudev for device information) . Make the obtain_device_list_from_udev setting affect only the choice of readdir /dev vs libudev. The setting no longer controls if udev is used for device type checks. . Change obtain_device_list_from_udev default to 0. This helps avoid boot timeouts due to slow libudev queries, avoids reported failures from udev_enumerate_scan_devices, and avoids delays from "device not initialized in udev database" errors. Even without errors, for a system booting with 1024 PVs, lvm2-pvscan times improve from about 100 sec to 15 sec, and the pvscan command from about 64 sec to about 4 sec. . For external_device_info_source="none", remove all libudev device info queries, and use only lvm native device info. . For external_device_info_source="udev", first check lvm native device info, then check libudev info. . Remove sleep/retry loop when attempting libudev queries for device info. udev info will simply be skipped if it's not immediately available. . Only set up a libdev connection if it will be used by obtain_device_list_from_udev/external_device_info_source. . For native multipath component detection, use /etc/multipath/wwids. If a device has a wwid matching an entry in the wwids file, then it's considered a multipath component. This is necessary to natively detect multipath components when the mpath device is not set up.
2021-06-09 01:12:09 +03:00
log_very_verbose("DM_DISABLE_UDEV environment variable set.");
log_very_verbose("Overriding configuration to use udev_rules=0, udev_sync=0, verify_udev_operations=1.");
log_very_verbose("LVM will %s.", msg);
return 1;
}
return 0;
}
static int _check_config_by_source(struct cmd_context *cmd, config_source_t source)
{
struct dm_config_tree *cft;
struct cft_check_handle *handle;
if (!(cft = get_config_tree_by_source(cmd, source)) ||
!(handle = get_config_tree_check_handle(cmd, cft)))
return 1;
return config_def_check(handle);
}
static int _check_config(struct cmd_context *cmd)
{
int abort_on_error;
if (!find_config_tree_bool(cmd, config_checks_CFG, NULL))
return 1;
abort_on_error = find_config_tree_bool(cmd, config_abort_on_errors_CFG, NULL);
if ((!_check_config_by_source(cmd, CONFIG_STRING) ||
!_check_config_by_source(cmd, CONFIG_MERGED_FILES) ||
!_check_config_by_source(cmd, CONFIG_FILE)) &&
abort_on_error) {
log_error("LVM_ configuration invalid.");
return 0;
}
return 1;
}
static const char *_set_time_format(struct cmd_context *cmd)
{
/* Compared to strftime, we do not allow "newline" character - the %n in format. */
static const char *allowed_format_chars = "aAbBcCdDeFGghHIjklmMpPrRsStTuUVwWxXyYzZ%";
static const char *allowed_alternative_format_chars_e = "cCxXyY";
static const char *allowed_alternative_format_chars_o = "deHImMSuUVwWy";
static const char *chars_to_check;
const char *tf = find_config_tree_str(cmd, report_time_format_CFG, NULL);
const char *p_fmt;
size_t i;
char c;
if (!*tf) {
log_error("Configured time format is empty string.");
goto bad;
} else {
p_fmt = tf;
while ((c = *p_fmt)) {
if (c == '%') {
c = *++p_fmt;
if (c == 'E') {
c = *++p_fmt;
chars_to_check = allowed_alternative_format_chars_e;
} else if (c == 'O') {
c = *++p_fmt;
chars_to_check = allowed_alternative_format_chars_o;
} else
chars_to_check = allowed_format_chars;
for (i = 0; chars_to_check[i]; i++) {
if (c == chars_to_check[i])
break;
}
if (!chars_to_check[i])
goto_bad;
}
else if (isprint(c))
p_fmt++;
else {
log_error("Configured time format contains non-printable characters.");
goto bad;
}
}
}
return tf;
bad:
log_error("Invalid time format \"%s\" supplied.", tf);
return NULL;
}
int process_profilable_config(struct cmd_context *cmd)
{
const char *units;
if (!(cmd->default_settings.unit_factor =
dm_units_to_factor(units = find_config_tree_str(cmd, global_units_CFG, NULL),
&cmd->default_settings.unit_type, 1, NULL))) {
log_error("Unrecognised configuration setting for global/units: %s", units);
return 0;
}
cmd->si_unit_consistency = find_config_tree_bool(cmd, global_si_unit_consistency_CFG, NULL);
cmd->report_binary_values_as_numeric = find_config_tree_bool(cmd, report_binary_values_as_numeric_CFG, NULL);
cmd->report_mark_hidden_devices = find_config_tree_bool(cmd, report_mark_hidden_devices_CFG, NULL);
cmd->default_settings.suffix = find_config_tree_bool(cmd, global_suffix_CFG, NULL);
cmd->report_list_item_separator = find_config_tree_str(cmd, report_list_item_separator_CFG, NULL);
if (!(cmd->time_format = _set_time_format(cmd)))
return 0;
return 1;
}
static int _init_system_id(struct cmd_context *cmd)
{
const char *source, *system_id;
int local_set = 0;
cmd->system_id = NULL;
cmd->unknown_system_id = 0;
system_id = find_config_tree_str_allow_empty(cmd, local_system_id_CFG, NULL);
if (system_id && *system_id)
local_set = 1;
source = find_config_tree_str(cmd, global_system_id_source_CFG, NULL);
if (!source)
source = "none";
/* Defining local system_id but not using it is probably a config mistake. */
if (local_set && strcmp(source, "lvmlocal"))
log_warn("WARNING: local/system_id is set, so should global/system_id_source be \"lvmlocal\" not \"%s\"?", source);
if (!strcmp(source, "none"))
return 1;
if ((system_id = _system_id_from_source(cmd, source)) && *system_id) {
cmd->system_id = system_id;
return 1;
}
/*
* The source failed to resolve a system_id. In this case allow
* VGs with no system_id to be accessed, but not VGs with a system_id.
*/
log_warn("WARNING: No system ID found from system_id_source %s.", source);
cmd->unknown_system_id = 1;
return 1;
}
2002-11-18 17:01:16 +03:00
static int _process_config(struct cmd_context *cmd)
{
mode_t old_umask;
devices: rework libudev usage related to config settings: obtain_device_info_from_udev (controls if lvm gets a list of devices from readdir /dev or from libudev) external_device_info_source (controls if lvm asks libudev for device information) . Make the obtain_device_list_from_udev setting affect only the choice of readdir /dev vs libudev. The setting no longer controls if udev is used for device type checks. . Change obtain_device_list_from_udev default to 0. This helps avoid boot timeouts due to slow libudev queries, avoids reported failures from udev_enumerate_scan_devices, and avoids delays from "device not initialized in udev database" errors. Even without errors, for a system booting with 1024 PVs, lvm2-pvscan times improve from about 100 sec to 15 sec, and the pvscan command from about 64 sec to about 4 sec. . For external_device_info_source="none", remove all libudev device info queries, and use only lvm native device info. . For external_device_info_source="udev", first check lvm native device info, then check libudev info. . Remove sleep/retry loop when attempting libudev queries for device info. udev info will simply be skipped if it's not immediately available. . Only set up a libdev connection if it will be used by obtain_device_list_from_udev/external_device_info_source. . For native multipath component detection, use /etc/multipath/wwids. If a device has a wwid matching an entry in the wwids file, then it's considered a multipath component. This is necessary to natively detect multipath components when the mpath device is not set up.
2021-06-09 01:12:09 +03:00
const char *dev_ext_info_src = NULL;
const char *read_ahead;
struct stat st;
const struct dm_config_node *cn;
const struct dm_config_value *cv;
int64_t pv_min_kb;
int udev_disabled = 0;
char sysfs_dir[PATH_MAX];
2002-11-18 17:01:16 +03:00
if (!_check_config(cmd))
return_0;
config: use config checks and add support for creating trees from config definition (config_def_create_tree fn) Configuration checking is initiated during config load/processing (_process_config fn) which is part of the command context creation/refresh. This patch also defines 5 types of trees that could be created from the configuration definition (config_settings.h), the cfg_def_tree_t: - CFG_DEF_TREE_CURRENT that denotes a tree of all the configuration nodes that are explicitly defined in lvm.conf/--config - CFG_DEF_TREE_MISSING that denotes a tree of all missing configuration nodes for which default valus are used since they're not explicitly used in lvm.conf/--config - CFG_DEF_TREE_DEFAULT that denotes a tree of all possible configuration nodes with default values assigned, no matter what the actual lvm.conf/--config is - CFG_DEF_TREE_NEW that denotes a tree of all new configuration nodes that appeared in given version - CFG_DEF_TREE_COMPLETE that denotes a tree of the whole configuration tree that is used in LVM2 (a combination of CFG_DEF_TREE_CURRENT + CFG_DEF_TREE_MISSING). This is not implemented yet, it will be added later... The function that creates the definition tree of given type: struct dm_config_tree *config_def_create_tree(struct config_def_tree_spec *spec); Where the "spec" specifies the tree type to be created: struct config_def_tree_spec { cfg_def_tree_t type; /* tree type */ uint16_t version; /* tree at this LVM2 version */ int ignoreadvanced; /* do not include advanced configs */ int ignoreunsupported; /* do not include unsupported configs */ }; This tree can be passed to already existing functions that write the tree on output (like we already do with cmd->cft). There is a new lvm.conf section called "config" with two new options: - config/checks which enables/disables checking (enabled by default) - config/abort_on_errors which enables/disables aborts on any type of mismatch found in the config (disabled by default)
2013-03-05 20:36:10 +04:00
2002-11-18 17:01:16 +03:00
/* umask */
cmd->default_settings.umask = find_config_tree_int(cmd, global_umask_CFG, NULL);
2002-11-18 17:01:16 +03:00
if ((old_umask = umask((mode_t) cmd->default_settings.umask)) !=
(mode_t) cmd->default_settings.umask)
log_verbose("Set umask from %04o to %04o",
old_umask, cmd->default_settings.umask);
2002-11-18 17:01:16 +03:00
/* dev dir */
2006-08-21 16:54:53 +04:00
if (dm_snprintf(cmd->dev_dir, sizeof(cmd->dev_dir), "%s/",
find_config_tree_str(cmd, devices_dir_CFG, NULL)) < 0) {
2002-11-18 17:01:16 +03:00
log_error("Device directory given in config file too long");
return 0;
}
#ifdef DEVMAPPER_SUPPORT
2002-11-18 17:01:16 +03:00
dm_set_dev_dir(cmd->dev_dir);
if (!dm_set_uuid_prefix(UUID_PREFIX))
return_0;
#endif
cmd->device_id_sysfs_dir = find_config_tree_str(cmd, devices_device_id_sysfs_dir_CFG, NULL);
2002-11-18 17:01:16 +03:00
dev_ext_info_src = find_config_tree_str(cmd, devices_external_device_info_source_CFG, NULL);
devices: rework libudev usage related to config settings: obtain_device_info_from_udev (controls if lvm gets a list of devices from readdir /dev or from libudev) external_device_info_source (controls if lvm asks libudev for device information) . Make the obtain_device_list_from_udev setting affect only the choice of readdir /dev vs libudev. The setting no longer controls if udev is used for device type checks. . Change obtain_device_list_from_udev default to 0. This helps avoid boot timeouts due to slow libudev queries, avoids reported failures from udev_enumerate_scan_devices, and avoids delays from "device not initialized in udev database" errors. Even without errors, for a system booting with 1024 PVs, lvm2-pvscan times improve from about 100 sec to 15 sec, and the pvscan command from about 64 sec to about 4 sec. . For external_device_info_source="none", remove all libudev device info queries, and use only lvm native device info. . For external_device_info_source="udev", first check lvm native device info, then check libudev info. . Remove sleep/retry loop when attempting libudev queries for device info. udev info will simply be skipped if it's not immediately available. . Only set up a libdev connection if it will be used by obtain_device_list_from_udev/external_device_info_source. . For native multipath component detection, use /etc/multipath/wwids. If a device has a wwid matching an entry in the wwids file, then it's considered a multipath component. This is necessary to natively detect multipath components when the mpath device is not set up.
2021-06-09 01:12:09 +03:00
if (dev_ext_info_src &&
strcmp(dev_ext_info_src, "none") &&
strcmp(dev_ext_info_src, "udev")) {
log_warn("WARNING: unknown external device info source, using none.");
dev_ext_info_src = NULL;
}
if (dev_ext_info_src && !strcmp(dev_ext_info_src, "udev")) {
if (udev_init_library_context()) {
init_external_device_info_source(DEV_EXT_UDEV);
} else {
log_warn("WARNING: failed to init udev for external device info, using none.");
dev_ext_info_src = NULL;
}
}
devices: rework libudev usage related to config settings: obtain_device_info_from_udev (controls if lvm gets a list of devices from readdir /dev or from libudev) external_device_info_source (controls if lvm asks libudev for device information) . Make the obtain_device_list_from_udev setting affect only the choice of readdir /dev vs libudev. The setting no longer controls if udev is used for device type checks. . Change obtain_device_list_from_udev default to 0. This helps avoid boot timeouts due to slow libudev queries, avoids reported failures from udev_enumerate_scan_devices, and avoids delays from "device not initialized in udev database" errors. Even without errors, for a system booting with 1024 PVs, lvm2-pvscan times improve from about 100 sec to 15 sec, and the pvscan command from about 64 sec to about 4 sec. . For external_device_info_source="none", remove all libudev device info queries, and use only lvm native device info. . For external_device_info_source="udev", first check lvm native device info, then check libudev info. . Remove sleep/retry loop when attempting libudev queries for device info. udev info will simply be skipped if it's not immediately available. . Only set up a libdev connection if it will be used by obtain_device_list_from_udev/external_device_info_source. . For native multipath component detection, use /etc/multipath/wwids. If a device has a wwid matching an entry in the wwids file, then it's considered a multipath component. This is necessary to natively detect multipath components when the mpath device is not set up.
2021-06-09 01:12:09 +03:00
if (!dev_ext_info_src || !strcmp(dev_ext_info_src, "none"))
init_external_device_info_source(DEV_EXT_NONE);
2002-11-18 17:01:16 +03:00
/* proc dir */
2006-08-21 16:54:53 +04:00
if (dm_snprintf(cmd->proc_dir, sizeof(cmd->proc_dir), "%s",
find_config_tree_str(cmd, global_proc_CFG, NULL)) < 0) {
2002-11-18 17:01:16 +03:00
log_error("Device directory given in config file too long");
return 0;
}
if (*cmd->proc_dir && !dir_exists(cmd->proc_dir)) {
log_warn("WARNING: proc dir %s not found - some checks will be bypassed",
cmd->proc_dir);
cmd->proc_dir[0] = '\0';
}
_get_sysfs_dir(cmd, sysfs_dir, sizeof(sysfs_dir));
dm_set_sysfs_dir(sysfs_dir);
2002-11-18 17:01:16 +03:00
/* activation? */
cmd->default_settings.activation = find_config_tree_bool(cmd, global_activation_CFG, NULL);
set_activation(cmd->default_settings.activation, 0);
2002-11-18 17:01:16 +03:00
cmd->auto_set_activation_skip = find_config_tree_bool(cmd, activation_auto_set_activation_skip_CFG, NULL);
read_ahead = find_config_tree_str(cmd, activation_readahead_CFG, NULL);
if (!strcasecmp(read_ahead, "auto"))
cmd->default_settings.read_ahead = DM_READ_AHEAD_AUTO;
else if (!strcasecmp(read_ahead, "none"))
cmd->default_settings.read_ahead = DM_READ_AHEAD_NONE;
else {
log_error("Invalid readahead specification");
return 0;
}
/*
* If udev is disabled using DM_DISABLE_UDEV environment
* variable, override existing config and hardcode these:
* - udev_rules = 0
* - udev_sync = 0
* - udev_fallback = 1
*/
udev_disabled = _check_disable_udev("manage logical volume symlinks in device directory");
cmd->default_settings.udev_rules = udev_disabled ? 0 :
find_config_tree_bool(cmd, activation_udev_rules_CFG, NULL);
cmd->default_settings.udev_sync = udev_disabled ? 0 :
find_config_tree_bool(cmd, activation_udev_sync_CFG, NULL);
2009-08-04 19:36:13 +04:00
/*
* Set udev_fallback lazily on first use since it requires
* checking DM driver version which is an extra ioctl!
* This also prevents unnecessary use of mapper/control.
* If udev is disabled globally, set fallback mode immediately.
*/
cmd->default_settings.udev_fallback = udev_disabled ? 1 : -1;
cmd->default_settings.issue_discards = find_config_tree_bool(cmd, devices_issue_discards_CFG, NULL);
init_retry_deactivation(find_config_tree_bool(cmd, activation_retry_deactivation_CFG, NULL));
init_activation_checks(find_config_tree_bool(cmd, activation_checks_CFG, NULL));
cmd->use_linear_target = find_config_tree_bool(cmd, activation_use_linear_target_CFG, NULL);
cmd->stripe_filler = find_config_tree_str(cmd, activation_missing_stripe_filler_CFG, NULL);
/* FIXME Missing error code checks from the stats, not log_warn?, notify if setting overridden, delay message/check till it is actually used (eg consider if lvm shell - file could appear later after this check)? */
if (!strcmp(cmd->stripe_filler, "/dev/ioerror") &&
stat(cmd->stripe_filler, &st))
cmd->stripe_filler = "error";
else if (strcmp(cmd->stripe_filler, "error") &&
strcmp(cmd->stripe_filler, "zero")) {
if (stat(cmd->stripe_filler, &st)) {
log_warn("WARNING: activation/missing_stripe_filler = \"%s\" "
"is invalid,", cmd->stripe_filler);
log_warn(" stat failed: %s", strerror(errno));
log_warn("Falling back to \"error\" missing_stripe_filler.");
cmd->stripe_filler = "error";
} else if (!S_ISBLK(st.st_mode)) {
log_warn("WARNING: activation/missing_stripe_filler = \"%s\" "
"is not a block device.", cmd->stripe_filler);
log_warn("Falling back to \"error\" missing_stripe_filler.");
cmd->stripe_filler = "error";
}
}
if ((cn = find_config_tree_array(cmd, activation_mlock_filter_CFG, NULL)))
for (cv = cn->v; cv; cv = cv->next)
if ((cv->type != DM_CFG_STRING) || !cv->v.str[0])
log_error("Ignoring invalid activation/mlock_filter entry in config file");
cmd->metadata_read_only = find_config_tree_bool(cmd, global_metadata_read_only_CFG, NULL);
pv_min_kb = find_config_tree_int64(cmd, devices_pv_min_size_CFG, NULL);
if (pv_min_kb < PV_MIN_SIZE_KB) {
log_warn("Ignoring too small pv_min_size %" PRId64 "KB, using default %dKB.",
pv_min_kb, PV_MIN_SIZE_KB);
pv_min_kb = PV_MIN_SIZE_KB;
}
/* LVM stores sizes internally in units of 512-byte sectors. */
init_pv_min_size((uint64_t)pv_min_kb * (1024 >> SECTOR_SHIFT));
cmd->check_pv_dev_sizes = find_config_tree_bool(cmd, metadata_check_pv_device_sizes_CFG, NULL);
cmd->event_activation = find_config_tree_bool(cmd, global_event_activation_CFG, NULL);
if (!process_profilable_config(cmd))
return_0;
if (find_config_tree_bool(cmd, report_two_word_unknown_device_CFG, NULL))
init_unknown_device_name("unknown device");
if (!_init_system_id(cmd))
return_0;
init_io_memory_size(find_config_tree_int(cmd, global_io_memory_size_CFG, NULL));
2002-11-18 17:01:16 +03:00
return 1;
}
2004-05-04 22:28:15 +04:00
static int _set_tag(struct cmd_context *cmd, const char *tag)
2002-11-18 17:01:16 +03:00
{
log_very_verbose("Setting host tag: %s", dm_pool_strdup(cmd->libmem, tag));
2002-11-18 17:01:16 +03:00
2004-05-04 22:28:15 +04:00
if (!str_list_add(cmd->libmem, &cmd->tags, tag)) {
log_error("_set_tag: str_list_add %s failed", tag);
2002-11-18 17:01:16 +03:00
return 0;
}
2004-05-04 22:28:15 +04:00
return 1;
}
static int _check_host_filters(struct cmd_context *cmd, const struct dm_config_node *hn,
2004-05-04 22:28:15 +04:00
int *passes)
{
const struct dm_config_node *cn;
const struct dm_config_value *cv;
2004-05-04 22:28:15 +04:00
*passes = 1;
for (cn = hn; cn; cn = cn->sib) {
if (!cn->v)
continue;
if (!strcmp(cn->key, "host_list")) {
*passes = 0;
if (cn->v->type == DM_CFG_EMPTY_ARRAY)
2004-05-04 22:28:15 +04:00
continue;
for (cv = cn->v; cv; cv = cv->next) {
if (cv->type != DM_CFG_STRING) {
2004-05-04 22:28:15 +04:00
log_error("Invalid hostname string "
"for tag %s", cn->key);
return 0;
}
if (!strcmp(cv->v.str, cmd->hostname)) {
*passes = 1;
return 1;
}
}
}
if (!strcmp(cn->key, "host_filter")) {
log_error("host_filter not supported yet");
return 0;
}
}
return 1;
}
static int _init_tags(struct cmd_context *cmd, struct dm_config_tree *cft)
2004-05-04 22:28:15 +04:00
{
const struct dm_config_node *tn, *cn;
2004-05-04 22:28:15 +04:00
const char *tag;
int passes;
/* Access tags section directly */
if (!(tn = find_config_node(cmd, cft, tags_CFG_SECTION)) || !tn->child)
2002-11-18 17:01:16 +03:00
return 1;
2004-05-04 22:28:15 +04:00
/* NB hosttags 0 when already 1 intentionally does not delete the tag */
if (!cmd->hosttags && find_config_bool(cmd, cft, tags_hosttags_CFG)) {
2004-05-04 22:28:15 +04:00
/* FIXME Strip out invalid chars: only A-Za-z0-9_+.- */
2008-01-30 16:19:47 +03:00
if (!_set_tag(cmd, cmd->hostname))
return_0;
2004-05-04 22:28:15 +04:00
cmd->hosttags = 1;
}
for (cn = tn->child; cn; cn = cn->sib) {
if (cn->v)
continue;
tag = cn->key;
if (*tag == '@')
tag++;
if (!validate_name(tag)) {
log_error("Invalid tag in config file: %s", cn->key);
return 0;
}
if (cn->child) {
passes = 0;
2008-01-30 16:19:47 +03:00
if (!_check_host_filters(cmd, cn->child, &passes))
return_0;
2004-05-04 22:28:15 +04:00
if (!passes)
continue;
}
2008-01-30 16:19:47 +03:00
if (!_set_tag(cmd, tag))
return_0;
2004-05-04 22:28:15 +04:00
}
return 1;
}
static int _load_config_file(struct cmd_context *cmd, const char *tag, int local)
2004-05-04 22:28:15 +04:00
{
static char config_file[PATH_MAX] = "";
2004-05-04 22:28:15 +04:00
const char *filler = "";
struct config_tree_list *cfl;
if (*tag)
filler = "_";
else if (local) {
filler = "";
tag = "local";
}
2004-05-04 22:28:15 +04:00
2006-08-21 16:54:53 +04:00
if (dm_snprintf(config_file, sizeof(config_file), "%s/lvm%s%s.conf",
cmd->system_dir, filler, tag) < 0) {
2004-05-04 22:28:15 +04:00
log_error("LVM_SYSTEM_DIR or tag was too long");
return 0;
}
if (!(cfl = dm_pool_alloc(cmd->libmem, sizeof(*cfl)))) {
2004-05-04 22:28:15 +04:00
log_error("config_tree_list allocation failed");
return 0;
}
if (!(cfl->cft = config_file_open_and_read(config_file, CONFIG_FILE, cmd)))
return_0;
2002-11-18 17:01:16 +03:00
dm_list_add(&cmd->config_files, &cfl->list);
2004-05-04 22:28:15 +04:00
2012-02-27 14:05:35 +04:00
if (*tag) {
if (!_init_tags(cmd, cfl->cft))
return_0;
} else
2004-05-04 22:28:15 +04:00
/* Use temporary copy of lvm.conf while loading other files */
cmd->cft = cfl->cft;
return 1;
}
/*
* Find and read lvm.conf.
*/
2004-05-04 22:28:15 +04:00
static int _init_lvm_conf(struct cmd_context *cmd)
{
/* No config file if LVM_SYSTEM_DIR is empty */
if (!*cmd->system_dir) {
if (!(cmd->cft = config_open(CONFIG_FILE, NULL, 0))) {
2004-05-04 22:28:15 +04:00
log_error("Failed to create config tree");
return 0;
}
return 1;
}
if (!_load_config_file(cmd, "", 0))
2008-01-30 16:19:47 +03:00
return_0;
2004-05-04 22:28:15 +04:00
return 1;
}
/* Read any additional config files */
static int _init_tag_configs(struct cmd_context *cmd)
{
struct dm_str_list *sl;
2004-05-04 22:28:15 +04:00
/* Tag list may grow while inside this loop */
dm_list_iterate_items(sl, &cmd->tags) {
if (!_load_config_file(cmd, sl->str, 0))
2008-01-30 16:19:47 +03:00
return_0;
2004-05-04 22:28:15 +04:00
}
2002-11-18 17:01:16 +03:00
return 1;
}
static int _init_profiles(struct cmd_context *cmd)
{
const char *dir;
if (!(dir = find_config_tree_str(cmd, config_profile_dir_CFG, NULL)))
return_0;
if (!cmd->profile_params) {
if (!(cmd->profile_params = dm_pool_zalloc(cmd->libmem, sizeof(*cmd->profile_params)))) {
log_error("profile_params alloc failed");
return 0;
}
dm_list_init(&cmd->profile_params->profiles_to_load);
dm_list_init(&cmd->profile_params->profiles);
}
if (!(dm_strncpy(cmd->profile_params->dir, dir, sizeof(cmd->profile_params->dir)))) {
log_error("_init_profiles: dm_strncpy failed");
return 0;
}
return 1;
}
static struct dm_config_tree *_merge_config_files(struct cmd_context *cmd, struct dm_config_tree *cft)
2004-05-04 22:28:15 +04:00
{
struct config_tree_list *cfl;
/* Replace temporary duplicate copy of lvm.conf */
if (cft->root) {
if (!(cft = config_open(CONFIG_MERGED_FILES, NULL, 0))) {
2004-05-04 22:28:15 +04:00
log_error("Failed to create config tree");
return 0;
}
}
dm_list_iterate_items(cfl, &cmd->config_files) {
2004-05-04 22:28:15 +04:00
/* Merge all config trees into cmd->cft using merge/tag rules */
if (!merge_config_tree(cmd, cft, cfl->cft, CONFIG_MERGE_TYPE_TAGS))
2008-01-30 16:19:47 +03:00
return_0;
2004-05-04 22:28:15 +04:00
}
return cft;
2004-05-04 22:28:15 +04:00
}
static void _destroy_tags(struct cmd_context *cmd)
{
struct dm_list *slh, *slht;
2004-05-04 22:28:15 +04:00
dm_list_iterate_safe(slh, slht, &cmd->tags) {
dm_list_del(slh);
2004-05-04 22:28:15 +04:00
}
}
int config_files_changed(struct cmd_context *cmd)
{
struct config_tree_list *cfl;
dm_list_iterate_items(cfl, &cmd->config_files) {
if (config_file_changed(cfl->cft))
2004-05-04 22:28:15 +04:00
return 1;
}
return 0;
}
static void _destroy_config(struct cmd_context *cmd)
2004-05-04 22:28:15 +04:00
{
struct config_tree_list *cfl;
struct dm_config_tree *cft;
struct profile *profile, *tmp_profile;
2004-05-04 22:28:15 +04:00
/*
* Configuration cascade:
* CONFIG_STRING -> CONFIG_PROFILE -> CONFIG_FILE/CONFIG_MERGED_FILES
*/
/* CONFIG_FILE/CONFIG_MERGED_FILES */
if ((cft = remove_config_tree_by_source(cmd, CONFIG_MERGED_FILES)))
config_destroy(cft);
else if ((cft = remove_config_tree_by_source(cmd, CONFIG_FILE))) {
dm_list_iterate_items(cfl, &cmd->config_files) {
if (cfl->cft == cft)
dm_list_del(&cfl->list);
}
config_destroy(cft);
}
2004-05-04 22:28:15 +04:00
dm_list_iterate_items(cfl, &cmd->config_files)
config_destroy(cfl->cft);
dm_list_init(&cmd->config_files);
/* CONFIG_PROFILE */
if (cmd->profile_params) {
config: differentiate command and metadata profiles and consolidate profile handling code - When defining configuration source, the code now uses separate CONFIG_PROFILE_COMMAND and CONFIG_PROFILE_METADATA markers (before, it was just CONFIG_PROFILE that did not make the difference between the two). This helps when checking the configuration if it contains correct set of options which are all in either command-profilable or metadata-profilable group without mixing these groups together - so it's a firm distinction. The "command profile" can't contain "metadata profile" and vice versa! This is strictly checked and if the settings are mixed, such profile is rejected and it's not used. So in the end, the CONFIG_PROFILE_COMMAND set of options and CONFIG_PROFILE_METADATA are mutually exclusive sets. - Marking configuration with one or the other marker will also determine the way these configuration sources are positioned in the configuration cascade which is now: CONFIG_STRING -> CONFIG_PROFILE_COMMAND -> CONFIG_PROFILE_METADATA -> CONFIG_FILE/CONFIG_MERGED_FILES - Marking configuration with one or the other marker will also make it possible to issue a command context refresh (will be probably a part of a future patch) if needed for settings in global profile set. For settings in metadata profile set this is impossible since we can't refresh cmd context in the middle of reading VG/LV metadata and for each VG/LV separately because each VG/LV can have a different metadata profile assinged and it's not possible to change these settings at this level. - When command profile is incorrect, it's rejected *and also* the command exits immediately - the profile *must* be correct for the command that was run with a profile to be executed. Before this patch, when the profile was found incorrect, there was just the warning message and the command continued without profile applied. But it's more correct to exit immediately in this case. - When metadata profile is incorrect, we reject it during command runtime (as we know the profile name from metadata and not early from command line as it is in case of command profiles) and we *do continue* with the command as we're in the middle of operation. Also, the metadata profile is applied directly and on the fly on find_config_tree_* fn call and even if the metadata profile is found incorrect, we still need to return the non-profiled value as found in the other configuration provided or default value. To exit immediately even in this case, we'd need to refactor existing find_config_tree_* fns so they can return error. Currently, these fns return only config values (which end up with default values in the end if the config is not found). - To check the profile validity before use to be sure it's correct, one can use : lvm dumpconfig --commandprofile/--metadataprofile ProfileName --validate (the --commandprofile/--metadataprofile for dumpconfig will come as part of the subsequent patch) - This patch also adds a reference to --commandprofile and --metadataprofile in the cmd help string (which was missing before for the --profile for some commands). We do not mention --profile now as people should use --commandprofile or --metadataprofile directly. However, the --profile is still supported for backward compatibility and it's translated as: --profile == --metadataprofile for lvcreate, vgcreate, lvchange and vgchange (as these commands are able to attach profile to metadata) --profile == --commandprofile for all the other commands (--metadataprofile is not allowed there as it makes no sense) - This patch also contains some cleanups to make the code handling the profiles more readable...
2014-05-20 16:13:10 +04:00
remove_config_tree_by_source(cmd, CONFIG_PROFILE_COMMAND);
remove_config_tree_by_source(cmd, CONFIG_PROFILE_METADATA);
/*
* Destroy config trees for any loaded profiles and
* move these profiles to profile_to_load list.
* Whenever these profiles are referenced later,
* they will get loaded again automatically.
*/
dm_list_iterate_items_safe(profile, tmp_profile, &cmd->profile_params->profiles) {
if (cmd->is_interactive && (profile == cmd->profile_params->shell_profile))
continue;
config_destroy(profile->cft);
profile->cft = NULL;
dm_list_move(&cmd->profile_params->profiles_to_load, &profile->list);
}
}
/* CONFIG_STRING */
if ((cft = remove_config_tree_by_source(cmd, CONFIG_STRING)))
config_destroy(cft);
if (cmd->cft)
log_error(INTERNAL_ERROR "_destroy_config: "
"cmd config tree not destroyed fully");
2004-05-04 22:28:15 +04:00
}
2002-11-18 17:01:16 +03:00
static int _init_dev_cache(struct cmd_context *cmd)
{
const struct dm_config_node *cn;
const struct dm_config_value *cv;
size_t len, udev_dir_len = strlen(DM_UDEV_DEV_DIR);
int len_diff;
int device_list_from_udev;
2002-11-18 17:01:16 +03:00
if (!dev_cache_init(cmd))
return_0;
2002-11-18 17:01:16 +03:00
devices: rework libudev usage related to config settings: obtain_device_info_from_udev (controls if lvm gets a list of devices from readdir /dev or from libudev) external_device_info_source (controls if lvm asks libudev for device information) . Make the obtain_device_list_from_udev setting affect only the choice of readdir /dev vs libudev. The setting no longer controls if udev is used for device type checks. . Change obtain_device_list_from_udev default to 0. This helps avoid boot timeouts due to slow libudev queries, avoids reported failures from udev_enumerate_scan_devices, and avoids delays from "device not initialized in udev database" errors. Even without errors, for a system booting with 1024 PVs, lvm2-pvscan times improve from about 100 sec to 15 sec, and the pvscan command from about 64 sec to about 4 sec. . For external_device_info_source="none", remove all libudev device info queries, and use only lvm native device info. . For external_device_info_source="udev", first check lvm native device info, then check libudev info. . Remove sleep/retry loop when attempting libudev queries for device info. udev info will simply be skipped if it's not immediately available. . Only set up a libdev connection if it will be used by obtain_device_list_from_udev/external_device_info_source. . For native multipath component detection, use /etc/multipath/wwids. If a device has a wwid matching an entry in the wwids file, then it's considered a multipath component. This is necessary to natively detect multipath components when the mpath device is not set up.
2021-06-09 01:12:09 +03:00
if ((device_list_from_udev = find_config_tree_bool(cmd, devices_obtain_device_list_from_udev_CFG, NULL))) {
if (!udev_init_library_context())
device_list_from_udev = 0;
}
init_obtain_device_list_from_udev(device_list_from_udev);
if (!(cn = find_config_tree_array(cmd, devices_scan_CFG, NULL))) {
log_error(INTERNAL_ERROR "Unable to find configuration for devices/scan.");
return 0;
2002-11-18 17:01:16 +03:00
}
for (cv = cn->v; cv; cv = cv->next) {
if (cv->type != DM_CFG_STRING) {
2002-11-18 17:01:16 +03:00
log_error("Invalid string in config file: "
"devices/scan");
return 0;
}
if (device_list_from_udev) {
len = strlen(cv->v.str);
/*
* DM_UDEV_DEV_DIR always has '/' at its end.
* If the item in the conf does not have it, be sure
* to make the right comparison without the '/' char!
*/
len_diff = len && cv->v.str[len - 1] != '/' ?
udev_dir_len - 1 != len :
udev_dir_len != len;
if (len_diff || strncmp(DM_UDEV_DEV_DIR, cv->v.str, len)) {
log_very_verbose("Non standard udev dir %s, resetting "
"devices/obtain_device_list_from_udev.",
cv->v.str);
device_list_from_udev = 0;
init_obtain_device_list_from_udev(0);
}
}
2002-11-18 17:01:16 +03:00
if (!dev_cache_add_dir(cv->v.str)) {
log_error("Failed to add %s to internal device cache",
cv->v.str);
return 0;
}
}
return 1;
}
2022-04-01 23:09:18 +03:00
#define MAX_FILTERS 10
2018-12-04 21:44:43 +03:00
static struct dev_filter *_init_filter_chain(struct cmd_context *cmd)
2002-11-18 17:01:16 +03:00
{
int nr_filt = 0;
const struct dm_config_node *cn;
struct dev_filter *filters[MAX_FILTERS] = { 0 };
struct dev_filter *composite;
2002-11-18 17:01:16 +03:00
/*
* Filters listed in order: top one gets applied first.
* Failure to initialise some filters is not fatal.
* Update MAX_FILTERS definition above when adding new filters.
*/
filters: make sure regex filter is evaluated before any filter that needs disk access The regex filter (controlled by devices/filter lvm.conf setting) was evaluated as the very last filter. However, this is not optimal when it comes to restricting disk access - users define devices/filter as well as devices/global_filter to avoid this. The devices/global_filter is already positioned at the beginning of the filter chain. We need to do the same for devices/filter. Filter chains before this patch: A: when lvmetad is not used: persistent_filter -> sysfs_filter -> global_regex_filter -> type_filter -> usable->filter -> mpath_component_filter -> partition_filter -> md_component_filter -> fw_raid_filter -> regex_filter B: when lvmetad is used: B1: to update lvmetad: sysfs_filter -> global_regex_filter -> type_filter -> usable_filter -> mpath_component_filter -> partition_filter -> md_component_filter -> fw_raid_filter B2: to retrieve info from lvmetad: persistent_filter -> usable_filter -> regex_filter From the chain list above we can see that particularly in case when lvmetad is not used, the regex filter is the very last one that is processed. If lvmetad is used, it doesn't matter much as there's the global_regex_filter which is used instead when updating lvmetad and when retrieving info from lvmetad, putting regex_filter in front of usable_filter wouldn't change much since usabled_filter is not reading disks directly. This patch puts the regex filter to the front even in case lvmetad is not used, hence reinstating the state as it was before commit a7be3b12dfe7388d1648595e6cc4c7a1379bb8a7 (which moved the regex_filter position in the chain). Still, the arguments for the commit a7be3b12dfe7388d1648595e6cc4c7a1379bb8a7 still apply and they're still satisfied since component filters (MD, mpath...) are evaluated first just before updating lvmetad. So with this patch, we end up with: A: when lvmetad is not used: persistent_filter -> sysfs_filter -> global_regex_filter -> regex_filter -> type_filter -> usable->filter -> mpath_component_filter -> partition_filter -> md_component_filter -> fw_raid_filter B: when lvmetad is used: B1: to update lvmetad: sysfs_filter -> global_regex_filter -> type_filter -> usable_filter -> mpath_component_filter -> partition_filter -> md_component_filter -> fw_raid_filter B2: to retrieve info from lvmetad: persistent_filter -> regex_filter -> usable_filter This way, specifying the regex_filter in non-lvmetad case causes the devices to be filtered based on regex first before processing any other filters which can access disks (like md_component_filter). This patch also streamlines the code for better readability.
2015-09-08 16:03:15 +03:00
/* global regex filter. Optional. */
if ((cn = find_config_tree_node(cmd, devices_global_filter_CFG, NULL))) {
if (!(filters[nr_filt] = regex_filter_create(cn->v, 0, 1))) {
log_error("Failed to create global regex device filter");
goto bad;
}
nr_filt++;
}
2002-11-18 17:01:16 +03:00
filters: make sure regex filter is evaluated before any filter that needs disk access The regex filter (controlled by devices/filter lvm.conf setting) was evaluated as the very last filter. However, this is not optimal when it comes to restricting disk access - users define devices/filter as well as devices/global_filter to avoid this. The devices/global_filter is already positioned at the beginning of the filter chain. We need to do the same for devices/filter. Filter chains before this patch: A: when lvmetad is not used: persistent_filter -> sysfs_filter -> global_regex_filter -> type_filter -> usable->filter -> mpath_component_filter -> partition_filter -> md_component_filter -> fw_raid_filter -> regex_filter B: when lvmetad is used: B1: to update lvmetad: sysfs_filter -> global_regex_filter -> type_filter -> usable_filter -> mpath_component_filter -> partition_filter -> md_component_filter -> fw_raid_filter B2: to retrieve info from lvmetad: persistent_filter -> usable_filter -> regex_filter From the chain list above we can see that particularly in case when lvmetad is not used, the regex filter is the very last one that is processed. If lvmetad is used, it doesn't matter much as there's the global_regex_filter which is used instead when updating lvmetad and when retrieving info from lvmetad, putting regex_filter in front of usable_filter wouldn't change much since usabled_filter is not reading disks directly. This patch puts the regex filter to the front even in case lvmetad is not used, hence reinstating the state as it was before commit a7be3b12dfe7388d1648595e6cc4c7a1379bb8a7 (which moved the regex_filter position in the chain). Still, the arguments for the commit a7be3b12dfe7388d1648595e6cc4c7a1379bb8a7 still apply and they're still satisfied since component filters (MD, mpath...) are evaluated first just before updating lvmetad. So with this patch, we end up with: A: when lvmetad is not used: persistent_filter -> sysfs_filter -> global_regex_filter -> regex_filter -> type_filter -> usable->filter -> mpath_component_filter -> partition_filter -> md_component_filter -> fw_raid_filter B: when lvmetad is used: B1: to update lvmetad: sysfs_filter -> global_regex_filter -> type_filter -> usable_filter -> mpath_component_filter -> partition_filter -> md_component_filter -> fw_raid_filter B2: to retrieve info from lvmetad: persistent_filter -> regex_filter -> usable_filter This way, specifying the regex_filter in non-lvmetad case causes the devices to be filtered based on regex first before processing any other filters which can access disks (like md_component_filter). This patch also streamlines the code for better readability.
2015-09-08 16:03:15 +03:00
/* regex filter. Optional. */
if ((cn = find_config_tree_node(cmd, devices_filter_CFG, NULL))) {
if (!(filters[nr_filt] = regex_filter_create(cn->v, 1, 0))) {
log_error("Failed to create regex device filter");
goto bad;
filters: make sure regex filter is evaluated before any filter that needs disk access The regex filter (controlled by devices/filter lvm.conf setting) was evaluated as the very last filter. However, this is not optimal when it comes to restricting disk access - users define devices/filter as well as devices/global_filter to avoid this. The devices/global_filter is already positioned at the beginning of the filter chain. We need to do the same for devices/filter. Filter chains before this patch: A: when lvmetad is not used: persistent_filter -> sysfs_filter -> global_regex_filter -> type_filter -> usable->filter -> mpath_component_filter -> partition_filter -> md_component_filter -> fw_raid_filter -> regex_filter B: when lvmetad is used: B1: to update lvmetad: sysfs_filter -> global_regex_filter -> type_filter -> usable_filter -> mpath_component_filter -> partition_filter -> md_component_filter -> fw_raid_filter B2: to retrieve info from lvmetad: persistent_filter -> usable_filter -> regex_filter From the chain list above we can see that particularly in case when lvmetad is not used, the regex filter is the very last one that is processed. If lvmetad is used, it doesn't matter much as there's the global_regex_filter which is used instead when updating lvmetad and when retrieving info from lvmetad, putting regex_filter in front of usable_filter wouldn't change much since usabled_filter is not reading disks directly. This patch puts the regex filter to the front even in case lvmetad is not used, hence reinstating the state as it was before commit a7be3b12dfe7388d1648595e6cc4c7a1379bb8a7 (which moved the regex_filter position in the chain). Still, the arguments for the commit a7be3b12dfe7388d1648595e6cc4c7a1379bb8a7 still apply and they're still satisfied since component filters (MD, mpath...) are evaluated first just before updating lvmetad. So with this patch, we end up with: A: when lvmetad is not used: persistent_filter -> sysfs_filter -> global_regex_filter -> regex_filter -> type_filter -> usable->filter -> mpath_component_filter -> partition_filter -> md_component_filter -> fw_raid_filter B: when lvmetad is used: B1: to update lvmetad: sysfs_filter -> global_regex_filter -> type_filter -> usable_filter -> mpath_component_filter -> partition_filter -> md_component_filter -> fw_raid_filter B2: to retrieve info from lvmetad: persistent_filter -> regex_filter -> usable_filter This way, specifying the regex_filter in non-lvmetad case causes the devices to be filtered based on regex first before processing any other filters which can access disks (like md_component_filter). This patch also streamlines the code for better readability.
2015-09-08 16:03:15 +03:00
}
nr_filt++;
filters: make sure regex filter is evaluated before any filter that needs disk access The regex filter (controlled by devices/filter lvm.conf setting) was evaluated as the very last filter. However, this is not optimal when it comes to restricting disk access - users define devices/filter as well as devices/global_filter to avoid this. The devices/global_filter is already positioned at the beginning of the filter chain. We need to do the same for devices/filter. Filter chains before this patch: A: when lvmetad is not used: persistent_filter -> sysfs_filter -> global_regex_filter -> type_filter -> usable->filter -> mpath_component_filter -> partition_filter -> md_component_filter -> fw_raid_filter -> regex_filter B: when lvmetad is used: B1: to update lvmetad: sysfs_filter -> global_regex_filter -> type_filter -> usable_filter -> mpath_component_filter -> partition_filter -> md_component_filter -> fw_raid_filter B2: to retrieve info from lvmetad: persistent_filter -> usable_filter -> regex_filter From the chain list above we can see that particularly in case when lvmetad is not used, the regex filter is the very last one that is processed. If lvmetad is used, it doesn't matter much as there's the global_regex_filter which is used instead when updating lvmetad and when retrieving info from lvmetad, putting regex_filter in front of usable_filter wouldn't change much since usabled_filter is not reading disks directly. This patch puts the regex filter to the front even in case lvmetad is not used, hence reinstating the state as it was before commit a7be3b12dfe7388d1648595e6cc4c7a1379bb8a7 (which moved the regex_filter position in the chain). Still, the arguments for the commit a7be3b12dfe7388d1648595e6cc4c7a1379bb8a7 still apply and they're still satisfied since component filters (MD, mpath...) are evaluated first just before updating lvmetad. So with this patch, we end up with: A: when lvmetad is not used: persistent_filter -> sysfs_filter -> global_regex_filter -> regex_filter -> type_filter -> usable->filter -> mpath_component_filter -> partition_filter -> md_component_filter -> fw_raid_filter B: when lvmetad is used: B1: to update lvmetad: sysfs_filter -> global_regex_filter -> type_filter -> usable_filter -> mpath_component_filter -> partition_filter -> md_component_filter -> fw_raid_filter B2: to retrieve info from lvmetad: persistent_filter -> regex_filter -> usable_filter This way, specifying the regex_filter in non-lvmetad case causes the devices to be filtered based on regex first before processing any other filters which can access disks (like md_component_filter). This patch also streamlines the code for better readability.
2015-09-08 16:03:15 +03:00
}
/* device type filter. Required. */
if (!(filters[nr_filt] = lvm_type_filter_create(cmd->dev_types))) {
log_error("Failed to create lvm type filter");
goto bad;
2002-11-18 17:01:16 +03:00
}
nr_filt++;
2002-11-18 17:01:16 +03:00
device usage based on devices file The LVM devices file lists devices that lvm can use. The default file is /etc/lvm/devices/system.devices, and the lvmdevices(8) command is used to add or remove device entries. If the file does not exist, or if lvm.conf includes use_devicesfile=0, then lvm will not use a devices file. When the devices file is in use, the regex filter is not used, and the filter settings in lvm.conf or on the command line are ignored. LVM records devices in the devices file using hardware-specific IDs, such as the WWID, and attempts to use subsystem-specific IDs for virtual device types. These device IDs are also written in the VG metadata. When no hardware or virtual ID is available, lvm falls back using the unstable device name as the device ID. When devnames are used, lvm performs extra scanning to find devices if their devname changes, e.g. after reboot. When proper device IDs are used, an lvm command will not look at devices outside the devices file, but when devnames are used as a fallback, lvm will scan devices outside the devices file to locate PVs on renamed devices. A config setting search_for_devnames can be used to control the scanning for renamed devname entries. Related to the devices file, the new command option --devices <devnames> allows a list of devices to be specified for the command to use, overriding the devices file. The listed devices act as a sort of devices file in terms of limiting which devices lvm will see and use. Devices that are not listed will appear to be missing to the lvm command. Multiple devices files can be kept in /etc/lvm/devices, which allows lvm to be used with different sets of devices, e.g. system devices do not need to be exposed to a specific application, and the application can use lvm on its own set of devices that are not exposed to the system. The option --devicesfile <filename> is used to select the devices file to use with the command. Without the option set, the default system devices file is used. Setting --devicesfile "" causes lvm to not use a devices file. An existing, empty devices file means lvm will see no devices. The new command vgimportdevices adds PVs from a VG to the devices file and updates the VG metadata to include the device IDs. vgimportdevices -a will import all VGs into the system devices file. LVM commands run by dmeventd not use a devices file by default, and will look at all devices on the system. A devices file can be created for dmeventd (/etc/lvm/devices/dmeventd.devices) If this file exists, lvm commands run by dmeventd will use it. Internal implementaion: - device_ids_read - read the devices file . add struct dev_use (du) to cmd->use_devices for each devices file entry - dev_cache_scan - get /dev entries . add struct device (dev) to dev_cache for each device on the system - device_ids_match - match devices file entries to /dev entries . match each du on cmd->use_devices to a dev in dev_cache, using device ID . on match, set du->dev, dev->id, dev->flags MATCHED_USE_ID - label_scan - read lvm headers and metadata from devices . filters are applied, those that do not need data from the device . filter-deviceid skips devs without MATCHED_USE_ID, i.e. skips /dev entries that are not listed in the devices file . read lvm label from dev . filters are applied, those that use data from the device . read lvm metadata from dev . add info/vginfo structs for PVs/VGs (info is "lvmcache") - device_ids_find_renamed_devs - handle devices with unstable devname ID where devname changed . this step only needed when devs do not have proper device IDs, and their dev names change, e.g. after reboot sdb becomes sdc. . detect incorrect match because PVID in the devices file entry does not match the PVID found when the device was read above . undo incorrect match between du and dev above . search system devices for new location of PVID . update devices file with new devnames for PVIDs on renamed devices . label_scan the renamed devs - continue with command processing
2020-06-23 21:25:41 +03:00
/* filter based on the device_ids saved in the devices file */
if (!(filters[nr_filt] = deviceid_filter_create(cmd))) {
log_error("Failed to create deviceid device filter");
goto bad;
}
nr_filt++;
/*
* sysfs filter. Only available on 2.6 kernels. Non-critical.
* Eliminates unavailable devices.
* TODO: this may be unnecessary now with device ids
* (currently not used for devs match to device id using syfs)
*/
if (find_config_tree_bool(cmd, devices_sysfs_scan_CFG, NULL)) {
if ((filters[nr_filt] = sysfs_filter_create()))
nr_filt++;
}
/* usable device filter. Required. */
if (!(filters[nr_filt] = usable_filter_create(cmd, cmd->dev_types, FILTER_MODE_NO_LVMETAD))) {
log_error("Failed to create usabled device filter");
goto bad;
}
nr_filt++;
/* mpath component filter. Optional, non-critical. */
if (find_config_tree_bool(cmd, devices_multipath_component_detection_CFG, NULL)) {
if ((filters[nr_filt] = mpath_filter_create(cmd->dev_types)))
nr_filt++;
}
/* partitioned device filter. Required. */
if (!(filters[nr_filt] = partitioned_filter_create(cmd->dev_types))) {
log_error("Failed to create partitioned device filter");
goto bad;
}
nr_filt++;
2018-05-01 22:32:15 +03:00
/* signature filter. Required. */
if (!(filters[nr_filt] = signature_filter_create(cmd->dev_types))) {
log_error("Failed to create signature device filter");
goto bad;
}
nr_filt++;
/* md component filter. Optional, non-critical. */
if (find_config_tree_bool(cmd, devices_md_component_detection_CFG, NULL)) {
init_md_filtering(1);
if ((filters[nr_filt] = md_filter_create(cmd, cmd->dev_types)))
nr_filt++;
}
/* firmware raid filter. Optional, non-critical. */
if (find_config_tree_bool(cmd, devices_fw_raid_component_detection_CFG, NULL)) {
init_fwraid_filtering(1);
if ((filters[nr_filt] = fwraid_filter_create(cmd->dev_types)))
nr_filt++;
}
devices: rework libudev usage related to config settings: obtain_device_info_from_udev (controls if lvm gets a list of devices from readdir /dev or from libudev) external_device_info_source (controls if lvm asks libudev for device information) . Make the obtain_device_list_from_udev setting affect only the choice of readdir /dev vs libudev. The setting no longer controls if udev is used for device type checks. . Change obtain_device_list_from_udev default to 0. This helps avoid boot timeouts due to slow libudev queries, avoids reported failures from udev_enumerate_scan_devices, and avoids delays from "device not initialized in udev database" errors. Even without errors, for a system booting with 1024 PVs, lvm2-pvscan times improve from about 100 sec to 15 sec, and the pvscan command from about 64 sec to about 4 sec. . For external_device_info_source="none", remove all libudev device info queries, and use only lvm native device info. . For external_device_info_source="udev", first check lvm native device info, then check libudev info. . Remove sleep/retry loop when attempting libudev queries for device info. udev info will simply be skipped if it's not immediately available. . Only set up a libdev connection if it will be used by obtain_device_list_from_udev/external_device_info_source. . For native multipath component detection, use /etc/multipath/wwids. If a device has a wwid matching an entry in the wwids file, then it's considered a multipath component. This is necessary to natively detect multipath components when the mpath device is not set up.
2021-06-09 01:12:09 +03:00
if (!(composite = composite_filter_create(nr_filt, filters)))
goto_bad;
return composite;
bad:
while (--nr_filt >= 0)
filters[nr_filt]->destroy(filters[nr_filt]);
return NULL;
2002-11-18 17:01:16 +03:00
}
/*
2018-12-04 21:44:43 +03:00
* cmd->filter ==
* persistent(cache) filter -> sysfs filter -> internal filter -> global regex filter ->
* regex_filter -> type filter -> usable device filter ->
filters: make sure regex filter is evaluated before any filter that needs disk access The regex filter (controlled by devices/filter lvm.conf setting) was evaluated as the very last filter. However, this is not optimal when it comes to restricting disk access - users define devices/filter as well as devices/global_filter to avoid this. The devices/global_filter is already positioned at the beginning of the filter chain. We need to do the same for devices/filter. Filter chains before this patch: A: when lvmetad is not used: persistent_filter -> sysfs_filter -> global_regex_filter -> type_filter -> usable->filter -> mpath_component_filter -> partition_filter -> md_component_filter -> fw_raid_filter -> regex_filter B: when lvmetad is used: B1: to update lvmetad: sysfs_filter -> global_regex_filter -> type_filter -> usable_filter -> mpath_component_filter -> partition_filter -> md_component_filter -> fw_raid_filter B2: to retrieve info from lvmetad: persistent_filter -> usable_filter -> regex_filter From the chain list above we can see that particularly in case when lvmetad is not used, the regex filter is the very last one that is processed. If lvmetad is used, it doesn't matter much as there's the global_regex_filter which is used instead when updating lvmetad and when retrieving info from lvmetad, putting regex_filter in front of usable_filter wouldn't change much since usabled_filter is not reading disks directly. This patch puts the regex filter to the front even in case lvmetad is not used, hence reinstating the state as it was before commit a7be3b12dfe7388d1648595e6cc4c7a1379bb8a7 (which moved the regex_filter position in the chain). Still, the arguments for the commit a7be3b12dfe7388d1648595e6cc4c7a1379bb8a7 still apply and they're still satisfied since component filters (MD, mpath...) are evaluated first just before updating lvmetad. So with this patch, we end up with: A: when lvmetad is not used: persistent_filter -> sysfs_filter -> global_regex_filter -> regex_filter -> type_filter -> usable->filter -> mpath_component_filter -> partition_filter -> md_component_filter -> fw_raid_filter B: when lvmetad is used: B1: to update lvmetad: sysfs_filter -> global_regex_filter -> type_filter -> usable_filter -> mpath_component_filter -> partition_filter -> md_component_filter -> fw_raid_filter B2: to retrieve info from lvmetad: persistent_filter -> regex_filter -> usable_filter This way, specifying the regex_filter in non-lvmetad case causes the devices to be filtered based on regex first before processing any other filters which can access disks (like md_component_filter). This patch also streamlines the code for better readability.
2015-09-08 16:03:15 +03:00
* mpath component filter -> partitioned filter -> md component filter -> fw raid filter
*
*/
int init_filters(struct cmd_context *cmd, unsigned load_persistent_cache)
2002-11-18 17:01:16 +03:00
{
struct dev_filter *pfilter, *filter = NULL, *filter_components[2] = {0};
2002-11-18 17:01:16 +03:00
if (!cmd->initialized.connections) {
log_error(INTERNAL_ERROR "connections must be initialized before filters");
return 0;
}
2018-12-04 21:44:43 +03:00
filter = _init_filter_chain(cmd);
if (!filter)
goto_bad;
2002-11-18 17:01:16 +03:00
init_ignore_suspended_devices(find_config_tree_bool(cmd, devices_ignore_suspended_devices_CFG, NULL));
Mirror: Fix hangs and lock-ups caused by attempting label reads of mirrors There is a problem with the way mirrors have been designed to handle failures that is resulting in stuck LVM processes and hung I/O. When mirrors encounter a write failure, they block I/O and notify userspace to reconfigure the mirror to remove failed devices. This process is open to a couple races: 1) Any LVM process other than the one that is meant to deal with the mirror failure can attempt to read the mirror, fail, and block other LVM commands (including the repair command) from proceeding due to holding a lock on the volume group. 2) If there are multiple mirrors that suffer a failure in the same volume group, a repair can block while attempting to read the LVM label from one mirror while trying to repair the other. Mitigation of these races has been attempted by disallowing label reading of mirrors that are either suspended or are indicated as blocking by the kernel. While this has closed the window of opportunity for hitting the above problems considerably, it hasn't closed it completely. This is because it is still possible to start an LVM command, read the status of the mirror as healthy, and then perform the read for the label at the moment after a the failure is discovered by the kernel. I can see two solutions to this problem: 1) Allow users to configure whether mirrors can be candidates for LVM labels (i.e. whether PVs can be created on mirror LVs). If the user chooses to allow label scanning of mirror LVs, it will be at the expense of a possible hang in I/O or LVM processes. 2) Instrument a way to allow asynchronous label reading - allowing blocked label reads to be ignored while continuing to process the LVM command. This would action would allow LVM commands to continue even though they would have otherwise blocked trying to read a mirror. They can then release their lock and allow a repair command to commence. In the event of #2 above, the repair command already in progress can continue and repair the failed mirror. This patch brings solution #1. If solution #2 is developed later on, the configuration option created in #1 can be negated - allowing mirrors to be scanned for labels by default once again.
2013-10-23 04:14:33 +04:00
init_ignore_lvm_mirrors(find_config_tree_bool(cmd, devices_ignore_lvm_mirrors_CFG, NULL));
/*
2018-12-04 21:44:43 +03:00
* persisent filter is a cache of the previous result real filter result.
* If a dev is found in persistent filter, the pass/fail result saved by
* the pfilter is used. If a dev does not existing in the persistent
* filter, the dev is passed on to the real filter, and when the result
* of the real filter is saved in the persistent filter.
*
* FIXME: we should apply the filter once at the start of the command,
* and not call the filters repeatedly. In that case we would not need
* the persistent/caching filter layer.
*/
if (!(pfilter = persistent_filter_create(cmd->dev_types, filter))) {
log_verbose("Failed to create persistent device filter.");
goto bad;
2002-11-18 17:01:16 +03:00
}
cmd->filter = pfilter;
cmd->initialized.filters = 1;
2002-11-18 17:01:16 +03:00
return 1;
bad:
if (!filter) {
/*
* composite filter not created - destroy
* each component directly
*/
if (filter_components[0])
filter_components[0]->destroy(filter_components[0]);
if (filter_components[1])
filter_components[1]->destroy(filter_components[1]);
} else {
/*
* composite filter created - destroy it - this
* will also destroy any of its components
*/
filter->destroy(filter);
}
cmd->initialized.filters = 0;
return 0;
2002-11-18 17:01:16 +03:00
}
struct format_type *get_format_by_name(struct cmd_context *cmd, const char *format)
{
struct format_type *fmt;
dm_list_iterate_items(fmt, &cmd->formats)
if (!strcasecmp(fmt->name, format) ||
!strcasecmp(fmt->name + 3, format) ||
(fmt->alias && !strcasecmp(fmt->alias, format)))
return fmt;
return NULL;
}
/* FIXME: there's only one format, get rid of the list of formats */
2002-11-18 17:01:16 +03:00
static int _init_formats(struct cmd_context *cmd)
{
struct format_type *fmt;
if (!(fmt = create_text_format(cmd)))
return 0;
dm_list_add(&cmd->formats, &fmt->list);
2002-11-18 17:01:16 +03:00
cmd->fmt_backup = fmt;
cmd->default_settings.fmt_name = fmt->name;
cmd->fmt = fmt;
2002-11-18 17:01:16 +03:00
return 1;
2002-11-18 17:01:16 +03:00
}
int init_lvmcache_orphans(struct cmd_context *cmd)
{
struct format_type *fmt;
dm_list_iterate_items(fmt, &cmd->formats)
if (!lvmcache_add_orphan_vginfo(cmd, fmt->orphan_vg_name, fmt))
return_0;
return 1;
}
struct segtype_library {
struct cmd_context *cmd;
void *lib;
const char *libname;
};
int lvm_register_segtype(struct segtype_library *seglib,
struct segment_type *segtype)
{
struct segment_type *segtype2;
segtype->library = seglib->lib;
dm_list_iterate_items(segtype2, &seglib->cmd->segtypes) {
if (strcmp(segtype2->name, segtype->name))
continue;
log_error("Duplicate segment type %s: "
"unloading shared library %s",
segtype->name, seglib->libname);
segtype->ops->destroy(segtype);
return 0;
}
dm_list_add(&seglib->cmd->segtypes, &segtype->list);
return 1;
}
2004-05-05 01:25:57 +04:00
static int _init_segtypes(struct cmd_context *cmd)
{
int i;
2004-05-05 01:25:57 +04:00
struct segment_type *segtype;
struct segtype_library seglib = { .cmd = cmd, .lib = NULL };
struct segment_type *(*init_segtype_array[])(struct cmd_context *cmd) = {
init_linear_segtype,
init_striped_segtype,
init_zero_segtype,
init_error_segtype,
/* disabled until needed init_free_segtype, */
#ifdef SNAPSHOT_INTERNAL
init_snapshot_segtype,
#endif
#ifdef MIRRORED_INTERNAL
init_mirrored_segtype,
#endif
NULL
};
2004-05-05 01:25:57 +04:00
for (i = 0; init_segtype_array[i]; i++) {
if (!(segtype = init_segtype_array[i](cmd)))
return 0;
segtype->library = NULL;
dm_list_add(&cmd->segtypes, &segtype->list);
}
#ifdef RAID_INTERNAL
if (!init_raid_segtypes(cmd, &seglib))
return 0;
#endif
#ifdef THIN_INTERNAL
if (!init_thin_segtypes(cmd, &seglib))
return 0;
#endif
#ifdef CACHE_INTERNAL
if (!init_cache_segtypes(cmd, &seglib))
return 0;
#endif
#ifdef VDO_INTERNAL
if (!init_vdo_segtypes(cmd, &seglib))
return_0;
#endif
#ifdef WRITECACHE_INTERNAL
if (!init_writecache_segtypes(cmd, &seglib))
return 0;
#endif
Allow dm-integrity to be used for raid images dm-integrity stores checksums of the data written to an LV, and returns an error if data read from the LV does not match the previously saved checksum. When used on raid images, dm-raid will correct the error by reading the block from another image, and the device user sees no error. The integrity metadata (checksums) are stored on an internal LV allocated by lvm for each linear image. The internal LV is allocated on the same PV as the image. Create a raid LV with an integrity layer over each raid image (for raid levels 1,4,5,6,10): lvcreate --type raidN --raidintegrity y [options] Add an integrity layer to images of an existing raid LV: lvconvert --raidintegrity y LV Remove the integrity layer from images of a raid LV: lvconvert --raidintegrity n LV Settings Use --raidintegritymode journal|bitmap (journal is default) to configure the method used by dm-integrity to ensure crash consistency. Initialization When integrity is added to an LV, the kernel needs to initialize the integrity metadata/checksums for all blocks in the LV. The data corruption checking performed by dm-integrity will only operate on areas of the LV that are already initialized. The progress of integrity initialization is reported by the "syncpercent" LV reporting field (and under the Cpy%Sync lvs column.) Example: create a raid1 LV with integrity: $ lvcreate --type raid1 -m1 --raidintegrity y -n rr -L1G foo Creating integrity metadata LV rr_rimage_0_imeta with size 12.00 MiB. Logical volume "rr_rimage_0_imeta" created. Creating integrity metadata LV rr_rimage_1_imeta with size 12.00 MiB. Logical volume "rr_rimage_1_imeta" created. Logical volume "rr" created. $ lvs -a foo LV VG Attr LSize Origin Cpy%Sync rr foo rwi-a-r--- 1.00g 4.93 [rr_rimage_0] foo gwi-aor--- 1.00g [rr_rimage_0_iorig] 41.02 [rr_rimage_0_imeta] foo ewi-ao---- 12.00m [rr_rimage_0_iorig] foo -wi-ao---- 1.00g [rr_rimage_1] foo gwi-aor--- 1.00g [rr_rimage_1_iorig] 39.45 [rr_rimage_1_imeta] foo ewi-ao---- 12.00m [rr_rimage_1_iorig] foo -wi-ao---- 1.00g [rr_rmeta_0] foo ewi-aor--- 4.00m [rr_rmeta_1] foo ewi-aor--- 4.00m
2019-11-21 01:07:27 +03:00
#ifdef INTEGRITY_INTERNAL
if (!init_integrity_segtypes(cmd, &seglib))
return 0;
#endif
2004-05-05 01:25:57 +04:00
return 1;
}
2004-03-08 20:25:59 +03:00
static int _init_hostname(struct cmd_context *cmd)
{
struct utsname uts;
if (uname(&uts)) {
log_sys_error("uname", "_init_hostname");
return 0;
}
if (!(cmd->hostname = dm_pool_strdup(cmd->libmem, uts.nodename))) {
log_error("_init_hostname: dm_pool_strdup failed");
2004-03-08 20:25:59 +03:00
return 0;
}
if (!(cmd->kernel_vsn = dm_pool_strdup(cmd->libmem, uts.release))) {
log_error("_init_hostname: dm_pool_strdup kernel_vsn failed");
2004-04-08 21:21:01 +04:00
return 0;
}
2004-03-08 20:25:59 +03:00
return 1;
}
static int _init_backup(struct cmd_context *cmd)
{
uint32_t days, min;
const char *dir;
if (!cmd->system_dir[0]) {
log_warn("WARNING: Metadata changes will NOT be backed up");
backup_init(cmd, "", 0);
archive_init(cmd, "", 0, 0, 0);
return 1;
}
/* set up archiving */
cmd->default_settings.archive =
find_config_tree_bool(cmd, backup_archive_CFG, NULL);
days = (uint32_t) find_config_tree_int(cmd, backup_retain_days_CFG, NULL);
min = (uint32_t) find_config_tree_int(cmd, backup_retain_min_CFG, NULL);
if (!(dir = find_config_tree_str(cmd, backup_archive_dir_CFG, NULL)))
return_0;
if (!archive_init(cmd, dir, days, min,
cmd->default_settings.archive)) {
log_debug("archive_init failed.");
return 0;
}
/* set up the backup */
cmd->default_settings.backup = find_config_tree_bool(cmd, backup_backup_CFG, NULL);
if (!(dir = find_config_tree_str(cmd, backup_backup_dir_CFG, NULL)))
return_0;
if (!backup_init(cmd, dir, cmd->default_settings.backup)) {
log_debug("backup_init failed.");
return 0;
}
return 1;
}
static void _init_rand(struct cmd_context *cmd)
{
if (read_urandom(&cmd->rand_seed, sizeof(cmd->rand_seed))) {
reset_lvm_errno(1);
return;
}
2023-04-26 15:05:12 +03:00
cmd->rand_seed = (unsigned) ((time(NULL) + getpid()) & 0xffffffff);
reset_lvm_errno(1);
}
static void _init_globals(struct cmd_context *cmd)
{
init_mirror_in_sync(0);
}
static int _init_lvmpolld(struct cmd_context *cmd)
{
const char *lvmpolld_socket;
lvmpolld_disconnect();
lvmpolld_socket = getenv("LVM_LVMPOLLD_SOCKET");
if (!lvmpolld_socket)
lvmpolld_socket = DEFAULT_RUN_DIR "/lvmpolld.socket";
lvmpolld_set_socket(lvmpolld_socket);
lvmpolld_set_active(find_config_tree_bool(cmd, global_use_lvmpolld_CFG, NULL));
return 1;
}
int init_connections(struct cmd_context *cmd)
{
if (!_init_lvmpolld(cmd)) {
log_error("Failed to initialize lvmpolld connection.");
goto bad;
}
cmd->initialized.connections = 1;
return 1;
bad:
cmd->initialized.connections = 0;
return 0;
}
int init_run_by_dmeventd(struct cmd_context *cmd)
{
init_dmeventd_monitor(DMEVENTD_MONITOR_IGNORE);
init_ignore_suspended_devices(1);
init_disable_dmeventd_monitoring(1); /* Lock settings */
device usage based on devices file The LVM devices file lists devices that lvm can use. The default file is /etc/lvm/devices/system.devices, and the lvmdevices(8) command is used to add or remove device entries. If the file does not exist, or if lvm.conf includes use_devicesfile=0, then lvm will not use a devices file. When the devices file is in use, the regex filter is not used, and the filter settings in lvm.conf or on the command line are ignored. LVM records devices in the devices file using hardware-specific IDs, such as the WWID, and attempts to use subsystem-specific IDs for virtual device types. These device IDs are also written in the VG metadata. When no hardware or virtual ID is available, lvm falls back using the unstable device name as the device ID. When devnames are used, lvm performs extra scanning to find devices if their devname changes, e.g. after reboot. When proper device IDs are used, an lvm command will not look at devices outside the devices file, but when devnames are used as a fallback, lvm will scan devices outside the devices file to locate PVs on renamed devices. A config setting search_for_devnames can be used to control the scanning for renamed devname entries. Related to the devices file, the new command option --devices <devnames> allows a list of devices to be specified for the command to use, overriding the devices file. The listed devices act as a sort of devices file in terms of limiting which devices lvm will see and use. Devices that are not listed will appear to be missing to the lvm command. Multiple devices files can be kept in /etc/lvm/devices, which allows lvm to be used with different sets of devices, e.g. system devices do not need to be exposed to a specific application, and the application can use lvm on its own set of devices that are not exposed to the system. The option --devicesfile <filename> is used to select the devices file to use with the command. Without the option set, the default system devices file is used. Setting --devicesfile "" causes lvm to not use a devices file. An existing, empty devices file means lvm will see no devices. The new command vgimportdevices adds PVs from a VG to the devices file and updates the VG metadata to include the device IDs. vgimportdevices -a will import all VGs into the system devices file. LVM commands run by dmeventd not use a devices file by default, and will look at all devices on the system. A devices file can be created for dmeventd (/etc/lvm/devices/dmeventd.devices) If this file exists, lvm commands run by dmeventd will use it. Internal implementaion: - device_ids_read - read the devices file . add struct dev_use (du) to cmd->use_devices for each devices file entry - dev_cache_scan - get /dev entries . add struct device (dev) to dev_cache for each device on the system - device_ids_match - match devices file entries to /dev entries . match each du on cmd->use_devices to a dev in dev_cache, using device ID . on match, set du->dev, dev->id, dev->flags MATCHED_USE_ID - label_scan - read lvm headers and metadata from devices . filters are applied, those that do not need data from the device . filter-deviceid skips devs without MATCHED_USE_ID, i.e. skips /dev entries that are not listed in the devices file . read lvm label from dev . filters are applied, those that use data from the device . read lvm metadata from dev . add info/vginfo structs for PVs/VGs (info is "lvmcache") - device_ids_find_renamed_devs - handle devices with unstable devname ID where devname changed . this step only needed when devs do not have proper device IDs, and their dev names change, e.g. after reboot sdb becomes sdc. . detect incorrect match because PVID in the devices file entry does not match the PVID found when the device was read above . undo incorrect match between du and dev above . search system devices for new location of PVID . update devices file with new devnames for PVIDs on renamed devices . label_scan the renamed devs - continue with command processing
2020-06-23 21:25:41 +03:00
cmd->run_by_dmeventd = 1;
return 0;
}
void destroy_config_context(struct cmd_context *cmd)
{
_destroy_config(cmd);
if (cmd->mem)
dm_pool_destroy(cmd->mem);
if (cmd->libmem)
dm_pool_destroy(cmd->libmem);
if (cmd->pending_delete_mem)
dm_pool_destroy(cmd->pending_delete_mem);
free(cmd);
}
/*
* A "config context" is a very light weight toolcontext that
* is only used for reading config settings from lvm.conf.
*
* FIXME: this needs to go back to parametrized create_toolcontext()
*/
struct cmd_context *create_config_context(void)
{
struct cmd_context *cmd;
if (!(cmd = zalloc(sizeof(*cmd))))
goto_out;
strncpy(cmd->system_dir, DEFAULT_SYS_DIR, sizeof(cmd->system_dir) - 1);
if (!_get_env_vars(cmd))
goto_out;
if (!(cmd->libmem = dm_pool_create("library", 4 * 1024)))
goto_out;
if (!(cmd->mem = dm_pool_create("command", 4 * 1024)))
goto out;
if (!(cmd->pending_delete_mem = dm_pool_create("pending_delete", 1024)))
goto_out;
dm_list_init(&cmd->config_files);
dm_list_init(&cmd->tags);
if (!_init_lvm_conf(cmd))
goto_out;
if (!_init_hostname(cmd))
goto_out;
if (!_init_tags(cmd, cmd->cft))
goto_out;
/* Load lvmlocal.conf */
if (*cmd->system_dir && !_load_config_file(cmd, "", 1))
goto_out;
if (!_init_tag_configs(cmd))
goto_out;
if (!(cmd->cft = _merge_config_files(cmd, cmd->cft)))
goto_out;
return cmd;
out:
if (cmd)
destroy_config_context(cmd);
return NULL;
}
2002-11-18 17:01:16 +03:00
/* Entry point */
struct cmd_context *create_toolcontext(unsigned is_clvmd,
const char *system_dir,
unsigned set_buffering,
unsigned threaded,
unsigned set_connections,
unsigned set_filters)
2002-11-18 17:01:16 +03:00
{
struct cmd_context *cmd;
int flags;
2002-11-18 17:01:16 +03:00
#ifdef M_MMAP_MAX
mallopt(M_MMAP_MAX, 0);
#endif
2002-11-18 17:01:16 +03:00
if (!setlocale(LC_ALL, ""))
log_very_verbose("setlocale failed");
2002-11-18 17:01:16 +03:00
2004-02-14 01:56:45 +03:00
#ifdef INTL_PACKAGE
bindtextdomain(INTL_PACKAGE, LOCALEDIR);
#endif
if (!(cmd = zalloc(sizeof(*cmd)))) {
2002-11-18 17:01:16 +03:00
log_error("Failed to allocate command context");
return NULL;
}
cmd->is_long_lived = is_clvmd;
cmd->is_clvmd = is_clvmd;
cmd->threaded = threaded ? 1 : 0;
cmd->handles_missing_pvs = 0;
cmd->handles_unknown_segments = 0;
2004-05-04 22:28:15 +04:00
cmd->hosttags = 0;
cmd->check_devs_used = 1;
dm_list_init(&cmd->arg_value_groups);
dm_list_init(&cmd->formats);
dm_list_init(&cmd->segtypes);
dm_list_init(&cmd->tags);
dm_list_init(&cmd->config_files);
label_init();
2002-11-18 17:01:16 +03:00
/* FIXME Make this configurable? */
reset_lvm_errno(1);
#ifndef VALGRIND_POOL
/* Set in/out stream buffering before glibc */
toolcontext: do not change stream for pthreaded programs With pthreaded daemons like 'dmeventd' using liblvm via plugin, lvm2 actually should not 'play' with streams at all - as there could be parallel outputs running. As a current quick workaround just disable change for pthreaded program (gettid() != getpid()). TODO: it's possible the change of buffering actually doesn't serve us any measurable benefit and could be dropped as whole later... Meanwhile this patch is fixing this occasional valgrind race report: Invalid read of size 4 at 0x571892C: vfprintf (in /usr/lib64/libc-2.26.9000.so) by 0x57216B3: fprintf (in /usr/lib64/libc-2.26.9000.so) by 0x5042886: dm_event_log (libdevmapper-event.c:925) by 0x10B015: _dmeventd_log (dmeventd.c:125) by 0x10D289: _unregister_for_event (dmeventd.c:1146) by 0x10E52E: _handle_request (dmeventd.c:1583) by 0x10E6D7: _do_process_request (dmeventd.c:1631) by 0x10E7C6: _process_request (dmeventd.c:1660) by 0x1101A4: main (dmeventd.c:2285) Address 0x6264d30 is 192 bytes inside a block of size 552 free'd at 0x4C2ED68: free (vg_replace_malloc.c:530) by 0x573907D: fclose@@GLIBC_2.2.5 (in /usr/lib64/libc-2.26.9000.so) by 0x6AC5C00: reopen_standard_stream (log.c:189) by 0x6A8E62C: destroy_toolcontext (toolcontext.c:2271) by 0x6BA5C22: lvm_fin (lvmcmdline.c:3339) by 0x6BD5EF3: lvm2_exit (lvmcmdlib.c:123) by 0x6856013: dmeventd_lvm2_exit (dmeventd_lvm.c:103) by 0x66535B8: unregister_device (dmeventd_thin.c:432) by 0x10CBBC: _do_unregister_device (dmeventd.c:926) by 0x10CD74: _monitor_unregister (dmeventd.c:979) by 0x10D094: _monitor_thread (dmeventd.c:1066) by 0x54B35E0: start_thread (in /usr/lib64/libpthread-2.26.9000.so) by 0x57C30EE: clone (in /usr/lib64/libc-2.26.9000.so) Block was alloc'd at at 0x4C2DBBB: malloc (vg_replace_malloc.c:299) by 0x573932B: fdopen@@GLIBC_2.2.5 (in /usr/lib64/libc-2.26.9000.so) by 0x6AC5DC2: reopen_standard_stream (log.c:200) by 0x6A8D11D: create_toolcontext (toolcontext.c:1898) by 0x6BA5B6B: init_lvm (lvmcmdline.c:3319) by 0x6BD5BC8: cmdlib_lvm2_init (lvmcmdlib.c:34) by 0x6BD5F04: lvm2_init (lvm2cmd.c:20) by 0x6855EA7: dmeventd_lvm2_init (dmeventd_lvm.c:67) by 0x665305F: register_device (dmeventd_thin.c:352) by 0x10CB7A: _do_register_device (dmeventd.c:916) by 0x10CEE4: _monitor_thread (dmeventd.c:1006) by 0x54B35E0: start_thread (in /usr/lib64/libpthread-2.26.9000.so) by 0x57C30EE: clone (in /usr/lib64/libc-2.26.9000.so) .... Process terminating with default action of signal 6 (SIGABRT): dumping core at 0x570016B: raise (in /usr/lib64/libc-2.26.9000.so) by 0x5701520: abort (in /usr/lib64/libc-2.26.9000.so) by 0x57437D8: __libc_message (in /usr/lib64/libc-2.26.9000.so) by 0x5743831: __libc_fatal (in /usr/lib64/libc-2.26.9000.so) by 0x5744056: _IO_vtable_check (in /usr/lib64/libc-2.26.9000.so) by 0x574751C: __overflow (in /usr/lib64/libc-2.26.9000.so) by 0x574191A: fputc (in /usr/lib64/libc-2.26.9000.so) by 0x50428E3: dm_event_log (libdevmapper-event.c:934) by 0x10B015: _dmeventd_log (dmeventd.c:125) by 0x10D289: _unregister_for_event (dmeventd.c:1146) by 0x10E52E: _handle_request (dmeventd.c:1583) by 0x10E6D7: _do_process_request (dmeventd.c:1631) by 0x10E7C6: _process_request (dmeventd.c:1660) by 0x1101A4: main (dmeventd.c:2285)
2018-02-09 02:26:22 +03:00
if (set_buffering
#ifdef SYS_gettid
/* For threaded programs no changes of streams */
/* On linux gettid() is implemented only via syscall */
&& (syscall(SYS_gettid) == getpid())
#endif
) {
/* Allocate 2 buffers */
if (!(cmd->linebuffer = malloc(2 * _linebuffer_size))) {
log_error("Failed to allocate line buffer.");
goto out;
}
/* nohup might set stdin O_WRONLY ! */
if (is_valid_fd(STDIN_FILENO) &&
((flags = fcntl(STDIN_FILENO, F_GETFL)) > 0) &&
(flags & O_ACCMODE) != O_WRONLY) {
2016-07-08 17:47:51 +03:00
if (!reopen_standard_stream(&stdin, "r"))
goto_out;
if (setvbuf(stdin, cmd->linebuffer, _IOLBF, _linebuffer_size)) {
log_sys_error("setvbuf", "");
goto out;
}
}
if (is_valid_fd(STDOUT_FILENO) &&
((flags = fcntl(STDOUT_FILENO, F_GETFL)) > 0) &&
(flags & O_ACCMODE) != O_RDONLY) {
2016-07-08 17:47:51 +03:00
if (!reopen_standard_stream(&stdout, "w"))
goto_out;
if (setvbuf(stdout, cmd->linebuffer + _linebuffer_size,
_IOLBF, _linebuffer_size)) {
log_sys_error("setvbuf", "");
goto out;
}
}
/* Buffers are used for lines without '\n' */
toolcontext: do not change stream for pthreaded programs With pthreaded daemons like 'dmeventd' using liblvm via plugin, lvm2 actually should not 'play' with streams at all - as there could be parallel outputs running. As a current quick workaround just disable change for pthreaded program (gettid() != getpid()). TODO: it's possible the change of buffering actually doesn't serve us any measurable benefit and could be dropped as whole later... Meanwhile this patch is fixing this occasional valgrind race report: Invalid read of size 4 at 0x571892C: vfprintf (in /usr/lib64/libc-2.26.9000.so) by 0x57216B3: fprintf (in /usr/lib64/libc-2.26.9000.so) by 0x5042886: dm_event_log (libdevmapper-event.c:925) by 0x10B015: _dmeventd_log (dmeventd.c:125) by 0x10D289: _unregister_for_event (dmeventd.c:1146) by 0x10E52E: _handle_request (dmeventd.c:1583) by 0x10E6D7: _do_process_request (dmeventd.c:1631) by 0x10E7C6: _process_request (dmeventd.c:1660) by 0x1101A4: main (dmeventd.c:2285) Address 0x6264d30 is 192 bytes inside a block of size 552 free'd at 0x4C2ED68: free (vg_replace_malloc.c:530) by 0x573907D: fclose@@GLIBC_2.2.5 (in /usr/lib64/libc-2.26.9000.so) by 0x6AC5C00: reopen_standard_stream (log.c:189) by 0x6A8E62C: destroy_toolcontext (toolcontext.c:2271) by 0x6BA5C22: lvm_fin (lvmcmdline.c:3339) by 0x6BD5EF3: lvm2_exit (lvmcmdlib.c:123) by 0x6856013: dmeventd_lvm2_exit (dmeventd_lvm.c:103) by 0x66535B8: unregister_device (dmeventd_thin.c:432) by 0x10CBBC: _do_unregister_device (dmeventd.c:926) by 0x10CD74: _monitor_unregister (dmeventd.c:979) by 0x10D094: _monitor_thread (dmeventd.c:1066) by 0x54B35E0: start_thread (in /usr/lib64/libpthread-2.26.9000.so) by 0x57C30EE: clone (in /usr/lib64/libc-2.26.9000.so) Block was alloc'd at at 0x4C2DBBB: malloc (vg_replace_malloc.c:299) by 0x573932B: fdopen@@GLIBC_2.2.5 (in /usr/lib64/libc-2.26.9000.so) by 0x6AC5DC2: reopen_standard_stream (log.c:200) by 0x6A8D11D: create_toolcontext (toolcontext.c:1898) by 0x6BA5B6B: init_lvm (lvmcmdline.c:3319) by 0x6BD5BC8: cmdlib_lvm2_init (lvmcmdlib.c:34) by 0x6BD5F04: lvm2_init (lvm2cmd.c:20) by 0x6855EA7: dmeventd_lvm2_init (dmeventd_lvm.c:67) by 0x665305F: register_device (dmeventd_thin.c:352) by 0x10CB7A: _do_register_device (dmeventd.c:916) by 0x10CEE4: _monitor_thread (dmeventd.c:1006) by 0x54B35E0: start_thread (in /usr/lib64/libpthread-2.26.9000.so) by 0x57C30EE: clone (in /usr/lib64/libc-2.26.9000.so) .... Process terminating with default action of signal 6 (SIGABRT): dumping core at 0x570016B: raise (in /usr/lib64/libc-2.26.9000.so) by 0x5701520: abort (in /usr/lib64/libc-2.26.9000.so) by 0x57437D8: __libc_message (in /usr/lib64/libc-2.26.9000.so) by 0x5743831: __libc_fatal (in /usr/lib64/libc-2.26.9000.so) by 0x5744056: _IO_vtable_check (in /usr/lib64/libc-2.26.9000.so) by 0x574751C: __overflow (in /usr/lib64/libc-2.26.9000.so) by 0x574191A: fputc (in /usr/lib64/libc-2.26.9000.so) by 0x50428E3: dm_event_log (libdevmapper-event.c:934) by 0x10B015: _dmeventd_log (dmeventd.c:125) by 0x10D289: _unregister_for_event (dmeventd.c:1146) by 0x10E52E: _handle_request (dmeventd.c:1583) by 0x10E6D7: _do_process_request (dmeventd.c:1631) by 0x10E7C6: _process_request (dmeventd.c:1660) by 0x1101A4: main (dmeventd.c:2285)
2018-02-09 02:26:22 +03:00
} else if (!set_buffering)
/* Without buffering, must not use stdin/stdout */
init_silent(1);
#endif
/*
* Environment variable LVM_SYSTEM_DIR overrides this below.
*/
strncpy(cmd->system_dir, (system_dir) ? system_dir : DEFAULT_SYS_DIR,
sizeof(cmd->system_dir) - 1);
2002-11-18 17:01:16 +03:00
if (!_get_env_vars(cmd))
goto_out;
2002-11-18 17:01:16 +03:00
/* Create system directory if it doesn't already exist */
if (*cmd->system_dir && !dm_create_dir(cmd->system_dir)) {
log_error("Failed to create LVM2 system dir for metadata backups, config "
"files and internal cache.");
log_error("Set environment variable LVM_SYSTEM_DIR to alternative location "
"or empty string.");
goto out;
}
2002-11-18 17:01:16 +03:00
if (!(cmd->libmem = dm_pool_create("library", 4 * 1024))) {
2004-03-08 20:25:59 +03:00
log_error("Library memory pool creation failed");
goto out;
2004-03-08 20:25:59 +03:00
}
if (!(cmd->mem = dm_pool_create("command", 4 * 1024))) {
log_error("Command memory pool creation failed");
goto out;
}
if (!(cmd->pending_delete_mem = dm_pool_create("pending_delete", 1024)))
goto_out;
2004-05-04 22:28:15 +04:00
if (!_init_lvm_conf(cmd))
goto_out;
2004-05-04 22:28:15 +04:00
_init_logging(cmd);
if (!_init_hostname(cmd))
goto_out;
2004-05-04 22:28:15 +04:00
if (!_init_tags(cmd, cmd->cft))
goto_out;
2004-05-04 22:28:15 +04:00
/* Load lvmlocal.conf */
if (*cmd->system_dir && !_load_config_file(cmd, "", 1))
goto_out;
2004-05-04 22:28:15 +04:00
if (!_init_tag_configs(cmd))
goto_out;
2004-05-04 22:28:15 +04:00
if (!(cmd->cft = _merge_config_files(cmd, cmd->cft)))
goto_out;
2004-05-04 22:28:15 +04:00
2002-11-18 17:01:16 +03:00
if (!_process_config(cmd))
goto_out;
2002-11-18 17:01:16 +03:00
if (!_init_profiles(cmd))
goto_out;
if (!(cmd->dev_types = create_dev_types(cmd->proc_dir,
find_config_tree_array(cmd, devices_types_CFG, NULL))))
goto_out;
init_use_aio(find_config_tree_bool(cmd, global_use_aio_CFG, NULL));
2002-11-18 17:01:16 +03:00
if (!_init_dev_cache(cmd))
goto_out;
2002-11-18 17:01:16 +03:00
device usage based on devices file The LVM devices file lists devices that lvm can use. The default file is /etc/lvm/devices/system.devices, and the lvmdevices(8) command is used to add or remove device entries. If the file does not exist, or if lvm.conf includes use_devicesfile=0, then lvm will not use a devices file. When the devices file is in use, the regex filter is not used, and the filter settings in lvm.conf or on the command line are ignored. LVM records devices in the devices file using hardware-specific IDs, such as the WWID, and attempts to use subsystem-specific IDs for virtual device types. These device IDs are also written in the VG metadata. When no hardware or virtual ID is available, lvm falls back using the unstable device name as the device ID. When devnames are used, lvm performs extra scanning to find devices if their devname changes, e.g. after reboot. When proper device IDs are used, an lvm command will not look at devices outside the devices file, but when devnames are used as a fallback, lvm will scan devices outside the devices file to locate PVs on renamed devices. A config setting search_for_devnames can be used to control the scanning for renamed devname entries. Related to the devices file, the new command option --devices <devnames> allows a list of devices to be specified for the command to use, overriding the devices file. The listed devices act as a sort of devices file in terms of limiting which devices lvm will see and use. Devices that are not listed will appear to be missing to the lvm command. Multiple devices files can be kept in /etc/lvm/devices, which allows lvm to be used with different sets of devices, e.g. system devices do not need to be exposed to a specific application, and the application can use lvm on its own set of devices that are not exposed to the system. The option --devicesfile <filename> is used to select the devices file to use with the command. Without the option set, the default system devices file is used. Setting --devicesfile "" causes lvm to not use a devices file. An existing, empty devices file means lvm will see no devices. The new command vgimportdevices adds PVs from a VG to the devices file and updates the VG metadata to include the device IDs. vgimportdevices -a will import all VGs into the system devices file. LVM commands run by dmeventd not use a devices file by default, and will look at all devices on the system. A devices file can be created for dmeventd (/etc/lvm/devices/dmeventd.devices) If this file exists, lvm commands run by dmeventd will use it. Internal implementaion: - device_ids_read - read the devices file . add struct dev_use (du) to cmd->use_devices for each devices file entry - dev_cache_scan - get /dev entries . add struct device (dev) to dev_cache for each device on the system - device_ids_match - match devices file entries to /dev entries . match each du on cmd->use_devices to a dev in dev_cache, using device ID . on match, set du->dev, dev->id, dev->flags MATCHED_USE_ID - label_scan - read lvm headers and metadata from devices . filters are applied, those that do not need data from the device . filter-deviceid skips devs without MATCHED_USE_ID, i.e. skips /dev entries that are not listed in the devices file . read lvm label from dev . filters are applied, those that use data from the device . read lvm metadata from dev . add info/vginfo structs for PVs/VGs (info is "lvmcache") - device_ids_find_renamed_devs - handle devices with unstable devname ID where devname changed . this step only needed when devs do not have proper device IDs, and their dev names change, e.g. after reboot sdb becomes sdc. . detect incorrect match because PVID in the devices file entry does not match the PVID found when the device was read above . undo incorrect match between du and dev above . search system devices for new location of PVID . update devices file with new devnames for PVIDs on renamed devices . label_scan the renamed devs - continue with command processing
2020-06-23 21:25:41 +03:00
devices_file_init(cmd);
memlock_init(cmd);
2002-11-18 17:01:16 +03:00
if (!_init_formats(cmd))
goto_out;
2002-11-18 17:01:16 +03:00
if (!lvmcache_init(cmd))
goto_out;
/* FIXME: move into lvmcache_init */
if (!init_lvmcache_orphans(cmd))
goto_out;
2004-05-05 01:25:57 +04:00
if (!_init_segtypes(cmd))
goto_out;
2004-05-05 01:25:57 +04:00
if (!_init_backup(cmd))
goto_out;
_init_rand(cmd);
_init_globals(cmd);
if (set_connections && !init_connections(cmd))
goto_out;
if (set_filters && !init_filters(cmd, 1))
goto_out;
2002-11-18 17:01:16 +03:00
cmd->current_settings = cmd->default_settings;
cmd->initialized.config = 1;
dm_list_init(&cmd->pending_delete);
out:
if (!cmd->initialized.config) {
destroy_toolcontext(cmd);
cmd = NULL;
}
2002-11-18 17:01:16 +03:00
return cmd;
}
static void _destroy_formats(struct cmd_context *cmd, struct dm_list *formats)
2002-11-18 17:01:16 +03:00
{
struct dm_list *fmtl, *tmp;
2002-11-18 17:01:16 +03:00
struct format_type *fmt;
dm_list_iterate_safe(fmtl, tmp, formats) {
fmt = dm_list_item(fmtl, struct format_type);
dm_list_del(&fmt->list);
2002-11-18 17:01:16 +03:00
fmt->ops->destroy(fmt);
}
}
static void _destroy_segtypes(struct dm_list *segtypes)
2004-05-05 01:25:57 +04:00
{
struct dm_list *sgtl, *tmp;
2004-05-05 01:25:57 +04:00
struct segment_type *segtype;
dm_list_iterate_safe(sgtl, tmp, segtypes) {
segtype = dm_list_item(sgtl, struct segment_type);
dm_list_del(&segtype->list);
2004-05-05 01:25:57 +04:00
segtype->ops->destroy(segtype);
}
}
static void _destroy_dev_types(struct cmd_context *cmd)
{
if (!cmd->dev_types)
return;
free(cmd->dev_types);
cmd->dev_types = NULL;
}
static void _destroy_filters(struct cmd_context *cmd)
{
if (cmd->filter) {
cmd->filter->destroy(cmd->filter);
cmd->filter = NULL;
}
cmd->initialized.filters = 0;
}
int refresh_filters(struct cmd_context *cmd)
{
int r, saved_ignore_suspended_devices = ignore_suspended_devices();
if (!cmd->initialized.filters)
/* if filters not initialized, there's nothing to refresh */
return 1;
_destroy_filters(cmd);
if (!(r = init_filters(cmd, 0)))
stack;
/*
* During repair code must not reset suspended flag.
*/
init_ignore_suspended_devices(saved_ignore_suspended_devices);
return r;
}
2004-05-04 22:28:15 +04:00
int refresh_toolcontext(struct cmd_context *cmd)
{
struct dm_config_tree *cft_cmdline, *cft_tmp;
config: differentiate command and metadata profiles and consolidate profile handling code - When defining configuration source, the code now uses separate CONFIG_PROFILE_COMMAND and CONFIG_PROFILE_METADATA markers (before, it was just CONFIG_PROFILE that did not make the difference between the two). This helps when checking the configuration if it contains correct set of options which are all in either command-profilable or metadata-profilable group without mixing these groups together - so it's a firm distinction. The "command profile" can't contain "metadata profile" and vice versa! This is strictly checked and if the settings are mixed, such profile is rejected and it's not used. So in the end, the CONFIG_PROFILE_COMMAND set of options and CONFIG_PROFILE_METADATA are mutually exclusive sets. - Marking configuration with one or the other marker will also determine the way these configuration sources are positioned in the configuration cascade which is now: CONFIG_STRING -> CONFIG_PROFILE_COMMAND -> CONFIG_PROFILE_METADATA -> CONFIG_FILE/CONFIG_MERGED_FILES - Marking configuration with one or the other marker will also make it possible to issue a command context refresh (will be probably a part of a future patch) if needed for settings in global profile set. For settings in metadata profile set this is impossible since we can't refresh cmd context in the middle of reading VG/LV metadata and for each VG/LV separately because each VG/LV can have a different metadata profile assinged and it's not possible to change these settings at this level. - When command profile is incorrect, it's rejected *and also* the command exits immediately - the profile *must* be correct for the command that was run with a profile to be executed. Before this patch, when the profile was found incorrect, there was just the warning message and the command continued without profile applied. But it's more correct to exit immediately in this case. - When metadata profile is incorrect, we reject it during command runtime (as we know the profile name from metadata and not early from command line as it is in case of command profiles) and we *do continue* with the command as we're in the middle of operation. Also, the metadata profile is applied directly and on the fly on find_config_tree_* fn call and even if the metadata profile is found incorrect, we still need to return the non-profiled value as found in the other configuration provided or default value. To exit immediately even in this case, we'd need to refactor existing find_config_tree_* fns so they can return error. Currently, these fns return only config values (which end up with default values in the end if the config is not found). - To check the profile validity before use to be sure it's correct, one can use : lvm dumpconfig --commandprofile/--metadataprofile ProfileName --validate (the --commandprofile/--metadataprofile for dumpconfig will come as part of the subsequent patch) - This patch also adds a reference to --commandprofile and --metadataprofile in the cmd help string (which was missing before for the --profile for some commands). We do not mention --profile now as people should use --commandprofile or --metadataprofile directly. However, the --profile is still supported for backward compatibility and it's translated as: --profile == --metadataprofile for lvcreate, vgcreate, lvchange and vgchange (as these commands are able to attach profile to metadata) --profile == --commandprofile for all the other commands (--metadataprofile is not allowed there as it makes no sense) - This patch also contains some cleanups to make the code handling the profiles more readable...
2014-05-20 16:13:10 +04:00
const char *profile_command_name, *profile_metadata_name;
struct profile *profile;
2004-05-04 22:28:15 +04:00
log_verbose("Reloading config files");
/*
* Don't update the persistent filter cache as we will
* perform a full rescan.
*/
2004-05-04 22:28:15 +04:00
activation_release();
hints_exit(cmd);
lvmcache_destroy(cmd, 0, 0);
label_scan_destroy(cmd);
2004-05-04 22:28:15 +04:00
label_exit();
2004-05-05 01:25:57 +04:00
_destroy_segtypes(&cmd->segtypes);
_destroy_formats(cmd, &cmd->formats);
if (!dev_cache_exit())
stack;
_destroy_dev_types(cmd);
2004-05-04 22:28:15 +04:00
_destroy_tags(cmd);
/* save config string passed on the command line */
cft_cmdline = remove_config_tree_by_source(cmd, CONFIG_STRING);
/* save the global profile name used */
config: differentiate command and metadata profiles and consolidate profile handling code - When defining configuration source, the code now uses separate CONFIG_PROFILE_COMMAND and CONFIG_PROFILE_METADATA markers (before, it was just CONFIG_PROFILE that did not make the difference between the two). This helps when checking the configuration if it contains correct set of options which are all in either command-profilable or metadata-profilable group without mixing these groups together - so it's a firm distinction. The "command profile" can't contain "metadata profile" and vice versa! This is strictly checked and if the settings are mixed, such profile is rejected and it's not used. So in the end, the CONFIG_PROFILE_COMMAND set of options and CONFIG_PROFILE_METADATA are mutually exclusive sets. - Marking configuration with one or the other marker will also determine the way these configuration sources are positioned in the configuration cascade which is now: CONFIG_STRING -> CONFIG_PROFILE_COMMAND -> CONFIG_PROFILE_METADATA -> CONFIG_FILE/CONFIG_MERGED_FILES - Marking configuration with one or the other marker will also make it possible to issue a command context refresh (will be probably a part of a future patch) if needed for settings in global profile set. For settings in metadata profile set this is impossible since we can't refresh cmd context in the middle of reading VG/LV metadata and for each VG/LV separately because each VG/LV can have a different metadata profile assinged and it's not possible to change these settings at this level. - When command profile is incorrect, it's rejected *and also* the command exits immediately - the profile *must* be correct for the command that was run with a profile to be executed. Before this patch, when the profile was found incorrect, there was just the warning message and the command continued without profile applied. But it's more correct to exit immediately in this case. - When metadata profile is incorrect, we reject it during command runtime (as we know the profile name from metadata and not early from command line as it is in case of command profiles) and we *do continue* with the command as we're in the middle of operation. Also, the metadata profile is applied directly and on the fly on find_config_tree_* fn call and even if the metadata profile is found incorrect, we still need to return the non-profiled value as found in the other configuration provided or default value. To exit immediately even in this case, we'd need to refactor existing find_config_tree_* fns so they can return error. Currently, these fns return only config values (which end up with default values in the end if the config is not found). - To check the profile validity before use to be sure it's correct, one can use : lvm dumpconfig --commandprofile/--metadataprofile ProfileName --validate (the --commandprofile/--metadataprofile for dumpconfig will come as part of the subsequent patch) - This patch also adds a reference to --commandprofile and --metadataprofile in the cmd help string (which was missing before for the --profile for some commands). We do not mention --profile now as people should use --commandprofile or --metadataprofile directly. However, the --profile is still supported for backward compatibility and it's translated as: --profile == --metadataprofile for lvcreate, vgcreate, lvchange and vgchange (as these commands are able to attach profile to metadata) --profile == --commandprofile for all the other commands (--metadataprofile is not allowed there as it makes no sense) - This patch also contains some cleanups to make the code handling the profiles more readable...
2014-05-20 16:13:10 +04:00
profile_command_name = cmd->profile_params->global_command_profile ?
cmd->profile_params->global_command_profile->name : NULL;
profile_metadata_name = cmd->profile_params->global_metadata_profile ?
cmd->profile_params->global_metadata_profile->name : NULL;
_destroy_config(cmd);
2004-05-04 22:28:15 +04:00
cmd->initialized.config = 0;
2004-05-04 22:28:15 +04:00
cmd->hosttags = 0;
cmd->lib_dir = NULL;
2004-05-04 22:28:15 +04:00
if (!_init_lvm_conf(cmd))
return_0;
2004-05-04 22:28:15 +04:00
/* Temporary duplicate cft pointer holding lvm.conf - replaced later */
cft_tmp = cmd->cft;
if (cft_cmdline)
cmd->cft = dm_config_insert_cascaded_tree(cft_cmdline, cft_tmp);
/* Reload the global profile. */
config: differentiate command and metadata profiles and consolidate profile handling code - When defining configuration source, the code now uses separate CONFIG_PROFILE_COMMAND and CONFIG_PROFILE_METADATA markers (before, it was just CONFIG_PROFILE that did not make the difference between the two). This helps when checking the configuration if it contains correct set of options which are all in either command-profilable or metadata-profilable group without mixing these groups together - so it's a firm distinction. The "command profile" can't contain "metadata profile" and vice versa! This is strictly checked and if the settings are mixed, such profile is rejected and it's not used. So in the end, the CONFIG_PROFILE_COMMAND set of options and CONFIG_PROFILE_METADATA are mutually exclusive sets. - Marking configuration with one or the other marker will also determine the way these configuration sources are positioned in the configuration cascade which is now: CONFIG_STRING -> CONFIG_PROFILE_COMMAND -> CONFIG_PROFILE_METADATA -> CONFIG_FILE/CONFIG_MERGED_FILES - Marking configuration with one or the other marker will also make it possible to issue a command context refresh (will be probably a part of a future patch) if needed for settings in global profile set. For settings in metadata profile set this is impossible since we can't refresh cmd context in the middle of reading VG/LV metadata and for each VG/LV separately because each VG/LV can have a different metadata profile assinged and it's not possible to change these settings at this level. - When command profile is incorrect, it's rejected *and also* the command exits immediately - the profile *must* be correct for the command that was run with a profile to be executed. Before this patch, when the profile was found incorrect, there was just the warning message and the command continued without profile applied. But it's more correct to exit immediately in this case. - When metadata profile is incorrect, we reject it during command runtime (as we know the profile name from metadata and not early from command line as it is in case of command profiles) and we *do continue* with the command as we're in the middle of operation. Also, the metadata profile is applied directly and on the fly on find_config_tree_* fn call and even if the metadata profile is found incorrect, we still need to return the non-profiled value as found in the other configuration provided or default value. To exit immediately even in this case, we'd need to refactor existing find_config_tree_* fns so they can return error. Currently, these fns return only config values (which end up with default values in the end if the config is not found). - To check the profile validity before use to be sure it's correct, one can use : lvm dumpconfig --commandprofile/--metadataprofile ProfileName --validate (the --commandprofile/--metadataprofile for dumpconfig will come as part of the subsequent patch) - This patch also adds a reference to --commandprofile and --metadataprofile in the cmd help string (which was missing before for the --profile for some commands). We do not mention --profile now as people should use --commandprofile or --metadataprofile directly. However, the --profile is still supported for backward compatibility and it's translated as: --profile == --metadataprofile for lvcreate, vgcreate, lvchange and vgchange (as these commands are able to attach profile to metadata) --profile == --commandprofile for all the other commands (--metadataprofile is not allowed there as it makes no sense) - This patch also contains some cleanups to make the code handling the profiles more readable...
2014-05-20 16:13:10 +04:00
if (profile_command_name) {
if (!(profile = add_profile(cmd, profile_command_name, CONFIG_PROFILE_COMMAND)) ||
!override_config_tree_from_profile(cmd, profile))
return_0;
}
if (profile_metadata_name) {
if (!(profile = add_profile(cmd, profile_metadata_name, CONFIG_PROFILE_METADATA)) ||
!override_config_tree_from_profile(cmd, profile))
return_0;
}
/* Uses cmd->cft i.e. cft_cmdline + lvm.conf */
2004-05-04 22:28:15 +04:00
_init_logging(cmd);
/* Init tags from lvm.conf. */
if (!_init_tags(cmd, cft_tmp))
return_0;
2004-05-04 22:28:15 +04:00
/* Load lvmlocal.conf */
if (*cmd->system_dir && !_load_config_file(cmd, "", 1))
return_0;
/* Doesn't change cmd->cft */
2004-05-04 22:28:15 +04:00
if (!_init_tag_configs(cmd))
return_0;
2004-05-04 22:28:15 +04:00
/* Merge all the tag config files with lvm.conf, returning a
* fresh cft pointer in place of cft_tmp. */
if (!(cmd->cft = _merge_config_files(cmd, cft_tmp)))
return_0;
2004-05-04 22:28:15 +04:00
/* Finally we can make the proper, fully-merged, cmd->cft */
if (cft_cmdline)
cmd->cft = dm_config_insert_cascaded_tree(cft_cmdline, cmd->cft);
2004-05-04 22:28:15 +04:00
if (!_process_config(cmd))
return_0;
2004-05-04 22:28:15 +04:00
if (!_init_profiles(cmd))
return_0;
if (!(cmd->dev_types = create_dev_types(cmd->proc_dir,
find_config_tree_array(cmd, devices_types_CFG, NULL))))
return_0;
2004-05-04 22:28:15 +04:00
if (!_init_dev_cache(cmd))
return_0;
2004-05-04 22:28:15 +04:00
device usage based on devices file The LVM devices file lists devices that lvm can use. The default file is /etc/lvm/devices/system.devices, and the lvmdevices(8) command is used to add or remove device entries. If the file does not exist, or if lvm.conf includes use_devicesfile=0, then lvm will not use a devices file. When the devices file is in use, the regex filter is not used, and the filter settings in lvm.conf or on the command line are ignored. LVM records devices in the devices file using hardware-specific IDs, such as the WWID, and attempts to use subsystem-specific IDs for virtual device types. These device IDs are also written in the VG metadata. When no hardware or virtual ID is available, lvm falls back using the unstable device name as the device ID. When devnames are used, lvm performs extra scanning to find devices if their devname changes, e.g. after reboot. When proper device IDs are used, an lvm command will not look at devices outside the devices file, but when devnames are used as a fallback, lvm will scan devices outside the devices file to locate PVs on renamed devices. A config setting search_for_devnames can be used to control the scanning for renamed devname entries. Related to the devices file, the new command option --devices <devnames> allows a list of devices to be specified for the command to use, overriding the devices file. The listed devices act as a sort of devices file in terms of limiting which devices lvm will see and use. Devices that are not listed will appear to be missing to the lvm command. Multiple devices files can be kept in /etc/lvm/devices, which allows lvm to be used with different sets of devices, e.g. system devices do not need to be exposed to a specific application, and the application can use lvm on its own set of devices that are not exposed to the system. The option --devicesfile <filename> is used to select the devices file to use with the command. Without the option set, the default system devices file is used. Setting --devicesfile "" causes lvm to not use a devices file. An existing, empty devices file means lvm will see no devices. The new command vgimportdevices adds PVs from a VG to the devices file and updates the VG metadata to include the device IDs. vgimportdevices -a will import all VGs into the system devices file. LVM commands run by dmeventd not use a devices file by default, and will look at all devices on the system. A devices file can be created for dmeventd (/etc/lvm/devices/dmeventd.devices) If this file exists, lvm commands run by dmeventd will use it. Internal implementaion: - device_ids_read - read the devices file . add struct dev_use (du) to cmd->use_devices for each devices file entry - dev_cache_scan - get /dev entries . add struct device (dev) to dev_cache for each device on the system - device_ids_match - match devices file entries to /dev entries . match each du on cmd->use_devices to a dev in dev_cache, using device ID . on match, set du->dev, dev->id, dev->flags MATCHED_USE_ID - label_scan - read lvm headers and metadata from devices . filters are applied, those that do not need data from the device . filter-deviceid skips devs without MATCHED_USE_ID, i.e. skips /dev entries that are not listed in the devices file . read lvm label from dev . filters are applied, those that use data from the device . read lvm metadata from dev . add info/vginfo structs for PVs/VGs (info is "lvmcache") - device_ids_find_renamed_devs - handle devices with unstable devname ID where devname changed . this step only needed when devs do not have proper device IDs, and their dev names change, e.g. after reboot sdb becomes sdc. . detect incorrect match because PVID in the devices file entry does not match the PVID found when the device was read above . undo incorrect match between du and dev above . search system devices for new location of PVID . update devices file with new devnames for PVIDs on renamed devices . label_scan the renamed devs - continue with command processing
2020-06-23 21:25:41 +03:00
devices_file_init(cmd);
2004-05-04 22:28:15 +04:00
if (!_init_formats(cmd))
return_0;
2004-05-04 22:28:15 +04:00
if (!lvmcache_init(cmd))
return_0;
if (!init_lvmcache_orphans(cmd))
return_0;
2004-05-05 01:25:57 +04:00
if (!_init_segtypes(cmd))
return_0;
2004-05-05 01:25:57 +04:00
if (!_init_backup(cmd))
return_0;
cmd->initialized.config = 1;
if (!dm_list_empty(&cmd->pending_delete)) {
log_debug(INTERNAL_ERROR "Unprocessed pending delete for %d devices.",
dm_list_size(&cmd->pending_delete));
dm_list_init(&cmd->pending_delete);
}
if (cmd->initialized.connections && !init_connections(cmd))
return_0;
if (!refresh_filters(cmd))
return_0;
reset_lvm_errno(1);
2004-05-04 22:28:15 +04:00
return 1;
}
2002-11-18 17:01:16 +03:00
void destroy_toolcontext(struct cmd_context *cmd)
{
struct dm_config_tree *cft_cmdline;
int flags;
archive_exit(cmd);
backup_exit(cmd);
hints_exit(cmd);
lvmcache_destroy(cmd, 0, 0);
label_scan_destroy(cmd);
2002-11-18 17:01:16 +03:00
label_exit();
2004-05-05 01:25:57 +04:00
_destroy_segtypes(&cmd->segtypes);
_destroy_formats(cmd, &cmd->formats);
_destroy_filters(cmd);
2002-11-18 17:01:16 +03:00
dev_cache_exit();
_destroy_dev_types(cmd);
2004-05-04 22:28:15 +04:00
_destroy_tags(cmd);
2011-09-02 05:59:07 +04:00
if ((cft_cmdline = remove_config_tree_by_source(cmd, CONFIG_STRING)))
config_destroy(cft_cmdline);
if (cmd->cft_def_hash)
dm_hash_destroy(cmd->cft_def_hash);
dm_device_list_destroy(&cmd->cache_dm_devs);
#ifndef VALGRIND_POOL
if (cmd->linebuffer) {
/* Reset stream buffering to defaults */
if (is_valid_fd(STDIN_FILENO) &&
((flags = fcntl(STDIN_FILENO, F_GETFL)) > 0) &&
(flags & O_ACCMODE) != O_WRONLY) {
2016-07-08 17:47:51 +03:00
if (reopen_standard_stream(&stdin, "r"))
setlinebuf(stdin);
2016-07-08 17:47:51 +03:00
else
cmd->linebuffer = NULL; /* Leave buffer in place (deliberate leak) */
}
if (is_valid_fd(STDOUT_FILENO) &&
((flags = fcntl(STDOUT_FILENO, F_GETFL)) > 0) &&
(flags & O_ACCMODE) != O_RDONLY) {
2016-07-08 17:47:51 +03:00
if (reopen_standard_stream(&stdout, "w"))
setlinebuf(stdout);
2016-07-08 17:47:51 +03:00
else
cmd->linebuffer = NULL; /* Leave buffer in place (deliberate leak) */
}
free(cmd->linebuffer);
}
#endif
destroy_config_context(cmd);
2002-11-18 17:01:16 +03:00
lvmpolld_disconnect();
2005-10-17 22:21:05 +04:00
activation_exit();
reset_log_duplicated();
2002-11-18 17:01:16 +03:00
fin_log();
fin_syslog();
reset_lvm_errno(0);
2002-11-18 17:01:16 +03:00
}