mdadm-1.6.0
[thirdparty/mdadm.git] / mdstat.c
1 /*
2  * mdstat - parse /proc/mdstat file. Part of:
3  * mdadm - manage Linux "md" devices aka RAID arrays.
4  *
5  * Copyright (C) 2002 Neil Brown <neilb@cse.unsw.edu.au>
6  *
7  *
8  *    This program is free software; you can redistribute it and/or modify
9  *    it under the terms of the GNU General Public License as published by
10  *    the Free Software Foundation; either version 2 of the License, or
11  *    (at your option) any later version.
12  *
13  *    This program is distributed in the hope that it will be useful,
14  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *    GNU General Public License for more details.
17  *
18  *    You should have received a copy of the GNU General Public License
19  *    along with this program; if not, write to the Free Software
20  *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  *    Author: Neil Brown
23  *    Email: <neilb@cse.unsw.edu.au>
24  *    Paper: Neil Brown
25  *           School of Computer Science and Engineering
26  *           The University of New South Wales
27  *           Sydney, 2052
28  *           Australia
29  */
30
31 /*
32  * The /proc/mdstat file comes in at least 3 flavours:
33  * In an unpatched 2.2 kernel (md 0.36.6):
34  *  Personalities : [n raidx] ...
35  *  read_ahead {not set|%d sectors}
36  *  md0 : {in}active{ raidX /dev/hda...  %d blocks{ maxfault=%d}}
37  *  md1 : .....
38  *
39  * Normally only 4 md lines, but all are listed.
40  *
41  * In a patched 2.2 kernel (md 0.90.0)
42  *  Personalities : [raidx] ...
43  *  read_ahead {not set|%d sectors}
44  *  mdN : {in}active {(readonly)} raidX dev[%d]{(F)} ... %d blocks STATUS RESYNC
45  *  ... Only initialised arrays listed
46  *  unused: dev dev dev | <none>
47  *
48  * STATUS is personality dependant:
49  *    linear:  %dk rounding
50  *    raid0:   %dk chunks
51  *    raid1:   [%d/%d] [U_U]   ( raid/working.  operational or not)
52  *    raid5:   level 4/5, %dk chunk, algorithm %d [%d/%d] [U_U]
53  *
54  * RESYNC is  empty or:
55  *    {resync|recovery}=%u%% finish=%u.%umin
56  *  or
57  *    resync=DELAYED
58  *
59  * In a 2.4 kernel (md 0.90.0/2.4)
60  *  Personalities : [raidX] ...
61  *  read_ahead {not set|%d sectors}
62  *  mdN : {in}active {(read-only)} raidX dev[%d]{(F)} ...
63  *       %d blocks STATUS
64  *       RESYNC
65  *  unused: dev dev .. | <none>
66  *
67  *  STATUS matches 0.90.0/2.2
68  *  RESYNC includes [===>....],
69  *          adds a space after {resync|recovery} and before and after '='
70  *          adds a decimal to the recovery percent.
71  *          adds (%d/%d) resync amount  and max_blocks, before finish.
72  *          adds speed=%dK/sec after finish
73  *
74  *
75  *
76  * Out of this we want to extract:
77  *   list of devices, active or not
78  *   pattern of failed drives (so need number of drives)
79  *   percent resync complete
80  *
81  * As continuation is indicated by leading space, we use
82  *  conf_line from config.c to read logical lines
83  *
84  */
85
86 #include        "mdadm.h"
87 #include        "dlink.h"
88 #include        <sys/select.h>
89
90 void free_mdstat(struct mdstat_ent *ms)
91 {
92         while (ms) {
93                 struct mdstat_ent *t;
94                 if (ms->dev) free(ms->dev);
95                 if (ms->level) free(ms->level);
96                 if (ms->pattern) free(ms->pattern);
97                 t = ms;
98                 ms = ms->next;
99                 free(t);
100         }
101 }
102
103 static int mdstat_fd = -1;
104 struct mdstat_ent *mdstat_read(int hold)
105 {
106         FILE *f;
107         struct mdstat_ent *all, **end;
108         char *line;
109
110         if (hold && mdstat_fd != -1) {
111                 lseek(mdstat_fd, 0L, 0);
112                 f = fdopen(dup(mdstat_fd), "r");
113         } else
114                 f = fopen("/proc/mdstat", "r");
115         if (f == NULL)
116                 return NULL;
117
118         all = NULL;
119         end = &all;
120         for (; (line = conf_line(f)) ; free_line(line)) {
121                 struct mdstat_ent *ent;
122                 char *w;
123                 int devnum;
124                 char *ep;
125
126                 if (strcmp(line, "Personalities")==0)
127                         continue;
128                 if (strcmp(line, "read_ahead")==0)
129                         continue;
130                 if (strcmp(line, "unused")==0)
131                         continue;
132                 /* Better be an md line.. */
133                 if (strncmp(line, "md", 2)!= 0)
134                         continue;
135                 if (strncmp(line, "md_d", 4) == 0)
136                         devnum = -1-strtoul(line+4, &ep, 10);
137                 else if (strncmp(line, "md", 2) == 0)
138                         devnum = strtoul(line+2, &ep, 10);
139                 else
140                         continue;
141                 if (ep == NULL || *ep ) {
142                         /* fprintf(stderr, Name ": bad /proc/mdstat line starts: %s\n", line); */
143                         continue;
144                 }
145
146                 ent = malloc(sizeof(*ent));
147                 if (!ent) {
148                         fprintf(stderr, Name ": malloc failed reading /proc/mdstat.\n");
149                         free_line(line);
150                         break;
151                 }
152                 ent->dev = ent->level = ent->pattern= NULL;
153                 ent->next = NULL;
154                 ent->percent = -1;
155                 ent->active = -1;
156
157                 ent->dev = strdup(line);
158                 ent->devnum = devnum;
159                 
160                 for (w=dl_next(line); w!= line ; w=dl_next(w)) {
161                         int l = strlen(w);
162                         char *eq;
163                         if (strcmp(w, "active")==0)
164                                 ent->active = 1;
165                         else if (strcmp(w, "inactive")==0)
166                                 ent->active = 0;
167                         else if (ent->active >=0 &&
168                                  ent->level == NULL &&
169                                  w[0] != '(' /*readonly*/)
170                                 ent->level = strdup(w);
171                         else if (!ent->pattern &&
172                                  w[0] == '[' &&
173                                  (w[1] == 'U' || w[1] == '_')) {
174                                 ent->pattern = strdup(w+1);
175                                 if (ent->pattern[l-2]==']')
176                                         ent->pattern[l-2] = '\0';
177                         } else if (ent->percent == -1 &&
178                                    strncmp(w, "re", 2)== 0 &&
179                                    w[l-1] == '%' &&
180                                    (eq=strchr(w, '=')) != NULL ) {
181                                 ent->percent = atoi(eq+1);
182                         } else if (ent->percent == -1 &&
183                                    w[0] >= '0' && 
184                                    w[0] <= '9' &&
185                                    w[l-1] == '%') {
186                                 ent->percent = atoi(w);
187                         }
188                 }
189                 *end = ent;
190                 end = &ent->next;
191         }
192         if (hold && mdstat_fd == -1)
193                 mdstat_fd = dup(fileno(f));
194         fclose(f);
195         return all;
196 }
197
198 void mdstat_wait(int seconds)
199 {
200         fd_set fds;
201         struct timeval tm;
202         FD_ZERO(&fds);
203         if (mdstat_fd >= 0)
204                 FD_SET(mdstat_fd, &fds);
205         tm.tv_sec = seconds;
206         tm.tv_usec = 0;
207         select(mdstat_fd >2 ? mdstat_fd+1:3, NULL, NULL, &fds, &tm);
208 }