* 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
* 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
* 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 [===>....],
#include "mdadm.h"
#include "dlink.h"
+#include <sys/select.h>
+#include <ctype.h>
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;
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;
}
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;
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);
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)
+ 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;
}