X-Git-Url: http://git.ipfire.org/?p=thirdparty%2Fmdadm.git;a=blobdiff_plain;f=mdstat.c;h=7e600d0cb03a7d072978226f5cd5cdab3a51f992;hp=4bb29d85c822237e1a0244ff477561f125e0e5c5;hb=45c43276d02a32876c7e1f9f0d04580595141b3d;hpb=37ea3936a696c2b102963ba5117165ef6be8d4b4 diff --git a/mdstat.c b/mdstat.c index 4bb29d85..7e600d0c 100644 --- a/mdstat.c +++ b/mdstat.c @@ -2,7 +2,7 @@ * mdstat - parse /proc/mdstat file. Part of: * mdadm - manage Linux "md" devices aka RAID arrays. * - * Copyright (C) 2002-2006 Neil Brown + * Copyright (C) 2002-2009 Neil Brown * * * This program is free software; you can redistribute it and/or modify @@ -20,12 +20,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Neil Brown - * Email: - * Paper: Neil Brown - * School of Computer Science and Engineering - * The University of New South Wales - * Sydney, 2052 - * Australia + * Email: */ /* @@ -88,14 +83,41 @@ #include #include +static void free_member_devnames(struct dev_member *m) +{ + while(m) { + struct dev_member *t = m; + + m = m->next; + free(t->name); + free(t); + } +} + +static int add_member_devname(struct dev_member **m, char *name) +{ + struct dev_member *new; + char *t; + + if ((t = strchr(name, '[')) == NULL) + /* not a device */ + return 0; + + new = xmalloc(sizeof(*new)); + new->name = strndup(name, t - name); + new->next = *m; + *m = new; + return 1; +} + 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); - if (ms->metadata_version) free(ms->metadata_version); + free(ms->level); + free(ms->pattern); + free(ms->metadata_version); + free_member_devnames(ms->members); t = ms; ms = ms->next; free(t); @@ -108,10 +130,19 @@ struct mdstat_ent *mdstat_read(int hold, int start) FILE *f; struct mdstat_ent *all, *rv, **end, **insert_here; char *line; + int fd; if (hold && mdstat_fd != -1) { - lseek(mdstat_fd, 0L, 0); - f = fdopen(dup(mdstat_fd), "r"); + off_t offset = lseek(mdstat_fd, 0L, 0); + if (offset == (off_t)-1) { + mdstat_close(); + return NULL; + } + fd = dup(mdstat_fd); + if (fd >= 0) + f = fdopen(fd, "r"); + else + return NULL; } else f = fopen("/proc/mdstat", "r"); if (f == NULL) @@ -124,103 +155,109 @@ struct mdstat_ent *mdstat_read(int hold, int start) for (; (line = conf_line(f)) ; free_line(line)) { struct mdstat_ent *ent; char *w; - int devnum; + char devnm[32]; int in_devs = 0; - char *ep; - if (strcmp(line, "Personalities")==0) + if (strcmp(line, "Personalities") == 0) continue; - if (strcmp(line, "read_ahead")==0) + if (strcmp(line, "read_ahead") == 0) continue; - if (strcmp(line, "unused")==0) + if (strcmp(line, "unused") == 0) continue; insert_here = NULL; /* Better be an md line.. */ - if (strncmp(line, "md", 2)!= 0) - continue; - if (strncmp(line, "md_d", 4) == 0) - devnum = -1-strtoul(line+4, &ep, 10); - else if (strncmp(line, "md", 2) == 0) - devnum = strtoul(line+2, &ep, 10); - else - continue; - if (ep == NULL || *ep ) { - /* fprintf(stderr, Name ": bad /proc/mdstat line starts: %s\n", line); */ + if (strncmp(line, "md", 2)!= 0 || strlen(line) >= 32 || + (line[2] != '_' && !isdigit(line[2]))) continue; - } + strcpy(devnm, line); - ent = malloc(sizeof(*ent)); - if (!ent) { - fprintf(stderr, Name ": malloc failed reading /proc/mdstat.\n"); - free_line(line); - break; - } - ent->dev = ent->level = ent->pattern= NULL; + ent = xmalloc(sizeof(*ent)); + ent->level = ent->pattern= NULL; ent->next = NULL; - ent->percent = -1; + ent->percent = RESYNC_NONE; ent->active = -1; ent->resync = 0; ent->metadata_version = NULL; ent->raid_disks = 0; - ent->chunk_size = 0; ent->devcnt = 0; + ent->members = NULL; - ent->dev = strdup(line); - ent->devnum = devnum; + strcpy(ent->devnm, devnm); for (w=dl_next(line); w!= line ; w=dl_next(w)) { int l = strlen(w); char *eq; - if (strcmp(w, "active")==0) + if (strcmp(w, "active") == 0) ent->active = 1; - else if (strcmp(w, "inactive")==0) + else if (strcmp(w, "inactive") == 0) { ent->active = 0; - else if (ent->active >=0 && + in_devs = 1; + } else if (ent->active > 0 && ent->level == NULL && w[0] != '(' /*readonly*/) { - ent->level = strdup(w); + ent->level = xstrdup(w); in_devs = 1; - } else if (in_devs && strcmp(w, "blocks")==0) + } else if (in_devs && strcmp(w, "blocks") == 0) in_devs = 0; else if (in_devs) { - ent->devcnt++; - if (strncmp(w, "md", 2)==0) { + char *ep = strchr(w, '['); + ent->devcnt += + add_member_devname(&ent->members, w); + if (ep && strncmp(w, "md", 2) == 0) { /* This has an md device as a component. * If that device is already in the * list, make sure we insert before * there. */ struct mdstat_ent **ih; - int dn2 = devname2devnum(w); ih = &all; while (ih != insert_here && *ih && - (*ih)->devnum != dn2) + ((int)strlen((*ih)->devnm) != + ep-w || + strncmp((*ih)->devnm, w, + ep-w) != 0)) ih = & (*ih)->next; insert_here = ih; } } else if (strcmp(w, "super") == 0 && dl_next(w) != line) { w = dl_next(w); - ent->metadata_version = strdup(w); + ent->metadata_version = xstrdup(w); } else if (w[0] == '[' && isdigit(w[1])) { ent->raid_disks = atoi(w+1); } else if (!ent->pattern && - w[0] == '[' && - (w[1] == 'U' || w[1] == '_')) { - ent->pattern = strdup(w+1); - if (ent->pattern[l-2]==']') + w[0] == '[' && + (w[1] == 'U' || w[1] == '_')) { + ent->pattern = xstrdup(w+1); + if (ent->pattern[l-2] == ']') ent->pattern[l-2] = '\0'; - } else if (ent->percent == -1 && - strncmp(w, "re", 2)== 0 && + } else if (ent->percent == RESYNC_NONE && + strncmp(w, "re", 2) == 0 && w[l-1] == '%' && - (eq=strchr(w, '=')) != NULL ) { + (eq = strchr(w, '=')) != NULL ) { ent->percent = atoi(eq+1); - if (strncmp(w,"resync", 4)==0) + if (strncmp(w,"resync", 6) == 0) + ent->resync = 1; + else if (strncmp(w, "reshape", 7) == 0) + ent->resync = 2; + else + ent->resync = 0; + } else if (ent->percent == RESYNC_NONE && + (w[0] == 'r' || w[0] == 'c')) { + if (strncmp(w, "resync", 6) == 0) ent->resync = 1; - } else if (ent->percent == -1 && - strncmp(w, "resync", 4)==0) { - ent->resync = 1; - } else if (ent->percent == -1 && + if (strncmp(w, "reshape", 7) == 0) + ent->resync = 2; + if (strncmp(w, "recovery", 8) == 0) + ent->resync = 0; + if (strncmp(w, "check", 5) == 0) + ent->resync = 3; + + if (l > 8 && strcmp(w+l-8, "=DELAYED") == 0) + ent->percent = RESYNC_DELAYED; + if (l > 8 && strcmp(w+l-8, "=PENDING") == 0) + ent->percent = RESYNC_PENDING; + } else if (ent->percent == RESYNC_NONE && w[0] >= '0' && w[0] <= '9' && w[l-1] == '%') { @@ -252,44 +289,133 @@ struct mdstat_ent *mdstat_read(int hold, int start) e->next = rv; rv = e; } - } else rv = all; + } else + rv = all; return rv; } +void mdstat_close(void) +{ + if (mdstat_fd >= 0) + close(mdstat_fd); + mdstat_fd = -1; +} + void mdstat_wait(int seconds) { fd_set fds; struct timeval tm; + int maxfd = 0; FD_ZERO(&fds); - if (mdstat_fd >= 0) + if (mdstat_fd >= 0) { FD_SET(mdstat_fd, &fds); + maxfd = mdstat_fd; + } tm.tv_sec = seconds; tm.tv_usec = 0; - select(mdstat_fd >2 ? mdstat_fd+1:3, NULL, NULL, &fds, &tm); + select(maxfd + 1, NULL, NULL, &fds, &tm); } void mdstat_wait_fd(int fd, const sigset_t *sigmask) { fd_set fds, rfds; + int maxfd = 0; FD_ZERO(&fds); FD_ZERO(&rfds); if (mdstat_fd >= 0) FD_SET(mdstat_fd, &fds); - FD_SET(fd, &rfds); - pselect(mdstat_fd >2 ? mdstat_fd+1:3, &rfds, NULL, &fds, + if (fd >= 0) { + struct stat stb; + fstat(fd, &stb); + if ((stb.st_mode & S_IFMT) == S_IFREG) + /* Must be a /proc or /sys fd, so expect + * POLLPRI + * i.e. an 'exceptional' event. + */ + FD_SET(fd, &fds); + else + FD_SET(fd, &rfds); + + if (fd > maxfd) + maxfd = fd; + + } + if (mdstat_fd > maxfd) + maxfd = mdstat_fd; + + pselect(maxfd + 1, &rfds, NULL, &fds, NULL, sigmask); } -int mddev_busy(int devnum) +int mddev_busy(char *devnm) { struct mdstat_ent *mdstat = mdstat_read(0, 0); struct mdstat_ent *me; for (me = mdstat ; me ; me = me->next) - if (me->devnum == devnum) + if (strcmp(me->devnm, devnm) == 0) break; free_mdstat(mdstat); return me != NULL; } + +struct mdstat_ent *mdstat_by_component(char *name) +{ + struct mdstat_ent *mdstat = mdstat_read(0, 0); + + while (mdstat) { + struct dev_member *m; + struct mdstat_ent *ent; + if (mdstat->metadata_version && + strncmp(mdstat->metadata_version, "external:", 9) == 0 && + is_subarray(mdstat->metadata_version+9)) + /* don't return subarrays, only containers */ + ; + else for (m = mdstat->members; m; m = m->next) { + if (strcmp(m->name, name) == 0) { + free_mdstat(mdstat->next); + mdstat->next = NULL; + return mdstat; + } + } + ent = mdstat; + mdstat = mdstat->next; + ent->next = NULL; + free_mdstat(ent); + } + return NULL; +} + +struct mdstat_ent *mdstat_by_subdev(char *subdev, char *container) +{ + struct mdstat_ent *mdstat = mdstat_read(0, 0); + struct mdstat_ent *ent = NULL; + + while (mdstat) { + /* metadata version must match: + * external:[/-]%s/%s + * where first %s is 'container' and second %s is 'subdev' + */ + if (ent) + free_mdstat(ent); + ent = mdstat; + mdstat = mdstat->next; + ent->next = NULL; + + if (ent->metadata_version == NULL || + strncmp(ent->metadata_version, "external:", 9) != 0) + continue; + + if (!metadata_container_matches(ent->metadata_version+9, + container) || + !metadata_subdev_matches(ent->metadata_version+9, + subdev)) + continue; + + free_mdstat(mdstat); + return ent; + } + return NULL; +}