]> git.ipfire.org Git - thirdparty/mdadm.git/blobdiff - mdstat.c
main: factor out code to parse layout for raid10 and faulty.
[thirdparty/mdadm.git] / mdstat.c
index c5b8f1e565d5f26f61acbad964ffd6e65ccd30f6..8de51cf755fa0e718f321d54c3714abd92f04cd9 100644 (file)
--- a/mdstat.c
+++ b/mdstat.c
@@ -2,7 +2,7 @@
  * mdstat - parse /proc/mdstat file. Part of:
  * mdadm - manage Linux "md" devices aka RAID arrays.
  *
- * Copyright (C) 2002 Neil Brown <neilb@cse.unsw.edu.au>
+ * Copyright (C) 2002-2006 Neil Brown <neilb@suse.de>
  *
  *
  *    This program is free software; you can redistribute it and/or modify
@@ -43,7 +43,7 @@
  *  read_ahead {not set|%d sectors}
  *  mdN : {in}active {(readonly)} raidX dev[%d]{(F)} ... %d blocks STATUS RESYNC
  *  ... Only initialised arrays listed
- *  unused: dev dev dev | <none>
+ *  unused devices: {dev dev ... | <none>}
  *
  * STATUS is personality dependant:
  *    linear:  %dk rounding
@@ -62,7 +62,7 @@
  *  mdN : {in}active {(read-only)} raidX dev[%d]{(F)} ...
  *       %d blocks STATUS
  *       RESYNC
- *  unused: dev dev .. | <none>
+ *  unused devices: {dev dev .. | <none>}
  *
  *  STATUS matches 0.90.0/2.2
  *  RESYNC includes [===>....],
@@ -85,6 +85,8 @@
 
 #include       "mdadm.h"
 #include       "dlink.h"
+#include       <sys/select.h>
+#include       <ctype.h>
 
 void free_mdstat(struct mdstat_ent *ms)
 {
@@ -93,27 +95,38 @@ void free_mdstat(struct mdstat_ent *ms)
                if (ms->dev) free(ms->dev);
                if (ms->level) free(ms->level);
                if (ms->pattern) free(ms->pattern);
+               if (ms->metadata_version) free(ms->metadata_version);
                t = ms;
                ms = ms->next;
                free(t);
        }
 }
 
