New API: list-md-devices.
[libguestfs.git] / daemon / md.c
1 /* libguestfs - the guestfsd daemon
2  * Copyright (C) 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18
19 #include <config.h>
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <inttypes.h>
25 #include <glob.h>
26
27 #include "daemon.h"
28 #include "actions.h"
29 #include "optgroups.h"
30
31 int
32 optgroup_mdadm_available (void)
33 {
34   return prog_exists ("mdadm");
35 }
36
37 static size_t
38 count_bits (uint64_t bitmap)
39 {
40   size_t c;
41
42   if (bitmap == 0)
43     return 0;
44
45   c = bitmap & 1 ? 1 : 0;
46   bitmap >>= 1;
47   return c + count_bits (bitmap);
48 }
49
50 /* Takes optional arguments, consult optargs_bitmask. */
51 int
52 do_mdadm_create (const char *name, char *const *devices,
53                  int64_t missingbitmap, int nrdevices, int spare,
54                  int64_t chunk, const char *level)
55 {
56   char nrdevices_s[32];
57   char spare_s[32];
58   char chunk_s[32];
59   size_t j;
60   int r;
61   char *err;
62   uint64_t umissingbitmap = (uint64_t) missingbitmap;
63
64   /* Check the optional parameters and set defaults where appropriate. */
65   if (!(optargs_bitmask & GUESTFS_MDADM_CREATE_MISSINGBITMAP_BITMASK))
66     umissingbitmap = 0;
67
68   if (optargs_bitmask & GUESTFS_MDADM_CREATE_SPARE_BITMASK) {
69     if (spare < 0) {
70       reply_with_error ("spare must not be negative");
71       return -1;
72     }
73   }
74   else
75     spare = 0;
76
77   if (optargs_bitmask & GUESTFS_MDADM_CREATE_NRDEVICES_BITMASK) {
78     if (nrdevices < 2) {
79       reply_with_error ("nrdevices is less than 2");
80       return -1;
81     }
82   }
83   else
84     nrdevices = count_strings (devices) + count_bits (umissingbitmap);
85
86   if (optargs_bitmask & GUESTFS_MDADM_CREATE_LEVEL_BITMASK) {
87     if (STRNEQ (level, "linear") && STRNEQ (level, "raid0") &&
88         STRNEQ (level, "0") && STRNEQ (level, "stripe") &&
89         STRNEQ (level, "raid1") && STRNEQ (level, "1") &&
90         STRNEQ (level, "mirror") &&
91         STRNEQ (level, "raid4") && STRNEQ (level, "4") &&
92         STRNEQ (level, "raid5") && STRNEQ (level, "5") &&
93         STRNEQ (level, "raid6") && STRNEQ (level, "6") &&
94         STRNEQ (level, "raid10") && STRNEQ (level, "10")) {
95       reply_with_error ("unknown level parameter: %s", level);
96       return -1;
97     }
98   }
99   else
100     level = "raid1";
101
102   if (optargs_bitmask & GUESTFS_MDADM_CREATE_CHUNK_BITMASK) {
103     /* chunk is bytes in the libguestfs API, but K when we pass it to mdadm */
104     if ((chunk & 1023) != 0) {
105       reply_with_error ("chunk size must be a multiple of 1024 bytes");
106       return -1;
107     }
108   }
109
110   /* Check invariant. */
111   if (count_strings (devices) + count_bits (umissingbitmap) !=
112       (size_t) (nrdevices + spare)) {
113     reply_with_error ("devices (%zu) + bits set in missingbitmap (%zu) is not equal to nrdevices (%d) + spare (%d)",
114                       count_strings (devices), count_bits (umissingbitmap),
115                       nrdevices, spare);
116     return -1;
117   }
118
119   size_t MAX_ARGS = nrdevices + 16;
120   const char *argv[MAX_ARGS];
121   size_t i = 0;
122
123   ADD_ARG (argv, i, "mdadm");
124   ADD_ARG (argv, i, "--create");
125   /* --run suppresses "Continue creating array" question */
126   ADD_ARG (argv, i, "--run");
127   ADD_ARG (argv, i, name);
128   ADD_ARG (argv, i, "--level");
129   ADD_ARG (argv, i, level);
130   ADD_ARG (argv, i, "--raid-devices");
131   snprintf (nrdevices_s, sizeof nrdevices_s, "%d", nrdevices);
132   ADD_ARG (argv, i, nrdevices_s);
133   if (optargs_bitmask & GUESTFS_MDADM_CREATE_SPARE_BITMASK) {
134     ADD_ARG (argv, i, "--spare-devices");
135     snprintf (spare_s, sizeof spare_s, "%d", spare);
136     ADD_ARG (argv, i, spare_s);
137   }
138   if (optargs_bitmask & GUESTFS_MDADM_CREATE_CHUNK_BITMASK) {
139     ADD_ARG (argv, i, "--chunk");
140     snprintf (chunk_s, sizeof chunk_s, "%" PRIi64, chunk / 1024);
141     ADD_ARG (argv, i, chunk_s);
142   }
143
144   /* Add devices and "missing". */
145   j = 0;
146   while (devices[j] != NULL || umissingbitmap != 0) {
147     if (umissingbitmap & 1)
148       ADD_ARG (argv, i, "missing");
149     else {
150       ADD_ARG (argv, i, devices[j]);
151       j++;
152     }
153     umissingbitmap >>= 1;
154   }
155
156   ADD_ARG (argv, i, NULL);
157
158   r = commandv (NULL, &err, argv);
159   if (r == -1) {
160     reply_with_error ("mdadm: %s: %s", name, err);
161     free (err);
162     return -1;
163   }
164
165   free (err);
166
167   udev_settle ();
168
169   return 0;
170 }
171
172 static int
173 glob_errfunc (const char *epath, int eerrno)
174 {
175   fprintf (stderr, "glob: failure reading %s: %s\n", epath, strerror (eerrno));
176   return 1;
177 }
178
179 char **
180 do_list_md_devices (void)
181 {
182   char **r = NULL;
183   int size = 0, alloc = 0;
184   glob_t mds;
185
186   memset(&mds, 0, sizeof(mds));
187
188 #define PREFIX "/sys/block/md"
189 #define SUFFIX "/md"
190
191   /* Look for directories under /sys/block matching md[0-9]*
192    * As an additional check, we also make sure they have a md subdirectory.
193    */
194   int err = glob (PREFIX "[0-9]*" SUFFIX, GLOB_ERR, glob_errfunc, &mds);
195   if (err == GLOB_NOSPACE) {
196     reply_with_error ("glob: returned GLOB_NOSPACE: "
197                       "rerun with LIBGUESTFS_DEBUG=1");
198     goto error;
199   } else if (err == GLOB_ABORTED) {
200     reply_with_error ("glob: returned GLOB_ABORTED: "
201                       "rerun with LIBGUESTFS_DEBUG=1");
202     goto error;
203   }
204
205   for (size_t i = 0; i < mds.gl_pathc; i++) {
206     size_t len = strlen (mds.gl_pathv[i]) - strlen (PREFIX) - strlen (SUFFIX);
207
208 #define DEV "/dev/md"
209     char *dev = malloc (strlen(DEV) + len  + 1);
210     if (NULL == dev) {
211       reply_with_perror("malloc");
212       goto error;
213     }
214
215     char *n = dev;
216     n = mempcpy(n, DEV, strlen(DEV));
217     n = mempcpy(n, &mds.gl_pathv[i][strlen(PREFIX)], len);
218     *n = '\0';
219
220     if (add_string_nodup (&r, &size, &alloc, dev) == -1) goto error;
221   }
222
223   if (add_string_nodup (&r, &size, &alloc, NULL) == -1) goto error;
224   globfree (&mds);
225
226   return r;
227
228 error:
229   globfree (&mds);
230   if (r != NULL) free_strings (r);
231   return NULL;
232 }