summaryrefslogtreecommitdiffstats
path: root/daemon/lvm-filter.c
diff options
context:
space:
mode:
authorRichard Jones <rjones@redhat.com>2010-07-16 13:01:21 +0100
committerRichard Jones <rjones@redhat.com>2010-07-16 15:49:50 +0100
commitd2cf9a15a9f22623dbbed33fb66c5077f1275df2 (patch)
tree53dff5d18deb2180df1adb2c010341c5907b1b0c /daemon/lvm-filter.c
parent321ca1ef91a90cec5b94058b84420e8018e3f1d8 (diff)
downloadlibguestfs-d2cf9a15a9f22623dbbed33fb66c5077f1275df2.tar.gz
libguestfs-d2cf9a15a9f22623dbbed33fb66c5077f1275df2.tar.xz
libguestfs-d2cf9a15a9f22623dbbed33fb66c5077f1275df2.zip
New APIs: lvm-set-filter and lvm-clear-filter.
These APIs allow you to change the device filter, the list of block devices that LVM "sees". Either you can set it to a fixed list of devices / partitions, or you can clear it so that LVM sees everything.
Diffstat (limited to 'daemon/lvm-filter.c')
-rw-r--r--daemon/lvm-filter.c244
1 files changed, 244 insertions, 0 deletions
diff --git a/daemon/lvm-filter.c b/daemon/lvm-filter.c
new file mode 100644
index 00000000..e487c6be
--- /dev/null
+++ b/daemon/lvm-filter.c
@@ -0,0 +1,244 @@
+/* libguestfs - the guestfsd daemon
+ * Copyright (C) 2010 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "daemon.h"
+#include "c-ctype.h"
+#include "actions.h"
+
+/* Does the current line match the regexp /^\s*filter\s*=/ */
+static int
+is_filter_line (const char *line)
+{
+ while (*line && c_isspace (*line))
+ line++;
+ if (!*line)
+ return 0;
+
+ if (! STRPREFIX (line, "filter"))
+ return 0;
+ line += 6;
+
+ while (*line && c_isspace (*line))
+ line++;
+ if (!*line)
+ return 0;
+
+ if (*line != '=')
+ return 0;
+
+ return 1;
+}
+
+/* Rewrite the 'filter = [ ... ]' line in /etc/lvm/lvm.conf. */
+static int
+set_filter (const char *filter)
+{
+ if (verbose)
+ fprintf (stderr, "LVM: setting device filter to %s\n", filter);
+
+ FILE *ifp = fopen ("/etc/lvm/lvm.conf", "r");
+ if (ifp == NULL) {
+ reply_with_perror ("open: /etc/lvm/lvm.conf");
+ return -1;
+ }
+ FILE *ofp = fopen ("/etc/lvm/lvm.conf.new", "w");
+ if (ofp == NULL) {
+ reply_with_perror ("open: /etc/lvm/lvm.conf.new");
+ fclose (ifp);
+ return -1;
+ }
+
+ char *line = NULL;
+ size_t len = 0;
+ while (getline (&line, &len, ifp) != -1) {
+ int r;
+ if (is_filter_line (line)) {
+ if (verbose)
+ fprintf (stderr, "LVM: replacing config line:\n%s", line);
+ r = fprintf (ofp, " filter = [ %s ]\n", filter);
+ } else {
+ r = fprintf (ofp, "%s", line);
+ }
+ if (r < 0) {
+ /* NB. fprintf doesn't set errno on error. */
+ reply_with_error ("/etc/lvm/lvm.conf.new: write failed");
+ fclose (ifp);
+ fclose (ofp);
+ free (line);
+ unlink ("/etc/lvm/lvm.conf.new");
+ return -1;
+ }
+ }
+
+ free (line);
+
+ if (fclose (ifp) == EOF) {
+ reply_with_perror ("/etc/lvm/lvm.conf.new");
+ unlink ("/etc/lvm/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");
+ 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");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+vgchange (const char *vgchange_flag)
+{
+ char *err;
+ int r = command (NULL, &err, "lvm", "vgchange", vgchange_flag, NULL);
+ if (r == -1) {
+ reply_with_error ("vgscan: %s", err);
+ free (err);
+ return -1;
+ }
+
+ free (err);
+ return 0;
+}
+
+/* Deactivate all VGs. */
+static int
+deactivate (void)
+{
+ return vgchange ("-an");
+}
+
+/* Reactivate all VGs. */
+static int
+reactivate (void)
+{
+ return vgchange ("-ay");
+}
+
+/* Clear the cache and rescan. */
+static int
+rescan (void)
+{
+ unlink ("/etc/lvm/cache/.cache");
+
+ char *err;
+ int r = command (NULL, &err, "lvm", "vgscan", NULL);
+ if (r == -1) {
+ reply_with_error ("vgscan: %s", err);
+ free (err);
+ return -1;
+ }
+
+ free (err);
+ return 0;
+}
+
+/* Construct the new, specific filter string. We can assume that
+ * the 'devices' array does not contain any regexp metachars,
+ * because it's already been checked by the stub code.
+ */
+static char *
+make_filter_string (char *const *devices)
+{
+ size_t i;
+ size_t len = 64;
+ for (i = 0; devices[i] != NULL; ++i)
+ len += strlen (devices[i]) + 16;
+
+ char *filter = malloc (len);
+ if (filter == NULL) {
+ reply_with_perror ("malloc");
+ return NULL;
+ }
+
+ char *p = filter;
+ for (i = 0; devices[i] != NULL; ++i) {
+ /* Because of the way matching works in LVM, each match clause
+ * should be either:
+ * "a|^/dev/sda|", for whole block devices, or
+ * "a|^/dev/sda1$|", for single partitions
+ * (the assumption being we have <= 26 block devices XXX).
+ */
+ size_t slen = strlen (devices[i]);
+ char str[slen+16];
+
+ if (c_isdigit (devices[i][slen-1]))
+ snprintf (str, slen+16, "\"a|^%s$|\", ", devices[i]);
+ else
+ snprintf (str, slen+16, "\"a|^%s|\", ", devices[i]);
+
+ strcpy (p, str);
+ p += strlen (str);
+ }
+ strcpy (p, "\"r|.*|\"");
+
+ return filter; /* Caller must free. */
+}
+
+int
+do_lvm_set_filter (char *const *devices)
+{
+ char *filter = make_filter_string (devices);
+ if (filter == NULL)
+ return -1;
+
+ if (deactivate () == -1) {
+ free (filter);
+ return -1;
+ }
+
+ int r = set_filter (filter);
+ free (filter);
+ if (r == -1)
+ return -1;
+
+ if (rescan () == -1)
+ return -1;
+
+ return reactivate ();
+}
+
+int
+do_lvm_clear_filter (void)
+{
+ if (deactivate () == -1)
+ return -1;
+
+ if (set_filter ("\"a/.*/\"") == -1)
+ return -1;
+
+ if (rescan () == -1)
+ return -1;
+
+ return reactivate ();
+}