| /* |
| * mdstat - parse /proc/mdstat file. Part of: |
| * mdadm - manage Linux "md" devices aka RAID arrays. |
| * |
| * Copyright (C) 2002-2006 Neil Brown <neilb@suse.de> |
| * |
| * |
| * 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: <neilb@cse.unsw.edu.au> |
| * Paper: Neil Brown |
| * School of Computer Science and Engineering |
| * The University of New South Wales |
| * Sydney, 2052 |
| * Australia |
| */ |
| |
| /* |
| * 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 ... | <none>} |
| * |
| * 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 .. | <none>} |
| * |
| * 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 <sys/select.h> |
| |
| 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); |
| 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->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 && 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; |
| if (strncmp(w, "md_d", 4)==0) |
| dn2 = -1-strtoul(w+4, &ep, 10); |
| else |
| dn2 = strtoul(w+2, &ep, 10); |
| ih = &all; |
| while (ih != insert_here && *ih && |
| (*ih)->devnum != dn2) |
| ih = & (*ih)->next; |
| insert_here = ih; |
| } 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; |
| FD_ZERO(&fds); |
| if (mdstat_fd >= 0) |
| FD_SET(mdstat_fd, &fds); |
| tm.tv_sec = seconds; |
| tm.tv_usec = 0; |
| select(mdstat_fd >2 ? mdstat_fd+1:3, NULL, NULL, &fds, &tm); |
| } |
| |
| 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; |
| } |