]> git.ipfire.org Git - thirdparty/mdadm.git/blame - mdstat.c
Create.c: fix uclibc build
[thirdparty/mdadm.git] / mdstat.c
CommitLineData
e0d19036
NB
1/*
2 * mdstat - parse /proc/mdstat file. Part of:
3 * mdadm - manage Linux "md" devices aka RAID arrays.
4 *
e736b623 5 * Copyright (C) 2002-2009 Neil Brown <neilb@suse.de>
e0d19036
NB
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
e736b623 23 * Email: <neilb@suse.de>
e0d19036
NB
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
8f21823f 41 * unused devices: {dev dev ... | <none>}
e0d19036
NB
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
8f21823f 60 * unused devices: {dev dev .. | <none>}
e0d19036
NB
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"
dd0781e5 83#include <sys/select.h>
549e9569 84#include <ctype.h>
e0d19036 85
3b57c466
N
86static 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
0ad6835c 97static int add_member_devname(struct dev_member **m, char *name)
3b57c466
N
98{
99 struct dev_member *new;
100 char *t;
101
102 if ((t = strchr(name, '[')) == NULL)
103 /* not a device */
0ad6835c 104 return 0;
3b57c466 105
503975b9 106 new = xmalloc(sizeof(*new));
3b57c466
N
107 new->name = strndup(name, t - name);
108 new->next = *m;
109 *m = new;
0ad6835c 110 return 1;
3b57c466
N
111}
112
e0d19036
NB
113void free_mdstat(struct mdstat_ent *ms)
114{
115 while (ms) {
116 struct mdstat_ent *t;
3b57c466
N
117 free(ms->level);
118 free(ms->pattern);
119 free(ms->metadata_version);
120 free_member_devnames(ms->members);
e0d19036
NB
121 t = ms;
122 ms = ms->next;
123 free(t);
124 }
125}
126
dd0781e5 127static int mdstat_fd = -1;
22a88995 128struct mdstat_ent *mdstat_read(int hold, int start)
e0d19036
NB
129{
130 FILE *f;
22a88995 131 struct mdstat_ent *all, *rv, **end, **insert_here;
e0d19036 132 char *line;
d94a4f62 133 int fd;
e0d19036 134
dd0781e5 135 if (hold && mdstat_fd != -1) {
52209d6e
TM
136 off_t offset = lseek(mdstat_fd, 0L, 0);
137 if (offset == (off_t)-1) {
52209d6e
TM
138 return NULL;
139 }
d94a4f62
JS
140 fd = dup(mdstat_fd);
141 if (fd >= 0)
142 f = fdopen(fd, "r");
143 else
144 return NULL;
dd0781e5
NB
145 } else
146 f = fopen("/proc/mdstat", "r");
e0d19036
NB
147 if (f == NULL)
148 return NULL;
e4dc5106
DL
149 else
150 fcntl(fileno(f), F_SETFD, FD_CLOEXEC);
e0d19036
NB
151
152 all = NULL;
153 end = &all;
154 for (; (line = conf_line(f)) ; free_line(line)) {
155 struct mdstat_ent *ent;
156 char *w;
4dd2df09 157 char devnm[32];
22a88995 158 int in_devs = 0;
e0d19036 159
a9db8995 160 if (strcmp(line, "Personalities") == 0)
e0d19036 161 continue;
a9db8995 162 if (strcmp(line, "read_ahead") == 0)
e0d19036 163 continue;
a9db8995 164 if (strcmp(line, "unused") == 0)
e0d19036 165 continue;
22a88995 166 insert_here = NULL;
e0d19036 167 /* Better be an md line.. */
d7be7d87
JS
168 if (strncmp(line, "md", 2)!= 0 || strlen(line) >= 32 ||
169 (line[2] != '_' && !isdigit(line[2])))
98c6faba 170 continue;
4dd2df09 171 strcpy(devnm, line);
e0d19036 172
503975b9 173 ent = xmalloc(sizeof(*ent));
9581efb1 174 ent->level = ent->pattern= NULL;
e0d19036 175 ent->next = NULL;
9dad51d4 176 ent->percent = RESYNC_NONE;
e0d19036 177 ent->active = -1;
e5329c37 178 ent->resync = 0;
549e9569
NB
179 ent->metadata_version = NULL;
180 ent->raid_disks = 0;
549e9569 181 ent->devcnt = 0;
3b57c466 182 ent->members = NULL;
e0d19036 183
4dd2df09 184 strcpy(ent->devnm, devnm);
aba69144 185
e0d19036
NB
186 for (w=dl_next(line); w!= line ; w=dl_next(w)) {
187 int l = strlen(w);
188 char *eq;
a9db8995 189 if (strcmp(w, "active") == 0)
e0d19036 190 ent->active = 1;
a9db8995 191 else if (strcmp(w, "inactive") == 0) {
e0d19036 192 ent->active = 0;
b6d7a7fb 193 in_devs = 1;
b090e910
JR
194 } else if (strcmp(w, "bitmap:") == 0) {
195 /* We need to stop parsing here;
196 * otherwise, ent->raid_disks will be
197 * overwritten by the wrong value.
198 */
199 break;
b6d7a7fb 200 } else if (ent->active > 0 &&
e0d19036 201 ent->level == NULL &&
22a88995 202 w[0] != '(' /*readonly*/) {
503975b9 203 ent->level = xstrdup(w);
22a88995 204 in_devs = 1;
a9db8995 205 } else if (in_devs && strcmp(w, "blocks") == 0)
22a88995 206 in_devs = 0;
549e9569 207 else if (in_devs) {
4dd2df09 208 char *ep = strchr(w, '[');
0ad6835c
ML
209 ent->devcnt +=
210 add_member_devname(&ent->members, w);
a9db8995 211 if (ep && strncmp(w, "md", 2) == 0) {
549e9569
NB
212 /* This has an md device as a component.
213 * If that device is already in the
214 * list, make sure we insert before
215 * there.
216 */
217 struct mdstat_ent **ih;
549e9569
NB
218 ih = &all;
219 while (ih != insert_here && *ih &&
d7be7d87
JS
220 ((int)strlen((*ih)->devnm) !=
221 ep-w ||
222 strncmp((*ih)->devnm, w,
223 ep-w) != 0))
549e9569
NB
224 ih = & (*ih)->next;
225 insert_here = ih;
226 }
227 } else if (strcmp(w, "super") == 0 &&
228 dl_next(w) != line) {
229 w = dl_next(w);
503975b9 230 ent->metadata_version = xstrdup(w);
549e9569
NB
231 } else if (w[0] == '[' && isdigit(w[1])) {
232 ent->raid_disks = atoi(w+1);
22a88995 233 } else if (!ent->pattern &&
a9db8995
ZL
234 w[0] == '[' &&
235 (w[1] == 'U' || w[1] == '_')) {
503975b9 236 ent->pattern = xstrdup(w+1);
a9db8995 237 if (ent->pattern[l-2] == ']')
e0d19036 238 ent->pattern[l-2] = '\0';
9dad51d4 239 } else if (ent->percent == RESYNC_NONE &&
a9db8995 240 strncmp(w, "re", 2) == 0 &&
e0d19036 241 w[l-1] == '%' &&
a9db8995 242 (eq = strchr(w, '=')) != NULL ) {
e0d19036 243 ent->percent = atoi(eq+1);
a9db8995 244 if (strncmp(w,"resync", 6) == 0)
e5329c37 245 ent->resync = 1;
a9db8995 246 else if (strncmp(w, "reshape", 7) == 0)
f94c116f
N
247 ent->resync = 2;
248 else
249 ent->resync = 0;
9dad51d4 250 } else if (ent->percent == RESYNC_NONE &&
f94c116f 251 (w[0] == 'r' || w[0] == 'c')) {
35c34037 252 if (strncmp(w, "resync", 6) == 0)
f94c116f 253 ent->resync = 1;
a9db8995 254 if (strncmp(w, "reshape", 7) == 0)
f94c116f 255 ent->resync = 2;
a9db8995 256 if (strncmp(w, "recovery", 8) == 0)
aa8d7dc7 257 ent->resync = 0;
a9db8995 258 if (strncmp(w, "check", 5) == 0)
f94c116f
N
259 ent->resync = 3;
260
2d3603ba 261 if (l > 8 && strcmp(w+l-8, "=DELAYED") == 0)
9dad51d4 262 ent->percent = RESYNC_DELAYED;
2d3603ba 263 if (l > 8 && strcmp(w+l-8, "=PENDING") == 0)
9dad51d4 264 ent->percent = RESYNC_PENDING;
1c294b5d
LZ
265 if (l > 7 && strcmp(w+l-7, "=REMOTE") == 0)
266 ent->percent = RESYNC_REMOTE;
9dad51d4 267 } else if (ent->percent == RESYNC_NONE &&
aba69144 268 w[0] >= '0' &&
e0d19036
NB
269 w[0] <= '9' &&
270 w[l-1] == '%') {
271 ent->percent = atoi(w);
272 }
273 }
22a88995
NB
274 if (insert_here && (*insert_here)) {
275 ent->next = *insert_here;
276 *insert_here = ent;
277 } else {
278 *end = ent;
279 end = &ent->next;
280 }
e0d19036 281 }
e4dc5106 282 if (hold && mdstat_fd == -1) {
dd0781e5 283 mdstat_fd = dup(fileno(f));
e4dc5106
DL
284 fcntl(mdstat_fd, F_SETFD, FD_CLOEXEC);
285 }
e0d19036 286 fclose(f);
22a88995
NB
287
288 /* If we might want to start array,
289 * reverse the order, so that components comes before composites
290 */
291 if (start) {
292 rv = NULL;
293 while (all) {
294 struct mdstat_ent *e = all;
295 all = all->next;
296 e->next = rv;
297 rv = e;
298 }
a9db8995
ZL
299 } else
300 rv = all;
22a88995 301 return rv;
e0d19036 302}
dd0781e5 303
a7a0d8a1
N
304void mdstat_close(void)
305{
306 if (mdstat_fd >= 0)
307 close(mdstat_fd);
308 mdstat_fd = -1;
309}
310
cab9c67d
BK
311/*
312 * function: mdstat_wait
313 * Description: Function waits for event on mdstat.
314 * Parameters:
315 * seconds - timeout for waiting
316 * Returns:
317 * > 0 - detected event
318 * 0 - timeout
319 * < 0 - detected error
320 */
321int mdstat_wait(int seconds)
dd0781e5
NB
322{
323 fd_set fds;
324 struct timeval tm;
0b5ec75e 325 int maxfd = 0;
dd0781e5 326 FD_ZERO(&fds);
0b5ec75e 327 if (mdstat_fd >= 0) {
dd0781e5 328 FD_SET(mdstat_fd, &fds);
0b5ec75e 329 maxfd = mdstat_fd;
e2308733 330 } else
cab9c67d
BK
331 return -1;
332
dd0781e5
NB
333 tm.tv_sec = seconds;
334 tm.tv_usec = 0;
cab9c67d
BK
335
336 return select(maxfd + 1, NULL, NULL, &fds, &tm);
dd0781e5 337}
8382f19b 338
58a4ba2a 339void mdstat_wait_fd(int fd, const sigset_t *sigmask)
549e9569
NB
340{
341 fd_set fds, rfds;
5d4d1b26 342 int maxfd = 0;
549e9569
NB
343
344 FD_ZERO(&fds);
345 FD_ZERO(&rfds);
346 if (mdstat_fd >= 0)
347 FD_SET(mdstat_fd, &fds);
5d4d1b26 348
58a4ba2a 349 if (fd >= 0) {
28005287
N
350 struct stat stb;
351 fstat(fd, &stb);
352 if ((stb.st_mode & S_IFMT) == S_IFREG)
353 /* Must be a /proc or /sys fd, so expect
354 * POLLPRI
355 * i.e. an 'exceptional' event.
356 */
357 FD_SET(fd, &fds);
358 else
359 FD_SET(fd, &rfds);
5d4d1b26
N
360
361 if (fd > maxfd)
362 maxfd = fd;
363
28005287 364 }
0b5ec75e
N
365 if (mdstat_fd > maxfd)
366 maxfd = mdstat_fd;
549e9569 367
0b5ec75e 368 pselect(maxfd + 1, &rfds, NULL, &fds,
1ed3f387 369 NULL, sigmask);
549e9569
NB
370}
371
4dd2df09 372int mddev_busy(char *devnm)
8382f19b
NB
373{
374 struct mdstat_ent *mdstat = mdstat_read(0, 0);
375 struct mdstat_ent *me;
376
377 for (me = mdstat ; me ; me = me->next)
4dd2df09 378 if (strcmp(me->devnm, devnm) == 0)
8382f19b
NB
379 break;
380 free_mdstat(mdstat);
381 return me != NULL;
382}
3b57c466
N
383
384struct mdstat_ent *mdstat_by_component(char *name)
385{
386 struct mdstat_ent *mdstat = mdstat_read(0, 0);
387
388 while (mdstat) {
389 struct dev_member *m;
390 struct mdstat_ent *ent;
391 if (mdstat->metadata_version &&
392 strncmp(mdstat->metadata_version, "external:", 9) == 0 &&
393 is_subarray(mdstat->metadata_version+9))
394 /* don't return subarrays, only containers */
395 ;
396 else for (m = mdstat->members; m; m = m->next) {
397 if (strcmp(m->name, name) == 0) {
398 free_mdstat(mdstat->next);
399 mdstat->next = NULL;
400 return mdstat;
401 }
402 }
403 ent = mdstat;
404 mdstat = mdstat->next;
405 ent->next = NULL;
406 free_mdstat(ent);
407 }
408 return NULL;
409}
78b10e66 410
4dd2df09 411struct mdstat_ent *mdstat_by_subdev(char *subdev, char *container)
78b10e66
N
412{
413 struct mdstat_ent *mdstat = mdstat_read(0, 0);
4dd2df09 414 struct mdstat_ent *ent = NULL;
78b10e66
N
415
416 while (mdstat) {
78b10e66 417 /* metadata version must match:
4dd2df09
N
418 * external:[/-]%s/%s
419 * where first %s is 'container' and second %s is 'subdev'
78b10e66 420 */
4dd2df09
N
421 if (ent)
422 free_mdstat(ent);
78b10e66
N
423 ent = mdstat;
424 mdstat = mdstat->next;
425 ent->next = NULL;
4dd2df09
N
426
427 if (ent->metadata_version == NULL ||
428 strncmp(ent->metadata_version, "external:", 9) != 0)
429 continue;
430
431 if (!metadata_container_matches(ent->metadata_version+9,
432 container) ||
433 !metadata_subdev_matches(ent->metadata_version+9,
434 subdev))
435 continue;
436
437 free_mdstat(mdstat);
438 return ent;
78b10e66
N
439 }
440 return NULL;
441}