/*
 * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
 * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
 *
 * This file is part of LVM2.
 *
 * This copyrighted material is made available to anyone wishing to use,
 * modify, copy, or redistribute it subject to the terms and conditions
 * of the GNU General Public License v.2.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#include "lib.h"
#include "toolcontext.h"
#include "pool.h"
#include "metadata.h"
#include "defaults.h"
#include "lvm-string.h"
#include "activate.h"
#include "filter.h"
#include "filter-composite.h"
#include "filter-md.h"
#include "filter-persistent.h"
#include "filter-regex.h"
#include "filter-sysfs.h"
#include "label.h"
#include "lvm-file.h"
#include "format-text.h"
#include "display.h"
#include "memlock.h"
#include "str_list.h"
#include "segtype.h"
#include "lvmcache.h"
#include "dev-cache.h"
#include "archiver.h"

#ifdef HAVE_LIBDL
#include "sharedlib.h"
#endif

#ifdef LVM1_INTERNAL
#include "format1.h"
#endif

#ifdef POOL_INTERNAL
#include "format_pool.h"
#endif

#include <locale.h>
#include <sys/stat.h>
#include <sys/utsname.h>
#include <syslog.h>
#include <time.h>

#ifdef linux
#  include <malloc.h>
#endif

static FILE *_log;

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 (lvm_snprintf(cmd->sys_dir, sizeof(cmd->sys_dir),
				 "%s", e) < 0) {
			log_error("LVM_SYSTEM_DIR environment variable "
				  "is too long.");
			return 0;
		}
	}

	return 1;
}

static void _init_logging(struct cmd_context *cmd)
{
	int append = 1;
	time_t t;

	const char *log_file;

	/* Syslog */
	cmd->default_settings.syslog =
	    find_config_int(cmd->cft->root, "log/syslog", DEFAULT_SYSLOG);
	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_int(cmd->cft->root, "log/level", DEFAULT_LOGLEVEL);
	init_debug(cmd->default_settings.debug);

	/* Verbose level for tty output */
	cmd->default_settings.verbose =
	    find_config_int(cmd->cft->root, "log/verbose", DEFAULT_VERBOSE);
	init_verbose(cmd->default_settings.verbose + VERBOSE_BASE_LEVEL);

	/* Log message formatting */
	init_indent(find_config_int(cmd->cft->root, "log/indent",
				    DEFAULT_INDENT));

	cmd->default_settings.msg_prefix = find_config_str(cmd->cft->root,
							   "log/prefix",
							   DEFAULT_MSG_PREFIX);
	init_msg_prefix(cmd->default_settings.msg_prefix);

	cmd->default_settings.cmd_name = find_config_int(cmd->cft->root,
							 "log/command_names",
							 DEFAULT_CMD_NAME);
	init_cmd_name(cmd->default_settings.cmd_name);

	/* Test mode */
	cmd->default_settings.test =
	    find_config_int(cmd->cft->root, "global/test", 0);

	/* Settings for logging to file */
	if (find_config_int(cmd->cft->root, "log/overwrite", DEFAULT_OVERWRITE))
		append = 0;

	log_file = find_config_str(cmd->cft->root, "log/file", 0);

	if (log_file) {
		release_log_memory();
		fin_log();
		init_log_file(log_file, append);
	}

	log_file = find_config_str(cmd->cft->root, "log/activate_file", 0);
	if (log_file)
		init_log_direct(log_file, append);

	init_log_while_suspended(find_config_int(cmd->cft->root,
						 "log/activation", 0));

	t = time(NULL);
	log_verbose("Logging initialised at %s", ctime(&t));

	/* Tell device-mapper about our logging */
#ifdef DEVMAPPER_SUPPORT
	dm_log_init(print_log);
#endif
}

