/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2000-2001 by Sun Microsystems, Inc. * All rights reserved. */ #pragma ident "%Z%%M% %I% %E% SMI" /* * Routines to support fssnap subcommand of switchout. See switchout.c for * the real fssnap command. */ #include #include #include #include #include #include static void fssnap_display_info(ulong_t, int *, int); #define MAX_INFO_DESCRIPTORS (10) static char *infosubopts[] = { #define INFO_SNAPSHOT (0) "snapnumber", #define INFO_BLKDEV (1) "blockdevname", #define INFO_CHARDEV (2) "rawdevname", #define INFO_MNTPT (3) "mountpoint", #define INFO_STATE (4) "state", #define INFO_BACKPATH (5) "backing-store", #define INFO_BACKSIZE (6) "backing-store-len", #define INFO_MAXSIZE (7) "maxsize", #define INFO_CREATETIME (8) "createtime", #define INFO_CHUNKSIZE (9) "chunksize", NULL }; #define BLOCK_PATH "/dev/" SNAP_BLOCK_NAME "/" #define CHAR_PATH "/dev/" SNAP_CHAR_NAME "/" /* labels are truncated to this many characters when displayed */ #define MAX_LABEL_LEN (30) /* * fssnap_show_status() - display file system snapshot status * * displays snapshot information. If mountpoint is set, information is * only displayed for the snapshot (if one exists) on that file system. * If mountpoint is NULL, information is displayed for all snapshots. * * If opts is defined, it is parsed as a list of suboptions (via * getsubopt()) corresponding to the options list defined above. These * options determine what data should be displayed and in what order. An * option may appear more than once. * * The labels parameter is a boolean that determines whether labels * (internationalized) are displayed before each data element. If it is * 0, labels are not displayed, otherwise they are. The labels parameter * is ignored if brief is nonzero. * * The brief parameter is also a boolean and specifies a mode where only * the snapshot number and mount point are displayed, regardless of the * value of labels. This could be used for listing all active snapshots. * * Based on these parameters, an order list is created that tells * fssnap_display_info() what info to display and in what order. * * Note that when labels are not specified, the assumption is that the * output is made for script readable consumption. For this reason, text * is not I18N'd and numbers are left as bytes instead of converted to KB. */ void fssnap_show_status(char *mountpoint, char *opts, int labels, int brief) { int *order, orderlen = MAX_INFO_DESCRIPTORS+1; kstat_ctl_t *kslib; kstat_t *mnt; kstat_t *kshigh; kstat_named_t *highp; char *suboptions, *v, *n; int i = 0; int num, usenum = 0; kslib = kstat_open(); kshigh = kstat_lookup(kslib, SNAP_NAME, 0, FSSNAP_KSTAT_HIGHWATER); /* * First check and see if they gave us a mount point or a device * name (ie /dev/fssnap/X or /dev/rfssnap/X). */ if (mountpoint) { if (strncmp(BLOCK_PATH, mountpoint, strlen(BLOCK_PATH)) == 0 || strncmp(CHAR_PATH, mountpoint, strlen(CHAR_PATH)) == 0) { n = strrchr(mountpoint, '/'); n++; if (isdigit(*n)) { errno = 0; num = (int)strtol(n, NULL, 10); if (errno == 0) { usenum++; } } } } if (opts) { i = 0; order = (int *)malloc(orderlen * sizeof (int)); if (order == NULL) { fprintf(stderr, gettext("cannot allocate order list.\n")); return; } suboptions = opts; while (*suboptions != '\0') { /* * -1 means invalid option, MAX_INFO_DESCRIPTORS is * the end. */ order[i++] = getsubopt(&suboptions, infosubopts, &v); if (i >= orderlen) { order = (int *)realloc(order, sizeof (int) * (orderlen *= 2)); if (order == NULL) { fprintf(stderr, gettext("cannot reallocate order " "list.\n")); return; } } } order[i] = MAX_INFO_DESCRIPTORS; } else { order = (int *)malloc(orderlen * sizeof (int)); if (order == NULL) { fprintf(stderr, gettext("cannot allocate order list.\n")); return; } for (i = 0; i <= MAX_INFO_DESCRIPTORS; i++) order[i] = i; } /* check if fssnap module is loaded */ if (kshigh == NULL) { kstat_close(kslib); return; } (void) kstat_read(kslib, kshigh, NULL); highp = kstat_data_lookup(kshigh, FSSNAP_KSTAT_HIGHWATER); /* Loop up to the maximum number of snapshots */ for (i = 0; i <= highp->value.ui32; i++) { mnt = kstat_lookup(kslib, SNAP_NAME, i, FSSNAP_KSTAT_MNTPT); /* if this snapshot is not allocated, skip to the next */ if (mnt == NULL) continue; if (kstat_read(kslib, mnt, NULL) == -1) continue; if (mountpoint != NULL) { if ((usenum && i != num) || (!usenum && strcmp(mountpoint, mnt->ks_data) != 0)) continue; } if (brief) printf("%4d\t%s\n", i, (char *)mnt->ks_data); else fssnap_display_info(i, order, labels); } } static void fssnap_display_info(ulong_t snapnum, int *order, int labels) { kstat_ctl_t *kslib; kstat_t *back, *num; kstat_named_t *numvalp; kstat_t *mnt; u_longlong_t inuse, size = 0; char buf[BUFSIZ], *first; int i; /* load num kstat */ kslib = kstat_open(); num = kstat_lookup(kslib, SNAP_NAME, snapnum, FSSNAP_KSTAT_NUM); if (num == NULL) return; if (kstat_read(kslib, num, NULL) == -1) return; for (i = 0; order[i] != MAX_INFO_DESCRIPTORS; i++) { switch (order[i]) { case INFO_SNAPSHOT: if (labels) printf("%-*s: %lu\n", MAX_LABEL_LEN, gettext("Snapshot number"), snapnum); else printf("%lu\n", snapnum); break; case INFO_BLKDEV: if (labels) printf("%-*s: /dev/%s/%lu\n", MAX_LABEL_LEN, gettext("Block Device"), SNAP_BLOCK_NAME, snapnum); else printf("/dev/%s/%lu\n", SNAP_BLOCK_NAME, snapnum); break; case INFO_CHARDEV: if (labels) printf("%-*s: /dev/%s/%lu\n", MAX_LABEL_LEN, gettext("Raw Device"), SNAP_CHAR_NAME, snapnum); else printf("/dev/%s/%lu\n", SNAP_CHAR_NAME, snapnum); break; case INFO_MNTPT: mnt = kstat_lookup(kslib, SNAP_NAME, snapnum, FSSNAP_KSTAT_MNTPT); if (mnt == NULL) { fprintf(stderr, gettext("cannot read mount point kstat\n")); continue; } if (kstat_read(kslib, mnt, NULL) == -1) { continue; } if (labels) printf("%-*s: %s\n", MAX_LABEL_LEN, gettext("Mount point"), (char *)mnt->ks_data); else printf("%s\n", (char *)mnt->ks_data); break; case INFO_STATE: /* state */ numvalp = kstat_data_lookup(num, FSSNAP_KSTAT_NUM_STATE); if (numvalp == NULL) { fprintf(stderr, gettext("cannot read state kstat\n")); continue; } if (labels) { printf("%-*s: ", MAX_LABEL_LEN, gettext("Device state")); switch (numvalp->value.i32) { case 0: printf(gettext("creating\n")); break; case 1: printf(gettext("idle\n")); break; case 2: printf(gettext("active\n")); break; case 3: printf(gettext("disabled\n")); break; default: printf(gettext("unknown\n")); break; } } else { switch (numvalp->value.i32) { case 0: printf("creating\n"); break; case 1: printf("idle\n"); break; case 2: printf("active\n"); break; case 3: printf("disabled\n"); break; default: printf("unknown\n"); break; } } break; case INFO_BACKPATH: /* backing file kstat */ back = kstat_lookup(kslib, SNAP_NAME, snapnum, FSSNAP_KSTAT_BFNAME); if (back == NULL || (kstat_read(kslib, back, NULL) == -1) || (back->ks_data == NULL)) { fprintf(stderr, gettext("cannot read backing file name " "kstat from kernel\n")); continue; } if (labels) printf("%-*s: %s\n", MAX_LABEL_LEN, gettext("Backing store path"), (char *)back->ks_data); else printf("%s\n", (char *)back->ks_data); break; case INFO_BACKSIZE: numvalp = kstat_data_lookup(num, FSSNAP_KSTAT_NUM_BFSIZE); if (numvalp == NULL) { fprintf(stderr, gettext("cannot read backing file size " "kstat from kernel\n")); continue; } size = numvalp->value.ui64; if (labels) printf("%-*s: %llu KB\n", MAX_LABEL_LEN, gettext("Backing store size"), size / 1024LL); else printf("%llu\n", size); break; case INFO_MAXSIZE: numvalp = kstat_data_lookup(num, FSSNAP_KSTAT_NUM_MAXSIZE); if (numvalp == NULL) { fprintf(stderr, gettext("cannot read backing file maxsize " "kstat from kernel\n")); continue; } if (labels) { printf("%-*s: ", MAX_LABEL_LEN, gettext("Maximum backing store size")); if (numvalp->value.ui64 == 0LL) printf(gettext("Unlimited\n")); else printf("%llu KB\n", numvalp->value.ui64 / 1024LL); } else { printf("%llu\n", numvalp->value.ui64); } break; case INFO_CREATETIME: { /* snapshot creation time */ char buf[256]; struct tm *tm; char *p; numvalp = kstat_data_lookup(num, FSSNAP_KSTAT_NUM_CREATETIME); if (numvalp == NULL) { fprintf(stderr, gettext("cannot read snapshot create time " "kstat from kernel\n")); continue; } if (labels) { printf("%-*s: ", MAX_LABEL_LEN, gettext("Snapshot create time")); /* get the localized time */ tm = localtime(&numvalp->value.l); if (strftime(buf, sizeof (buf), "%c\n", tm) == 0) /* Wouldn't fit in buf, fall back */ p = ctime(&numvalp->value.l); else p = buf; } else { /* * for script-readable options we want * the locale-independent time only. */ p = ctime(&numvalp->value.l); } /* p should already have a \n appended */ printf("%s", p); break; } case INFO_CHUNKSIZE: numvalp = kstat_data_lookup(num, FSSNAP_KSTAT_NUM_CHUNKSIZE); if (numvalp == NULL) { fprintf(stderr, gettext("cannot read chunksize kstat\n")); continue; } if (labels) printf("%-*s: %lu KB\n", MAX_LABEL_LEN, gettext("Copy-on-write granularity"), numvalp->value.ui32 / 1024L); else printf("%lu\n", numvalp->value.ui32); break; case -1: /* * Print a place holder for unknown options so that * the user can determine which option was not * understood and the number outputted is the same * number they requested. */ printf("?\n"); break; default: printf(gettext("No such data type %d.\n"), order[i]); break; } } }