diff --git a/appliance/packagelist.in b/appliance/packagelist.in
index 9b723d0..9da24e7 100644
--- a/appliance/packagelist.in
+++ b/appliance/packagelist.in
@@ -39,6 +39,7 @@
   ntfs-3g
   reiserfs-utils
   libselinux
+  pcre
   systemd /* for /sbin/reboot */
   udev
   util-linux-ng
@@ -59,6 +60,7 @@
   hfsplus
   iproute
   libaugeas0
+  libpcre3
   linux-image
   nilfs-tools
   ntfs-3g
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index 7757067..921392a 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -129,6 +129,7 @@ guestfsd_SOURCES = \
 	luks.c \
 	lvm.c \
 	lvm-filter.c \
+	md.c \
 	mkfs.c \
 	mknod.c \
 	modprobe.c \
@@ -173,7 +174,8 @@ guestfsd_LDADD = \
 	$(LIBSOCKET) \
 	$(LIB_CLOCK_GETTIME) \
 	$(LIBINTL) \
-	$(SERVENT_LIB)
+	$(SERVENT_LIB) \
+	$(PCRE_LIBS)
 
 guestfsd_CPPFLAGS = -I$(top_srcdir)/gnulib/lib
 guestfsd_CFLAGS = $(WARN_CFLAGS) $(WERROR_CFLAGS)
diff --git a/daemon/daemon.h b/daemon/daemon.h
index 6ed68fd..3ca4693 100644
--- a/daemon/daemon.h
+++ b/daemon/daemon.h
@@ -47,6 +47,7 @@ extern int xwrite (int sock, const void *buf, size_t len)
 extern int xread (int sock, void *buf, size_t len)
   __attribute__((__warn_unused_result__));
 
+extern int add_string_nodup (char ***argv, int *size, int *alloc, char *str);
 extern int add_string (char ***argv, int *size, int *alloc, const char *str);
 extern size_t count_strings (char *const *argv);
 extern void sort_strings (char **argv, int len);
diff --git a/daemon/guestfsd.c b/daemon/guestfsd.c
index eacbc1d..930ee27 100644
--- a/daemon/guestfsd.c
+++ b/daemon/guestfsd.c
@@ -415,10 +415,9 @@ xread (int sock, void *v_buf, size_t len)
 }
 
 int
-add_string (char ***argv, int *size, int *alloc, const char *str)
+add_string_nodup (char ***argv, int *size, int *alloc, char *str)
 {
   char **new_argv;
-  char *new_str;
 
   if (*size >= *alloc) {
     *alloc += 64;
@@ -426,11 +425,23 @@ add_string (char ***argv, int *size, int *alloc, const char *str)
     if (new_argv == NULL) {
       reply_with_perror ("realloc");
       free_strings (*argv);
+      *argv = NULL;
       return -1;
     }
     *argv = new_argv;
   }
 
+  (*argv)[*size] = str;
+
+  (*size)++;
+  return 0;
+}
+
+int
+add_string (char ***argv, int *size, int *alloc, const char *str)
+{
+  char *new_str;
+
   if (str) {
     new_str = strdup (str);
     if (new_str == NULL) {
@@ -438,13 +449,11 @@ add_string (char ***argv, int *size, int *alloc, const char *str)
       free_strings (*argv);
       return -1;
     }
-  } else
+  } else {
     new_str = NULL;
+  }
 
-  (*argv)[*size] = new_str;
-
-  (*size)++;
-  return 0;
+  return add_string_nodup (argv, size, alloc, new_str);
 }
 
 size_t