static int _process_config(struct cmd_context *cmd)
{
	mode_t old_umask;

	/* umask */
	cmd->default_settings.umask = find_config_int(cmd->cft->root,
						      "global/umask",
						      DEFAULT_UMASK);

	if ((old_umask = umask((mode_t) cmd->default_settings.umask)) !=
	    (mode_t) cmd->default_settings.umask)
		log_verbose("Set umask to %04o", cmd->default_settings.umask);

	/* dev dir */
	if (lvm_snprintf(cmd->dev_dir, sizeof(cmd->dev_dir), "%s/",
			 find_config_str(cmd->cft->root, "devices/dir",
					 DEFAULT_DEV_DIR)) < 0) {
		log_error("Device directory given in config file too long");
		return 0;
	}
#ifdef DEVMAPPER_SUPPORT
	dm_set_dev_dir(cmd->dev_dir);
#endif

	/* proc dir */
	if (lvm_snprintf(cmd->proc_dir, sizeof(cmd->proc_dir), "%s",
			 find_config_str(cmd->cft->root, "global/proc",
					 DEFAULT_PROC_DIR)) < 0) {
		log_error("Device directory given in config file too long");
		return 0;
	}

	if (*cmd->proc_dir && !dir_exists(cmd->proc_dir)) {
		log_error("Warning: proc dir %s not found - some checks will be bypassed",
			  cmd->proc_dir);
		cmd->proc_dir[0] = '\0';
	}

	/* activation? */
	cmd->default_settings.activation = find_config_int(cmd->cft->root,
							   "global/activation",
							   DEFAULT_ACTIVATION);
	set_activation(cmd->default_settings.activation);

	cmd->default_settings.suffix = find_config_int(cmd->cft->root,
						       "global/suffix",
						       DEFAULT_SUFFIX);

	if (!(cmd->default_settings.unit_factor =
	      units_to_bytes(find_config_str(cmd->cft->root,
					     "global/units",
					     DEFAULT_UNITS),
			     &cmd->default_settings.unit_type))) {
		log_error("Invalid units specification");
		return 0;
	}

	return 1;
}

static int _set_tag(struct cmd_context *cmd, const char *tag)
{
	log_very_verbose("Setting host tag: %s", pool_strdup(cmd->libmem, tag));

	if (!str_list_add(cmd->libmem, &cmd->tags, tag)) {
		log_error("_set_tag: str_list_add %s failed", tag);
		return 0;
	}

	return 1;
}

static int _check_host_filters(struct cmd_context *cmd, struct config_node *hn,
			       int *passes)
{
	struct config_node *cn;
	struct config_value *cv;

	*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 == CFG_EMPTY_ARRAY)
				continue;
			for (cv = cn->v; cv; cv = cv->next) {
				if (cv->type != CFG_STRING) {
					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 config_tree *cft)
{
	const struct config_node *tn, *cn;
	const char *tag;
	int passes;

	if (!(tn = find_config_node(cft->root, "tags")) || !tn->child)
		return 1;

	/* NB hosttags 0 when already 1 intentionally does not delete the tag */
	if (!cmd->hosttags && find_config_int(cft->root, "tags/hosttags",
					      DEFAULT_HOSTTAGS)) {
		/* FIXME Strip out invalid chars: only A-Za-z0-9_+.- */
		if (!_set_tag(cmd, cmd->hostname)) {
			stack;
			return 0;
		}
		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;
			if (!_check_host_filters(cmd, cn->child, &passes)) {
				stack;
				return 0;
			}
			if (!passes)
				continue;
		}
		if (!_set_tag(cmd, tag)) {
			stack;
			return 0;
		}
	}

	return 1;
}

