+
+static int
+glob_errfunc (const char *epath, int eerrno)
+{
+ fprintf (stderr, "glob: failure reading %s: %s\n", epath, strerror (eerrno));
+ return 1;
+}
+
+char **
+do_list_md_devices (void)
+{
+ char **r = NULL;
+ int size = 0, alloc = 0;
+ glob_t mds;
+
+ memset(&mds, 0, sizeof(mds));
+
+#define PREFIX "/sys/block/md"
+#define SUFFIX "/md"
+
+ /* Look for directories under /sys/block matching md[0-9]*
+ * As an additional check, we also make sure they have a md subdirectory.
+ */
+ int err = glob (PREFIX "[0-9]*" SUFFIX, GLOB_ERR, glob_errfunc, &mds);
+ if (err == GLOB_NOSPACE) {
+ reply_with_error ("glob: returned GLOB_NOSPACE: "
+ "rerun with LIBGUESTFS_DEBUG=1");
+ goto error;
+ } else if (err == GLOB_ABORTED) {
+ reply_with_error ("glob: returned GLOB_ABORTED: "
+ "rerun with LIBGUESTFS_DEBUG=1");
+ goto error;
+ }
+
+ for (size_t i = 0; i < mds.gl_pathc; i++) {
+ size_t len = strlen (mds.gl_pathv[i]) - strlen (PREFIX) - strlen (SUFFIX);
+
+#define DEV "/dev/md"
+ char *dev = malloc (strlen(DEV) + len + 1);
+ if (NULL == dev) {
+ reply_with_perror("malloc");
+ goto error;
+ }
+
+ char *n = dev;
+ n = mempcpy(n, DEV, strlen(DEV));
+ n = mempcpy(n, &mds.gl_pathv[i][strlen(PREFIX)], len);
+ *n = '\0';
+
+ if (add_string_nodup (&r, &size, &alloc, dev) == -1) goto error;
+ }
+
+ if (add_string_nodup (&r, &size, &alloc, NULL) == -1) goto error;
+ globfree (&mds);
+
+ return r;
+
+error:
+ globfree (&mds);
+ if (r != NULL) free_strings (r);
+ return NULL;
+}
+
+char **
+do_md_detail(const char *md)
+{
+ int r;
+
+ char *out = NULL, *err = NULL;
+ char **lines = NULL;
+
+ char **ret = NULL;
+ int size = 0, alloc = 0;
+
+ const char *mdadm[] = { "mdadm", "-D", "--export", md, NULL };
+ r = commandv(&out, &err, mdadm);
+ if (r == -1) {
+ reply_with_error("%s", err);
+ goto error;
+ }
+
+ /* Split the command output into lines */
+ lines = split_lines(out);
+ if (lines == NULL) {
+ reply_with_perror("malloc");
+ goto error;
+ }
+
+ /* Parse the output of mdadm -D --export:
+ * MD_LEVEL=raid1
+ * MD_DEVICES=2
+ * MD_METADATA=1.0
+ * MD_UUID=cfa81b59:b6cfbd53:3f02085b:58f4a2e1
+ * MD_NAME=localhost.localdomain:0
+ */
+ for (char **i = lines; *i != NULL; i++) {
+ char *line = *i;
+
+ /* Skip blank lines (shouldn't happen) */
+ if (line[0] == '\0') continue;
+
+ /* Split the line in 2 at the equals sign */
+ char *eq = strchr(line, '=');
+ if (eq) {
+ *eq = '\0'; eq++;
+
+ /* Remove the MD_ prefix from the key and translate the remainder to lower
+ * case */
+ if (STRPREFIX(line, "MD_")) {
+ line += 3;
+ for (char *j = line; *j != '\0'; j++) {
+ *j = c_tolower(*j);
+ }
+ }
+
+ /* Add the key/value pair to the output */
+ if (add_string(&ret, &size, &alloc, line) == -1 ||
+ add_string(&ret, &size, &alloc, eq) == -1) goto error;
+ } else {
+ /* Ignore lines with no equals sign (shouldn't happen). Log to stderr so
+ * it will show up in LIBGUESTFS_DEBUG. */
+ fprintf(stderr, "md-detail: unexpected mdadm output ignored: %s", line);
+ }
+ }
+
+ free(out);
+ free(err);
+ free(lines); /* We freed the contained strings when we freed out */
+
+ if (add_string(&ret, &size, &alloc, NULL) == -1) return NULL;
+
+ return ret;
+
+error:
+ free(out);
+ free(err);
+ if (lines) free(lines);
+ if (ret) free_strings(ret);
+
+ return NULL;
+}
+
+int
+do_md_stop(const char *md)
+{
+ int r;
+ char *err = NULL;
+
+ const char *mdadm[] = { "mdadm", "--stop", md, NULL};
+ r = commandv(NULL, &err, mdadm);
+ if (r == -1) {
+ reply_with_error("%s", err);
+ free(err);
+ return -1;
+ }
+ return 0;
+}