diff --git a/daemon/md.c b/daemon/md.c
new file mode 100644
index 0000000..6b8430a
--- /dev/null
+++ b/daemon/md.c
@@ -0,0 +1,95 @@
+/* libguestfs - the guestfsd daemon
+ * Copyright (C) 2011 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <config.h>
+
+#include <pcre.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "daemon.h"
+#include "actions.h"
+
+char **
+do_list_mddevices (void)
+{
+    /* Compile and keep a regular expression for matching md lines */
+    static pcre *mdre = NULL;
+    if (NULL == mdre) {
+        const char *err;
+        int erroffset;
+        mdre = pcre_compile("^md(\\d+)\\s*:", 0, &err, &erroffset, NULL);
+        if (NULL == mdre) {
+            reply_with_error("(%s:%i) pcre_compile failure at offset %i: %s",
+                             __FILE__, __LINE__, erroffset, err);
+            return NULL;
+        }
+    }
+
+    char **r = NULL;
+    int size = 0, alloc = 0;
+
+    FILE *mdstat = fopen("/proc/mdstat", "r");
+    if (!mdstat) {
+        reply_with_perror("open /proc/mdstat");
+        return NULL;
+    }
+
+    /* Test each line of /proc/mdstat against the regular expression */
+    char *line = NULL;
+    size_t bufsize = 0;
+    ssize_t len = 0;
+    while ((len = getline(&line, &bufsize, mdstat)) != -1) {
+        int ovec[6];
+
+        int matches = pcre_exec(mdre, NULL, line, len, 0, 0,
+                                ovec, sizeof(ovec) / sizeof(ovec[0]));
+        /* No match, next line */
+        if (matches < 0) continue;
+
+        /* ovec wasn't big enough. This should be impossible. */
+        if (matches == 0) {
+            reply_with_error("(%s:%i) pcre_exec output vector too small",
+                             __FILE__, __LINE__);
+            if (r != NULL) {
+                free_strings(r);
+                r = NULL;
+            }
+            goto error;
+        }
+
+        /* We got a match. Add it to the returned list */
+        char *dev = malloc(strlen("/dev/md") + ovec[1] + 1);
+        if (NULL == dev) {
+            reply_with_perror("malloc");
+            goto error;
+        }
+        char *n = mempcpy(dev, "/dev/md", strlen("/dev/md"));
+        n = mempcpy(n, line + ovec[2], ovec[3] - ovec[2]);
+        *n = '\0';
+
+        if (add_string_nodup(&r, &size, &alloc, dev) == -1) goto error;
+    }
+
+    return r;
+
+error:
+    fclose(mdstat);
+    if (r != NULL) free_strings(r);
+    return NULL;
+}
diff --git a/generator/generator_actions.ml b/generator/generator_actions.ml
index d3fa3e0..1cf5288 100644
--- a/generator/generator_actions.ml
+++ b/generator/generator_actions.ml
@@ -6332,6 +6332,16 @@ is for copying blocks within existing files.  See C<guestfs_cp>,
 C<guestfs_cp_a> and C<guestfs_mv> for general file copying and
 moving functions.");
 
+  ("list_mddevices", (RStringList "mddevices", [], []), 298, [],
+   [],
+   "list Linux md devices",
+   "\
+List all Linux md devices.
+
+The non-textual names as known to the appliance are returned, eg.
+C</dev/md127>. Note that this will normally be different to the name used by the
+guest.");
+
 ]
 
 let all_functions = non_daemon_functions @ daemon_functions
diff --git a/regressions/test-list-mddevices.sh b/regressions/test-list-mddevices.sh
new file mode 100755
index 0000000..2415322
--- /dev/null
+++ b/regressions/test-list-mddevices.sh
@@ -0,0 +1,43 @@
+#!/bin/bash -
+# libguestfs
+# Copyright (C) 2011 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+# Test guestfish list-mddevices command
+
+set -e
+
+../fish/guestfish <<EOF >/dev/null 2>&1
+sparse md-test1.img 100M
+sparse md-test2.img 100M
+run
+debug sh "/usr/bin/yes | mdadm -C /dev/md/test --level=1 --raid-devices=2 /dev/vda /dev/vdb"
+EOF
+
+output=$(
+../fish/guestfish -a md-test1.img -a md-test2.img <<EOF
+run
+list-mddevices
+EOF
+)
+
+if [ "$output" != "/dev/md127" ]; then
+    echo "$0: error: output of list-mddevices did not match expected output"
+    echo "$output"
+    exit 1
+fi
+
+rm -f md-test1.img md-test2.img
diff --git a/src/MAX_PROC_NR b/src/MAX_PROC_NR
index 95de1ee..a1f7f63 100644
--- a/src/MAX_PROC_NR
+++ b/src/MAX_PROC_NR
@@ -1 +1 @@
-297
+298