static int _load_config_file(struct cmd_context *cmd, const char *tag)
{
	char config_file[PATH_MAX] = "";
	const char *filler = "";
	struct stat info;
	struct config_tree_list *cfl;

	if (*tag)
		filler = "_";

	if (lvm_snprintf(config_file, sizeof(config_file), "%s/lvm%s%s.conf",
			 cmd->sys_dir, filler, tag) < 0) {
		log_error("LVM_SYSTEM_DIR or tag was too long");
		return 0;
	}

	if (!(cfl = pool_alloc(cmd->libmem, sizeof(*cfl)))) {
		log_error("config_tree_list allocation failed");
		return 0;
	}

	if (!(cfl->cft = create_config_tree(config_file))) {
		log_error("config_tree allocation failed");
		return 0;
	}

	/* Is there a config file? */
	if (stat(config_file, &info) == -1) {
		if (errno == ENOENT) {
			list_add(&cmd->config_files, &cfl->list);
			goto out;
		}
		log_sys_error("stat", config_file);
		destroy_config_tree(cfl->cft);
		return 0;
	}

	log_very_verbose("Loading config file: %s", config_file);
	if (!read_config_file(cfl->cft)) {
		log_error("Failed to load config file %s", config_file);
		destroy_config_tree(cfl->cft);
		return 0;
	}

	list_add(&cmd->config_files, &cfl->list);

      out:
	if (*tag)
		_init_tags(cmd, cfl->cft);
	else
		/* 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->sys_dir) {
		if (!(cmd->cft = create_config_tree(NULL))) {
			log_error("Failed to create config tree");
			return 0;
		}
		return 1;
	}

	if (!_load_config_file(cmd, "")) {
		stack;
		return 0;
	}

	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 */
	list_iterate_items(sl, &cmd->tags) {
		if (!_load_config_file(cmd, sl->str)) {
			stack;
			return 0;
		}

	}

	return 1;
}

static int _merge_config_files(struct cmd_context *cmd)
{
	struct config_tree_list *cfl;

	/* Replace temporary duplicate copy of lvm.conf */
	if (cmd->cft->root) {
		if (!(cmd->cft = create_config_tree(NULL))) {
			log_error("Failed to create config tree");
			return 0;
		}
	}

	list_iterate_items(cfl, &cmd->config_files) {
		/* Merge all config trees into cmd->cft using merge/tag rules */
		if (!merge_config_tree(cmd, cmd->cft, cfl->cft)) {
			stack;
			return 0;
		}
	}

	return 1;
}

static void _destroy_tags(struct cmd_context *cmd)
{
	struct list *slh, *slht;

	list_iterate_safe(slh, slht, &cmd->tags) {
		list_del(slh);
	}
}

int config_files_changed(struct cmd_context *cmd)
{
	struct config_tree_list *cfl;

	list_iterate_items(cfl, &cmd->config_files) {
		if (config_file_changed(cfl->cft))
			return 1;
	}

	return 0;
}

static void _destroy_tag_configs(struct cmd_context *cmd)
{
	struct config_tree_list *cfl;

	if (cmd->cft && cmd->cft->root) {
		destroy_config_tree(cmd->cft);
		cmd->cft = NULL;
	}

	list_iterate_items(cfl, &cmd->config_files) {
		destroy_config_tree(cfl->cft);
	}

	list_init(&cmd->config_files);
}

static int _init_dev_cache(struct cmd_context *cmd)
{
	const struct config_node *cn;
	struct config_value *cv;

	if (!dev_cache_init()) {
		stack;
		return 0;
	}

	if (!(cn = find_config_node(cmd->cft->root, "devices/scan"))) {
		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 != CFG_STRING) {
			log_error("Invalid string in config file: "
				  "devices/scan");
			return 0;
		}

		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_node(cmd->cft->root, "devices/loopfiles")))
		return 1;

	for (cv = cn->v; cv; cv = cv->next) {
		if (cv->type != 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;
		}
	}


	return 1;
}

#define MAX_FILTERS 4

static struct dev_filter *_init_filter_components(struct cmd_context *cmd)
{
	unsigned nr_filt = 0;
	const struct config_node *cn;
	struct dev_filter *filters[MAX_FILTERS];

