daemon: Use a private copy of /etc/lvm so we don't modify the real config.
authorRichard W.M. Jones <rjones@redhat.com>
Fri, 4 Nov 2011 15:55:13 +0000 (15:55 +0000)
committerRichard W.M. Jones <rjones@redhat.com>
Fri, 4 Nov 2011 16:37:12 +0000 (16:37 +0000)
In the libguestfs live case we need to be careful not to modify the
real /etc/lvm/lvm.conf file (when setting the filter rule).

When the daemon starts, make a complete copy of /etc/lvm in a
temporary directory, and adjust LVM_SYSTEM_DIR to point to the copy.
All changes are made in the temporary copy.

daemon/daemon.h
daemon/guestfsd.c
daemon/lvm-filter.c

index b4f99a8..6ed68fd 100644 (file)
@@ -134,6 +134,9 @@ extern int e2prog (char *name); /* Massive hack for RHEL 5. */
 /*-- in lvm.c --*/
 extern int lv_canonical (const char *device, char **ret);
 
+/*-- in lvm-filter.c --*/
+extern void copy_lvm (void);
+
 /*-- in proto.c --*/
 extern void main_loop (int sock) __attribute__((noreturn));
 
index 51bb0b7..6373f34 100644 (file)
@@ -239,6 +239,11 @@ main (int argc, char *argv[])
   _umask (0);
 #endif
 
+  /* Make a private copy of /etc/lvm so we can change the config (see
+   * daemon/lvm-filter.c).
+   */
+  copy_lvm ();
+
   /* Connect to virtio-serial channel. */
   int sock = open (VIRTIO_SERIAL_CHANNEL, O_RDWR | O_CLOEXEC);
   if (sock == -1) {
index a62d40a..21e2fde 100644 (file)
 #include <inttypes.h>
 #include <string.h>
 #include <unistd.h>
+#include <sys/stat.h>
 
-#include "daemon.h"
 #include "c-ctype.h"
+#include "ignore-value.h"
+
+#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.
+ */
+static char lvm_system_dir[] = "/tmp/lvmXXXXXX";
+
+static void rm_lvm_system_dir (void);
+
+void
+copy_lvm (void)
+{
+  struct stat statbuf;
+  char cmd[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;
+  }
+
+  if (mkdtemp (lvm_system_dir) == NULL) {
+    perror (lvm_system_dir);
+    exit (EXIT_FAILURE);
+  }
+
+  /* Hopefully no dotfiles in there ... XXX */
+  snprintf (cmd, sizeof cmd, "cp -a /etc/lvm/* %s", 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. */
+  setenv ("LVM_SYSTEM_DIR", lvm_system_dir, 1);
+
+  /* Set a handler to remove the temporary directory at exit. */
+  atexit (rm_lvm_system_dir);
+}
+
+static void
+rm_lvm_system_dir (void)
+{
+  char cmd[64];
+
+  snprintf (cmd, sizeof cmd, "rm -rf %s", lvm_system_dir);
+  ignore_value (system (cmd));
+}
+
 /* Does the current line match the regexp /^\s*filter\s*=/ */
 static int
 is_filter_line (const char *line)
@@ -52,18 +120,24 @@ is_filter_line (const char *line)
   return 1;
 }
 
-/* Rewrite the 'filter = [ ... ]' line in /etc/lvm/lvm.conf. */
+/* Rewrite the 'filter = [ ... ]' line in lvm.conf. */
 static int
 set_filter (const char *filter)
 {
-  FILE *ifp = fopen ("/etc/lvm/lvm.conf", "r");
+  char lvm_conf[64];
+  snprintf (lvm_conf, sizeof lvm_conf, "%s/lvm.conf", lvm_system_dir);
+
+  char lvm_conf_new[64];
+  snprintf (lvm_conf_new, sizeof lvm_conf, "%s/lvm.conf.new", lvm_system_dir);
+
+  FILE *ifp = fopen (lvm_conf, "r");
   if (ifp == NULL) {
-    reply_with_perror ("open: /etc/lvm/lvm.conf");
+    reply_with_perror ("open: %s", lvm_conf);
     return -1;
   }
-  FILE *ofp = fopen ("/etc/lvm/lvm.conf.new", "w");
+  FILE *ofp = fopen (lvm_conf_new, "w");
   if (ofp == NULL) {
-    reply_with_perror ("open: /etc/lvm/lvm.conf.new");
+    reply_with_perror ("open: %s", lvm_conf_new);
     fclose (ifp);
     return -1;
   }
@@ -79,11 +153,11 @@ set_filter (const char *filter)
     }
     if (r < 0) {
       /* NB. fprintf doesn't set errno on error. */
-      reply_with_error ("/etc/lvm/lvm.conf.new: write failed");
+      reply_with_error ("%s: write failed", lvm_conf_new);
       fclose (ifp);
       fclose (ofp);
       free (line);
-      unlink ("/etc/lvm/lvm.conf.new");
+      unlink (lvm_conf_new);
       return -1;
     }
   }
@@ -91,20 +165,20 @@ set_filter (const char *filter)
   free (line);
 
   if (fclose (ifp) == EOF) {
-    reply_with_perror ("/etc/lvm/lvm.conf.new");
-    unlink ("/etc/lvm/lvm.conf.new");
+    reply_with_perror ("close: %s", lvm_conf);
+    unlink (lvm_conf_new);
     fclose (ofp);
     return -1;
   }
   if (fclose (ofp) == EOF) {
-    reply_with_perror ("/etc/lvm/lvm.conf.new");
-    unlink ("/etc/lvm/lvm.conf.new");
+    reply_with_perror ("close: %s", lvm_conf_new);
+    unlink (lvm_conf_new);
     return -1;
   }
 
-  if (rename ("/etc/lvm/lvm.conf.new", "/etc/lvm/lvm.conf") == -1) {
-    reply_with_perror ("rename: /etc/lvm/lvm.conf");
-    unlink ("/etc/lvm/lvm.conf.new");
+  if (rename (lvm_conf_new, lvm_conf) == -1) {
+    reply_with_perror ("rename: %s", lvm_conf);
+    unlink (lvm_conf_new);
     return -1;
   }
 
@@ -144,7 +218,10 @@ reactivate (void)
 static int
 rescan (void)
 {
-  unlink ("/etc/lvm/cache/.cache");
+  char lvm_cache[64];
+  snprintf (lvm_cache, sizeof lvm_cache, "%s/cache/.cache", lvm_system_dir);
+
+  unlink (lvm_cache);
 
   char *err;
   int r = command (NULL, &err, "lvm", "vgscan", NULL);