/* (c) Steve Parker, August 2008 http://steve-parker.org/ * vxdiff: Show and Compare vxvm configurations * GNU General Public License (GPL) version 2, June 1991 * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include #include #include #include #include #define VERSION "0.8" struct vxvol { char dg[256]; char vol[256]; long blocks; // 2048 blocks = 1Mb ... int will do about 1TB; long will do 9223372036854775807 which is 4294967295TB char layout[256]; char subvols[1024]; int plexes; int drl; int raid; struct vxvol *next; }; struct vxvol *input1; struct vxvol *input2; struct vxvol *read_vxprint(char *filename) { struct vxvol *thisvol = NULL; struct vxvol *newvol; FILE *vx; int bufsize = 2048; int subvols_too_long=0; char *buf; char thing[256]; char name[256]; char size[256]; char dg[256]; char drl[256]; char layout[256]; char subvol[256]; struct vxvol *ll; buf = (char *)malloc(bufsize); sprintf(dg, "%s", "-"); ll = NULL; vx = fopen(filename, "r"); while (fgets(buf, bufsize, vx)) { sprintf(thing, "#"); // consider blank lines as comments; don't sscanf() them sscanf(buf, "%255s %255s %*s %*s %*s %255s", thing, name, size); if (!strcmp(thing, "dg")) { sprintf(dg, "%s", name); } if (!strcmp(thing, "v")) { if (!strcmp(dg, "-")) { fprintf(stderr, "Warning: Found volume %s with no associated diskgroup.\n", name); } newvol = (struct vxvol *)malloc(sizeof(struct vxvol)); if (ll == NULL) ll = newvol; else thisvol->next = newvol; thisvol = newvol; thisvol->plexes = 0; thisvol->drl = 0; thisvol->raid = -1; sprintf(thisvol->layout, "unknown"); sprintf(thisvol->subvols, ""); sprintf(thisvol->dg, "%s", dg); thisvol->blocks = atol(size); sprintf(thisvol->vol, "%s", name); thisvol->next = NULL; } if (!strcmp(thing, "pl")) { if (thisvol == NULL) { fprintf(stderr, "Error: Found plex %s with no associated volume. Exiting.\n", name); exit(1); } sscanf(buf, "pl %*s %*s %*s %*s %255s %255s", drl, layout); if (!strcmp(drl, "LOGONLY")) thisvol->drl++; else thisvol->plexes++; sprintf(thisvol->layout, "%s", layout); if (!strcmp(layout, "RAID")) thisvol->raid = 5; else if (thisvol->plexes > 1) thisvol->raid = 1; else thisvol->raid = 0; } if (!strcmp(thing, "sv")) { if (thisvol == NULL) { fprintf(stderr, "Error: Found subvolume %s with no associated volume. Exiting.\n", name); exit(1); } thisvol->raid = 10; sscanf(buf, "%*s %*s %*s %255s", subvol); if (strlen(thisvol->subvols) == 0) sprintf(thisvol->subvols, "%s", subvol); else { if (((strlen(thisvol->subvols) + strlen(subvol)) >=2000) && (subvols_too_long==0)) { strcat(thisvol->subvols, " ... and others, too many to list!"); subvols_too_long=1; fprintf(stderr, "Warning: Too many subvolumes in %s; truncated listing!\n", thisvol->vol); } else { strcat(thisvol->subvols, ","); strcat(thisvol->subvols, subvol); } } } } free(buf); fclose(vx); return ll; } struct vxvol *read_input(char *filename) { FILE *i; struct vxvol *thisvol; struct vxvol *newvol; int bufsize = 2048; char *buf; buf = (char *)malloc(bufsize); char dg[256]; char vol[256]; int layoutoffset; int mb; struct vxvol *ll; ll = NULL; i = fopen(filename, "r"); while (fgets(buf, bufsize, i)) { sprintf(dg, "-"); sprintf(vol, "-"); mb = 0; if ((strlen(buf) > 5) && (buf[0] != '#')) { newvol = (struct vxvol *)malloc(sizeof(struct vxvol)); if (ll == NULL) ll = newvol; else thisvol->next = newvol; thisvol = newvol; sscanf(buf, "%255s %255s %d %n", dg, vol, &mb, &layoutoffset); sprintf(thisvol->dg, "%s", dg); sprintf(thisvol->vol, "%s", vol); thisvol->blocks = mb * 2048; thisvol->raid = -1; if (strlen(buf + layoutoffset) < 1) sprintf(thisvol->layout, "unspecified"); else { sprintf(thisvol->layout, "%s", buf + layoutoffset); thisvol->layout[strlen(thisvol->layout) - 1] = '\0'; // strip the trailing '\n' } thisvol->next = NULL; } } fclose(i); return ll; } void describe_volume(struct vxvol *thisvol, char *layout) { if (thisvol->raid == -1) { // not from vxprint if (!strcmp(thisvol->layout, "unspecified")) sprintf(layout, " (unspecified layout) "); else sprintf(layout, "%s", thisvol->layout); } else { switch (thisvol->raid) { case 0: sprintf(layout, "Simple"); break; case 10: if (!strcmp(thisvol->layout, "CONCAT")) sprintf(layout, "%d Way Concat Pro Mirror", thisvol->plexes); else sprintf(layout, "%d Way Striped Pro Mirror", thisvol->plexes); break; case 1: if (!strcmp(thisvol->layout, "CONCAT")) sprintf(layout, "%d Way Concat Mirror", thisvol->plexes); else sprintf(layout, "%d Way Striped Mirror", thisvol->plexes); break; case 5: sprintf(layout, "RAID 5"); break; } } } void compare_vol(struct vxvol *in1, struct vxvol *in2, char *name1, char *name2) { #define SIZE_DIFFS 1 #define LAYOUT_DIFFS 2 char layout1[256]; char layout2[256]; int diffs = 0; // bitmap: 1=size, 2=layout, 4=future, 8=future, etc if (in1->blocks != in2->blocks) { diffs += SIZE_DIFFS; } describe_volume(in1, layout1); describe_volume(in2, layout2); // ignore if either is an unspecified layout if ((strcmp(layout1, " (unspecified layout) ")) && (strcmp(layout2, " (unspecified layout) "))) { if (strcmp(layout1, layout2)) { diffs += LAYOUT_DIFFS; } } if (diffs != 0) { printf("%s/%s: Difference(s) in:", in1->dg, in1->vol); if (diffs & SIZE_DIFFS) printf(" *Size*"); if (diffs & LAYOUT_DIFFS) printf(" *Layout*"); printf("\n"); printf(" %-6dMB %-20s (%s)\n", in1->blocks / 2048, layout1, name1); printf(" %-6dMB %-20s (%s)\n", in2->blocks / 2048, layout2, name2); } } void compare_ll(struct vxvol *in1, struct vxvol *in2, char *name1, char *name2, int show_comparisons) { // iterate through in1, find matches in in2 // Anything only in in2 will be missed, so we'll have to do this in reverse also. struct vxvol *thisvol; struct vxvol *loopvol; int found = 0; thisvol = in1; while (thisvol != NULL) { loopvol = in2; found = 0; while (loopvol != NULL) { if ((!strcmp(thisvol->vol, loopvol->vol)) && (!strcmp(thisvol->dg, loopvol->dg))) { found = 1; if (show_comparisons == 1) { // don't want to say the same thing twice compare_vol(thisvol, loopvol, name1, name2); } } loopvol = loopvol->next; } if (found == 0) { printf("%s/%s: not in %s\n", thisvol->dg, thisvol->vol, name2); } thisvol = thisvol->next; } } void show_vols(struct vxvol *in) { struct vxvol *thisvol; char layout[256]; thisvol = in; while (thisvol != NULL) { describe_volume(thisvol, layout); printf("%s/%s %dMb %s", thisvol->dg, thisvol->vol, thisvol->blocks / 2048, layout); if (thisvol->drl > 0) { printf(" with %d DRL", thisvol->drl); if (thisvol->drl > 1) printf("s"); } if (strlen(thisvol->subvols) > 0) printf(" with subvolume(s) %s", thisvol->subvols); printf("\n"); thisvol = thisvol->next; } } int is_vxprint(char *filename) { // If it contains a line starting "^dg ", then it's a valid vxprint! FILE *f; char s[256]; char dg[20]; f = fopen(filename, "r"); if (!f) { fprintf(stderr, "Error: Cannot read %s\n", filename); exit(1); } while (fgets(s, 255, f)) { sscanf(s, "%21s %*s", dg); if (!strcmp(dg, "dg")) { fclose(f); //fprintf(stderr, "%s is a vxprint file\n", filename); return 1; } } fclose(f); //fprintf(stderr, "%s is not a vxprint file; will parse as 'dg vol size [layout]'\n", filename); return 0; } void freell(struct vxvol *ll) { struct vxvol *i; struct vxvol *j; i = ll; while (i != NULL) { j = i->next; free(i); i = j; } } int main(int argc, char *argv[]) { if ((argc != 2) && (argc != 3)) { fprintf(stderr, "Usage: vxdiff file1 [file2] (Version %s)\n", VERSION); fprintf(stderr, " where the input files are the output of 'vxprint -Ath' or equivalent\n"); fprintf(stderr, " *or* a text file containing one line per volume, of the format:\n"); fprintf(stderr, " diskgroup volume_name size(Mb) [layout]\n"); fprintf(stderr, "\n * If one file is supplied, it is described.\n"); fprintf(stderr, " * If two files are supplied, the configurations are compared.\n\n"); fprintf(stderr, " This program is distributed in the hope that it will be useful,\n"); fprintf(stderr, " but WITHOUT ANY WARRANTY; without even the implied warranty of\n"); fprintf(stderr, " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"); fprintf(stderr, " GNU General Public License for more details.\n"); fprintf(stderr, "\n Contact http://steve-parker.org/ for details\n\n"); exit(2); } if (is_vxprint(argv[1]) == 1) input1 = read_vxprint(argv[1]); else input1 = read_input(argv[1]); if (argc > 2) { if (is_vxprint(argv[2]) == 1) input2 = read_vxprint(argv[2]); else input2 = read_input(argv[2]); compare_ll(input1, input2, argv[1], argv[2], 1); compare_ll(input2, input1, argv[2], argv[1], 0); } if (argc == 2) { show_vols(input1); } // Not much point in doing this; there's a while linked list to free, but the OS will do it for us on exit anyway freell(input1); freell(input2); }