	memset(filters, 0, sizeof(filters));

	/*
	 * 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.
	 * Listed first because it's very efficient at eliminating 
	 * unavailable devices.
	 */
	if (find_config_bool(cmd->cft->root, "devices/sysfs_scan",
			     DEFAULT_SYSFS_SCAN)) {
		if ((filters[nr_filt] = sysfs_filter_create(cmd->proc_dir)))
			nr_filt++;
	}

	/* regex filter. Optional. */
	if (!(cn = find_config_node(cmd->cft->root, "devices/filter")))
		log_very_verbose("devices/filter not found in config file: "
				 "no regex filter installed");

	else if (!(filters[nr_filt++] = regex_filter_create(cn->v))) {
		log_error("Failed to create regex device filter");
		return NULL;
	}

	/* device type filter. Required. */
	cn = find_config_node(cmd->cft->root, "devices/types");
	if (!(filters[nr_filt++] = lvm_type_filter_create(cmd->proc_dir, cn))) {
		log_error("Failed to create lvm type filter");
		return NULL;
	}

	/* md component filter. Optional, non-critical. */
	if (find_config_bool(cmd->cft->root, "devices/md_component_detection",
			     DEFAULT_MD_COMPONENT_DETECTION)) {
		init_md_filtering(1);
		if ((filters[nr_filt] = md_filter_create()))
			nr_filt++;
	}

	/* Only build a composite filter if we really need it. */
	return (nr_filt == 1) ?
	    filters[0] : composite_filter_create(nr_filt, filters);
}

static int _init_filters(struct cmd_context *cmd)
{
	const char *dev_cache;
	struct dev_filter *f3, *f4;
	struct stat st;
	char cache_file[PATH_MAX];

	cmd->dump_filter = 0;

	if (!(f3 = _init_filter_components(cmd)))
		return 0;

	if (lvm_snprintf(cache_file, sizeof(cache_file),
			 "%s/.cache", cmd->sys_dir) < 0) {
		log_error("Persistent cache filename too long ('%s/.cache').",
			  cmd->sys_dir);
		return 0;
	}

	dev_cache = find_config_str(cmd->cft->root, "devices/cache",
				    cache_file);
	if (!(f4 = persistent_filter_create(f3, dev_cache))) {
		log_error("Failed to create persistent device filter");
		return 0;
	}

	/* Should we ever dump persistent filter state? */
	if (find_config_int(cmd->cft->root, "devices/write_cache_state", 1))
		cmd->dump_filter = 1;

	if (!*cmd->sys_dir)
		cmd->dump_filter = 0;

	if (!stat(dev_cache, &st) &&
	    (st.st_mtime > config_file_timestamp(cmd->cft)) &&
	    !persistent_filter_load(f4))
		log_verbose("Failed to load existing device cache from %s",
			    dev_cache);

	cmd->filter = f4;

	return 1;
}

static int _init_formats(struct cmd_context *cmd)
{
	const char *format;

	struct format_type *fmt;

#ifdef HAVE_LIBDL
	const struct config_node *cn;
#endif

	label_init();

#ifdef LVM1_INTERNAL
	if (!(fmt = init_lvm1_format(cmd)))
		return 0;
	fmt->library = NULL;
	list_add(&cmd->formats, &fmt->list);
#endif

#ifdef POOL_INTERNAL
	if (!(fmt = init_pool_format(cmd)))
		return 0;
	fmt->library = NULL;
	list_add(&cmd->formats, &fmt->list);
#endif

#ifdef HAVE_LIBDL
	/* Load any formats in shared libs */
	if ((cn = find_config_node(cmd->cft->root, "global/format_libraries"))) {

		struct config_value *cv;
		struct format_type *(*init_format_fn) (struct cmd_context *);
		void *lib;

		for (cv = cn->v; cv; cv = cv->next) {
			if (cv->type != CFG_STRING) {
				log_error("Invalid string in config file: "
					  "global/format_libraries");
				return 0;
			}
			if (!(lib = load_shared_library(cmd->cft, cv->v.str,
							"format"))) {
				stack;
				return 0;
			}

			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)))
				return 0;
			fmt->library = lib;
			list_add(&cmd->formats, &fmt->list);
		}
	}
