/* 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 #include #include #include #include #include #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 (); }