1
0
mirror of git://sourceware.org/git/lvm2.git synced 2024-10-28 11:55:55 +03:00
lvm2/lib/commands/toolcontext.c

1775 lines
44 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-2012 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
2002-11-18 17:01:16 +03:00
*/
#include "lib.h"
#include "toolcontext.h"
#include "metadata.h"
#include "defaults.h"
#include "lvm-string.h"
#include "activate.h"
#include "filter.h"
#include "label.h"
#include "lvm-file.h"
#include "format-text.h"
#include "display.h"
#include "memlock.h"
2004-03-08 21:13:22 +03:00
#include "str_list.h"
2004-09-16 22:40:56 +04:00
#include "segtype.h"
2004-05-05 01:25:57 +04:00
#include "lvmcache.h"
#include "lvmetad.h"
#include "archiver.h"
2002-11-18 17:01:16 +03:00
#ifdef HAVE_LIBDL
#include "sharedlib.h"
#endif
2002-11-18 17:01:16 +03:00
#ifdef LVM1_INTERNAL
#include "format1.h"
#endif
2004-06-07 23:10:21 +04:00
#ifdef POOL_INTERNAL
#include "format_pool.h"
#endif
2002-11-18 17:01:16 +03:00
#include <locale.h>
#include <sys/stat.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 linux
# include <malloc.h>
#endif
static const size_t linebuffer_size = 4096;
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;
}
}
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;
}
strncpy(buf, sys_mnt, buf_size);
}
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_node(cmd, log_debug_classes_CFG, NULL)))
return DEFAULT_LOGGED_DEBUG_CLASSES;
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, "lvmetad"))
debug_classes |= LOG_CLASS_LVMETAD;
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
log_verbose("Unrecognised value for log/debug_classes: %s", cv->v.str);
}
return debug_classes;
}
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);
2002-11-18 17:01:16 +03:00
if (cmd->default_settings.syslog != 1)
fin_syslog();
if (cmd->default_settings.syslog > 1)
init_syslog(cmd->default_settings.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_bool(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);
cmd->default_settings.cmd_name = find_config_tree_bool(cmd, log_command_names_CFG, NULL);
2002-11-18 17:01:16 +03:00
init_cmd_name(cmd->default_settings.cmd_name);
/* 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) {
release_log_memory();
fin_log();
init_log_file(log_file, append);
2004-03-08 21:13:22 +03:00
}
log_file = find_config_tree_str(cmd, log_activate_file_CFG, NULL);
if (log_file)
init_log_direct(log_file, append);
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);
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
dm_log_with_errno_init(print_log);
#endif
reset_log_duplicated();
reset_lvm_errno(1);
2002-11-18 17:01:16 +03:00
}
static int _check_disable_udev(const char *msg) {
if (getenv("DM_DISABLE_UDEV")) {
log_very_verbose("DM_DISABLE_UDEV environment variable set. "
"Overriding configuration to use "
"udev_rules=0, udev_sync=0, verify_udev_operations=1.");
if (udev_is_running())
log_warn("Udev is running and DM_DISABLE_UDEV environment variable is set. "
"Bypassing udev, LVM will %s.", msg);
return 1;
}
return 0;
}
static int _check_config(struct cmd_context *cmd)
{
if (!find_config_tree_bool(cmd, config_checks_CFG, NULL))
return 1;
if (!cmd->cft_check_handle) {
if (!(cmd->cft_check_handle = dm_pool_zalloc(cmd->libmem, sizeof(*cmd->cft_check_handle)))) {
log_error("Configuration check handle allocation failed.");
return 0;
}
}
cmd->cft_check_handle->cft = cmd->cft;
if (!config_def_check(cmd, cmd->cft_check_handle) &&
find_config_tree_bool(cmd, config_abort_on_errors_CFG, NULL)) {
log_error("LVM configuration invalid.");
return 0;
}
return 1;
}
2002-11-18 17:01:16 +03:00
static int _process_config(struct cmd_context *cmd)
{
mode_t old_umask;
const char *read_ahead;
struct stat st;
const struct dm_config_node *cn;
const struct dm_config_value *cv;
int64_t pv_min_kb;
const char *lvmetad_socket;
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("LVM-"))
return_0;
#endif
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);
2002-11-18 17:01:16 +03:00
set_activation(cmd->default_settings.activation);
cmd->auto_set_activation_skip = find_config_tree_bool(cmd, activation_auto_set_activation_skip_CFG, NULL);
cmd->default_settings.suffix = find_config_tree_bool(cmd, global_suffix_CFG, NULL);
if (!(cmd->default_settings.unit_factor =
units_to_bytes(find_config_tree_str(cmd, global_units_CFG, NULL),
&cmd->default_settings.unit_type))) {
log_error("Invalid units specification");
return 0;
}
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;
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";
if (strcmp(cmd->stripe_filler, "error")) {
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";
}
}
cmd->si_unit_consistency = find_config_tree_bool(cmd, global_si_unit_consistency_CFG, NULL);
if ((cn = find_config_tree_node(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));
init_detect_internal_vg_cache_corruption
(find_config_tree_bool(cmd, global_detect_internal_vg_cache_corruption_CFG, NULL));
lvmetad_disconnect();
lvmetad_socket = getenv("LVM_LVMETAD_SOCKET");
if (!lvmetad_socket)
lvmetad_socket = DEFAULT_RUN_DIR "/lvmetad.socket";
/* TODO?
lvmetad_socket = find_config_tree_str(cmd, "lvmetad/socket_path",
DEFAULT_RUN_DIR "/lvmetad.socket");
*/
lvmetad_set_socket(lvmetad_socket);
cn = find_config_tree_node(cmd, devices_global_filter_CFG, NULL);
lvmetad_set_token(cn ? cn->v : NULL);
if (find_config_tree_int(cmd, global_locking_type_CFG, NULL) == 3 &&
find_config_tree_bool(cmd, global_use_lvmetad_CFG, NULL)) {
log_warn("WARNING: configuration setting use_lvmetad overridden to 0 due to locking_type 3. "
"Clustered environment not supported by lvmetad yet.");
lvmetad_set_active(0);
} else
lvmetad_set_active(find_config_tree_bool(cmd, global_use_lvmetad_CFG, NULL));
lvmetad_init(cmd);
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;
if (!(tn = find_config_tree_node(cmd, tags_CFG_SECTION, NULL)) || !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_tree_bool(cmd, tags_hosttags_CFG, NULL)) {
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)
{
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 = "_";
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)))
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 first config file */
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;
}
2008-01-30 16:19:47 +03:00
if (!_load_config_file(cmd, ""))
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 str_list *sl;
/* Tag list may grow while inside this loop */
dm_list_iterate_items(sl, &cmd->tags) {
2008-01-30 16:19:47 +03:00
if (!_load_config_file(cmd, sl->str))
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)
{
static char default_dir[PATH_MAX];
const char *dir;
struct profile_params *pp;
if (!(pp = dm_pool_zalloc(cmd->libmem, sizeof(*pp)))) {
log_error("profile_params alloc failed");
return 0;
}
if (!(dir = find_config_tree_str(cmd, config_profile_dir_CFG, NULL))) {
if (dm_snprintf(default_dir, sizeof(default_dir), "%s/%s",
cmd->system_dir, DEFAULT_PROFILE_SUBDIR) == -1) {
log_error("Couldn't create default profile path '%s/%s'.",
cmd->system_dir, DEFAULT_PROFILE_SUBDIR);
return 0;
}
dir = default_dir;
}
pp->dir = dm_pool_strdup(cmd->libmem, dir);
dm_list_init(&pp->profiles_to_load);
dm_list_init(&pp->profiles);
cmd->profile_params = pp;
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;
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
remove_config_tree_by_source(cmd, CONFIG_FILE);
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) {
remove_config_tree_by_source(cmd, CONFIG_PROFILE);
dm_list_iterate_items(profile, &cmd->profile_params->profiles_to_load)
config_destroy(profile->cft);
dm_list_iterate_items(profile, &cmd->profile_params->profiles)
config_destroy(profile->cft);
dm_list_init(&cmd->profile_params->profiles_to_load);
dm_list_init(&cmd->profile_params->profiles);
}
/* 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
init_dev_disable_after_error_count(
find_config_tree_int(cmd, devices_disable_after_error_count_CFG, NULL));
if (!dev_cache_init(cmd))
return_0;
2002-11-18 17:01:16 +03:00
/*
* Override existing config and hardcode device_list_from_udev = 0 if:
* - udev is not running
* - udev is disabled using DM_DISABLE_UDEV environment variable
*/
if (_check_disable_udev("obtain device list by scanning device directory"))
device_list_from_udev = 0;
else
device_list_from_udev = udev_is_running() ?
find_config_tree_bool(cmd, devices_obtain_device_list_from_udev_CFG, NULL) : 0;
init_obtain_device_list_from_udev(device_list_from_udev);
if (!(cn = find_config_tree_node(cmd, devices_scan_CFG, NULL))) {
2002-11-18 17:01:16 +03:00
if (!dev_cache_add_dir("/dev")) {
log_error("Failed to add /dev to internal "
"device cache");
return 0;
}
log_verbose("device/scan not in config file: "
"Defaulting to /dev");
return 1;
}
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;
}
}
if (!(cn = find_config_tree_node(cmd, devices_loopfiles_CFG, NULL)))
return 1;
for (cv = cn->v; cv; cv = cv->next) {
if (cv->type != DM_CFG_STRING) {
log_error("Invalid string in config file: "
"devices/loopfiles");
return 0;
}
if (!dev_cache_add_loopfile(cv->v.str)) {
log_error("Failed to add loopfile %s to internal "
"device cache", cv->v.str);
return 0;
}
}
2002-11-18 17:01:16 +03:00
return 1;
}
#define MAX_FILTERS 6
2002-11-18 17:01:16 +03:00
static struct dev_filter *_init_filter_components(struct cmd_context *cmd)
{
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.
*/
/*
* sysfs filter. Only available on 2.6 kernels. Non-critical.
2008-01-30 17:00:02 +03:00
* Listed first because it's very efficient at eliminating
* unavailable devices.
*/
if (find_config_tree_bool(cmd, devices_sysfs_scan_CFG, NULL)) {
if ((filters[nr_filt] = sysfs_filter_create()))
nr_filt++;
}
2002-11-18 17:01:16 +03:00
/* regex filter. Optional. */
if (!(cn = find_config_tree_node(cmd, devices_filter_CFG, NULL)))
log_very_verbose("devices/filter not found in config file: "
"no regex filter installed");
else if (!(filters[nr_filt] = regex_filter_create(cn->v))) {
2002-11-18 17:01:16 +03:00
log_error("Failed to create regex device filter");
goto bad;
} else
nr_filt++;
2002-11-18 17:01:16 +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
/* 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++;
/* 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->dev_types)))
nr_filt++;
}
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
}
static int _init_filters(struct cmd_context *cmd, unsigned load_persistent_cache)
2002-11-18 17:01:16 +03:00
{
static char cache_file[PATH_MAX];
const char *dev_cache = NULL, *cache_dir, *cache_file_prefix;
struct dev_filter *f3 = NULL, *f4 = NULL, *toplevel_components[2] = { 0 };
2002-11-18 17:01:16 +03:00
struct stat st;
const struct dm_config_node *cn;
2002-11-18 17:01:16 +03:00
cmd->dump_filter = 0;
if (!(f3 = _init_filter_components(cmd)))
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));
/*
* If 'cache_dir' or 'cache_file_prefix' is set, ignore 'cache'.
*/
cache_dir = find_config_tree_str(cmd, devices_cache_dir_CFG, NULL);
cache_file_prefix = find_config_tree_str(cmd, devices_cache_file_prefix_CFG, NULL);
if (cache_dir || cache_file_prefix) {
if (dm_snprintf(cache_file, sizeof(cache_file),
"%s%s%s/%s.cache",
cache_dir ? "" : cmd->system_dir,
cache_dir ? "" : "/",
cache_dir ? : DEFAULT_CACHE_SUBDIR,
cache_file_prefix ? : DEFAULT_CACHE_FILE_PREFIX) < 0) {
log_error("Persistent cache filename too long.");
goto bad;
}
} else if (!(dev_cache = find_config_tree_str(cmd, devices_cache_CFG, NULL)) &&
(dm_snprintf(cache_file, sizeof(cache_file),
"%s/%s/%s.cache",
cmd->system_dir, DEFAULT_CACHE_SUBDIR,
DEFAULT_CACHE_FILE_PREFIX) < 0)) {
log_error("Persistent cache filename too long.");
goto bad;
}
if (!dev_cache)
dev_cache = cache_file;
if (!(f4 = persistent_filter_create(cmd->dev_types, f3, dev_cache))) {
log_verbose("Failed to create persistent device filter.");
goto bad;
2002-11-18 17:01:16 +03:00
}
/* Should we ever dump persistent filter state? */
if (find_config_tree_bool(cmd, devices_write_cache_state_CFG, NULL))
2002-11-18 17:01:16 +03:00
cmd->dump_filter = 1;
if (!*cmd->system_dir)
2002-11-18 17:01:16 +03:00
cmd->dump_filter = 0;
/*
* Only load persistent filter device cache on startup if it is newer
* than the config file and this is not a long-lived process. Also avoid
* it when lvmetad is enabled.
*/
if (!find_config_tree_bool(cmd, global_use_lvmetad_CFG, NULL) &&
load_persistent_cache && !cmd->is_long_lived &&
!stat(dev_cache, &st) &&
(st.st_ctime > config_file_timestamp(cmd->cft)) &&
!persistent_filter_load(f4, NULL))
2002-11-18 17:01:16 +03:00
log_verbose("Failed to load existing device cache from %s",
dev_cache);
2002-11-18 17:01:16 +03:00
if (!(cn = find_config_tree_node(cmd, devices_global_filter_CFG, NULL))) {
cmd->filter = f4;
} else if (!(cmd->lvmetad_filter = regex_filter_create(cn->v)))
goto_bad;
else {
toplevel_components[0] = cmd->lvmetad_filter;
toplevel_components[1] = f4;
if (!(cmd->filter = composite_filter_create(2, toplevel_components)))
goto_bad;
}
2002-11-18 17:01:16 +03:00
return 1;
bad:
if (f4)
f4->destroy(f4);
else if (f3)
f3->destroy(f3);
if (toplevel_components[0])
toplevel_components[0]->destroy(toplevel_components[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;
}
2002-11-18 17:01:16 +03:00
static int _init_formats(struct cmd_context *cmd)
{
const char *format;
struct format_type *fmt;
#ifdef HAVE_LIBDL
const struct dm_config_node *cn;
#endif
2002-11-18 17:01:16 +03:00
#ifdef LVM1_INTERNAL
if (!(fmt = init_lvm1_format(cmd)))
return 0;
fmt->library = NULL;
dm_list_add(&cmd->formats, &fmt->list);
2002-11-18 17:01:16 +03:00
#endif
2004-06-07 23:10:21 +04:00
#ifdef POOL_INTERNAL
if (!(fmt = init_pool_format(cmd)))
return 0;
fmt->library = NULL;
dm_list_add(&cmd->formats, &fmt->list);
2004-06-07 23:10:21 +04:00
#endif
#ifdef HAVE_LIBDL
/* Load any formats in shared libs if not static */
if (!is_static() &&
(cn = find_config_tree_node(cmd, global_format_libraries_CFG, NULL))) {
const struct dm_config_value *cv;
struct format_type *(*init_format_fn) (struct cmd_context *);
void *lib;
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: "
"global/format_libraries");
return 0;
}
if (!(lib = load_shared_library(cmd, cv->v.str,
2008-01-30 16:19:47 +03:00
"format", 0)))
return_0;
2002-11-18 17:01:16 +03:00
if (!(init_format_fn = dlsym(lib, "init_format"))) {
log_error("Shared library %s does not contain "
"format functions", cv->v.str);
dlclose(lib);
return 0;
}
if (!(fmt = init_format_fn(cmd))) {
dlclose(lib);
return_0;
}
2002-11-18 17:01:16 +03:00
fmt->library = lib;
dm_list_add(&cmd->formats, &fmt->list);
2002-11-18 17:01:16 +03:00
}
}
#endif
2002-11-18 17:01:16 +03:00
if (!(fmt = create_text_format(cmd)))
return 0;
fmt->library = NULL;
dm_list_add(&cmd->formats, &fmt->list);
2002-11-18 17:01:16 +03:00
cmd->fmt_backup = fmt;
format = find_config_tree_str(cmd, global_format_CFG, NULL);
2002-11-18 17:01:16 +03:00
dm_list_iterate_items(fmt, &cmd->formats) {
2002-11-18 17:01:16 +03:00
if (!strcasecmp(fmt->name, format) ||
(fmt->alias && !strcasecmp(fmt->alias, format))) {
cmd->default_settings.fmt_name = fmt->name;
cmd->fmt = fmt;
2002-11-18 17:01:16 +03:00
return 1;
}
}
log_error("_init_formats: Default format (%s) not found", format);
return 0;
}
int init_lvmcache_orphans(struct cmd_context *cmd)
{
struct format_type *fmt;
dm_list_iterate_items(fmt, &cmd->formats)
if (!lvmcache_add_orphan_vginfo(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;
segtype->cmd = seglib->cmd;
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;
}
static int _init_single_segtype(struct cmd_context *cmd,
struct segtype_library *seglib)
{
struct segment_type *(*init_segtype_fn) (struct cmd_context *);
struct segment_type *segtype;
if (!(init_segtype_fn = dlsym(seglib->lib, "init_segtype"))) {
log_error("Shared library %s does not contain segment type "
"functions", seglib->libname);
return 0;
}
if (!(segtype = init_segtype_fn(seglib->cmd)))
return_0;
return lvm_register_segtype(seglib, segtype);
}
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_striped_segtype,
init_zero_segtype,
init_error_segtype,
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
#ifdef HAVE_LIBDL
const struct dm_config_node *cn;
2004-05-05 01:25:57 +04:00
#endif
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 REPLICATOR_INTERNAL
if (!init_replicator_segtype(cmd, &seglib))
2004-05-05 01:25:57 +04:00
return 0;
#endif
#ifdef RAID_INTERNAL
if (!init_raid_segtypes(cmd, &seglib))
return 0;
#endif
#ifdef THIN_INTERNAL
if (!init_thin_segtypes(cmd, &seglib))
return 0;
#endif
2004-05-05 01:25:57 +04:00
#ifdef HAVE_LIBDL
/* Load any formats in shared libs unless static */
if (!is_static() &&
(cn = find_config_tree_node(cmd, global_segment_libraries_CFG, NULL))) {
2004-05-05 01:25:57 +04:00
const struct dm_config_value *cv;
int (*init_multiple_segtypes_fn) (struct cmd_context *,
struct segtype_library *);
2004-05-05 01:25:57 +04:00
for (cv = cn->v; cv; cv = cv->next) {
if (cv->type != DM_CFG_STRING) {
2004-05-05 01:25:57 +04:00
log_error("Invalid string in config file: "
"global/segment_libraries");
return 0;
}
seglib.libname = cv->v.str;
if (!(seglib.lib = load_shared_library(cmd,
seglib.libname,
2008-01-30 16:19:47 +03:00
"segment type", 0)))
return_0;
2004-05-05 01:25:57 +04:00
if ((init_multiple_segtypes_fn =
dlsym(seglib.lib, "init_multiple_segtypes"))) {
if (dlsym(seglib.lib, "init_segtype"))
log_warn("WARNING: Shared lib %s has "
"conflicting init fns. Using"
" init_multiple_segtypes().",
seglib.libname);
} else
init_multiple_segtypes_fn =
_init_single_segtype;
if (!init_multiple_segtypes_fn(cmd, &seglib)) {
struct dm_list *sgtl, *tmp;
log_error("init_multiple_segtypes() failed: "
"Unloading shared library %s",
seglib.libname);
dm_list_iterate_safe(sgtl, tmp, &cmd->segtypes) {
segtype = dm_list_item(sgtl, struct segment_type);
if (segtype->library == seglib.lib) {
dm_list_del(&segtype->list);
segtype->ops->destroy(segtype);
}
}
dlclose(seglib.lib);
return_0;
2004-05-05 01:25:57 +04:00
}
}
}
#endif
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)
{
static char default_dir[PATH_MAX];
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);
2006-08-21 16:54:53 +04:00
if (dm_snprintf
(default_dir, sizeof(default_dir), "%s/%s", cmd->system_dir,
DEFAULT_ARCHIVE_SUBDIR) == -1) {
log_error("Couldn't create default archive path '%s/%s'.",
cmd->system_dir, DEFAULT_ARCHIVE_SUBDIR);
return 0;
}
if (!(dir = find_config_tree_str(cmd, backup_archive_dir_CFG, NULL)))
dir = default_dir;
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);
2006-08-21 16:54:53 +04:00
if (dm_snprintf
(default_dir, sizeof(default_dir), "%s/%s", cmd->system_dir,
DEFAULT_BACKUP_SUBDIR) == -1) {
log_error("Couldn't create default backup path '%s/%s'.",
cmd->system_dir, DEFAULT_BACKUP_SUBDIR);
return 0;
}
if (!(dir = find_config_tree_str(cmd, backup_backup_dir_CFG, NULL)))
dir = default_dir;
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;
}
cmd->rand_seed = (unsigned) time(NULL) + (unsigned) getpid();
reset_lvm_errno(1);
}
static void _init_globals(struct cmd_context *cmd)
{
init_full_scan_done(0);
init_mirror_in_sync(0);
}
/*
* Close and reopen stream on file descriptor fd.
*/
static int _reopen_stream(FILE *stream, int fd, const char *mode, const char *name, FILE **new_stream)
{
int fd_copy, new_fd;
if ((fd_copy = dup(fd)) < 0) {
log_sys_error("dup", name);
return 0;
}
if (fclose(stream))
log_sys_error("fclose", name);
if ((new_fd = dup2(fd_copy, fd)) < 0)
log_sys_error("dup2", name);
else if (new_fd != fd)
log_error("dup2(%d, %d) returned %d", fd_copy, fd, new_fd);
if (close(fd_copy) < 0)
log_sys_error("close", name);
if (!(*new_stream = fdopen(fd, mode))) {
log_sys_error("fdopen", name);
return 0;
}
return 1;
}
2002-11-18 17:01:16 +03:00
/* Entry point */
struct cmd_context *create_toolcontext(unsigned is_long_lived,
const char *system_dir,
unsigned set_buffering,
unsigned threaded)
2002-11-18 17:01:16 +03:00
{
struct cmd_context *cmd;
FILE *new_stream;
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
2002-11-18 17:01:16 +03:00
init_syslog(DEFAULT_LOG_FACILITY);
if (!(cmd = dm_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_long_lived;
cmd->threaded = threaded ? 1 : 0;
cmd->handles_missing_pvs = 0;
cmd->handles_unknown_segments = 0;
cmd->independent_metadata_areas = 0;
2004-05-04 22:28:15 +04:00
cmd->hosttags = 0;
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 */
if (set_buffering) {
/* Allocate 2 buffers */
if (!(cmd->linebuffer = dm_malloc(2 * linebuffer_size))) {
log_error("Failed to allocate line buffer.");
goto out;
}
if (is_valid_fd(STDIN_FILENO)) {
if (!_reopen_stream(stdin, STDIN_FILENO, "r", "stdin", &new_stream))
goto_out;
stdin = new_stream;
if (setvbuf(stdin, cmd->linebuffer, _IOLBF, linebuffer_size)) {
log_sys_error("setvbuf", "");
goto out;
}
}
if (is_valid_fd(STDOUT_FILENO)) {
if (!_reopen_stream(stdout, STDOUT_FILENO, "w", "stdout", &new_stream))
goto_out;
stdout = new_stream;
if (setvbuf(stdout, cmd->linebuffer + linebuffer_size,
_IOLBF, linebuffer_size)) {
log_sys_error("setvbuf", "");
goto out;
}
}
/* Buffers are used for lines without '\n' */
} else
/* Without buffering, must not use stdin/stdout */
init_silent(1);
#endif
/*
* Environment variable LVM_SYSTEM_DIR overrides this below.
*/
if (system_dir)
strncpy(cmd->system_dir, system_dir, sizeof(cmd->system_dir) - 1);
else
strcpy(cmd->system_dir, DEFAULT_SYS_DIR);
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
}
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
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_node(cmd, devices_types_CFG, NULL))))
goto_out;
2002-11-18 17:01:16 +03:00
if (!_init_dev_cache(cmd))
goto_out;
2002-11-18 17:01:16 +03:00
if (!_init_filters(cmd, 1))
goto_out;
2002-11-18 17:01:16 +03:00
if (!(cmd->mem = dm_pool_create("command", 4 * 1024))) {
2002-11-18 17:01:16 +03:00
log_error("Command memory pool creation failed");
goto out;
2002-11-18 17:01:16 +03:00
}
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 (!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);
2008-04-03 01:31:14 +04:00
cmd->default_settings.cache_vgmetadata = 1;
2002-11-18 17:01:16 +03:00
cmd->current_settings = cmd->default_settings;
cmd->config_initialized = 1;
out:
if (!cmd->config_initialized) {
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;
void *lib;
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
lib = fmt->library;
fmt->ops->destroy(fmt);
#ifdef HAVE_LIBDL
2002-11-18 17:01:16 +03:00
if (lib)
dlclose(lib);
#endif
2002-11-18 17:01:16 +03:00
}
cmd->independent_metadata_areas = 0;
2002-11-18 17:01:16 +03:00
}
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;
void *lib;
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
lib = segtype->library;
segtype->ops->destroy(segtype);
#ifdef HAVE_LIBDL
/*
* If no segtypes remain from this library, close it.
*/
if (lib) {
struct segment_type *segtype2;
dm_list_iterate_items(segtype2, segtypes)
if (segtype2->library == lib)
goto skip_dlclose;
2004-05-05 01:25:57 +04:00
dlclose(lib);
skip_dlclose:
;
}
2004-05-05 01:25:57 +04:00
#endif
}
}
static void _destroy_dev_types(struct cmd_context *cmd)
{
if (!cmd->dev_types)
return;
dm_free(cmd->dev_types);
cmd->dev_types = NULL;
}
int refresh_filters(struct cmd_context *cmd)
{
int r, saved_ignore_suspended_devices = ignore_suspended_devices();
if (cmd->filter) {
cmd->filter->destroy(cmd->filter);
cmd->filter = NULL;
}
cmd->lvmetad_filter = NULL;
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;
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();
lvmcache_destroy(cmd, 0);
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);
2004-05-04 22:28:15 +04:00
if (cmd->filter) {
cmd->filter->destroy(cmd->filter);
cmd->filter = NULL;
}
dev_cache_exit();
_destroy_dev_types(cmd);
2004-05-04 22:28:15 +04:00
_destroy_tags(cmd);
cft_cmdline = remove_config_tree_by_source(cmd, CONFIG_STRING);
_destroy_config(cmd);
2004-05-04 22:28:15 +04:00
cmd->config_initialized = 0;
2004-05-04 22:28:15 +04:00
cmd->hosttags = 0;
if (!_init_lvm_conf(cmd))
return 0;
/* 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);
/* 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))
2004-05-04 22:28:15 +04:00
return 0;
/* Doesn't change cmd->cft */
2004-05-04 22:28:15 +04:00
if (!_init_tag_configs(cmd))
return 0;
/* 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)))
2004-05-04 22:28:15 +04:00
return 0;
/* 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;
if (!(cmd->dev_types = create_dev_types(cmd->proc_dir,
find_config_tree_node(cmd, devices_types_CFG, NULL))))
return 0;
2004-05-04 22:28:15 +04:00
if (!_init_dev_cache(cmd))
return 0;
if (!_init_filters(cmd, 0))
2004-05-04 22:28:15 +04:00
return 0;
if (!_init_formats(cmd))
return 0;
if (!init_lvmcache_orphans(cmd))
return 0;
2004-05-05 01:25:57 +04:00
if (!_init_segtypes(cmd))
return 0;
if (!_init_backup(cmd))
return 0;
cmd->config_initialized = 1;
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;
FILE *new_stream;
if (cmd->dump_filter && cmd->filter && cmd->filter->dump &&
!cmd->filter->dump(cmd->filter, 1))
stack;
2002-11-18 17:01:16 +03:00
archive_exit(cmd);
backup_exit(cmd);
lvmcache_destroy(cmd, 0);
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);
if (cmd->filter)
cmd->filter->destroy(cmd->filter);
if (cmd->mem)
dm_pool_destroy(cmd->mem);
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);
_destroy_config(cmd);
if (cmd->cft_def_hash)
dm_hash_destroy(cmd->cft_def_hash);
if (cmd->libmem)
dm_pool_destroy(cmd->libmem);
#ifndef VALGRIND_POOL
if (cmd->linebuffer) {
/* Reset stream buffering to defaults */
if (is_valid_fd(STDIN_FILENO)) {
if (_reopen_stream(stdin, STDIN_FILENO, "r", "stdin", &new_stream)) {
stdin = new_stream;
setlinebuf(stdin);
} else
cmd->linebuffer = NULL; /* Leave buffer in place (deliberate leak) */
}
if (is_valid_fd(STDOUT_FILENO)) {
if (_reopen_stream(stdout, STDOUT_FILENO, "w", "stdout", &new_stream)) {
stdout = new_stream;
setlinebuf(stdout);
} else
cmd->linebuffer = NULL; /* Leave buffer in place (deliberate leak) */
}
dm_free(cmd->linebuffer);
}
#endif
dm_free(cmd);
2002-11-18 17:01:16 +03:00
lvmetad_release_token();
lvmetad_disconnect();
release_log_memory();
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
}