#endif

	if (!(fmt = create_text_format(cmd)))
		return 0;
	fmt->library = NULL;
	list_add(&cmd->formats, &fmt->list);

	cmd->fmt_backup = fmt;

	format = find_config_str(cmd->cft->root, "global/format",
				 DEFAULT_FORMAT);

	list_iterate_items(fmt, &cmd->formats) {
		if (!strcasecmp(fmt->name, format) ||
		    (fmt->alias && !strcasecmp(fmt->alias, format))) {
			cmd->default_settings.fmt = fmt;
			return 1;
		}
	}

	log_error("_init_formats: Default format (%s) not found", format);
	return 0;
}

static int _init_segtypes(struct cmd_context *cmd)
{
	struct segment_type *segtype;

#ifdef HAVE_LIBDL
	const struct config_node *cn;
#endif

	if (!(segtype = init_striped_segtype(cmd)))
		return 0;
	segtype->library = NULL;
	list_add(&cmd->segtypes, &segtype->list);

	if (!(segtype = init_zero_segtype(cmd)))
		return 0;
	segtype->library = NULL;
	list_add(&cmd->segtypes, &segtype->list);

	if (!(segtype = init_error_segtype(cmd)))
		return 0;
	segtype->library = NULL;
	list_add(&cmd->segtypes, &segtype->list);

#ifdef SNAPSHOT_INTERNAL
	if (!(segtype = init_snapshot_segtype(cmd)))
		return 0;
	segtype->library = NULL;
	list_add(&cmd->segtypes, &segtype->list);
#endif

#ifdef MIRRORED_INTERNAL
	if (!(segtype = init_mirrored_segtype(cmd)))
		return 0;
	segtype->library = NULL;
	list_add(&cmd->segtypes, &segtype->list);
#endif

#ifdef HAVE_LIBDL
	/* Load any formats in shared libs */
	if ((cn = find_config_node(cmd->cft->root, "global/segment_libraries"))) {

		struct config_value *cv;
		struct segment_type *(*init_segtype_fn) (struct cmd_context *);
		void *lib;
		struct list *sgtl, *tmp;
		struct segment_type *segtype2;

		for (cv = cn->v; cv; cv = cv->next) {
			if (cv->type != CFG_STRING) {
				log_error("Invalid string in config file: "
					  "global/segment_libraries");
				return 0;
			}
			if (!(lib = load_shared_library(cmd->cft, cv->v.str,
							"segment type"))) {
				stack;
				return 0;
			}

			if (!(init_segtype_fn = dlsym(lib, "init_segtype"))) {
				log_error("Shared library %s does not contain "
					  "segment type functions", cv->v.str);
				dlclose(lib);
				return 0;
			}

			if (!(segtype = init_segtype_fn(cmd)))
				return 0;
			segtype->library = lib;
			list_add(&cmd->segtypes, &segtype->list);

			list_iterate_safe(sgtl, tmp, &cmd->segtypes) {
				segtype2 = list_item(sgtl, struct segment_type);
				if (!strcmp(segtype2->name, segtype->name)) {
					log_error("Duplicate segment type %s: "
						  "unloading shared library %s",
						  segtype->name, cv->v.str);
					list_del(&segtype->list);
					segtype->ops->destroy(segtype);
					dlclose(lib);
					break;
				}

			}
		}
	}
#endif

	return 1;
}

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 = pool_strdup(cmd->libmem, uts.nodename))) {
		log_error("_init_hostname: pool_strdup failed");
		return 0;
	}

	if (!(cmd->kernel_vsn = pool_strdup(cmd->libmem, uts.release))) {
		log_error("_init_hostname: pool_strdup kernel_vsn failed");
		return 0;
	}

	return 1;
}

