daemon: Reimplement handling of lvm.conf and filters.

LVM is fine with a completely empty configuration file (meaning "all
defaults"), so start with one instead of copying the system
configuration file.

Also this means we can very easily implement lvm_set_filter
functionality without using Augeas, since we no longer have to worry
about existing filters being present.

Thanks: Alasdair Kergon, Zdenek Kabelac.
This commit is contained in:
Richard W.M. Jones
2017-07-26 15:39:32 +01:00
parent fe8c11ae4d
commit 10cf01419a
5 changed files with 61 additions and 203 deletions

View File

@@ -32,7 +32,6 @@ include $(top_srcdir)/subdir-rules.mk
EXTRA_DIST = \
99-guestfs-serial.rules \
excludefiles.in \
guestfs_lvm_conf.aug \
guestfs_shadow.aug \
hostfiles.in \
init \
@@ -87,12 +86,11 @@ packagelist: packagelist.in Makefile
cmp -s $@ $@-t || mv $@-t $@
rm -f $@-t
supermin.d/daemon.tar.gz: ../daemon/guestfsd guestfs_lvm_conf.aug guestfs_shadow.aug
supermin.d/daemon.tar.gz: ../daemon/guestfsd guestfs_shadow.aug
rm -f $@ $@-t
rm -rf tmp-d
mkdir -p tmp-d$(DAEMON_SUPERMIN_DIR) tmp-d/etc tmp-d/usr/share/guestfs
ln ../daemon/guestfsd tmp-d$(DAEMON_SUPERMIN_DIR)/guestfsd
ln $(srcdir)/guestfs_lvm_conf.aug tmp-d/usr/share/guestfs/guestfs_lvm_conf.aug
ln $(srcdir)/guestfs_shadow.aug tmp-d/usr/share/guestfs/guestfs_shadow.aug
( cd tmp-d && tar zcf - * ) > $@-t
rm -r tmp-d

View File

@@ -1,74 +0,0 @@
(*
Module: LVM
Parses LVM metadata.
Author: Gabriel de Perthuis <g2p.code+augeas@gmail.com>
About: License
This file is licensed under the LGPL v2+.
About: Configuration files
This lens applies to files in /etc/lvm/backup and /etc/lvm/archive.
About: Examples
The <Test_LVM> file contains various examples and tests.
*)
module Guestfs_LVM_conf =
autoload xfm
(* See lvm2/libdm/libdm-config.c for tokenisation;
* libdm uses a blacklist but I prefer the safer whitelist approach. *)
(* View: identifier
* The left hand side of a definition *)
let identifier = /[a-zA-Z0-9_-]+/
(* strings can contain backslash-escaped dquotes, but I don't know
* how to get the message across to augeas *)
let str = [label "str". Quote.do_dquote (store /([^\"]|\\\\.)*/)]
let int = [label "int". store Rx.relinteger]
(* View: flat_literal
* A literal without structure *)
let flat_literal = int|str
(* allow multiline and mixed int/str, used for raids and stripes *)
(* View: list
* A list containing flat literals *)
let list = [
label "list" . counter "list"
. del /\[[ \t\n]*/ "["
.([seq "list". flat_literal . del /,[ \t\n]*/ ", "]*
. [seq "list". flat_literal . del /[ \t\n]*/ ""])?
. Util.del_str "]"]
(* View: val
* Any value that appears on the right hand side of an assignment *)
let val = flat_literal | list
(* View: nondef
* A line that doesn't contain a statement *)
let nondef =
Util.empty
| Util.comment
(* Build.block couldn't be reused, because of recursion and
* a different philosophy of whitespace handling. *)
(* View: def
* An assignment, or a block containing definitions *)
let rec def = [
Util.indent . key identifier . (
del /[ \t]*\{\n/ " {\n"
.[label "dict".(nondef | def)*]
. Util.indent . Util.del_str "}\n"
|Sep.space_equal . val . Util.comment_or_eol)]
(* View: lns
* The main lens *)
let lns = (nondef | def)*
let filter =
incl "/etc/lvm/archive/*.vg"
. incl "/etc/lvm/backup/*"
. Util.stdexcl
let xfm = transform lns filter

View File

@@ -253,7 +253,7 @@ extern char *get_blkid_tag (const char *device, const char *tag);
extern int lv_canonical (const char *device, char **ret);
/* lvm-filter.c */
extern void copy_lvm (void);
extern void clean_lvm_config (void);
extern void start_lvmetad (void);
/* zero.c */

View File

@@ -269,7 +269,7 @@ main (int argc, char *argv[])
* daemon/lvm-filter.c).
*/
if (!test_mode) {
copy_lvm ();
clean_lvm_config ();
start_lvmetad ();
}

View File

@@ -36,65 +36,47 @@
#include "daemon.h"
#include "actions.h"
/* This runs during daemon start up and creates a complete copy of
* /etc/lvm so that we can modify it as we desire. We set
* LVM_SYSTEM_DIR to point to the copy. Note that the final directory
* layout is:
/* This runs during daemon start up and creates a fresh LVM
* configuration which we can modify as we desire. LVM allows
* configuration to be completely empty (meaning "all defaults").
*
* The final directory layout is:
*
* /tmp/lvmXXXXXX (lvm_system_dir set to this)
* /tmp/lvmXXXXXX/lvm ($LVM_SYSTEM_DIR set to this)
* /tmp/lvmXXXXXX/lvm/lvm.conf (configuration file)
* /tmp/lvmXXXXXX/lvm/cache
* etc.
* /tmp/lvmXXXXXX/lvm/lvm.conf (configuration file - initially empty)
*/
static char lvm_system_dir[] = "/tmp/lvmXXXXXX";
static void rm_lvm_system_dir (void);
static void debug_lvm_config (void);
void
copy_lvm (void)
clean_lvm_config (void)
{
struct stat statbuf;
char cmd[64], env[64];
int r;
/* If /etc/lvm directory doesn't exist (or isn't a directory) assume
* that this system doesn't support LVM and do nothing.
*/
r = stat ("/etc/lvm", &statbuf);
if (r == -1) {
perror ("copy_lvm: stat: /etc/lvm");
return;
}
if (! S_ISDIR (statbuf.st_mode)) {
fprintf (stderr, "copy_lvm: warning: /etc/lvm is not a directory\n");
return;
}
char env[64], conf[64];
FILE *fp;
if (mkdtemp (lvm_system_dir) == NULL)
error (EXIT_FAILURE, errno, "mkdtemp: %s", lvm_system_dir);
/* Copy the entire directory */
snprintf (cmd, sizeof cmd, "%s -a /etc/lvm/ %s", "cp", lvm_system_dir);
r = system (cmd);
if (r == -1) {
perror (cmd);
rmdir (lvm_system_dir);
exit (EXIT_FAILURE);
}
if (WEXITSTATUS (r) != 0) {
fprintf (stderr, "cp command failed with return code %d\n",
WEXITSTATUS (r));
rmdir (lvm_system_dir);
exit (EXIT_FAILURE);
}
/* Set environment variable so we use the copy. */
snprintf (env, sizeof env, "%s/lvm", lvm_system_dir);
mkdir (env, 0755);
snprintf (conf, sizeof conf, "%s/lvm/lvm.conf", lvm_system_dir);
fp = fopen (conf, "w");
if (fp == NULL) {
perror ("clean_lvm_config: cannot create empty lvm.conf");
exit (EXIT_FAILURE);
}
fclose (fp);
/* Set environment variable so we use the clean configuration. */
setenv ("LVM_SYSTEM_DIR", env, 1);
/* Set a handler to remove the temporary directory at exit. */
atexit (rm_lvm_system_dir);
debug_lvm_config ();
}
/* Try to run lvmetad, without failing if it couldn't. */
@@ -125,98 +107,40 @@ rm_lvm_system_dir (void)
static int
set_filter (char *const *filters)
{
CLEANUP_AUG_CLOSE augeas *aug = NULL;
int r;
int count;
const char *filter_types[] = { "filter", "global_filter", NULL };
CLEANUP_FREE char *conf = NULL;
FILE *fp;
size_t i, j;
/* Small optimization: do not load the files at init time,
* but do that only after having applied the transformation.
*/
const int flags = AUG_NO_ERR_CLOSE | AUG_NO_LOAD;
aug = aug_init (lvm_system_dir, "/usr/share/guestfs/", flags);
if (!aug) {
reply_with_error ("augeas initialization failed");
if (asprintf (&conf, "%s/lvm/lvm.conf", lvm_system_dir) == -1) {
reply_with_perror ("asprintf");
return -1;
}
fp = fopen (conf, "we");
if (fp == NULL) {
reply_with_perror ("open: %s", conf);
return -1;
}
if (aug_error (aug) != AUG_NOERROR) {
AUGEAS_ERROR ("aug_init");
return -1;
}
fprintf (fp, "devices {\n");
for (j = 0; filter_types[j] != NULL; ++j) {
fprintf (fp, " %s = [\n", filter_types[j]);
fprintf (fp, " ");
r = aug_transform (aug, "guestfs_lvm_conf", "/lvm/lvm.conf",
0 /* = included */);
if (r == -1) {
AUGEAS_ERROR ("aug_transform");
return -1;
}
if (aug_load (aug) == -1) {
AUGEAS_ERROR ("aug_load");
return -1;
}
/* Remove all the old filters ... */
r = aug_rm (aug, "/files/lvm/lvm.conf/devices/dict/filter/list/*");
if (r == -1) {
AUGEAS_ERROR ("aug_rm/filter");
return -1;
}
r = aug_rm (aug, "/files/lvm/lvm.conf/devices/dict/global_filter/list/*");
if (r == -1) {
AUGEAS_ERROR ("aug_rm/global_filter");
return -1;
}
/* ... and add the new ones. */
for (count = 0; filters[count] != NULL; ++count) {
char buf[128];
snprintf (buf, sizeof buf,
"/files/lvm/lvm.conf/devices/dict/filter/list/%d/str",
count + 1);
if (aug_set (aug, buf, filters[count]) == -1) {
AUGEAS_ERROR ("aug_set/filter: %d: %s", count, filters[count]);
return -1;
for (i = 0; filters[i] != NULL; ++i) {
if (i > 0)
fprintf (fp, ",\n ");
fprintf (fp, "\"%s\"", filters[i]);
}
snprintf (buf, sizeof buf,
"/files/lvm/lvm.conf/devices/dict/global_filter/list/%d/str",
count + 1);
fprintf (fp, "\n");
fprintf (fp, " ]\n");
}
fprintf (fp, "}\n");
if (aug_set (aug, buf, filters[count]) == -1) {
AUGEAS_ERROR ("aug_set/global_filter: %d: %s", count, filters[count]);
return -1;
}
}
fclose (fp);
/* Safety check for the written filter nodes. */
r = aug_match (aug, "/files/lvm/lvm.conf/devices/dict/filter/list/*/str",
NULL);
if (r == -1) {
AUGEAS_ERROR ("aug_match/filter");
return -1;
}
if (r != count) {
reply_with_error ("filters# vs matches mismatch: %d vs %d", count, r);
return -1;
}
r = aug_match (aug, "/files/lvm/lvm.conf/devices/dict/global_filter/list/*/str",
NULL);
if (r == -1) {
AUGEAS_ERROR ("aug_match/global_filter");
return -1;
}
if (r != count) {
reply_with_error ("global_filter# vs matches mismatch: %d vs %d", count, r);
return -1;
}
if (aug_save (aug) == -1) {
AUGEAS_ERROR ("aug_save");
return -1;
}
debug_lvm_config ();
return 0;
}
@@ -267,6 +191,16 @@ rescan (void)
return 0;
}
/* Show what lvm thinks is the current config. Useful for debugging. */
static void
debug_lvm_config (void)
{
if (verbose) {
fprintf (stderr, "lvm config:\n");
ignore_value (system ("lvm config"));
}
}
/* Construct the new, specific filter strings. We can assume that
* the 'devices' array does not contain any regexp metachars,
* because it's already been checked by the stub code.