Assemble: add support for RAID0 layouts.
[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-2009 Neil Brown <neilb@suse.de>
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@suse.de>
24  */
25
26 /*
27  * The /proc/mdstat file comes in at least 3 flavours:
28  * In an unpatched 2.2 kernel (md 0.36.6):
29  *  Personalities : [n raidx] ...
30  *  read_ahead {not set|%d sectors}
31  *  md0 : {in}active{ raidX /dev/hda...  %d blocks{ maxfault=%d}}
32  *  md1 : .....
33  *
34  * Normally only 4 md lines, but all are listed.
35  *
36  * In a patched 2.2 kernel (md 0.90.0)
37  *  Personalities : [raidx] ...
38  *  read_ahead {not set|%d sectors}
39  *  mdN : {in}active {(readonly)} raidX dev[%d]{(F)} ... %d blocks STATUS RESYNC
40  *  ... Only initialised arrays listed
41  *  unused devices: {dev dev ... | <none>}
42  *
43  * STATUS is personality dependant:
44  *    linear:  %dk rounding
45  *    raid0:   %dk chunks
46  *    raid1:   [%d/%d] [U_U]   ( raid/working.  operational or not)
47  *    raid5:   level 4/5, %dk chunk, algorithm %d [%d/%d] [U_U]
48  *
49  * RESYNC is  empty or:
50  *    {resync|recovery}=%u%% finish=%u.%umin
51  *  or
52  *    resync=DELAYED
53  *
54  * In a 2.4 kernel (md 0.90.0/2.4)
55  *  Personalities : [raidX] ...
56  *  read_ahead {not set|%d sectors}
57  *  mdN : {in}active {(read-only)} raidX dev[%d]{(F)} ...
58  *       %d blocks STATUS
59  *       RESYNC
60  *  unused devices: {dev dev .. | <none>}
61  *
62  *  STATUS matches 0.90.0/2.2
63  *  RESYNC includes [===>....],
64  *          adds a space after {resync|recovery} and before and after '='
65  *          adds a decimal to the recovery percent.
66  *          adds (%d/%d) resync amount  and max_blocks, before finish.
67  *          adds speed=%dK/sec after finish
68  *
69  *
70  *
71  * Out of this we want to extract:
72  *   list of devices, active or not
73  *   pattern of failed drives (so need number of drives)
74  *   percent resync complete
75  *
76  * As continuation is indicated by leading space, we use
77  *  conf_line from config.c to read logical lines
78  *
79  */
80
81 #include        "mdadm.h"
82 #include        "dlink.h"
83 #include        <sys/select.h>
84 #include        <ctype.h>
85
86 static void free_member_devnames(struct dev_member *m)
87 {
88         while(m) {
89                 struct dev_member *t = m;
90
91                 m = m->next;
92                 free(t->name);
93                 free(t);
94         }
95 }
96
97 static int add_member_devname(struct dev_member **m, char *name)
98 {
99         struct dev_member *new;
100         char *t;
101
102         if ((t = strchr(name, '[')) == NULL)
103                 /* not a device */
104                 return 0;
105
106         new = xmalloc(sizeof(*new));
107         new->name = strndup(name, t - name);
108         new->next = *m;
109         *m = new;
110         return 1;
111 }
112
113 void free_mdstat(struct mdstat_ent *ms)
114 {
115         while (ms) {
116                 struct mdstat_ent *t;
117                 free(ms->level);
118                 free(ms->pattern);
119                 free(ms->metadata_version);
120                 free_member_devnames(ms->members);
121                 t = ms;
122                 ms = ms->next;
123                 free(t);
124         }
125 }
126
127 static int mdstat_fd = -1;
128 struct mdstat_ent *mdstat_read(int hold, int start)
129 {
130         FILE *f;
131         struct mdstat_ent *all, *rv, **end, **insert_here;
132         char *line;
133         int fd;
134
135         if (hold && mdstat_fd != -1) {
136                 off_t offset = lseek(mdstat_fd, 0L, 0);
137                 if (offset == (off_t)-1) {
138                         mdstat_close();
139                         return NULL;
140                 }
141                 fd = dup(mdstat_fd);
142                 if (fd >= 0)
143                         f = fdopen(fd, "r");
144                 else
145                         return NULL;
146         } else
147                 f = fopen("/proc/mdstat", "r");
148         if (f == NULL)
149                 return NULL;
150         else
151                 fcntl(fileno(f), F_SETFD, FD_CLOEXEC);
152
153         all = NULL;
154         end = &all;
155         for (; (line = conf_line(f)) ; free_line(line)) {
156                 struct mdstat_ent *ent;
157                 char *w;
158                 char devnm[32];
159                 int in_devs = 0;
160
161                 if (strcmp(line, "Personalities") == 0)
162                         continue;
163                 if (strcmp(line, "read_ahead") == 0)
164                         continue;
165                 if (strcmp(line, "unused") == 0)
166                         continue;
167                 insert_here = NULL;
168                 /* Better be an md line.. */
169                 if (strncmp(line, "md", 2)!= 0 || strlen(line) >= 32 ||
170                     (line[2] != '_' && !isdigit(line[2])))
171                         continue;
172                 strcpy(devnm, line);
173
174                 ent = xmalloc(sizeof(*ent));
175                 ent->level = ent->pattern= NULL;
176                 ent->next = NULL;
177                 ent->percent = RESYNC_NONE;
178                 ent->active = -1;
179                 ent->resync = 0;
180                 ent->metadata_version = NULL;
181                 ent->raid_disks = 0;
182                 ent->devcnt = 0;
183                 ent->members = NULL;
184
185                 strcpy(ent->devnm, devnm);
186
187                 for (w=dl_next(line); w!= line ; w=dl_next(w)) {
188                         int l = strlen(w);
189                         char *eq;
190                         if (strcmp(w, "active") == 0)
191                                 ent->active = 1;
192                         else if (strcmp(w, "inactive") == 0) {
193                                 ent->active = 0;
194                                 in_devs = 1;
195                         } else if (ent->active > 0 &&
196                                  ent->level == NULL &&
197                                  w[0] != '(' /*readonly*/) {
198                                 ent->level = xstrdup(w);
199                                 in_devs = 1;
200                         } else if (in_devs && strcmp(w, "blocks") == 0)
201                                 in_devs = 0;
202                         else if (in_devs) {
203                                 char *ep = strchr(w, '[');
204                                 ent->devcnt +=
205                                         add_member_devname(&ent->members, w);
206                                 if (ep && strncmp(w, "md", 2) == 0) {
207                                         /* This has an md device as a component.
208                                          * If that device is already in the
209                                          * list, make sure we insert before
210                                          * there.
211                                          */
212                                         struct mdstat_ent **ih;
213                                         ih = &all;
214                                         while (ih != insert_here && *ih &&
215                                                ((int)strlen((*ih)->devnm) !=
216                                                 ep-w ||
217                                                 strncmp((*ih)->devnm, w,
218                                                         ep-w) != 0))
219                                                 ih = & (*ih)->next;
220                                         insert_here = ih;
221                                 }
222                         } else if (strcmp(w, "super") == 0 &&
223                                    dl_next(w) != line) {
224                                 w = dl_next(w);
225                                 ent->metadata_version = xstrdup(w);
226                         } else if (w[0] == '[' && isdigit(w[1])) {
227                                 ent->raid_disks = atoi(w+1);
228                         } else if (!ent->pattern &&
229                                    w[0] == '[' &&
230                                    (w[1] == 'U' || w[1] == '_')) {
231                                 ent->pattern = xstrdup(w+1);
232                                 if (ent->pattern[l-2] == ']')
233                                         ent->pattern[l-2] = '\0';
234                         } else if (ent->percent == RESYNC_NONE &&
235                                    strncmp(w, "re", 2) == 0 &&
236                                    w[l-1] == '%' &&
237                                    (eq = strchr(w, '=')) != NULL ) {
238                                 ent->percent = atoi(eq+1);
239                                 if (strncmp(w,"resync", 6) == 0)
240                                         ent->resync = 1;
241                                 else if (strncmp(w, "reshape", 7) == 0)
242                                         ent->resync = 2;
243                                 else
244                                         ent->resync = 0;
245                         } else if (ent->percent == RESYNC_NONE &&
246                                    (w[0] == 'r' || w[0] == 'c')) {
247                                 if (strncmp(w, "resync", 6) == 0)
248                                         ent->resync = 1;
249                                 if (strncmp(w, "reshape", 7) == 0)
250                                         ent->resync = 2;
251                                 if (strncmp(w, "recovery", 8) == 0)
252                                         ent->resync = 0;
253                                 if (strncmp(w, "check", 5) == 0)
254                                         ent->resync = 3;
255
256                                 if (l > 8 && strcmp(w+l-8, "=DELAYED") == 0)
257                                         ent->percent = RESYNC_DELAYED;
258                                 if (l > 8 && strcmp(w+l-8, "=PENDING") == 0)
259                                         ent->percent = RESYNC_PENDING;
260                         } else if (ent->percent == RESYNC_NONE &&
261                                    w[0] >= '0' &&
262                                    w[0] <= '9' &&
263                                    w[l-1] == '%') {
264                                 ent->percent = atoi(w);
265                         }
266                 }
267                 if (insert_here && (*insert_here)) {
268                         ent->next = *insert_here;
269                         *insert_here = ent;
270                 } else {
271                         *end = ent;
272                         end = &ent->next;
273                 }
274         }
275         if (hold && mdstat_fd == -1) {
276                 mdstat_fd = dup(fileno(f));
277                 fcntl(mdstat_fd, F_SETFD, FD_CLOEXEC);
278         }
279         fclose(f);
280
281         /* If we might want to start array,
282          * reverse the order, so that components comes before composites
283          */
284         if (start) {
285                 rv = NULL;
286                 while (all) {
287                         struct mdstat_ent *e = all;
288                         all = all->next;
289                         e->next = rv;
290                         rv = e;
291                 }
292         } else
293                 rv = all;
294         return rv;
295 }
296
297 void mdstat_close(void)
298 {
299         if (mdstat_fd >= 0)
300                 close(mdstat_fd);
301         mdstat_fd = -1;
302 }
303
304 void mdstat_wait(int seconds)
305 {
306         fd_set fds;
307         struct timeval tm;
308         int maxfd = 0;
309         FD_ZERO(&fds);
310         if (mdstat_fd >= 0) {
311                 FD_SET(mdstat_fd, &fds);
312                 maxfd = mdstat_fd;
313         }
314         tm.tv_sec = seconds;
315         tm.tv_usec = 0;
316         select(maxfd + 1, NULL, NULL, &fds, &tm);
317 }
318
319 void mdstat_wait_fd(int fd, const sigset_t *sigmask)
320 {
321         fd_set fds, rfds;
322         int maxfd = 0;
323
324         FD_ZERO(&fds);
325         FD_ZERO(&rfds);
326         if (mdstat_fd >= 0)
327                 FD_SET(mdstat_fd, &fds);
328
329         if (fd >= 0) {
330                 struct stat stb;
331                 fstat(fd, &stb);
332                 if ((stb.st_mode & S_IFMT) == S_IFREG)
333                         /* Must be a /proc or /sys fd, so expect
334                          * POLLPRI
335                          * i.e. an 'exceptional' event.
336                          */
337                         FD_SET(fd, &fds);
338                 else
339                         FD_SET(fd, &rfds);
340
341                 if (fd > maxfd)
342                         maxfd = fd;
343
344         }
345         if (mdstat_fd > maxfd)
346                 maxfd = mdstat_fd;
347
348         pselect(maxfd + 1, &rfds, NULL, &fds,
349                 NULL, sigmask);
350 }
351
352 int mddev_busy(char *devnm)
353 {
354         struct mdstat_ent *mdstat = mdstat_read(0, 0);
355         struct mdstat_ent *me;
356
357         for (me = mdstat ; me ; me = me->next)
358                 if (strcmp(me->devnm, devnm) == 0)
359                         break;
360         free_mdstat(mdstat);
361         return me != NULL;
362 }
363
364 struct mdstat_ent *mdstat_by_component(char *name)
365 {
366         struct mdstat_ent *mdstat = mdstat_read(0, 0);
367
368         while (mdstat) {
369                 struct dev_member *m;
370                 struct mdstat_ent *ent;
371                 if (mdstat->metadata_version &&
372                     strncmp(mdstat->metadata_version, "external:", 9) == 0 &&
373                     is_subarray(mdstat->metadata_version+9))
374                         /* don't return subarrays, only containers */
375                         ;
376                 else for (m = mdstat->members; m; m = m->next) {
377                                 if (strcmp(m->name, name) == 0) {
378                                         free_mdstat(mdstat->next);
379                                         mdstat->next = NULL;
380                                         return mdstat;
381                                 }
382                         }
383                 ent = mdstat;
384                 mdstat = mdstat->next;
385                 ent->next = NULL;
386                 free_mdstat(ent);
387         }
388         return NULL;
389 }
390
391 struct mdstat_ent *mdstat_by_subdev(char *subdev, char *container)
392 {
393         struct mdstat_ent *mdstat = mdstat_read(0, 0);
394         struct mdstat_ent *ent = NULL;
395
396         while (mdstat) {
397                 /* metadata version must match:
398                  *   external:[/-]%s/%s
399                  * where first %s is 'container' and second %s is 'subdev'
400                  */
401                 if (ent)
402                         free_mdstat(ent);
403                 ent = mdstat;
404                 mdstat = mdstat->next;
405                 ent->next = NULL;
406
407                 if (ent->metadata_version == NULL ||
408                     strncmp(ent->metadata_version, "external:", 9) != 0)
409                         continue;
410
411                 if (!metadata_container_matches(ent->metadata_version+9,
412                                                container) ||
413                     !metadata_subdev_matches(ent->metadata_version+9,
414                                              subdev))
415                         continue;
416
417                 free_mdstat(mdstat);
418                 return ent;
419         }
420         return NULL;
421 }