static int _init_backup(struct cmd_context *cmd)
{
	uint32_t days, min;
	char default_dir[PATH_MAX];
	const char *dir;

	if (!cmd->sys_dir) {
		log_warn("WARNING: Metadata changes will NOT be backed up");
		backup_init(cmd, "");
		archive_init(cmd, "", 0, 0);
		return 1;
	}

	/* set up archiving */
	cmd->default_settings.archive =
	    find_config_bool(cmd->cft->root, "backup/archive",
			     DEFAULT_ARCHIVE_ENABLED);

	days = (uint32_t) find_config_int(cmd->cft->root, "backup/retain_days",
					  DEFAULT_ARCHIVE_DAYS);

	min = (uint32_t) find_config_int(cmd->cft->root, "backup/retain_min",
					 DEFAULT_ARCHIVE_NUMBER);

	if (lvm_snprintf
	    (default_dir, sizeof(default_dir), "%s/%s", cmd->sys_dir,
	     DEFAULT_ARCHIVE_SUBDIR) == -1) {
		log_err("Couldn't create default archive path '%s/%s'.",
			cmd->sys_dir, DEFAULT_ARCHIVE_SUBDIR);
		return 0;
	}

	dir = find_config_str(cmd->cft->root, "backup/archive_dir",
			      default_dir);

	if (!archive_init(cmd, dir, days, min)) {
		log_debug("backup_init failed.");
		return 0;
	}

	/* set up the backup */
	cmd->default_settings.backup =
	    find_config_bool(cmd->cft->root, "backup/backup",
			     DEFAULT_BACKUP_ENABLED);

	if (lvm_snprintf
	    (default_dir, sizeof(default_dir), "%s/%s", cmd->sys_dir,
	     DEFAULT_BACKUP_SUBDIR) == -1) {
		log_err("Couldn't create default backup path '%s/%s'.",
			cmd->sys_dir, DEFAULT_BACKUP_SUBDIR);
		return 0;
	}

	dir = find_config_str(cmd->cft->root, "backup/backup_dir", default_dir);

	if (!backup_init(cmd, dir)) {
		log_debug("backup_init failed.");
		return 0;
	}

	return 1;
}

/* Entry point */
struct cmd_context *create_toolcontext(struct arg *the_args)
{
	struct cmd_context *cmd;

#ifdef M_MMAP_MAX
	mallopt(M_MMAP_MAX, 0);
#endif

	if (!setlocale(LC_ALL, ""))
		log_very_verbose("setlocale failed");

#ifdef INTL_PACKAGE
	bindtextdomain(INTL_PACKAGE, LOCALEDIR);
#endif

	init_syslog(DEFAULT_LOG_FACILITY);

	if (!(cmd = dbg_malloc(sizeof(*cmd)))) {
		log_error("Failed to allocate command context");
		return NULL;
	}
	memset(cmd, 0, sizeof(*cmd));
	cmd->args = the_args;
	cmd->hosttags = 0;
	list_init(&cmd->formats);
	list_init(&cmd->segtypes);
	list_init(&cmd->tags);
	list_init(&cmd->config_files);

	strcpy(cmd->sys_dir, DEFAULT_SYS_DIR);

	if (!_get_env_vars(cmd))
		goto error;

