diff options
author | Richard Jones <rjones@redhat.com> | 2010-07-16 13:01:21 +0100 |
---|---|---|
committer | Richard Jones <rjones@redhat.com> | 2010-07-16 15:49:50 +0100 |
commit | d2cf9a15a9f22623dbbed33fb66c5077f1275df2 (patch) | |
tree | 53dff5d18deb2180df1adb2c010341c5907b1b0c /daemon/lvm-filter.c | |
parent | 321ca1ef91a90cec5b94058b84420e8018e3f1d8 (diff) | |
download | libguestfs-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.c | 244 |
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 (); +} |