]> git.ipfire.org Git - thirdparty/mdadm.git/blob - mdstat.c
mdmonitor: use MAILFROM to set sendmail envelope sender address
[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 "xmalloc.h"
84
85 #include <sys/select.h>
86 #include <ctype.h>
87
88 static void free_member_devnames(struct dev_member *m)
89 {
90 while(m) {
91 struct dev_member *t = m;
92
93 m = m->next;
94 free(t->name);
95 free(t);
96 }
97 }
98
99 static int add_member_devname(struct dev_member **m, char *name)
100 {
101 struct dev_member *new;
102 char *t;
103
104 if ((t = strchr(name, '[')) == NULL)
105 /* not a device */
106 return 0;
107
108 new = xmalloc(sizeof(*new));
109 new->name = strndup(name, t - name);
110 new->next = *m;
111 *m = new;
112 return 1;
113 }
114
115 /* Detach element from the list, it may modify list_head */
116 static void mdstat_ent_list_detach_element(struct mdstat_ent **list_head, struct mdstat_ent *el)
117 {
118 struct mdstat_ent *ent = *list_head;
119
120 if (ent == el) {
121 *list_head = ent->next;
122 } else {
123 while (ent) {
124 if (ent->next == el) {
125 ent->next = el->next;
126 break;
127 }
128
129 ent = ent->next;
130 }
131
132 }
133
134 /* Guard if not found or list is empty - not allowed */
135 assert(ent);
136 el->next = NULL;
137 }
138
139 void free_mdstat(struct mdstat_ent *ms)
140 {
141 while (ms) {
142 struct mdstat_ent *t;
143 free(ms->level);
144 free(ms->pattern);
145 free(ms->metadata_version);
146 free_member_devnames(ms->members);
147 t = ms;
148 ms = ms->next;
149 free(t);
150 }
151 }
152
153 bool is_mdstat_ent_external(struct mdstat_ent *ent)
154 {
155 if (!ent->metadata_version)
156 return false;
157
158 if (strncmp(ent->metadata_version, "external:", 9) == 0)
159 return true;
160 return false;
161 }
162
163 bool is_mdstat_ent_subarray(struct mdstat_ent *ent)
164 {
165 if (is_mdstat_ent_external(ent) && is_subarray(ent->metadata_version + 9))
166 return true;
167 return false;
168 }
169
170 bool is_container_member(struct mdstat_ent *mdstat, char *container)
171 {
172 if (is_mdstat_ent_external(mdstat) &&
173 metadata_container_matches(mdstat->metadata_version + 9, container))
174 return true;
175
176 return false;
177 }
178
179 static int mdstat_fd = -1;
180 struct mdstat_ent *mdstat_read(int hold, int start)
181 {
182 FILE *f;
183 struct mdstat_ent *all, *rv, **end, **insert_here;
184 char *line;
185 int fd;
186
187 if (hold && mdstat_fd != -1) {
188 off_t offset = lseek(mdstat_fd, 0L, 0);
189 if (offset == (off_t)-1) {
190 return NULL;
191 }
192 fd = dup(mdstat_fd);
193 if (fd >= 0)
194 f = fdopen(fd, "r");
195 else
196 return NULL;
197 } else
198 f = fopen("/proc/mdstat", "r");
199 if (f == NULL)
200 return NULL;
201
202 if (fcntl(fileno(f), F_SETFD, FD_CLOEXEC) < 0) {
203 fclose(f);
204 return NULL;
205 }
206
207 all = NULL;
208 end = &all;
209 for (; (line = conf_line(f)) ; free_line(line)) {
210 struct mdstat_ent *ent;
211 char *w;
212 char devnm[32];
213 int in_devs = 0;
214
215 if (strcmp(line, "Personalities") == 0)
216 continue;
217 if (strcmp(line, "read_ahead") == 0)
218 continue;
219 if (strcmp(line, "unused") == 0)
220 continue;
221 insert_here = NULL;
222 /* Better be an md line.. */
223 if (strncmp(line, "md", 2)!= 0 || strlen(line) >= 32 ||
224 (line[2] != '_' && !isdigit(line[2])))
225 continue;
226 strcpy(devnm, line);
227
228 ent = xmalloc(sizeof(*ent));
229 ent->level = ent->pattern= NULL;
230 ent->next = NULL;
231 ent->percent = RESYNC_NONE;
232 ent->active = -1;
233 ent->resync = 0;
234 ent->metadata_version = NULL;
235 ent->raid_disks = 0;
236 ent->devcnt = 0;
237 ent->members = NULL;
238
239 strcpy(ent->devnm, devnm);
240
241 for (w=dl_next(line); w!= line ; w=dl_next(w)) {
242 int l = strlen(w);
243 char *eq;
244 if (strcmp(w, "active") == 0)
245 ent->active = 1;
246 else if (strcmp(w, "inactive") == 0) {
247 ent->active = 0;
248 in_devs = 1;
249 } else if (strcmp(w, "bitmap:") == 0) {
250 /* We need to stop parsing here;
251 * otherwise, ent->raid_disks will be
252 * overwritten by the wrong value.
253 */
254 break;
255 } else if (ent->active > 0 &&
256 ent->level == NULL &&
257 w[0] != '(' /*readonly*/) {
258 ent->level = xstrdup(w);
259 in_devs = 1;
260 } else if (in_devs && strcmp(w, "blocks") == 0)
261 in_devs = 0;
262 else if (in_devs) {
263 char *ep = strchr(w, '[');
264 ent->devcnt +=
265 add_member_devname(&ent->members, w);
266 if (ep && strncmp(w, "md", 2) == 0) {
267 /* This has an md device as a component.
268 * If that device is already in the
269 * list, make sure we insert before
270 * there.
271 */
272 struct mdstat_ent **ih;
273 ih = &all;
274 while (ih != insert_here && *ih &&
275 ((int)strlen((*ih)->devnm) !=
276 ep-w ||
277 strncmp((*ih)->devnm, w,
278 ep-w) != 0))
279 ih = & (*ih)->next;
280 insert_here = ih;
281 }
282 } else if (strcmp(w, "super") == 0 &&
283 dl_next(w) != line) {
284 w = dl_next(w);
285 ent->metadata_version = xstrdup(w);
286 } else if (w[0] == '[' && isdigit(w[1])) {
287 ent->raid_disks = atoi(w+1);
288 } else if (!ent->pattern &&
289 w[0] == '[' &&
290 (w[1] == 'U' || w[1] == '_')) {
291 ent->pattern = xstrdup(w+1);
292 if (ent->pattern[l-2] == ']')
293 ent->pattern[l-2] = '\0';
294 } else if (ent->percent == RESYNC_NONE &&
295 strncmp(w, "re", 2) == 0 &&
296 w[l-1] == '%' &&
297 (eq = strchr(w, '=')) != NULL ) {
298 ent->percent = atoi(eq+1);
299 if (strncmp(w,"resync", 6) == 0)
300 ent->resync = 1;
301 else if (strncmp(w, "reshape", 7) == 0)
302 ent->resync = 2;
303 else
304 ent->resync = 0;
305 } else if (ent->percent == RESYNC_NONE &&
306 (w[0] == 'r' || w[0] == 'c')) {
307 if (strncmp(w, "resync", 6) == 0)
308 ent->resync = 1;
309 if (strncmp(w, "reshape", 7) == 0)
310 ent->resync = 2;
311 if (strncmp(w, "recovery", 8) == 0)
312 ent->resync = 0;
313 if (strncmp(w, "check", 5) == 0)
314 ent->resync = 3;
315
316 if (l > 8 && strcmp(w+l-8, "=DELAYED") == 0)
317 ent->percent = RESYNC_DELAYED;
318 if (l > 8 && strcmp(w+l-8, "=PENDING") == 0)
319 ent->percent = RESYNC_PENDING;
320 if (l > 7 && strcmp(w+l-7, "=REMOTE") == 0)
321 ent->percent = RESYNC_REMOTE;
322 } else if (ent->percent == RESYNC_NONE &&
323 w[0] >= '0' &&
324 w[0] <= '9' &&
325 w[l-1] == '%') {
326 ent->percent = atoi(w);
327 }
328 }
329 if (insert_here && (*insert_here)) {
330 ent->next = *insert_here;
331 *insert_here = ent;
332 } else {
333 *end = ent;
334 end = &ent->next;
335 }
336 }
337 if (hold && mdstat_fd == -1) {
338 mdstat_fd = dup(fileno(f));
339 if (fcntl(mdstat_fd, F_SETFD, FD_CLOEXEC) < 0) {
340 fclose(f);
341 return NULL;
342 }
343 }
344 fclose(f);
345
346 /* If we might want to start array,
347 * reverse the order, so that components comes before composites
348 */
349 if (start) {
350 rv = NULL;
351 while (all) {
352 struct mdstat_ent *e = all;
353 all = all->next;
354 e->next = rv;
355 rv = e;
356 }
357 } else
358 rv = all;
359 return rv;
360 }
361
362 void mdstat_close(void)
363 {
364 if (mdstat_fd >= 0)
365 close(mdstat_fd);
366 mdstat_fd = -1;
367 }
368
369 /*
370 * function: mdstat_wait
371 * Description: Function waits for event on mdstat.
372 * Parameters:
373 * seconds - timeout for waiting
374 * Returns:
375 * > 0 - detected event
376 * 0 - timeout
377 * < 0 - detected error
378 */
379 int mdstat_wait(int seconds)
380 {
381 fd_set fds;
382 struct timeval tm;
383 int maxfd = 0;
384 FD_ZERO(&fds);
385 if (mdstat_fd >= 0) {
386 FD_SET(mdstat_fd, &fds);
387 maxfd = mdstat_fd;
388 } else
389 return -1;
390
391 tm.tv_sec = seconds;
392 tm.tv_usec = 0;
393
394 return select(maxfd + 1, NULL, NULL, &fds, &tm);
395 }
396
397 void mdstat_wait_fd(int fd, const sigset_t *sigmask)
398 {
399 fd_set fds, rfds;
400 int maxfd = 0;
401
402 FD_ZERO(&fds);
403 FD_ZERO(&rfds);
404 if (mdstat_fd >= 0)
405 FD_SET(mdstat_fd, &fds);
406
407 if (fd >= 0) {
408 struct stat stb;
409 if (fstat(fd, &stb) != 0)
410 return;
411 if ((stb.st_mode & S_IFMT) == S_IFREG)
412 /* Must be a /proc or /sys fd, so expect
413 * POLLPRI
414 * i.e. an 'exceptional' event.
415 */
416 FD_SET(fd, &fds);
417 else
418 FD_SET(fd, &rfds);
419
420 if (fd > maxfd)
421 maxfd = fd;
422
423 }
424 if (mdstat_fd > maxfd)
425 maxfd = mdstat_fd;
426
427 pselect(maxfd + 1, &rfds, NULL, &fds,
428 NULL, sigmask);
429 }
430
431 int mddev_busy(char *devnm)
432 {
433 struct mdstat_ent *mdstat = mdstat_read(0, 0);
434 struct mdstat_ent *me;
435
436 for (me = mdstat ; me ; me = me->next)
437 if (strcmp(me->devnm, devnm) == 0)
438 break;
439 free_mdstat(mdstat);
440 return me != NULL;
441 }
442
443 /**
444 * mdstat_find_by_member_devnm()- Return first array or external container with member device.
445 * @mdstat: Preloaded mdstat to iterate over.
446 * @member_devnm: devnm of the device to find.
447 *
448 * External subarrays are skipped.
449 */
450 struct mdstat_ent *mdstat_find_by_member_name(struct mdstat_ent *mdstat, char *member_devnm)
451 {
452 struct mdstat_ent *ent;
453
454 for (ent = mdstat; ent; ent = ent->next) {
455 struct dev_member *member;
456
457 if (is_mdstat_ent_subarray(ent))
458 continue;
459
460 for (member = ent->members; member; member = member->next)
461 if (strcmp(member->name, member_devnm) == 0)
462 return ent;
463 }
464
465 return NULL;
466 }
467
468
469 struct mdstat_ent *mdstat_by_component(char *name)
470 {
471 struct mdstat_ent *mdstat = mdstat_read(0, 0);
472 struct mdstat_ent *ent = mdstat_find_by_member_name(mdstat, name);
473
474 if (ent)
475 mdstat_ent_list_detach_element(&mdstat, ent);
476
477 free_mdstat(mdstat);
478
479 return ent;
480 }
481
482 struct mdstat_ent *mdstat_by_subdev(char *subdev, char *container)
483 {
484 struct mdstat_ent *mdstat = mdstat_read(0, 0);
485 struct mdstat_ent *ent = NULL;
486
487 for (ent = mdstat; ent; ent = ent->next) {
488 /* metadata version must match:
489 * external:[/-]%s/%s
490 * where first %s is 'container' and second %s is 'subdev'
491 */
492
493 if (!is_mdstat_ent_external(ent))
494 continue;
495
496 if (!metadata_container_matches(ent->metadata_version + 9, container))
497 continue;
498 if (!metadata_subdev_matches(ent->metadata_version + 9, subdev))
499 continue;
500
501 break;
502 }
503
504 if (ent)
505 mdstat_ent_list_detach_element(&mdstat, ent);
506
507 free_mdstat(mdstat);
508 return ent;
509 }