/* * mdstat - parse /proc/mdstat file. Part of: * mdadm - manage Linux "md" devices aka RAID arrays. * * Copyright (C) 2002-2009 Neil Brown * * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Neil Brown * Email: */ /* * The /proc/mdstat file comes in at least 3 flavours: * In an unpatched 2.2 kernel (md 0.36.6): * Personalities : [n raidx] ... * read_ahead {not set|%d sectors} * md0 : {in}active{ raidX /dev/hda... %d blocks{ maxfault=%d}} * md1 : ..... * * Normally only 4 md lines, but all are listed. * * In a patched 2.2 kernel (md 0.90.0) * Personalities : [raidx] ... * read_ahead {not set|%d sectors} * mdN : {in}active {(readonly)} raidX dev[%d]{(F)} ... %d blocks STATUS RESYNC * ... Only initialised arrays listed * unused devices: {dev dev ... | } * * STATUS is personality dependant: * linear: %dk rounding * raid0: %dk chunks * raid1: [%d/%d] [U_U] ( raid/working. operational or not) * raid5: level 4/5, %dk chunk, algorithm %d [%d/%d] [U_U] * * RESYNC is empty or: * {resync|recovery}=%u%% finish=%u.%umin * or * resync=DELAYED * * In a 2.4 kernel (md 0.90.0/2.4) * Personalities : [raidX] ... * read_ahead {not set|%d sectors} * mdN : {in}active {(read-only)} raidX dev[%d]{(F)} ... * %d blocks STATUS * RESYNC * unused devices: {dev dev .. | } * * STATUS matches 0.90.0/2.2 * RESYNC includes [===>....], * adds a space after {resync|recovery} and before and after '=' * adds a decimal to the recovery percent. * adds (%d/%d) resync amount and max_blocks, before finish. * adds speed=%dK/sec after finish * * * * Out of this we want to extract: * list of devices, active or not * pattern of failed drives (so need number of drives) * percent resync complete * * As continuation is indicated by leading space, we use * conf_line from config.c to read logical lines * */ #include "mdadm.h" #include "dlink.h" #include #include void free_mdstat(struct mdstat_ent *ms) { while (ms) { struct mdstat_ent *t; 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); } } static int mdstat_fd = -1; struct mdstat_ent *mdstat_read(int hold, int start) { FILE *f; struct mdstat_ent *all, *rv, **end, **insert_here; char *line; 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; if (strcmp(line, "read_ahead")==0) continue; if (strcmp(line, "unused")==0) continue; insert_here = NULL; /* Better be an md 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; } ent = malloc(sizeof(*ent)); if (!ent) { fprintf(stderr, Name ": malloc failed reading /proc/mdstat.\n"); free_line(line); 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 = devnum; for (w=dl_next(line); w!= line ; w=dl_next(w)) { int l = strlen(w); char *eq; if (strcmp(w, "active")==0) ent->active = 1; else if (strcmp(w, "inactive")==0) ent->active = 0; else if (ent->active >=0 && ent->level == NULL && w[0] != '(' /*readonly*/) { ent->level = strdup(w); 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); if (ent->pattern[l-2]==']') ent->pattern[l-2] = '\0'; } else if (ent->percent == -1 && strncmp(w, "re", 2)== 0 && 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 && 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); } } 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); /* 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 = 0; 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 (fd > maxfd) maxfd = fd; } 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; }