-struct mdstat_ent *mdstat_read()
+static int mdstat_fd = -1;
+struct mdstat_ent *mdstat_read(int hold, int start)
 {
        FILE *f;
-       struct mdstat_ent *all, **end;
+       struct mdstat_ent *all, *rv, **end, **insert_here;
        char *line;
 
-       f = fopen("/proc/mdstat", "r");
+       if (hold && mdstat_fd != -1) {
+               lseek(mdstat_fd, 0L, 0);
+               f = fdopen(dup(mdstat_fd), "r");
+       } else
+               f = fopen("/proc/mdstat", "r");
        if (f == NULL)
                return NULL;
+       else
+               fcntl(fileno(f), F_SETFD, FD_CLOEXEC);
 
        all = NULL;
        end = &all;
        for (; (line = conf_line(f)) ; free_line(line)) {
                struct mdstat_ent *ent;
                char *w;
+               int devnum;
+               int in_devs = 0;
+               char *ep;
 
                if (strcmp(line, "Personalities")==0)
                        continue;
@@ -121,10 +134,18 @@ struct mdstat_ent *mdstat_read()
                        continue;
                if (strcmp(line, "unused")==0)
                        continue;
+               insert_here = NULL;
                /* Better be an md line.. */
-               if (strncmp(line, "md", 2)!= 0
-                   || atoi(line+2)<0) {
-                       fprintf(stderr, Name ": bad /proc/mdstat line starts: %s\n", line);
+               if (strncmp(line, "md", 2)!= 0)
+                       continue;
+               if (strncmp(line, "md_d", 4) == 0)
+                       devnum = -1-strtoul(line+4, &ep, 10);
+               else if (strncmp(line, "md", 2) == 0)
+                       devnum = strtoul(line+2, &ep, 10);
+               else
+                       continue;
+               if (ep == NULL || *ep ) {
+                       /* fprintf(stderr, Name ": bad /proc/mdstat line starts: %s\n", line); */
                        continue;
                }
 
@@ -132,17 +153,21 @@ struct mdstat_ent *mdstat_read()
                if (!ent) {
                        fprintf(stderr, Name ": malloc failed reading /proc/mdstat.\n");
                        free_line(line);
-                       fclose(f);
-                       return all;
+                       break;
                }
                ent->dev = ent->level = ent->pattern= NULL;
                ent->next = NULL;
                ent->percent = -1;
                ent->active = -1;
+               ent->resync = 0;
+               ent->metadata_version = NULL;
+               ent->raid_disks = 0;
+               ent->chunk_size = 0;
+               ent->devcnt = 0;
 
                ent->dev = strdup(line);
-               ent->devnum = atoi(line+2);
-               
+               ent->devnum = devnum;
+
                for (w=dl_next(line); w!= line ; w=dl_next(w)) {
                        int l = strlen(w);
                        char *eq;
@@ -152,9 +177,34 @@ struct mdstat_ent *mdstat_read()
                                ent->active = 0;
                        else if (ent->active >=0 &&
                                 ent->level == NULL &&
-                                w[0] != '(' /*readonly*/)
+                                w[0] != '(' /*readonly*/) {
                                ent->level = strdup(w);
-                       else if (!ent->pattern &&
+                               in_devs = 1;
+                       } else if (in_devs && strcmp(w, "blocks")==0)
+                               in_devs = 0;
+                       else if (in_devs) {
+                               ent->devcnt++;
+                               if (strncmp(w, "md", 2)==0) {
+                                       /* This has an md device as a component.
+                                        * If that device is already in the
+                                        * list, make sure we insert before
+                                        * there.
+                                        */
+                                       struct mdstat_ent **ih;
+                                       int dn2 = devname2devnum(w);
+                                       ih = &all;
+                                       while (ih != insert_here && *ih &&
+                                              (*ih)->devnum != dn2)
+                                               ih = & (*ih)->next;
+                                       insert_here = ih;
+                               }
+                       } else if (strcmp(w, "super") == 0 &&
+                                  dl_next(w) != line) {
+                               w = dl_next(w);
+                               ent->metadata_version = strdup(w);
+                       } else if (w[0] == '[' && isdigit(w[1])) {
+                               ent->raid_disks = atoi(w+1);
+                       } else if (!ent->pattern &&
                                 w[0] == '[' &&
                                 (w[1] == 'U' || w[1] == '_')) {
                                ent->pattern = strdup(w+1);
@@ -165,16 +215,98 @@ struct mdstat_ent *mdstat_read()
                                   w[l-1] == '%' &&
                                   (eq=strchr(w, '=')) != NULL ) {
                                ent->percent = atoi(eq+1);
+                               if (strncmp(w,"resync", 4)==0)
+                                       ent->resync = 1;
                        } else if (ent->percent == -1 &&
-                                  w[0] >= '0' && 
+                                  strncmp(w, "resync", 4)==0) {
+                               ent->resync = 1;
+                       } else if (ent->percent == -1 &&
+                                  w[0] >= '0' &&
                                   w[0] <= '9' &&
                                   w[l-1] == '%') {
                                ent->percent = atoi(w);
                        }
                }
-               *end = ent;
-               end = &ent->next;
+               if (insert_here && (*insert_here)) {
+                       ent->next = *insert_here;
+                       *insert_here = ent;
+               } else {
+                       *end = ent;
+                       end = &ent->next;
+               }
+       }
+       if (hold && mdstat_fd == -1) {
+               mdstat_fd = dup(fileno(f));
+               fcntl(mdstat_fd, F_SETFD, FD_CLOEXEC);
        }
        fclose(f);
-       return all;
+
+       /* If we might want to start array,
+        * reverse the order, so that components comes before composites
+        */
+       if (start) {
+               rv = NULL;
+               while (all) {
+                       struct mdstat_ent *e = all;
+                       all = all->next;
+                       e->next = rv;
+                       rv = e;
+               }
+       } else rv = all;
+       return rv;
+}
+
+void mdstat_wait(int seconds)
+{
+       fd_set fds;
+       struct timeval tm;
+       int maxfd = 0;
+       FD_ZERO(&fds);
+       if (mdstat_fd >= 0) {
+               FD_SET(mdstat_fd, &fds);
+               maxfd = mdstat_fd;
+       }
+       tm.tv_sec = seconds;
+       tm.tv_usec = 0;
+       select(maxfd + 1, NULL, NULL, &fds, &tm);
+}
+
+void mdstat_wait_fd(int fd, const sigset_t *sigmask)
+{
+       fd_set fds, rfds;
+       int maxfd = fd;
+
+       FD_ZERO(&fds);
+       FD_ZERO(&rfds);
+       if (mdstat_fd >= 0)
+               FD_SET(mdstat_fd, &fds);
+       if (fd >= 0) {
+               struct stat stb;
+               fstat(fd, &stb);
+               if ((stb.st_mode & S_IFMT) == S_IFREG)
+                       /* Must be a /proc or /sys fd, so expect
+                        * POLLPRI
+                        * i.e. an 'exceptional' event.
+                        */
+                       FD_SET(fd, &fds);
+               else
+                       FD_SET(fd, &rfds);
+       }
+       if (mdstat_fd > maxfd)
+               maxfd = mdstat_fd;
+
+       pselect(maxfd + 1, &rfds, NULL, &fds,
+               NULL, sigmask);
+}
+
+int mddev_busy(int devnum)
+{
+       struct mdstat_ent *mdstat = mdstat_read(0, 0);
+       struct mdstat_ent *me;
+
+       for (me = mdstat ; me ; me = me->next)
+               if (me->devnum == devnum)
+                       break;
+       free_mdstat(mdstat);
+       return me != NULL;
 }