	/* Create system directory if it doesn't already exist */
	if (*cmd->sys_dir && !create_dir(cmd->sys_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 error;
	}

	if (!(cmd->libmem = pool_create("library", 4 * 1024))) {
		log_error("Library memory pool creation failed");
		return 0;
	}

	if (!_init_lvm_conf(cmd))
		goto error;

	_init_logging(cmd);

	if (!_init_hostname(cmd))
		goto error;

	if (!_init_tags(cmd, cmd->cft))
		goto error;

	if (!_init_tag_configs(cmd))
		goto error;

	if (!_merge_config_files(cmd))
		goto error;

	if (!_process_config(cmd))
		goto error;

	if (!_init_dev_cache(cmd))
		goto error;

	if (!_init_filters(cmd))
		goto error;

	if (!(cmd->mem = pool_create("command", 4 * 1024))) {
		log_error("Command memory pool creation failed");
		return 0;
	}

	memlock_init(cmd);

	if (!_init_formats(cmd))
		goto error;

	if (!_init_segtypes(cmd))
		goto error;

	if (!_init_backup(cmd))
		goto error;

	cmd->current_settings = cmd->default_settings;

	cmd->config_valid = 1;
	return cmd;

      error:
	dbg_free(cmd);
	return NULL;
}

static void _destroy_formats(struct list *formats)
{
	struct list *fmtl, *tmp;
	struct format_type *fmt;
	void *lib;

	list_iterate_safe(fmtl, tmp, formats) {
		fmt = list_item(fmtl, struct format_type);
		list_del(&fmt->list);
		lib = fmt->library;
		fmt->ops->destroy(fmt);
#ifdef HAVE_LIBDL
		if (lib)
			dlclose(lib);
#endif
	}
}

static void _destroy_segtypes(struct list *segtypes)
{
	struct list *sgtl, *tmp;
	struct segment_type *segtype;
	void *lib;

	list_iterate_safe(sgtl, tmp, segtypes) {
		segtype = list_item(sgtl, struct segment_type);
		list_del(&segtype->list);
		lib = segtype->library;
		segtype->ops->destroy(segtype);
#ifdef HAVE_LIBDL
		if (lib)
			dlclose(lib);
#endif
	}
}

int refresh_toolcontext(struct cmd_context *cmd)
{
	log_verbose("Reloading config files");

	if (cmd->config_valid) {
		if (cmd->dump_filter)
			persistent_filter_dump(cmd->filter);
	}

	activation_exit();
	lvmcache_destroy();
	label_exit();
	_destroy_segtypes(&cmd->segtypes);
	_destroy_formats(&cmd->formats);
	if (cmd->filter) {
		cmd->filter->destroy(cmd->filter);
		cmd->filter = NULL;
	}
	dev_cache_exit();
	_destroy_tags(cmd);
	_destroy_tag_configs(cmd);

	cmd->config_valid = 0;

	cmd->hosttags = 0;

	if (!_init_lvm_conf(cmd))
		return 0;

	_init_logging(cmd);

	if (!_init_tags(cmd, cmd->cft))
		return 0;

	if (!_init_tag_configs(cmd))
		return 0;

	if (!_merge_config_files(cmd))
		return 0;

	if (!_process_config(cmd))
		return 0;

	if (!_init_dev_cache(cmd))
		return 0;

	if (!_init_filters(cmd))
		return 0;

	if (!_init_formats(cmd))
		return 0;

	if (!_init_segtypes(cmd))
		return 0;

	cmd->config_valid = 1;
	return 1;
}

void destroy_toolcontext(struct cmd_context *cmd)
{
	if (cmd->dump_filter)
		persistent_filter_dump(cmd->filter);

	archive_exit(cmd);
	backup_exit(cmd);
	activation_exit();
	lvmcache_destroy();
	label_exit();
	_destroy_segtypes(&cmd->segtypes);
	_destroy_formats(&cmd->formats);
	cmd->filter->destroy(cmd->filter);
	pool_destroy(cmd->mem);
	dev_cache_exit();
	_destroy_tags(cmd);
	_destroy_tag_configs(cmd);
	pool_destroy(cmd->libmem);
	dbg_free(cmd);

	release_log_memory();
	dump_memory();
	fin_log();
	fin_syslog();

	if (_log)
		fclose(_log);

}
