Update TODO.
[libguestfs.git] / daemon / lvm-filter.c
1 /* libguestfs - the guestfsd daemon
2  * Copyright (C) 2010-2011 Red Hat Inc.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 #include <config.h>
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <inttypes.h>
24 #include <string.h>
25 #include <unistd.h>
26
27 #include "daemon.h"
28 #include "c-ctype.h"
29 #include "actions.h"
30
31 /* Does the current line match the regexp /^\s*filter\s*=/ */
32 static int
33 is_filter_line (const char *line)
34 {
35   while (*line && c_isspace (*line))
36     line++;
37   if (!*line)
38     return 0;
39
40   if (! STRPREFIX (line, "filter"))
41     return 0;
42   line += 6;
43
44   while (*line && c_isspace (*line))
45     line++;
46   if (!*line)
47     return 0;
48
49   if (*line != '=')
50     return 0;
51
52   return 1;
53 }
54
55 /* Rewrite the 'filter = [ ... ]' line in /etc/lvm/lvm.conf. */
56 static int
57 set_filter (const char *filter)
58 {
59   FILE *ifp = fopen ("/etc/lvm/lvm.conf", "r");
60   if (ifp == NULL) {
61     reply_with_perror ("open: /etc/lvm/lvm.conf");
62     return -1;
63   }
64   FILE *ofp = fopen ("/etc/lvm/lvm.conf.new", "w");
65   if (ofp == NULL) {
66     reply_with_perror ("open: /etc/lvm/lvm.conf.new");
67     fclose (ifp);
68     return -1;
69   }
70
71   char *line = NULL;
72   size_t len = 0;
73   while (getline (&line, &len, ifp) != -1) {
74     int r;
75     if (is_filter_line (line)) {
76       r = fprintf (ofp, "    filter = [ %s ]\n", filter);
77     } else {
78       r = fprintf (ofp, "%s", line);
79     }
80     if (r < 0) {
81       /* NB. fprintf doesn't set errno on error. */
82       reply_with_error ("/etc/lvm/lvm.conf.new: write failed");
83       fclose (ifp);
84       fclose (ofp);
85       free (line);
86       unlink ("/etc/lvm/lvm.conf.new");
87       return -1;
88     }
89   }
90
91   free (line);
92
93   if (fclose (ifp) == EOF) {
94     reply_with_perror ("/etc/lvm/lvm.conf.new");
95     unlink ("/etc/lvm/lvm.conf.new");
96     fclose (ofp);
97     return -1;
98   }
99   if (fclose (ofp) == EOF) {
100     reply_with_perror ("/etc/lvm/lvm.conf.new");
101     unlink ("/etc/lvm/lvm.conf.new");
102     return -1;
103   }
104
105   if (rename ("/etc/lvm/lvm.conf.new", "/etc/lvm/lvm.conf") == -1) {
106     reply_with_perror ("rename: /etc/lvm/lvm.conf");
107     unlink ("/etc/lvm/lvm.conf.new");
108     return -1;
109   }
110
111   return 0;
112 }
113
114 static int
115 vgchange (const char *vgchange_flag)
116 {
117   char *err;
118   int r = command (NULL, &err, "lvm", "vgchange", vgchange_flag, NULL);
119   if (r == -1) {
120     reply_with_error ("vgchange: %s", err);
121     free (err);
122     return -1;
123   }
124
125   free (err);
126   return 0;
127 }
128
129 /* Deactivate all VGs. */
130 static int
131 deactivate (void)
132 {
133   return vgchange ("-an");
134 }
135
136 /* Reactivate all VGs. */
137 static int
138 reactivate (void)
139 {
140   return vgchange ("-ay");
141 }
142
143 /* Clear the cache and rescan. */
144 static int
145 rescan (void)
146 {
147   unlink ("/etc/lvm/cache/.cache");
148
149   char *err;
150   int r = command (NULL, &err, "lvm", "vgscan", NULL);
151   if (r == -1) {
152     reply_with_error ("vgscan: %s", err);
153     free (err);
154     return -1;
155   }
156
157   free (err);
158   return 0;
159 }
160
161 /* Construct the new, specific filter string.  We can assume that
162  * the 'devices' array does not contain any regexp metachars,
163  * because it's already been checked by the stub code.
164  */
165 static char *
166 make_filter_string (char *const *devices)
167 {
168   size_t i;
169   size_t len = 64;
170   for (i = 0; devices[i] != NULL; ++i)
171     len += strlen (devices[i]) + 16;
172
173   char *filter = malloc (len);
174   if (filter == NULL) {
175     reply_with_perror ("malloc");
176     return NULL;
177   }
178
179   char *p = filter;
180   for (i = 0; devices[i] != NULL; ++i) {
181     /* Because of the way matching works in LVM, each match clause
182      * should be either:
183      *   "a|^/dev/sda|",      for whole block devices, or
184      *   "a|^/dev/sda1$|",    for single partitions
185      * (the assumption being we have <= 26 block devices XXX).
186      */
187     size_t slen = strlen (devices[i]);
188     char str[slen+16];
189
190     if (c_isdigit (devices[i][slen-1]))
191       snprintf (str, slen+16, "\"a|^%s$|\", ", devices[i]);
192     else
193       snprintf (str, slen+16, "\"a|^%s|\", ", devices[i]);
194
195     strcpy (p, str);
196     p += strlen (str);
197   }
198   strcpy (p, "\"r|.*|\"");
199
200   return filter;                /* Caller must free. */
201 }
202
203 int
204 do_lvm_set_filter (char *const *devices)
205 {
206   char *filter = make_filter_string (devices);
207   if (filter == NULL)
208     return -1;
209
210   if (deactivate () == -1) {
211     free (filter);
212     return -1;
213   }
214
215   int r = set_filter (filter);
216   free (filter);
217   if (r == -1)
218     return -1;
219
220   if (rescan () == -1)
221     return -1;
222
223   return reactivate ();
224 }
225
226 int
227 do_lvm_clear_filter (void)
228 {
229   if (deactivate () == -1)
230     return -1;
231
232   if (set_filter ("\"a/.*/\"") == -1)
233     return -1;
234
235   if (rescan () == -1)
236     return -1;
237
238   return reactivate ();
239 }