mirror of
https://github.com/libguestfs/libguestfs.git
synced 2026-03-21 22:53:37 +00:00
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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
|
||||
@@ -269,7 +269,7 @@ main (int argc, char *argv[])
|
||||
* daemon/lvm-filter.c).
|
||||
*/
|
||||
if (!test_mode) {
|
||||
copy_lvm ();
|
||||
clean_lvm_config ();
|
||||
start_lvmetad ();
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user