int override_config_tree_from_profile(struct cmd_context *cmd, struct profile *profile) { struct dm_config_tree *cft = cmd->cft, *cft_string = NULL; struct config_source *cs = dm_config_get_custom(cft); /* * Follow this sequence: * CONFIG_STRING -> CONFIG_PROFILE -> CONFIG_FILE/CONFIG_MERGED_FILES */ if (!profile->cft && !load_profile(cmd, profile)) return_0; if (cs->type == CONFIG_STRING) { cft_string = cft; cft = cft->cascade; cs = dm_config_get_custom(cft); if (cs->type == CONFIG_PROFILE) { log_error(INTERNAL_ERROR "override_config_tree_from_profile: " "config cascade already contains a profile config."); return 0; } dm_config_insert_cascaded_tree(cft_string, profile->cft); } cmd->cft = dm_config_insert_cascaded_tree(profile->cft, cft); cmd->cft = cft_string ? : profile->cft; return 1; }
/* Destructively merge a new config tree into an existing one */ int merge_config_tree(struct cmd_context *cmd, struct dm_config_tree *cft, struct dm_config_tree *newdata, config_merge_t merge_type) { struct dm_config_node *root = cft->root; struct dm_config_node *cn, *nextn, *oldn, *cn2; const struct dm_config_node *tn; struct config_source *cs, *csn; for (cn = newdata->root; cn; cn = nextn) { nextn = cn->sib; if (merge_type == CONFIG_MERGE_TYPE_TAGS) { /* Ignore tags section */ if (!strcmp(cn->key, "tags")) continue; /* If there's a tags node, skip if host tags don't match */ if ((tn = dm_config_find_node(cn->child, "tags"))) { if (!_match_host_tags(&cmd->tags, tn)) continue; } } if (!(oldn = dm_config_find_node(root, cn->key))) { _insert_config_node(&cft->root, cn); if (merge_type == CONFIG_MERGE_TYPE_TAGS) { /* Remove any "tags" nodes */ for (cn2 = cn->child; cn2; cn2 = cn2->sib) { if (!strcmp(cn2->key, "tags")) { cn->child = cn2->sib; continue; } if (cn2->sib && !strcmp(cn2->sib->key, "tags")) { cn2->sib = cn2->sib->sib; continue; } } } continue; } _merge_section(oldn, cn, merge_type); } /* * Persistent filter loading is based on timestamp, * so we need to know the newest timestamp to make right decision * whether the .cache isn't older then any of configs */ cs = dm_config_get_custom(cft); csn = dm_config_get_custom(newdata); if (cs && csn && (cs->timestamp < csn->timestamp)) cs->timestamp = csn->timestamp; return 1; }
int override_config_tree_from_string(struct cmd_context *cmd, const char *config_settings) { struct dm_config_tree *cft_new; struct config_source *cs = dm_config_get_custom(cmd->cft); /* * Follow this sequence: * CONFIG_STRING -> CONFIG_PROFILE -> CONFIG_FILE/CONFIG_MERGED_FILES */ if (cs->type == CONFIG_STRING) { log_error(INTERNAL_ERROR "override_config_tree_from_string: " "config cascade already contains a string config."); return 0; } if (!(cft_new = dm_config_from_string(config_settings))) { log_error("Failed to set overridden configuration entries."); return 0; } if (!(cs = dm_pool_zalloc(cft_new->mem, sizeof(struct config_source)))) { log_error("Failed to allocate config source."); dm_config_destroy(cft_new); return 0; } cs->type = CONFIG_STRING; dm_config_set_custom(cft_new, cs); cmd->cft = dm_config_insert_cascaded_tree(cft_new, cmd->cft); return 1; }
/* * Doesn't populate filename if the file is empty. */ int config_file_check(struct dm_config_tree *cft, const char **filename, struct stat *info) { struct config_file *cf = dm_config_get_custom(cft); struct stat _info; if (!info) info = &_info; if (stat(cf->filename, info)) { log_sys_error("stat", cf->filename); cf->exists = 0; return 0; } if (!S_ISREG(info->st_mode)) { log_error("%s is not a regular file", cf->filename); cf->exists = 0; return 0; } cf->exists = 1; cf->timestamp = info->st_ctime; cf->st_size = info->st_size; if (info->st_size == 0) log_verbose("%s is empty", cf->filename); else if (filename) *filename = cf->filename; return 1; }
int config_file_read(struct dm_config_tree *cft) { const char *filename = NULL; struct config_file *cf = dm_config_get_custom(cft); struct stat info; int r; if (!config_file_check(cft, &filename, &info)) return_0; /* Nothing to do. E.g. empty file. */ if (!filename) return 1; if (!cf->dev) { if (!(cf->dev = dev_create_file(filename, NULL, NULL, 1))) return_0; if (!dev_open_readonly_buffered(cf->dev)) return_0; } r = config_file_read_fd(cft, cf->dev, 0, (size_t) info.st_size, 0, 0, (checksum_fn_t) NULL, 0); if (!cf->keep_open) { if (!dev_close(cf->dev)) stack; cf->dev = NULL; } return r; }
void config_file_destroy(struct dm_config_tree *cft) { struct config_file *cf = dm_config_get_custom(cft); if (cf && cf->dev) if (!dev_close(cf->dev)) stack; dm_config_destroy(cft); }
/* * Return 1 if config files ought to be reloaded */ int config_file_changed(struct dm_config_tree *cft) { struct config_source *cs = dm_config_get_custom(cft); struct config_file *cf; struct stat info; if (cs->type != CONFIG_FILE) { log_error(INTERNAL_ERROR "config_file_changed: expected file config source, " "found %s config source.", _config_source_names[cs->type]); return 0; } cf = cs->source.file; if (!cf->filename) return 0; if (stat(cf->filename, &info) == -1) { /* Ignore a deleted config file: still use original data */ if (errno == ENOENT) { if (!cf->exists) return 0; log_very_verbose("Config file %s has disappeared!", cf->filename); goto reload; } log_sys_error("stat", cf->filename); log_error("Failed to reload configuration files"); return 0; } if (!S_ISREG(info.st_mode)) { log_error("Configuration file %s is not a regular file", cf->filename); goto reload; } /* Unchanged? */ if (cs->timestamp == info.st_ctime && cf->st_size == info.st_size) return 0; reload: log_verbose("Detected config file change to %s", cf->filename); return 1; }
void config_destroy(struct dm_config_tree *cft) { struct config_source *cs; struct config_file *cf; if (!cft) return; cs = dm_config_get_custom(cft); if ((cs->type == CONFIG_FILE) || (cs->type == CONFIG_PROFILE)) { cf = cs->source.file; if (cf && cf->dev) if (!dev_close(cf->dev)) stack; } dm_config_destroy(cft); }
/* * Doesn't populate filename if the file is empty. */ int config_file_check(struct dm_config_tree *cft, const char **filename, struct stat *info) { struct config_source *cs = dm_config_get_custom(cft); struct config_file *cf; struct stat _info; if ((cs->type != CONFIG_FILE) && (cs->type != CONFIG_PROFILE)) { log_error(INTERNAL_ERROR "config_file_check: expected file or profile config source, " "found %s config source.", _config_source_names[cs->type]); return 0; } if (!info) info = &_info; cf = cs->source.file; if (stat(cf->filename, info)) { log_sys_error("stat", cf->filename); cf->exists = 0; return 0; } if (!S_ISREG(info->st_mode)) { log_error("%s is not a regular file", cf->filename); cf->exists = 0; return 0; } cs->timestamp = info->st_ctime; cf->exists = 1; cf->st_size = info->st_size; if (info->st_size == 0) log_verbose("%s is empty", cf->filename); else if (filename) *filename = cf->filename; return 1; }
/* * Returns config tree if it was removed. */ struct dm_config_tree *remove_config_tree_by_source(struct cmd_context *cmd, config_source_t source) { struct dm_config_tree *previous_cft = NULL; struct dm_config_tree *cft = cmd->cft; struct config_source *cs; while (cft) { cs = dm_config_get_custom(cft); if (cs && (cs->type == source)) { if (previous_cft) { previous_cft->cascade = cft->cascade; cmd->cft = previous_cft; } else cmd->cft = cft->cascade; cft->cascade = NULL; break; } previous_cft = cft; cft = cft->cascade; } return cft; }
/* * Return 1 if config files ought to be reloaded */ int config_file_changed(struct dm_config_tree *cft) { struct config_file *cf = dm_config_get_custom(cft); struct stat info; if (!cf->filename) return 0; if (stat(cf->filename, &info) == -1) { /* Ignore a deleted config file: still use original data */ if (errno == ENOENT) { if (!cf->exists) return 0; log_very_verbose("Config file %s has disappeared!", cf->filename); goto reload; } log_sys_error("stat", cf->filename); log_error("Failed to reload configuration files"); return 0; } if (!S_ISREG(info.st_mode)) { log_error("Configuration file %s is not a regular file", cf->filename); goto reload; } /* Unchanged? */ if (cf->timestamp == info.st_ctime && cf->st_size == info.st_size) return 0; reload: log_verbose("Detected config file change to %s", cf->filename); return 1; }
config_source_t config_get_source_type(struct dm_config_tree *cft) { struct config_source *cs = dm_config_get_custom(cft); return cs ? cs->type : CONFIG_UNDEFINED; }
time_t config_file_timestamp(struct dm_config_tree *cft) { struct config_source *cs = dm_config_get_custom(cft); return cs->timestamp; }
int config_file_read_fd(struct dm_config_tree *cft, struct device *dev, off_t offset, size_t size, off_t offset2, size_t size2, checksum_fn_t checksum_fn, uint32_t checksum) { char *fb, *fe; int r = 0; int use_mmap = 1; off_t mmap_offset = 0; char *buf = NULL; struct config_source *cs = dm_config_get_custom(cft); if ((cs->type != CONFIG_FILE) && (cs->type != CONFIG_PROFILE)) { log_error(INTERNAL_ERROR "config_file_read_fd: expected file or profile config source, " "found %s config source.", _config_source_names[cs->type]); return 0; } /* Only use mmap with regular files */ if (!(dev->flags & DEV_REGULAR) || size2) use_mmap = 0; if (use_mmap) { mmap_offset = offset % lvm_getpagesize(); /* memory map the file */ fb = mmap((caddr_t) 0, size + mmap_offset, PROT_READ, MAP_PRIVATE, dev_fd(dev), offset - mmap_offset); if (fb == (caddr_t) (-1)) { log_sys_error("mmap", dev_name(dev)); goto out; } fb = fb + mmap_offset; } else { if (!(buf = dm_malloc(size + size2))) { log_error("Failed to allocate circular buffer."); return 0; } if (!dev_read_circular(dev, (uint64_t) offset, size, (uint64_t) offset2, size2, buf)) { goto out; } fb = buf; } if (checksum_fn && checksum != (checksum_fn(checksum_fn(INITIAL_CRC, (const uint8_t *)fb, size), (const uint8_t *)(fb + size), size2))) { log_error("%s: Checksum error", dev_name(dev)); goto out; } fe = fb + size + size2; if (!dm_config_parse(cft, fb, fe)) goto_out; r = 1; out: if (!use_mmap) dm_free(buf); else { /* unmap the file */ if (munmap(fb - mmap_offset, size + mmap_offset)) { log_sys_error("munmap", dev_name(dev)); r = 0; } } return r; }
time_t config_file_timestamp(struct dm_config_tree *cft) { struct config_file *cf = dm_config_get_custom(cft); assert(cf); return cf->timestamp; }