181 lines
4.8 KiB
C
181 lines
4.8 KiB
C
/*
|
|
* 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>
|
|
*
|
|
*
|
|
* 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: dev 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: 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"
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
struct mdstat_ent *mdstat_read()
|
|
{
|
|
FILE *f;
|
|
struct mdstat_ent *all, **end;
|
|
char *line;
|
|
|
|
f = fopen("/proc/mdstat", "r");
|
|
if (f == NULL)
|
|
return NULL;
|
|
|
|
all = NULL;
|
|
end = &all;
|
|
for (; (line = conf_line(f)) ; free_line(line)) {
|
|
struct mdstat_ent *ent;
|
|
char *w;
|
|
|
|
if (strcmp(line, "Personalities")==0)
|
|
continue;
|
|
if (strcmp(line, "read_ahead")==0)
|
|
continue;
|
|
if (strcmp(line, "unused")==0)
|
|
continue;
|
|
/* 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);
|
|
continue;
|
|
}
|
|
|
|
ent = malloc(sizeof(*ent));
|
|
if (!ent) {
|
|
fprintf(stderr, Name ": malloc failed reading /proc/mdstat.\n");
|
|
free_line(line);
|
|
fclose(f);
|
|
return all;
|
|
}
|
|
ent->dev = ent->level = ent->pattern= NULL;
|
|
ent->next = NULL;
|
|
ent->percent = -1;
|
|
ent->active = -1;
|
|
|
|
ent->dev = strdup(line);
|
|
ent->devnum = atoi(line+2);
|
|
|
|
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);
|
|
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);
|
|
} else if (ent->percent == -1 &&
|
|
w[0] >= '0' &&
|
|
w[0] <= '9' &&
|
|
w[l-1] == '%') {
|
|
ent->percent = atoi(w);
|
|
}
|
|
}
|
|
*end = ent;
|
|
end = &ent->next;
|
|
}
|
|
fclose(f);
|
|
return all;
|
|
}
|