/* * storage-summary.c * * Copyright (c) 2023 firk (firk@cantconnect.ru) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The origin of this software must not be misrepresented; you must * not claim that you wrote the original software. * 4. Altered versions in any form must be plainly marked as such, and * must not be misinterpreted as being the original software. * * This software is provided by the author and contributors `as is' * without any express or implied warranty. */ #include #include #include #include #include #include #include #include #include #include #include #define inline __inline__ __attribute__((always_inline)) /* this is for gcc 4.8 compiling dev/nvme/nvme.h */ #include #include #include #include #include #include #include #include #include #include #include #include #ifdef WITH_LIBZFS #ifndef LIBZFS_HACK #include WITH_LIBZFS #else /* using official libzfs.h is too complicated, requires at least few .h from /usr/src and depends on freebsd version; officially recommended "public" libzfs_core interface doesn't have the needed functions; but we may use hardcoded prototypes of the only few trivial functions we need and hope they will not change; libnvpair.h+sys/nvpair.h seems present in /usr/include but unusable due to depends*/ typedef enum { B_FALSE=0, B_TRUE=1 } boolean_t; typedef unsigned char uchar_t; typedef unsigned short ushort_t; typedef unsigned int uint_t; typedef unsigned long ulong_t; typedef unsigned long long u_longlong_t; typedef long long longlong_t, hrtime_t; typedef void nvlist_t; extern int nvlist_lookup_uint64(nvlist_t *, const char *, uint64_t *); extern int nvlist_lookup_string(nvlist_t *, const char *, char **); extern int nvlist_lookup_nvlist(nvlist_t *, const char *, nvlist_t **); extern int nvlist_lookup_nvlist_array(nvlist_t *, const char *, nvlist_t ***, uint_t *); extern boolean_t nvlist_exists(nvlist_t *, const char *); typedef struct zfs_handle zfs_handle_t; typedef struct zpool_handle zpool_handle_t; typedef struct libzfs_handle libzfs_handle_t; extern libzfs_handle_t *libzfs_init(void); extern void libzfs_fini(libzfs_handle_t *); extern void libzfs_print_on_error(libzfs_handle_t *, boolean_t); typedef int (*zpool_iter_f)(zpool_handle_t *, void *); extern int zpool_iter(libzfs_handle_t *, zpool_iter_f, void *); extern void zpool_close(zpool_handle_t *); extern const char *zpool_get_name(zpool_handle_t *); extern nvlist_t *zpool_get_config(zpool_handle_t *, nvlist_t **); #define ZPOOL_CONFIG_VDEV_TREE "vdev_tree" #define ZPOOL_CONFIG_CHILDREN "children" #define ZPOOL_CONFIG_L2CACHE "l2cache" #define ZPOOL_CONFIG_SPARES "spares" #define ZPOOL_CONFIG_IS_LOG "is_log" #define ZPOOL_CONFIG_IS_HOLE "is_hole" #define ZPOOL_CONFIG_TYPE "type" #define ZPOOL_CONFIG_ALLOCATION_BIAS "alloc_bias" /* not stored on disk */ #define VDEV_ALLOC_BIAS_SPECIAL "special" #define VDEV_ALLOC_BIAS_DEDUP "dedup" #define VDEV_TYPE_ROOT "root" #define VDEV_TYPE_MIRROR "mirror" #define VDEV_TYPE_REPLACING "replacing" #define VDEV_TYPE_RAIDZ "raidz" #define VDEV_TYPE_DISK "disk" #define VDEV_TYPE_FILE "file" #define VDEV_TYPE_MISSING "missing" #define VDEV_TYPE_HOLE "hole" #define VDEV_TYPE_SPARE "spare" #define VDEV_TYPE_LOG "log" #define VDEV_TYPE_L2CACHE "l2cache" #define VDEV_TYPE_INDIRECT "indirect" typedef enum { VDEV_NAME_PATH = 1 << 0, VDEV_NAME_GUID = 1 << 1, VDEV_NAME_FOLLOW_LINKS = 1 << 2, VDEV_NAME_TYPE_ID = 1 << 3, } vdev_name_t; extern char *zpool_vdev_name(libzfs_handle_t *, zpool_handle_t *, nvlist_t *, int name_flags); typedef enum { ZFS_TYPE_FILESYSTEM = (1 << 0), ZFS_TYPE_SNAPSHOT = (1 << 1), ZFS_TYPE_VOLUME = (1 << 2), ZFS_TYPE_POOL = (1 << 3), ZFS_TYPE_BOOKMARK = (1 << 4) } zfs_type_t; typedef int (*zfs_iter_f)(zfs_handle_t *, void *); extern int zfs_iter_root(libzfs_handle_t *, zfs_iter_f, void *); extern void zfs_close(zfs_handle_t *); extern zfs_type_t zfs_get_type(const zfs_handle_t *); extern const char *zfs_get_name(const zfs_handle_t *); extern const char *zfs_get_pool_name(const zfs_handle_t *); extern int zfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data); extern int zfs_iter_snapshots(zfs_handle_t *zhp, boolean_t simple, zfs_iter_f func, void *data, uint64_t min_txg, uint64_t max_txg); #endif #endif #include #include #include #include #include #define FCL_WITH_VA_LIST #include #include #define FCL_SIMPLE_NAMES #include /**************************************************************************************************************************/ /**************************************************** QUERY CAM DEVLIST **************************************************/ /**************************************************************************************************************************/ /* this is to ignore passX, xptX, sesX and other non-block-dev entries from cam devlist and to ignore virtual devices like mdX from geom devlist */ static char const *cam_devs[] = { "ada", "da", "ad", "cd", NULL }; static int potential_cam(char const *fullname, size_t len) { char const **cd; for(cd=cam_devs; *cd; cd++) if(strlen(*cd)==len && !strncmp(fullname,*cd,len)) return 1; return 0; } static int potential_cam_(char const *dev) { char const **cd; for(cd=cam_devs; *cd; cd++) if(!strcmp(dev,*cd)) return 1; return 0; } static char const * cam_trim(char const *st, size_t sz, size_t *newsz, size_t maxlen) { while(sz && *st==' ') { sz--; st++; } while(sz && (st[sz-1]==' ' || !st[sz-1])) sz--; if(sz>maxlen) sz = maxlen; *newsz = sz; return st; } #define CAM_TRIM(st, newsz, maxlen) cam_trim((char const*)st, sizeof(st), newsz, maxlen) static void cam_print_model(char *dst, size_t dstsz, struct device_match_result *dm) { size_t s1, s2, s3, s4; char const *S1, *S2, *S3, *S4; struct sep_identify_data *sid; if(dm->protocol==PROTO_SCSI) { S1 = CAM_TRIM(dm->inq_data.vendor, &s1, 15); S2 = CAM_TRIM(dm->inq_data.product, &s2, 47); S3 = CAM_TRIM(dm->inq_data.revision, &s3, 15); qp_snprintf(dst, dstsz, "%.*B %.*B %.*B", s1, S1, s2, S2, s3, S3); } else if(dm->protocol==PROTO_ATA || dm->protocol==PROTO_SATAPM) { S1 = CAM_TRIM(dm->ident_data.model, &s1, 47); S2 = CAM_TRIM(dm->ident_data.revision, &s2, 15); qp_snprintf(dst, dstsz, "%.*B %.*B", s1, S1, s2, S2); } else if(dm->protocol==PROTO_SEMB) { sid = (struct sep_identify_data *)&dm->ident_data; S1 = CAM_TRIM(sid->vendor_id, &s1, 15); S2 = CAM_TRIM(sid->product_id, &s2, 47); S3 = CAM_TRIM(sid->product_rev, &s3, 15); S4 = CAM_TRIM(sid->firmware_rev, &s4, 4); qp_snprintf(dst, dstsz, "%.*B %.*B %.*B %*B", s1, S1, s2, S2, s3, S3, s4, S4); #ifndef OLD_FREEBSD /* 10 is old, 12 is not */ } else if(dm->protocol==PROTO_MMCSD) { /* added in 11 or 12 */ /* additional syscalls needed for this; see camcontrol 12.x src */ qp_snprintf(dst, dstsz, "(MMCSD?)"); } else if(dm->protocol==PROTO_NVME) { /* added in 11 or 12 */ /* additional syscalls needed for this; see camcontrol 12.x src */ qp_snprintf(dst, dstsz, "(NVME?)"); #endif } else { qp_snprintf(dst, dstsz, ""); } } typedef struct { char dev_name[DEV_IDLEN]; int dev_unit; int dev_bus; uint64 wwpn; /* if present */ char dev_desc[128]; } cam_path; typedef struct { int path_id; int target_id; ullong lun_id; char dev_name[DEV_IDLEN]; int dev_unit; char const* model; void *geom; uint64 remote_wwpn; /* if present; really it should be same for same path_id+target_id */ } cam_dev; #define MAX_CAM_PATHS 256 #define MAX_CAM_DEVICES 4096 typedef struct { uint num_paths; uint num_devs; cam_path paths[MAX_CAM_PATHS]; cam_dev devs[MAX_CAM_DEVICES]; } cam_devlist; static cam_devlist CAM; static int cam_get_devlist(int debug) { union ccb ccb; int fd, err, path_id; uint32 bufsize, j; struct dev_match_result *dm, *dma; char model[254]; cam_path *path; cam_dev *dev; if((fd=open(XPT_DEVICE,O_RDWR))<0) { qlog("[cam-devlist] open(%s) error %{ERR}", XPT_DEVICE, errno); return -1; } /* this "request" preparation is copy-pasted from freebsd 10 camcontrol source; don't know what all this means */ bzero(&ccb, sizeof(union ccb)); ccb.ccb_h.path_id = CAM_XPT_PATH_ID; ccb.ccb_h.target_id = CAM_TARGET_WILDCARD; ccb.ccb_h.target_lun = CAM_LUN_WILDCARD; ccb.ccb_h.func_code = XPT_DEV_MATCH; /* here the result will be placed */ bufsize = sizeof(struct dev_match_result) * 100; ccb.cdm.match_buf_len = bufsize; if(!(ccb.cdm.matches = dma = fcl_malloc_a(sizeof(struct dev_match_result), 100))) { qlog("[cam-devlist] out of memory"); close(fd); return -1; } ccb.cdm.num_matches = 0; /* something useless */ ccb.cdm.num_patterns = 0; ccb.cdm.pattern_buf_len = 0; err = 0; dev = NULL; do { if(ioctl(fd, CAMIOCOMMAND, &ccb)==-1) { qlog("[cam-devlist] ioctl(CAMIOCOMMAND) error %{ERR}",errno); err = -1; break; } if(ccb.ccb_h.status!=CAM_REQ_CMP || ccb.cdm.status!=CAM_DEV_MATCH_LAST && ccb.cdm.status!=CAM_DEV_MATCH_MORE) { qlog("[cam-devlist] got CAM error 0x%LX, CDM error %d", (uint32)ccb.ccb_h.status, (int)ccb.cdm.status); err = -1; break; } for(j=0,dm=ccb.cdm.matches; !err && jresult.bus_result #define dm_dev dm->result.device_result #define dm_per dm->result.periph_result switch(dm->type) { case DEV_MATCH_BUS: dev = NULL; path_id = dm_bus.path_id; if(debug) qlog("[cam-devlist] bus path_id %d (%s%d:%d)", path_id, dm_bus.dev_name, (int)dm_bus.unit_number, (int)dm_bus.bus_id); if(path_id==-1) break; /* xpt0 */ if(path_id<0 || path_id>=MAX_CAM_PATHS) { qlog("[cam-devlist] bus path_id %d (%s%d:%d) out of range - ignoring", path_id, dm_bus.dev_name, (int)dm_bus.unit_number, (int)dm_bus.bus_id); break; } path = CAM.paths+path_id; if(path->dev_name[0]) { qlog("[cam-devlist] bus path_id %d duplicate (have %s%d:%d, dup %s%d:%d) - ignoring", path_id, path->dev_name, path->dev_unit, path->dev_bus, dm_bus.dev_name, (int)dm_bus.unit_number, (int)dm_bus.bus_id); break; } strlcpy(path->dev_name, dm_bus.dev_name, sizeof(path->dev_name)); path->dev_unit = dm_bus.unit_number; path->dev_bus = dm_bus.bus_id; if(CAM.num_paths<=(uint)path_id) CAM.num_paths = path_id+1; break; case DEV_MATCH_DEVICE: dev = NULL; if(dm_dev.flags & DEV_RESULT_UNCONFIGURED) break; cam_print_model(model, sizeof(model), &dm_dev); if(debug) qlog("[cam-devlist] device <%s> on path[%d] target %d lun %llx", model, (int)dm_dev.path_id, (int)dm_dev.target_id, (ullong)dm_dev.target_lun); if(CAM.num_devs==MAX_CAM_DEVICES) { qlog("[cam-devlist] too many devices, ignoring device <%s> on path[%d] target %d lun %llx", model, (int)dm_dev.path_id, (int)dm_dev.target_id, (ullong)dm_dev.target_lun); break; } assert(CAM.num_devspath_id = dm_dev.path_id; dev->target_id = dm_dev.target_id; dev->lun_id = dm_dev.target_lun; break; case DEV_MATCH_PERIPH: if(debug) qlog("[cam-devlist] periph %s%d", dm_per.periph_name, dm_per.unit_number); if(!dev) break; if(!potential_cam_(dm_per.periph_name)) break; assert(dev==CAM.devs+CAM.num_devs); if(!(dev->model = fcl_strdup(model))) { qlog("[cam-devlist] out of memory"); err = -1; break; } strlcpy(dev->dev_name, dm_per.periph_name, sizeof(dev->dev_name)); dev->dev_unit = dm_per.unit_number; dev = NULL; CAM.num_devs++; break; default: qlog("[cam-devlist] unknown match type %d", (int)dm->type); break; } #undef dm_bus #undef dm_dev #undef dm_per } } while(!err && ccb.ccb_h.status==CAM_REQ_CMP && ccb.cdm.status==CAM_DEV_MATCH_MORE); if(debug) qlog("[cam-devlist] end of data"); close(fd); fcl_free_check(dma); if(debug) qlog("[cam-devlist] end of scan"); return err; } /**************************************************************************************************************************/ /***************************************************** DUMP CAM DEVLIST ***************************************************/ /**************************************************************************************************************************/ static void dump_cam(void) { uint j; cam_path *p; cam_dev *d; for(j=0; jdev_name[0]) { qp_printf(&qp_out, "path[%u] = %s%d:%d", j, p->dev_name, p->dev_unit, p->dev_bus); if(*p->dev_desc) qp_printf(&qp_out, " <%S>", p->dev_desc); if(p->wwpn) qp_printf(&qp_out, " wwpn=%016LLX", p->wwpn); qp_putc(&qp_out,'\n'); } for(j=0; jpath_id<0 || d->path_id>=(int)CAM.num_paths || !(p=CAM.paths+d->path_id)->dev_name[0]) qp_printf(&qp_out, "path[%d] ", d->path_id); else qp_printf(&qp_out, "%s%d:%d ", p->dev_name, p->dev_unit, p->dev_bus); qp_printf(&qp_out, "target %d lun %llx = %s%d <%s>", d->target_id, d->lun_id, d->dev_name, d->dev_unit, d->model); if(d->remote_wwpn) qp_printf(&qp_out, " remote_wwpn=%016LLX", d->remote_wwpn); qp_putc(&qp_out, '\n'); } } static size_t xlen(size_t old, char const *st) { size_t x; x = strlen(st); return (old>x)?old:x; } static size_t vxlen(size_t old, size_t max, char const *fmt, ...) { size_t x; va_list arg; va_start(arg, fmt); x = qp_vstrnlenf(max, fmt, arg); va_end(arg); return (old>x)?old:x; } static void dump_cam_table(int nodec) { size_t devlen, lunlen, unitlen, namelen, l; char tmp[DEV_IDLEN+50], tmp2[50], tmp3[DEV_IDLEN+50]; int have_wwpn; uint j; cam_path *p; cam_dev *d; devlen = 0; namelen = 0; have_wwpn = 0; for(j=0; jdev_name[0]) { qp_snprintf(tmp, sizeof(tmp), "%s%d:%d", p->dev_name, p->dev_unit, p->dev_bus); devlen = xlen(devlen,tmp); namelen = vxlen(namelen,(size_t)-1,"%S",p->dev_desc); if(p->wwpn) have_wwpn = 1; } if(devlen) { if(devlen<10) devlen=10; if(nodec) { devlen |= 1; qp_printf(&qp_out,"#List of storage ports:\n## Device:Bus%*c %sAdapter Name\n", (size_t)(devlen-10), ' ', have_wwpn?"Adapter WWPN ":""); } else { qp_printf(&qp_out," List of storage ports:\n # | Device:Bus%*c| %sAdapter Name\n", (size_t)(devlen-9), ' ', have_wwpn?"Adapter WWPN | ":""); qp_printf(&qp_out, "%0*c\n", (size_t)(10+devlen+3+have_wwpn*19+namelen+1), '-'); } for(j=0; jdev_name[0]) { qp_snprintf(tmp, sizeof(tmp), "%s%d:%d", p->dev_name, p->dev_unit, p->dev_bus); qp_printf(&qp_out, nodec?" %-7u %*s ":" %-7u| %*s | ", j, devlen, tmp); if(have_wwpn) { if(p->wwpn) qp_printf(&qp_out, nodec?"%016LLX ":"%016LLX | ", p->wwpn); else qp_puts(&qp_out,nodec?" ":" | "); } if(*p->dev_desc) qp_printf(&qp_out, nodec?"<%S>":"%S", p->dev_desc); qp_putc(&qp_out,'\n'); qp_flush(&qp_out); } } devlen = 0; lunlen = 0; unitlen = 0; namelen = 0; have_wwpn = 0; for(j=0; jpath_id<0 || d->path_id>=(int)CAM.num_paths || !(p=CAM.paths+d->path_id)->dev_name[0]) qp_snprintf(tmp, sizeof(tmp), "#%d", d->path_id); else qp_snprintf(tmp, sizeof(tmp), "%s%d:%d", p->dev_name, p->dev_unit, p->dev_bus); qp_snprintf(tmp2, sizeof(tmp2), "%d:%llx", d->target_id, d->lun_id); qp_snprintf(tmp3, sizeof(tmp3), "%s%d", d->dev_name, d->dev_unit); devlen = xlen(devlen,tmp); lunlen = xlen(lunlen,tmp2); unitlen = xlen(unitlen,tmp3); namelen = xlen(namelen,d->model); if(d->remote_wwpn) have_wwpn = 1; } if(devlen) { if(devlen<4) devlen=4; if(lunlen<10) lunlen=10; if(unitlen<5) unitlen=5; if(nodec) { devlen |= 1; lunlen |= 1; if(unitlen<6) unitlen=6; unitlen |= 1; qp_printf(&qp_out,"#List of storage devices:\n#Port%*c Target:LUN%*c devfs%*c %sModel\n", (size_t)(devlen-4), ' ', (size_t)(lunlen-10), ' ', (size_t)(unitlen-5), ' ', have_wwpn?"Device WWPN ":""); } else { qp_printf(&qp_out," List of storage devices:\n Port%*c| Target:LUN%*c| devfs%*c| %sModel\n", (size_t)(devlen-3), ' ', (size_t)(lunlen-9), ' ', (size_t)(unitlen-4), ' ', have_wwpn?"Device WWPN | ":""); qp_printf(&qp_out, "%0*c\n", (size_t)(1+devlen+3+lunlen+3+unitlen+3+have_wwpn*19+namelen+1), '-'); } for(j=0; jpath_id<0 || d->path_id>=(int)CAM.num_paths || !(p=CAM.paths+d->path_id)->dev_name[0]) qp_snprintf(tmp, sizeof(tmp), "#%d", d->path_id); else qp_snprintf(tmp, sizeof(tmp), "%s%d:%d", p->dev_name, p->dev_unit, p->dev_bus); qp_snprintf(tmp2, sizeof(tmp2), "%d:%llx", d->target_id, d->lun_id); qp_snprintf(tmp3, sizeof(tmp3), "%s%d", d->dev_name, d->dev_unit); qp_printf(&qp_out, nodec?" %*s %*s %*s ":" %*s | %*s | %*s | ", devlen, tmp, lunlen, tmp2, unitlen, tmp3); if(have_wwpn) { if(d->remote_wwpn) qp_printf(&qp_out, nodec?"%016LLX ":"%016LLX | ", d->remote_wwpn); else qp_puts(&qp_out,nodec?" ":" | "); } qp_printf(&qp_out, nodec?"<%s>\n":"%s\n", d->model); qp_flush(&qp_out); } } } /**************************************************************************************************************************/ /**************************************************** QUERY GEOM DEVLIST **************************************************/ /**************************************************************************************************************************/ static void debug_geom_config(struct gconf *gc) { struct gconfig *c; LIST_FOREACH(c, gc, lg_config) { qlog("[geom-devlist] config %\"S = %\"S", c->lg_name, c->lg_val); } } static int geom_get_ident(char const *provname, char *r/* [DISK_IDENT_SIZE] */) { /* usually it is serial number of underlying hardware for primary devices and missing for others (ex. partitions) */ int fd; *r = 0; if((fd = g_open(provname,0))<0) return -1; if(g_get_ident(fd,r,DISK_IDENT_SIZE)<0) { g_close(fd); return -1; } g_close(fd); return 0; } static char const * geom_config_dupval(struct gconf *gc, char const *name) { struct gconfig *c; LIST_FOREACH(c, gc, lg_config) { if(!strcmp(c->lg_name,name)) return fcl_strdup_check(c->lg_val); } return NULL; } typedef struct { uint cat; char const *name; /* not escaped but comes from kernel source so safe */ } geom_class; typedef struct { geom_class *cl; /* =src->cl */ char const *name; /* not escaped */ uint ncons; struct geom_link *src; struct geom_link **consumers; char const *md_file; char const *mountpt; /* filled by get_mounts() later */ int is_swap; int is_zfs_vdev; int is_zvol; } geom_dev; typedef struct geom_link { geom_class *cl; uint ver_type; char const *name; /* not escaped */ uint nlow, nhigh; cam_dev *cam; geom_dev **low, **high; int displayed; char const *origin; /* escaped */ } geom_link; #define VER_DISK 1 #define VER_MD 2 #define VER_LABEL 3 #define VER_PART 4 #define VER_MIRROR 5 #define VER_MPATH 6 #define VER_ZVOL 7 #define VERZ_ZPOOL 100 #define VERZ_ZFS 101 #define VERZ_ZSNAP 102 /* first loop initialized all except: geom_dev.ncons, geom_dev.consumers, geom_link.nlow, geom_link.low, geom_link.cam */ #define MAX_GEOM_CLASSES 256 #define MAX_GEOM_LINKS 1024 #define MAX_GEOM_DEVICES 4096 typedef struct { uint num_classes; uint num_links; uint num_devs; geom_class classes[MAX_GEOM_CLASSES]; geom_link links[MAX_GEOM_LINKS]; geom_dev devs[MAX_GEOM_DEVICES]; geom_link *slinks[MAX_GEOM_LINKS]; } geom_devlist; static int zfs_needed; static geom_devlist GEOM; static geom_class * add_geom_class(uint cat, char const *name) { geom_class *r; if(GEOM.num_classes==MAX_GEOM_CLASSES) { qlog("[geom-devlist] too many classes"); exit(-1); } assert(GEOM.num_classescat = cat; r->name = fcl_strdup_check(name); GEOM.num_classes++; return r; } static geom_link * add_geom_link(geom_class *cl, char const *name) { geom_link *r; if(GEOM.num_links==MAX_GEOM_LINKS) { qlog("[geom-devlist] too many links"); exit(-1); } assert(GEOM.num_linkscl = cl; r->name = fcl_strdup_check(name); GEOM.num_links++; return r; } static geom_dev * add_geom_dev(geom_link *src, char const *name) { geom_dev *r; if(GEOM.num_devs==MAX_GEOM_DEVICES) { qlog("[geom-devlist] too many devices"); exit(-1); } assert(GEOM.num_devscl = src->cl; r->src = src; r->name = fcl_strdup_check(name); GEOM.num_devs++; return r; } static int geom_get_devlist(int debug) { char ident[DISK_IDENT_SIZE]; struct gmesh gm; struct gclass *class; struct ggeom *geom, *cgeom; struct gprovider *prov; struct gconsumer *cons; int err; geom_class *gcl; geom_link *gl; geom_dev *gd; void *arr[MAX_GEOM_DEVICES]; uint narr; if(err = geom_gettree(&gm)) { qlog("[geom-devlist] geom_gettree() error %{ERR}", err); return -1; } /* first loop - map struct gmesh to our tree; reuse lg_id fields for that */ LIST_FOREACH(class, &gm.lg_class, lg_class) { if(debug) { qlog("[geom-devlist] class %p %\"S", class->lg_id, class->lg_name); if(debug>=2) debug_geom_config(&class->lg_config); } class->lg_id = gcl = add_geom_class(0, class->lg_name); LIST_FOREACH(geom, &class->lg_geom, lg_geom) { assert(geom->lg_class==class); assert(geom->lg_rank>=1); if(debug) { qlog("[geom-devlist] geom %p %\"S rank=%u", geom->lg_id, geom->lg_name, geom->lg_rank); if(debug>=2) debug_geom_config(&geom->lg_config); } geom->lg_id = gl = NULL; narr = 0; LIST_FOREACH(prov, &geom->lg_provider, lg_provider) { assert(prov->lg_geom==geom); if(debug) { qlog("[geom-devlist] provider %p %\"S mode=%\"S mediasize=%lld sectorsize=%u stripeoffset=%lld stripesize=%lld", prov->lg_id, prov->lg_name, prov->lg_mode, (llong)prov->lg_mediasize, (uint)prov->lg_sectorsize, (llong)prov->lg_stripeoffset, (llong)prov->lg_stripesize); if(debug>=2) debug_geom_config(&prov->lg_config); } if(!gl) geom->lg_id = gl = add_geom_link(gcl, geom->lg_name); arr[narr++] = prov->lg_id = gd = add_geom_dev(gl, prov->lg_name); if(!strcmp(class->lg_name,"MD")) gd->md_file = geom_config_dupval(&prov->lg_config, "file"); } if(narr) { gl->high = fcl_memdup_check(arr, sizeof(*arr)*narr); gl->nhigh = narr; } LIST_FOREACH(cons, &geom->lg_consumer, lg_consumer) { assert(cons->lg_geom==geom); assert(cons->lg_provider); prov = cons->lg_provider; assert(prov->lg_geom->lg_rank==geom->lg_rank-1); if(debug) { qlog("[geom-devlist] consumer %p mode=%\"S provider=%\"S", cons->lg_id, cons->lg_mode, prov->lg_name); if(debug>=2) debug_geom_config(&cons->lg_config); } } } } /* second loop - fill consumers (links about geom consuming some device) */ LIST_FOREACH(class, &gm.lg_class, lg_class) { gcl = class->lg_id; LIST_FOREACH(geom, &class->lg_geom, lg_geom) { if(!strcmp(class->lg_name,"SWAP")) { LIST_FOREACH(cons, &geom->lg_consumer, lg_consumer) { gd = cons->lg_provider->lg_id; assert((char*)gd>=(char*)GEOM.devs && (char*)gd<(char*)(GEOM.devs+GEOM.num_devs)); assert(gd->src==cons->lg_provider->lg_geom->lg_id); gd->is_swap++; } } if(!strcmp(class->lg_name,"ZFS::VDEV")) { LIST_FOREACH(cons, &geom->lg_consumer, lg_consumer) { gd = cons->lg_provider->lg_id; assert((char*)gd>=(char*)GEOM.devs && (char*)gd<(char*)(GEOM.devs+GEOM.num_devs)); assert(gd->src==cons->lg_provider->lg_geom->lg_id); gd->is_zfs_vdev++; if(!zfs_needed) zfs_needed = 1; } } if(!strcmp(class->lg_name,"ZFS::ZVOL")) { if(!zfs_needed) zfs_needed = 1; } if(!(gl = geom->lg_id)) continue; LIST_FOREACH(prov, &geom->lg_provider, lg_provider) { gd = prov->lg_id; narr = 0; LIST_FOREACH(cons, &prov->lg_consumers, lg_consumers) { assert(cons->lg_provider==prov); cgeom = cons->lg_geom; if(!cgeom->lg_provider.lh_first) continue; /* ignore geoms that doesn't provide anything; they are not linked to geom_link anyway */ assert((char*)cgeom->lg_id>=(char*)GEOM.links && (char*)cgeom->lg_id<(char*)(GEOM.links+GEOM.num_links)); arr[narr++] = cgeom->lg_id; } if(narr) { gd->consumers = fcl_memdup_check(arr, sizeof(*arr)*narr); gd->ncons = narr; } } narr = 0; LIST_FOREACH(cons, &geom->lg_consumer, lg_consumer) { gd = cons->lg_provider->lg_id; assert((char*)gd>=(char*)GEOM.devs && (char*)gd<(char*)(GEOM.devs+GEOM.num_devs)); assert(gd->src==cons->lg_provider->lg_geom->lg_id); arr[narr++] = gd; } if(narr) { gl->low = fcl_memdup_check(arr, sizeof(*arr)*narr); gl->nlow = narr; } } } geom_deletetree(&gm); return err; } static void geom_verify_devlist(int debug) { uint j; geom_link *gl; for(j=0; jcl->name,"DISK")) { if(gl->nlow==0 && gl->nhigh==1 && !strcmp(gl->name,gl->high[0]->name)) gl->ver_type = VER_DISK; else qlog("[geom-verify] weird GEOM:DISK[%S]", gl->name); } else if(!strcmp(gl->cl->name,"MD")) { if(!gl->cam && gl->nlow==0 && gl->nhigh==1 && !strcmp(gl->name,gl->high[0]->name)) gl->ver_type = VER_MD; else qlog("[geom-verify] weird GEOM:MD[%S]", gl->name); } else if(!strcmp(gl->cl->name,"LABEL")) { if(!gl->cam && gl->nlow==1 && gl->nhigh==1 && !strcmp(gl->name,gl->low[0]->name)) gl->ver_type = VER_LABEL; else qlog("[geom-verify] weird GEOM:LABEL[%S]", gl->name); } else if(!strcmp(gl->cl->name,"PART")) { if(!gl->cam && gl->nlow==1 && !strcmp(gl->name,gl->low[0]->name)) gl->ver_type = VER_PART; else qlog("[geom-verify] weird GEOM:PART[%S]", gl->name); } else if(!strcmp(gl->cl->name,"MIRROR")) { if(!gl->cam && gl->nhigh==1 && !strncmp("mirror/",gl->high[0]->name,7) && !strcmp(gl->name,gl->high[0]->name+7)) gl->ver_type = VER_MIRROR; else qlog("[geom-verify] weird GEOM:MIRROR[%S]", gl->name); } else if(!strcmp(gl->cl->name,"MULTIPATH")) { if(!gl->cam && gl->nhigh==1 && !strncmp("multipath/",gl->high[0]->name,10) && !strcmp(gl->name,gl->high[0]->name+10)) gl->ver_type = VER_MPATH; else qlog("[geom-verify] weird GEOM:MULTIPATH[%S]", gl->name); } else if(!strcmp(gl->cl->name,"ZFS::ZVOL")) { if(!gl->cam && !gl->nlow && gl->nhigh==1 && !strncmp("zfs::zvol::",gl->name,11) && !strncmp("zvol/",gl->high[0]->name,5) && !strcmp(gl->name+11,gl->high[0]->name+5)) gl->ver_type = VER_ZVOL; else qlog("[geom-verify] weird GEOM:ZFS::ZVOL[%S]", gl->name); } } } #ifdef WITH_LIBZFS /**************************************************************************************************************************/ /**************************************************** QUERY ZFS DEVLIST **************************************************/ /**************************************************************************************************************************/ typedef struct { uint debug; uint snaps; uint depth; libzfs_handle_t *zfs; geom_class *gcl_zpool; geom_class *gcl_zfs; geom_class *gcl_zvol; geom_class *gcl_zsnap; geom_link *cur_pool; geom_dev *cur_fs; geom_link *cur_fss; void *arr[MAX_GEOM_DEVICES]; uint narr; void *arrs[MAX_GEOM_DEVICES]; uint narrs; qprint_ctx out; } zctx; static uint get_detailed_vdev_type(nvlist_t *nv) { uint64_t uu; char *bias, *type; if(!nvlist_lookup_uint64(nv,ZPOOL_CONFIG_IS_LOG,&uu) && uu) return 3; if(nvlist_lookup_string(nv,ZPOOL_CONFIG_ALLOCATION_BIAS,&bias)) return 0; if(!nvlist_lookup_string(nv,ZPOOL_CONFIG_TYPE,&type) && !strcmp(type,VDEV_TYPE_INDIRECT)) return 0; if(!strcmp(bias,VDEV_ALLOC_BIAS_DEDUP)) return 1; if(!strcmp(bias,VDEV_ALLOC_BIAS_SPECIAL)) return 2; return 0; } #define ctx (*(zctx*)cbdata) static void save_zpool_sub(zpool_handle_t *pool, nvlist_t *node, uint depth, void *cbdata) { char *name; nvlist_t **subs, *sub; uint_t n, j, w; uint64_t uu; w = 0; if(depth) { /* ignore dummy "root-0" node */ name = zpool_vdev_name(ctx.zfs, pool, node, 0/*VDEV_NAME_TYPE_ID*/); /* VDEV_NAME_TYPE_ID causes "mirror-0" "mirror-1" etc unique names for each mirror inside zpool */ qp_printf(&ctx.out, "%S", name); if(ctx.debug) qlog("[zfs-devlist] zpool node:%*c%\"S", (size_t)(depth*2+1), ' ', name); free(name); } if(!nvlist_lookup_nvlist_array(node, ZPOOL_CONFIG_CHILDREN, &subs, &n) && n) { for(j=0; j"); src[ctx.out.bufpos] = 0; if(ctx.debug) qlog("[zfs-devlist] zpool %\"S scheme %\"S", name, src); gl = add_geom_link(ctx.gcl_zpool, name); gl->ver_type = VERZ_ZPOOL; gl->origin = fcl_strdup_check(src); zpool_close(pool); return 0; } static int save_zfssnap(zfs_handle_t *fs, void *cbdata) { zfs_type_t t; char const *name; uint depth; geom_link *gl; char src[1024]; depth = ctx.depth; name = zfs_get_name(fs); t = zfs_get_type(fs); if(t!=ZFS_TYPE_SNAPSHOT) { qlog("[zfs-devlist]%*cdataset %\"S unexpected type %d", (size_t)(depth+1), ' ', name, (int)t); zfs_close(fs); return 0; } if(ctx.debug) qlog("[zfs-devlist]%*cdataset %\"S type SNAPSHOT", (size_t)(depth+1), ' ', name); if(!(gl = ctx.cur_fss)) { ctx.narrs = 0; ctx.cur_fss = gl = add_geom_link(ctx.gcl_zsnap, ctx.cur_fs->name); gl->ver_type = VERZ_ZSNAP; gl->low = fcl_memdup_check(&ctx.cur_fs, sizeof(ctx.cur_fs)); gl->nlow = 1; assert(!ctx.cur_fs->consumers && !ctx.cur_fs->ncons); ctx.cur_fs->consumers = fcl_memdup_check(&gl, sizeof(gl)); ctx.cur_fs->ncons = 1; } ctx.arrs[ctx.narrs++] = add_geom_dev(gl, name); zfs_close(fs); return 0; } static void iter_snaps(zfs_handle_t *fs, geom_dev *gd, void *cbdata) { ctx.cur_fs = gd; ctx.cur_fss = NULL; zfs_iter_snapshots(fs, 0/*1 what does it mean?*/, save_zfssnap, cbdata, 0, 0); if(ctx.cur_fss) { ctx.cur_fss->high = fcl_memdup_check(ctx.arrs, sizeof(*ctx.arrs)*ctx.narrs); ctx.cur_fss->nhigh = ctx.narrs; } } static int save_zfs(zfs_handle_t *fs, void *cbdata) { zfs_type_t t; char const *name; uint depth; geom_link *gl; geom_dev *gd; depth = ctx.depth++; name = zfs_get_name(fs); t = zfs_get_type(fs); switch(t) { case ZFS_TYPE_FILESYSTEM: if(ctx.debug) qlog("[zfs-devlist]%*cdataset %\"S type FILESYSTEM", (size_t)(depth+1), ' ', name); ctx.arr[ctx.narr++] = gd = add_geom_dev(ctx.cur_pool, name); gd->is_zvol = 0; if(ctx.snaps) iter_snaps(fs, gd, cbdata); zfs_iter_filesystems(fs, save_zfs, cbdata); break; case ZFS_TYPE_VOLUME: if(ctx.debug) qlog("[zfs-devlist]%*cdataset %\"S type VOLUME", (size_t)(depth+1), ' ', name); ctx.arr[ctx.narr++] = gd = add_geom_dev(ctx.cur_pool, name); gd->is_zvol = 1; if(ctx.snaps) iter_snaps(fs, gd, cbdata); break; default: qlog("[zfs-devlist]%*cdataset %\"S unexpected type %d", (size_t)(depth+1), ' ', name, (int)t); } zfs_close(fs); ctx.depth = depth; return 0; } static int save_zfsroot(zfs_handle_t *fs, void *cbdata) { zfs_type_t t; char const *name; geom_link *gl; geom_dev *gd; name = zfs_get_name(fs); t = zfs_get_type(fs); if(t!=ZFS_TYPE_FILESYSTEM) { qlog("[zfs-devlist] root dataset %\"S unexpected type %d", name, (int)t); zfs_close(fs); return 0; } if(ctx.debug) qlog("[zfs-devlist] root dataset %\"S type FILESYSTEM", name); ctx.cur_pool = gl = add_geom_link(ctx.gcl_zfs, zfs_get_pool_name(fs)); gl->ver_type = VERZ_ZFS; ctx.depth = 1; ctx.narr = 0; ctx.arr[ctx.narr++] = gd = add_geom_dev(gl, name); if(ctx.snaps) iter_snaps(fs, gd, cbdata); zfs_iter_filesystems(fs, save_zfs, cbdata); gl->high = fcl_memdup_check(ctx.arr, sizeof(*ctx.arr)*ctx.narr); gl->nhigh = ctx.narr; zfs_close(fs); return 0; } #undef ctx static int zfs_get_devlist(int debug, int snaps) { zctx ctx; if(zfs_needed<=0) { if(debug) qlog("[zfs-devlist] skipping due to no zfs-related geoms seen"); return 0; } bzero(&ctx, sizeof(ctx)); ctx.debug = debug; ctx.snaps = snaps; if(!(ctx.zfs = libzfs_init())) { qlog("[zfs-devlist] libzfs_init() failed"); return -1; } libzfs_print_on_error(ctx.zfs, B_TRUE); ctx.gcl_zpool = add_geom_class(1, "ZPOOL"); ctx.gcl_zfs = add_geom_class(1, "ZFS"); ctx.gcl_zvol = add_geom_class(1, "ZVOL"); ctx.gcl_zsnap = add_geom_class(1, "ZSNAPSHOT"); zpool_iter(ctx.zfs, &save_zpool, &ctx); zfs_iter_root(ctx.zfs, &save_zfsroot, &ctx); libzfs_fini(ctx.zfs); return 0; } #endif /**************************************************************************************************************************/ /**************************************************** LINK GEOM DEVLIST **************************************************/ /**************************************************************************************************************************/ static int devcmp(char const *devname, char const *fullname, size_t len) { if(strlen(devname)!=len || strncmp(devname,fullname,len)) return 1; return 0; } static void geom_link_cam(int debug) { uint j, unit, k; geom_link *gl; geom_dev *gd; cam_dev *cd; size_t p; for(j=0; jlow) continue; if(debug) qlog("[geom-cam] lowest-level geom (%s): %\"S provides %u devices", gl->cl->name, gl->name, gl->nhigh); if(gl->nhigh!=1) continue; if(!strcmp(gl->cl->name,"ZFS::ZVOL")) continue; gd = gl->high[0]; p = strlen(gd->name); while(p && gl->name[p-1]>='0' && gl->name[p-1]<='9') p--; if(str_to_uint(gl->name+p,&unit)<0) { qlog("[geom-cam] lowest-level geom-dev bad name %\"S", gd->name); continue; } if(potential_cam(gd->name,p)) { for(k=0,cd=CAM.devs; kdev_unit>=0 && (uint)cd->dev_unit==unit && !devcmp(cd->dev_name, gd->name, p)) { if(!cd->geom) { cd->geom = gl; gl->cam = cd; } else qlog("[geom-cam] lowest-level geom-dev %\"S seems duplicate", gd->name); break; } } if(!gl->cam) qlog("[geom-cam] lowest-level geom-dev %\"S not found in cam device list", gd->name); } else { if(debug) qlog("[geom-cam] lowest-level geom-dev %\"S ignoring", gd->name); } } } /**************************************************************************************************************************/ /**************************************************** SORT GEOM DEVLIST **************************************************/ /**************************************************************************************************************************/ static int devacmp(char const *a, char const *b) { unsigned char A, B; char const *i, *j; int r; while(1) { A=*a; B=*b; if(!A || !B) return A-B; if(A>='1' && A<='9' && B>='1' && B<='9') { i=a+1; j=b+1; r=A-B; while(1) { A=*i; B=*j; if(A<'0' || A>'9' || B<'0' || B>'9') break; if(!r) r = A-B; i++; j++; } if(A>='0' && A<='9') return 1; else if(B>='0' && B<='9') return -1; else if(r) return r; a=i; b=j; } if(A>B) return 1; if(Apath_idpath_id) return -1; if(a->path_id>b->path_id) return 1; if(a->target_idtarget_id) return -1; if(a->target_id>b->target_id) return 1; if(a->lun_idlun_id) return -1; if(a->lun_id>b->lun_id) return 1; return 0; } static int cmp_geom(void const *A, void const *B) { geom_link *a, *b; a = *(geom_link**)A; b = *(geom_link**)B; if(!a->nlow && !b->nlow) { if(!a->cam && !b->cam) return strcmp(a->name, b->name); if(!a->cam) return 1; if(!b->cam) return -1; return cmp_camdev(a->cam, b->cam); } if(!a->nlow) return (b->nlow?-1:0); return (b->nlow?devacmp(a->low[0]->name,b->low[0]->name):1); } static int cmp_geomdev(void const *A, void const *B) { geom_dev *a, *b; a = *(geom_dev**)A; b = *(geom_dev**)B; return devacmp(a->name,b->name); } static void sort_geoms(int s) { uint j; geom_link *gl; for(j=0; jnlow) qsort(gl->low, gl->nlow, sizeof(*gl->low), &cmp_geomdev); } if(s) qsort(GEOM.slinks, GEOM.num_links, sizeof(*GEOM.slinks), &cmp_geom); } /**************************************************************************************************************************/ /**************************************************** DUMP GEOM DEVLIST **************************************************/ /**************************************************************************************************************************/ static int useless_geom(geom_link *gl, int q) { uint j, k; geom_dev *gd; geom_link *cons; if(q<2 && strcmp(gl->cl->name,"LABEL")) return 0; if(gl->cl->cat) return 0; /* TODO: zfs nodes should have right column too */ for(j=0; jnhigh; j++) { gd = gl->high[j]; if(!skip_prefix(gd->name,"ufsid/") && !skip_prefix(gd->name,"gptid/") && !skip_prefix(gd->name,"diskid/") || gd->mountpt || gd->is_swap || gd->is_zfs_vdev) return 0; for(k=0; kncons; k++) { cons = gd->consumers[k]; if(!useless_geom(cons,q)) return 0; } } return 1; } /* output should be escaped */ static void dump_geom_dev(qprint_ctx *o, geom_dev *gd, int merge_labels, int q, int no_md_file) { geom_link *cons; uint j, k; int brace; brace = 0; if(gd->src->ver_type==VERZ_ZFS) { /* TODO: merge this zvol/ with GEOM:ZFS::ZVOL entry */ if(gd->is_zvol) qp_puts(o, "zvol/"); else qp_puts(o,"ZFS:"); } else if(gd->src->ver_type==VERZ_ZSNAP) { assert(gd->src->nlow==1 && gd->src->low[0]->src->ver_type==VERZ_ZFS); if(gd->src->low[0]->is_zvol) qp_puts(o, "zvol/"); else qp_puts(o,"ZFS:"); } qp_printf(o, "%S", gd->name); if(gd->md_file && !no_md_file) { qp_printf(o, "(F:%S", gd->md_file); brace = 1; } if(gd->mountpt) { qp_printf(o, "%c%S", (brace?',':'('), gd->mountpt); brace = 1; } if(gd->is_swap) { qp_printf(o, "%cSWAP", (brace?',':'(')); brace = 1; } if(gd->is_zfs_vdev) { qp_printf(o, "%cZFSV", (brace?',':'(')); brace = 1; } if(merge_labels) { for(j=0; jncons; j++) { cons = gd->consumers[j]; if(!strcmp(cons->cl->name,"LABEL") && !(q && useless_geom(cons,q))) { cons->displayed = 1; for(k=0; knhigh; k++) { qp_puts(o, brace?",L:":"(L:"); brace = 1; dump_geom_dev(o, cons->high[k], 1, q, 0); } } } } if(brace) qp_putc(o, ')'); } /* output should be escaped */ static void print_geom_src(qprint_ctx *o, geom_link *gl) { cam_dev *cd; cam_path *cp; char const *f; uint k, w; switch(gl->ver_type) { case VER_MD: if(f = gl->high[0]->md_file) qp_printf(o,"file:%S",f); return; case VERZ_ZPOOL: if(gl->origin) qp_printf(o,"%s",gl->origin); return; case VERZ_ZFS: qp_printf(o,"ZPOOL:%S",gl->name); return; } w = 0; if(cd = gl->cam) { qp_puts(o, "CAM["); if(cd->path_id<0 || cd->path_id>=(int)CAM.num_paths || !(cp=CAM.paths+cd->path_id)->dev_name[0]) qp_printf(o, "#%d|", cd->path_id); else qp_printf(o, "%s%d:%d:", cp->dev_name, cp->dev_unit, cp->dev_bus); qp_printf(o, "%d:%llx", cd->target_id, cd->lun_id); if(cd->remote_wwpn) qp_printf(o, "|%016LLX", cd->remote_wwpn); qp_putc(o, ']'); w = 1; } for(k=0; knlow; k++) { if(w) qp_putc(o,' '); dump_geom_dev(o, gl->low[k], 0, 0, 0); w = 1; } } static void dump_geom(int merge_geom_labels, int q) { uint j, k; geom_link *gl; /* every geom dev provided by some geom link, so no need to iterate devs */ for(j=0; j=2 && useless_geom(gl,q)) continue; if(gl->ver_type==VER_LABEL && merge_geom_labels) continue; gl->displayed = 1; print_geom_src(&qp_out, gl); qp_printf(&qp_out," >> %s:%s[%S] >> ", gl->cl->cat?"ZFS":"GEOM", gl->cl->name, gl->name); if(gl->ver_type==VERZ_ZPOOL) { qp_printf(&qp_out,"ZPOOL:%S",gl->name); } else { for(k=0; knhigh; k++) { if(k) qp_putc(&qp_out,' '); dump_geom_dev(&qp_out, gl->high[k], merge_geom_labels, q, gl->ver_type==VER_MD); } } qp_putc(&qp_out,'\n'); } } static void checkhide_geom(int merge_geom_labels, int q) { uint j; geom_link *gl; for(j=0; jdisplayed) continue; if((q && merge_geom_labels || q>=2) && useless_geom(gl,q)) continue; qlog("[geom] geom %s[%s] not displayed due to a bug! please report", gl->cl->name, gl->name); } } static void dump_geom_table(int merge_geom_labels, int q, int nodec, int fmt) { size_t cols[3], sum; uint j, k; geom_link *gl; char tmp[256], tmp2[256], tmp3[256]; qprint_ctx o; bzero(cols, sizeof(cols)); for(j=0; j=2 && useless_geom(gl,q)) continue; if(gl->ver_type==VER_LABEL && merge_geom_labels) continue; qp_init_buf(&o, NULL, sizeof(tmp)-1); print_geom_src(&o, gl); if(cols[0]ver_type) qp_printf(&o, "%s:%s", gl->cl->cat?"ZFS":"GEOM", gl->cl->name); else qp_printf(&o,"%s:%s[%S]", gl->cl->cat?"ZFS":"GEOM", gl->cl->name, gl->name); if(cols[1]nhigh<=MAX_GEOM_DEVICES); if(gl->ver_type==VERZ_ZPOOL) { qp_init_buf(&o, NULL, sizeof(tmp)-1); qp_printf(&o,"ZPOOL:%S",gl->name); if(cols[2]nhigh; k++) { if(k) qp_putc(&o, ' '); dump_geom_dev(&o, gl->high[k], merge_geom_labels, q, gl->ver_type==VER_MD); } if(cols[2]nhigh; k++) { qp_init_buf(&o, NULL, sizeof(tmp)-1); dump_geom_dev(&o, gl->high[k], merge_geom_labels, q, gl->ver_type==VER_MD); if(cols[2]=2 && useless_geom(gl,q)) continue; if(gl->ver_type==VER_LABEL && merge_geom_labels) continue; gl->displayed = 1; qp_init_buf(&o, tmp2, sizeof(tmp2)-1); print_geom_src(&o, gl); tmp2[o.bufpos] = 0; qp_init_buf(&o, tmp3, sizeof(tmp3)-1); if(gl->ver_type) qp_printf(&o, "%s:%s", gl->cl->cat?"ZFS":"GEOM", gl->cl->name); else qp_printf(&o,"%s:%s[%S]", gl->cl->cat?"ZFS":"GEOM", gl->cl->name, gl->name); tmp3[o.bufpos] = 0; qp_printf(&qp_out, nodec?" %*s %*s":" %*s | %*s |", cols[0], tmp2, cols[1], tmp3); assert(gl->nhigh<=MAX_GEOM_DEVICES); if(gl->ver_type==VERZ_ZPOOL) { qp_printf(&qp_out," ZPOOL:%S",gl->name); } else { for(k=0; knhigh; k++) { if(k) { if(fmt==2) qp_printf(&qp_out,nodec?"\n %*c %*c":"\n %*c | %*c |", cols[0], ' ', cols[1], ' '); if(fmt==3) qp_printf(&qp_out,nodec?"\n %*s %*s":"\n %*s | %*s |", cols[0], tmp2, cols[1], tmp3); } qp_putc(&qp_out, ' '); dump_geom_dev(&qp_out, gl->high[k], merge_geom_labels, q, gl->ver_type==VER_MD); } } qp_putc(&qp_out,'\n'); } } /**************************************************************************************************************************/ /********************************************************* WWPNs *********************************************************/ /**************************************************************************************************************************/ static void cam_fill_wwpns(int debug) { uint j; cam_path *p; cam_dev *d; char ctlname[100]; uint64 wwpn; size_t wwpnsize; for(j=0; jdev_name[0]) { if(qp_snprintf(ctlname, sizeof(ctlname), "dev.%s.%d.wwpn", p->dev_name, p->dev_unit)==0) { bzero(&wwpn, wwpnsize = sizeof(wwpn)); if(!(sysctlbyname(ctlname, &wwpn, &wwpnsize, NULL, 0)<0 || !wwpnsize || wwpnsize>8)) { p->wwpn = wwpn; if(debug) qlog("%s = %LLX", ctlname, wwpn); } } if(qp_snprintf(ctlname, sizeof(ctlname), "dev.%s.%d.%%desc", p->dev_name, p->dev_unit)==0) { bzero(p->dev_desc, wwpnsize = sizeof(p->dev_desc)); wwpnsize--; sysctlbyname(ctlname, &p->dev_desc, &wwpnsize, NULL, 0); if(*p->dev_desc && debug) qlog("%s = %\"S", ctlname, p->dev_desc); } } for(j=0; jdev_name[0]) { if(qp_snprintf(ctlname, sizeof(ctlname), "kern.cam.%s.%d.wwpn", d->dev_name, d->dev_unit)<0) continue; bzero(&wwpn, wwpnsize = sizeof(wwpn)); if(sysctlbyname(ctlname, &wwpn, &wwpnsize, NULL, 0)<0 || !wwpnsize || wwpnsize>8) continue; d->remote_wwpn = wwpn; if(debug) qlog("%s = %LLX", ctlname, wwpn); } } /**************************************************************************************************************************/ /********************************************************* MOUNTS *********************************************************/ /**************************************************************************************************************************/ static int get_mounts(int debug) { struct statfs *fs; char const *dev; int i; unsigned int j; if((i = getmntinfo(&fs, MNT_WAIT))<=0) { qlog("[mounts] getmntinfo() error %{ERR}", errno); return -1; } for( ; i; i--, fs++) { if(!(dev = skip_prefix(fs->f_mntfromname,"/dev/"))) continue; for(j=0; j %\"S", dev, fs->f_mntonname); GEOM.devs[j].mountpt = fcl_strdup_check(fs->f_mntonname); break; } } return 0; } /**************************************************************************************************************************/ /********************************************************** MAIN **********************************************************/ /**************************************************************************************************************************/ static int check_jailed(int verbose) { int jailed; size_t sz; jailed = 0; sz = sizeof(jailed); if(sysctlbyname("security.jail.jailed", &jailed, &sz, NULL, 0)<0) { qlog("[jail] read sysctl(security.jail.jailed) error %{ERR}", errno); return -1; } if(jailed && verbose) qlog("[jail] WARNING: running in a jail, a lot of information may be unavailable"); return jailed; } int main(int argc, char **argv) { char const *a; int r, err; size_t rsz; int debug, merge_geom_labels, q, s, fmt, nodec, nocam, zsnaps; debug = 0; merge_geom_labels = 0; q = 0; s = 0; fmt = 1; nodec = 0; nocam = 0; zsnaps = 0; qlog_set_prefix("storage-summary"); qlog_set_times(QLOG_CHECK_ENV); for(err=0,r=1; r