diff options
author | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
---|---|---|
committer | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
commit | 7c478bd95313f5f23a4c958a745db2134aa03244 (patch) | |
tree | c871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/lib/libdiskmgt | |
download | illumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz |
OpenSolaris Launch
Diffstat (limited to 'usr/src/lib/libdiskmgt')
37 files changed, 13044 insertions, 0 deletions
diff --git a/usr/src/lib/libdiskmgt/Makefile b/usr/src/lib/libdiskmgt/Makefile new file mode 100644 index 0000000000..ad9854966a --- /dev/null +++ b/usr/src/lib/libdiskmgt/Makefile @@ -0,0 +1,56 @@ +# +# 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 2002-2003 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.lib + +HDRS = libdiskmgt.h +HDRDIR = common +SUBDIRS = spec .WAIT $(MACH) $(BUILD64) $(MACH64) + +all := TARGET = all +clean := TARGET = clean +clobber := TARGET = clobber +install := TARGET = install +lint := TARGET = lint + +.KEEP_STATE: + +all clean clobber install lint: $(SUBDIRS) + +install_h: + +check: $(CHECKHDRS) + +$(POFILE): + +$(MACH) $(MACH64) spec: FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: + +include ../Makefile.targ diff --git a/usr/src/lib/libdiskmgt/Makefile.com b/usr/src/lib/libdiskmgt/Makefile.com new file mode 100644 index 0000000000..e376e7b5c3 --- /dev/null +++ b/usr/src/lib/libdiskmgt/Makefile.com @@ -0,0 +1,58 @@ +# +# 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 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +LIBRARY = libdiskmgt.a +VERS = .1 +OBJECTS = assoc_types.o \ + entry.o cache.o drive.o controller.o alias.o path.o \ + media.o slice.o partition.o findevs.o events.o \ + bus.o inuse_mnt.o inuse_svm.o inuse_lu.o inuse_fs.o \ + inuse_vxvm.o inuse_dump.o + +include ../../Makefile.lib + +LIBS = $(DYNLIB) $(LINTLIB) +LDLIBS += -ldevinfo -ladm -ldevid -lkstat -lsysevent \ + -lvolmgt -lnvpair -lefi -lc +LDFLAGS += -R/opt/VRTSvxvm/lib +$(LINTLIB) := SRCS = $(SRCDIR)/$(LINTSRC) + +SRCDIR = ../common +MAPDIR = ../spec/$(TRANSMACH) +SPECMAPFILE = $(MAPDIR)/mapfile + +CFLAGS += $(CCVERBOSE) +CPPFLAGS += -D_REENTRANT -I$(SRC)/lib/libdiskmgt/common + +.KEEP_STATE: + +all: $(LIBS) + +lint: lintcheck + +include ../../Makefile.targ diff --git a/usr/src/lib/libdiskmgt/amd64/Makefile b/usr/src/lib/libdiskmgt/amd64/Makefile new file mode 100644 index 0000000000..cb86d986a2 --- /dev/null +++ b/usr/src/lib/libdiskmgt/amd64/Makefile @@ -0,0 +1,37 @@ +# +# 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 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +MAPDIR= ../spec/amd64 +include ../Makefile.com +include ../../Makefile.lib.64 + +.KEEP_STATE: + +all: $(LIBS) + +install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64) diff --git a/usr/src/lib/libdiskmgt/common/alias.c b/usr/src/lib/libdiskmgt/common/alias.c new file mode 100644 index 0000000000..4281db9288 --- /dev/null +++ b/usr/src/lib/libdiskmgt/common/alias.c @@ -0,0 +1,226 @@ +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <fcntl.h> +#include <libdevinfo.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/dkio.h> +#include <sys/sunddi.h> +#include <sys/types.h> +#include <unistd.h> + +#include "libdiskmgt.h" +#include "disks_private.h" + +static int get_status(disk_t *diskp, int fd, nvlist_t *attrs); + +descriptor_t ** +alias_get_assoc_descriptors(descriptor_t *desc, dm_desc_type_t type, + int *errp) +{ + switch (type) { + case DM_DRIVE: + return (drive_get_assocs(desc, errp)); + } + + *errp = EINVAL; + return (NULL); +} + +nvlist_t * +alias_get_attributes(descriptor_t *dp, int *errp) +{ + alias_t *ap; + nvlist_t *attrs = NULL; + + /* Find the alias for this descriptor */ + + *errp = ENODEV; + for (ap = dp->p.disk->aliases; ap != NULL; ap = ap->next) { + if (libdiskmgt_str_eq(dp->name, ap->alias)) { + /* we found the alias for this descriptor */ + + if (nvlist_alloc(&attrs, NVATTRS, 0) != 0) { + *errp = ENOMEM; + return (NULL); + } + + if (ap->target >= 0) { + if (nvlist_add_uint32(attrs, DM_LUN, ap->lun) != 0) { + nvlist_free(attrs); + *errp = ENOMEM; + return (NULL); + } + + if (nvlist_add_uint32(attrs, DM_TARGET, ap->target) != 0) { + nvlist_free(attrs); + *errp = ENOMEM; + return (NULL); + } + } + + if (ap->wwn != NULL) { + if (nvlist_add_string(attrs, DM_WWN, ap->wwn) != 0) { + nvlist_free(attrs); + *errp = ENOMEM; + return (NULL); + } + } + + if (ap->devpaths != NULL) { + /* get the status for this alias */ + int fd; + + fd = open(ap->devpaths->devpath, O_RDONLY|O_NDELAY); + + if ((*errp = get_status(dp->p.disk, fd, attrs)) != 0) { + nvlist_free(attrs); + attrs = NULL; + } + + if (fd >= 0) { + (void) close(fd); + } + } + + *errp = 0; + break; + } + } + + return (attrs); +} + +descriptor_t * +alias_get_descriptor_by_name(char *name, int *errp) +{ + descriptor_t **aliases; + int i; + descriptor_t *alias = NULL; + + aliases = cache_get_descriptors(DM_ALIAS, errp); + if (*errp != 0) { + return (NULL); + } + + for (i = 0; aliases[i]; i++) { + if (libdiskmgt_str_eq(name, aliases[i]->name)) { + alias = aliases[i]; + } else { + /* clean up the unused descriptors */ + cache_free_descriptor(aliases[i]); + } + } + free(aliases); + + if (alias == NULL) { + *errp = ENODEV; + } + + return (alias); +} + +/* ARGSUSED */ +descriptor_t ** +alias_get_descriptors(int filter[], int *errp) +{ + return (cache_get_descriptors(DM_ALIAS, errp)); +} + +char * +alias_get_name(descriptor_t *desc) +{ + return (desc->name); +} + +/* ARGSUSED */ +nvlist_t * +alias_get_stats(descriptor_t *dp, int stat_type, int *errp) +{ + /* There are no stat types defined for aliases */ + *errp = EINVAL; + return (NULL); +} + +int +alias_make_descriptors() +{ + int error; + disk_t *dp; + + dp = cache_get_disklist(); + while (dp != NULL) { + alias_t *ap; + + ap = dp->aliases; + while (ap != NULL) { + if (ap->alias != NULL) { + cache_load_desc(DM_ALIAS, dp, ap->alias, NULL, &error); + if (error != 0) { + return (error); + } + } + ap = ap->next; + } + dp = dp->next; + } + + return (0); +} + +static int +get_status(disk_t *diskp, int fd, nvlist_t *attrs) +{ + struct dk_minfo minfo; + + /* Make sure media is inserted and spun up. */ + if (fd >= 0 && media_read_info(fd, &minfo)) { + + if (nvlist_add_uint32(attrs, DM_STATUS, DM_DISK_UP) != 0) { + return (ENOMEM); + } + + } else { + /* Not ready, so either no media or dead. */ + + if (diskp->removable) { + /* This is a removable drive with no media. */ + if (nvlist_add_uint32(attrs, DM_STATUS, DM_DISK_UP) != 0) { + return (ENOMEM); + } + } else { + /* not removable, so must be dead */ + if (nvlist_add_uint32(attrs, DM_STATUS, DM_DISK_DOWN) != 0) { + return (ENOMEM); + } + } + } + + return (0); +} diff --git a/usr/src/lib/libdiskmgt/common/assoc_types.c b/usr/src/lib/libdiskmgt/common/assoc_types.c new file mode 100644 index 0000000000..2ded240374 --- /dev/null +++ b/usr/src/lib/libdiskmgt/common/assoc_types.c @@ -0,0 +1,80 @@ +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "libdiskmgt.h" + +dm_desc_type_t alias_assoc_types [] = { + DM_DRIVE, + -1 +}; + +dm_desc_type_t bus_assoc_types [] = { + DM_CONTROLLER, + DM_BUS, + -1 +}; + +dm_desc_type_t controller_assoc_types [] = { + DM_DRIVE, + DM_PATH, + DM_BUS, + -1 +}; + +dm_desc_type_t drive_assoc_types [] = { + DM_CONTROLLER, + DM_PATH, + DM_ALIAS, + DM_MEDIA, + -1 +}; + +dm_desc_type_t media_assoc_types [] = { + DM_DRIVE, + DM_PARTITION, + DM_SLICE, + -1 +}; + +dm_desc_type_t partition_assoc_types [] = { + DM_MEDIA, + DM_SLICE, + -1 +}; + +dm_desc_type_t path_assoc_types [] = { + DM_DRIVE, + DM_CONTROLLER, + -1 +}; + +dm_desc_type_t slice_assoc_types [] = { + DM_MEDIA, + DM_PARTITION, + -1 +}; diff --git a/usr/src/lib/libdiskmgt/common/bus.c b/usr/src/lib/libdiskmgt/common/bus.c new file mode 100644 index 0000000000..2b406aa6a6 --- /dev/null +++ b/usr/src/lib/libdiskmgt/common/bus.c @@ -0,0 +1,250 @@ +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <fcntl.h> +#include <libdevinfo.h> +#include <stdlib.h> +#include <sys/sunddi.h> +#include <sys/types.h> +#include <sys/scsi/conf/autoconf.h> + +#include "libdiskmgt.h" +#include "disks_private.h" + +static descriptor_t **get_assoc_buses(descriptor_t *desc, int *errp); +static descriptor_t **get_assoc_controllers(descriptor_t *desc, int *errp); + +descriptor_t ** +bus_get_assoc_descriptors(descriptor_t *desc, dm_desc_type_t type, int *errp) +{ + switch (type) { + case DM_BUS: + return (get_assoc_buses(desc, errp)); + case DM_CONTROLLER: + return (get_assoc_controllers(desc, errp)); + } + + *errp = EINVAL; + return (NULL); +} + +nvlist_t * +bus_get_attributes(descriptor_t *dp, int *errp) +{ + bus_t *bp; + nvlist_t *attrs; + + if (nvlist_alloc(&attrs, NVATTRS, 0) != 0) { + *errp = ENOMEM; + return (NULL); + } + + bp = dp->p.bus; + + if (nvlist_add_string(attrs, DM_BTYPE, bp->btype) != 0) { + nvlist_free(attrs); + *errp = ENOMEM; + return (NULL); + } + + if (bp->freq != 0) { + if (nvlist_add_uint32(attrs, DM_CLOCK, bp->freq) != 0) { + nvlist_free(attrs); + *errp = ENOMEM; + return (NULL); + } + } + + if (bp->pname != NULL) { + if (nvlist_add_string(attrs, DM_PNAME, bp->pname) != 0) { + nvlist_free(attrs); + *errp = ENOMEM; + return (NULL); + } + } + + *errp = 0; + return (attrs); +} + +descriptor_t * +bus_get_descriptor_by_name(char *name, int *errp) +{ + descriptor_t **buses; + int i; + descriptor_t *bus = NULL; + + buses = cache_get_descriptors(DM_BUS, errp); + if (*errp != 0) { + return (NULL); + } + + for (i = 0; buses[i]; i++) { + if (libdiskmgt_str_eq(name, buses[i]->p.bus->name)) { + bus = buses[i]; + } else { + /* clean up the unused descriptors */ + cache_free_descriptor(buses[i]); + } + } + free(buses); + + if (bus == NULL) { + *errp = ENODEV; + } + + return (bus); +} + +/* ARGSUSED */ +descriptor_t ** +bus_get_descriptors(int filter[], int *errp) +{ + return (cache_get_descriptors(DM_BUS, errp)); +} + +char * +bus_get_name(descriptor_t *desc) +{ + return (desc->p.bus->name); +} + +/* ARGSUSED */ +nvlist_t * +bus_get_stats(descriptor_t *dp, int stat_type, int *errp) +{ + /* There are no stat types defined for controllers */ + *errp = EINVAL; + return (NULL); +} + +int +bus_make_descriptors() +{ + int error; + bus_t *bp; + + bp = cache_get_buslist(); + while (bp != NULL) { + cache_load_desc(DM_BUS, bp, NULL, NULL, &error); + if (error != 0) { + return (error); + } + bp = bp->next; + } + + return (0); +} + +static descriptor_t ** +get_assoc_buses(descriptor_t *desc, int *errp) +{ + bus_t *bp; + char *name; + descriptor_t **allbuses; + descriptor_t **buses; + int cnt; + int i; + int pos; + + bp = desc->p.bus; + name = bp->name; + + allbuses = cache_get_descriptors(DM_BUS, errp); + if (*errp != 0) { + return (NULL); + } + + /* Count how many we have (we overcount, but thats ok). */ + for (cnt = 0; allbuses[cnt]; cnt++); + + /* make the snapshot */ + buses = (descriptor_t **)calloc(cnt + 1, sizeof (descriptor_t *)); + if (buses == NULL) { + *errp = ENOMEM; + cache_free_descriptors(allbuses); + return (NULL); + } + + /* + * Get this buses parent bus and get the buses that I am the parent of. + */ + pos = 0; + for (i = 0; allbuses[i]; i++) { + if (libdiskmgt_str_eq(name, allbuses[i]->p.bus->pname)) { + buses[pos++] = allbuses[i]; + } else if (bp->pname != NULL && + libdiskmgt_str_eq(bp->pname, allbuses[i]->p.bus->name)) { + + buses[pos++] = allbuses[i]; + } else { + /* clean up the unused descriptor */ + cache_free_descriptor(allbuses[i]); + } + } + buses[pos] = NULL; + + free(allbuses); + + *errp = 0; + return (buses); +} + +static descriptor_t ** +get_assoc_controllers(descriptor_t *desc, int *errp) +{ + bus_t *bp; + descriptor_t **controllers; + int cnt; + int i; + + bp = desc->p.bus; + + /* Count how many we have. */ + for (cnt = 0; bp->controllers[cnt]; cnt++); + + /* make the snapshot */ + controllers = (descriptor_t **)calloc(cnt + 1, sizeof (descriptor_t *)); + if (controllers == NULL) { + *errp = ENOMEM; + return (NULL); + } + + for (i = 0; bp->controllers[i]; i++) { + controllers[i] = cache_get_desc(DM_CONTROLLER, bp->controllers[i], + NULL, NULL, errp); + if (*errp != 0) { + cache_free_descriptors(controllers); + return (NULL); + } + } + controllers[i] = NULL; + + *errp = 0; + return (controllers); +} diff --git a/usr/src/lib/libdiskmgt/common/cache.c b/usr/src/lib/libdiskmgt/common/cache.c new file mode 100644 index 0000000000..411d6c41b7 --- /dev/null +++ b/usr/src/lib/libdiskmgt/common/cache.c @@ -0,0 +1,991 @@ +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <fcntl.h> +#include <libdevinfo.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <synch.h> +#include <sys/sunddi.h> +#include <sys/types.h> +#include <libgen.h> + +#include "libdiskmgt.h" +#include "disks_private.h" +#include "partition.h" + +#define ALIASES 0 +#define DEVPATHS 1 + +/* + * Set DM_LIBDISKMGT_DEBUG in the environment. Two levels of debugging: + * 1 - errors, warnings and minimal tracing information + * 2 - verbose information + * All output prints on stderr. + */ +int dm_debug = 0; + +/* Lock protecting the cached data */ +static rwlock_t cache_lock = DEFAULTRWLOCK; +static disk_t *disk_listp = NULL; +static controller_t *controller_listp = NULL; +static bus_t *bus_listp = NULL; +static int cache_loaded = 0; + +descriptor_t *desc_listp = NULL; + +static void clear_descriptors(void *gp); +static void clr_ctrl_disk_ptr(controller_t *cp, disk_t *dp); +static void clr_path_disk_ptr(path_t *pp, disk_t *dp); +static void del_drive(disk_t *dp); +static void del_drive_by_name(char *name); +static descriptor_t *have_desc(int type, void *gp, char *name, char *mname); +static int initialize(); +static int make_descriptors(int type); +static int match_disk(disk_t *oldp, disk_t *newp); +static int match_aliases(disk_t *d1p, disk_t *d2p); +static int match_alias(alias_t *ap, alias_t *listp); +static descriptor_t *new_descriptor(dm_desc_type_t type, void *op, + char *name, char *mname); +static void rewalk_tree(); +static void update_desc(descriptor_t *descp, disk_t *newdisksp, + controller_t *newctrlp, bus_t *newbusp); +static void update_desc_busp(descriptor_t *descp, bus_t *busp); +static void update_desc_ctrlp(descriptor_t *descp, + controller_t *newstrlp); +static void update_desc_diskp(descriptor_t *descp, + disk_t *newdisksp); +static void update_desc_pathp(descriptor_t *descp, + controller_t *newctrlp); + +/* + * We only cache some of the data that we can obtain. For much of the data + * (e.g. slices & disks getting repartitioned) there are no events which would + * enable us to cache. As more events are added we can cache more information. + * + * Currently we cache the information we get from the dev tree walk. This is + * basically the information about the drives, aliases, devpaths, controllers + * and paths. We do not cache any information related to media, partitions + * or slices. + * + * A fundamental part of the API design is that the application can hold on + * to a set of descriptors for an indeterminate amount of time. Even if the + * application does not hold descriptors there is a window of time between the + * call that gets the descriptor and the use of the descriptor to get more + * information. Because of this, the cache design must work even if the object + * that the descriptor refers to no longer exists. + * + * Given this requirement, the code implements a two level cache. The + * descriptors that the application gets are really pointers into the first + * level of the cache. This first level contains the actual descriptors. + * These descriptors in turn refer to the objects we build from the dev tree + * walk which represent the drives and controllers. This is the second level + * in the cache. + * + * When we update the second level of the cache (the drives and controllers) + * we go through the first level (the descriptors) and update the pointers + * in those descriptors to refer to the new objects in the second level. If + * the object that the descriptor referred to is no longer in existence, we + * just null out the pointer in the descriptor. In this way the code that + * uses the descriptors knows that the object referred to by the descriptor + * no longer exists. + * + * We keep a reference count in the descriptors. This is incremented when + * we hand out a pointer to the descriptor and decremented when the application + * frees the descriptor it has. When the reference count goes to 0 we garbage + * collect the descriptors. In this way we only have to update active + * descriptors when we refresh the cache after an event. + * + * An example of the flow when we create descriptors: + * dm_get_descriptors libdiskmgt.c + * drive_get_descriptors drive.c + * cache_get_descriptors cache.c + * make_descriptors cache.c + * drive_make_descriptors drive.c + * cache_load_desc cache.c + * {update refcnts on descriptors & return them} + * + * The idea behind cache_get_descriptors and cache_load_desc is that we + * seperate the act of making the descriptor within the cache (which requires + * us to call back out to one of the object functions - drive_make_descriptors) + * from the act of handing out the descriptor (which requires us to increment + * the refcnt). In this way we keep all of the refcnt handling centralized + * in one function instead of forcing each object to ensure it replicates + * the refcnt handling correctly. + * + * Descriptors use two different kinds of indrection to refer to their + * corresponding object. For objects we cache (controllers, paths & drives) + * the descriptor keeps a pointer to that object. For objects that we + * dynamically build, the descriptor uses a combination of a pointer to the + * base object (usually the drive) along with a name (e.g. the media name or + * the alias). For objects that are based on media (e.g. a slice) we actually + * have to maintain a pointer (to the disk) and two names (e.g. the slice name + * and the media name which is the secondary name). + */ + +void +cache_free_alias(alias_t *aliasp) +{ + slice_t *dp; + + free(aliasp->alias); + free(aliasp->kstat_name); + free(aliasp->wwn); + + /* free devpaths */ + dp = aliasp->devpaths; + while (dp != NULL) { + slice_t *nextp; + + nextp = dp->next; + free(dp->devpath); + free(dp); + dp = nextp; + } + + /* free orig_paths */ + dp = aliasp->orig_paths; + while (dp != NULL) { + slice_t *nextp; + + nextp = dp->next; + free(dp->devpath); + free(dp); + dp = nextp; + } + + free(aliasp); +} + +void +cache_free_bus(bus_t *bp) +{ + free(bp->name); + free(bp->btype); + free(bp->kstat_name); + free(bp->pname); + free(bp->controllers); + free(bp); +} + +void +cache_free_controller(controller_t *cp) +{ + free(cp->name); + free(cp->kstat_name); + free(cp->disks); + if (cp->paths != NULL) { + int i; + + for (i = 0; cp->paths[i]; i++) { + /* free the path since it can't exist w/o the controller */ + cache_free_path(cp->paths[i]); + } + free(cp->paths); + } + + free(cp); +} + +void +cache_free_descriptor(descriptor_t *desc) +{ + if (!cache_is_valid_desc(desc)) { + return; + } + + desc->refcnt--; + + if (desc->refcnt <= 0) { + free(desc->name); + free(desc->secondary_name); + if (desc->prev == NULL) { + /* this is the first descriptor, update head ptr */ + desc_listp = desc->next; + } else { + desc->prev->next = desc->next; + } + if (desc->next != NULL) { + desc->next->prev = desc->prev; + } + free(desc); + } +} + +void +cache_free_descriptors(descriptor_t **desc_list) +{ + int i; + + for (i = 0; desc_list[i]; i++) { + cache_free_descriptor(desc_list[i]); + } + + free(desc_list); +} + +void +cache_free_disk(disk_t *dp) +{ + alias_t *ap; + + free(dp->device_id); + if (dp->devid != NULL) { + devid_free(dp->devid); + } + free(dp->kernel_name); + free(dp->product_id); + free(dp->vendor_id); + free(dp->controllers); + /* the path objects are freed when we free the controller */ + free(dp->paths); + ap = dp->aliases; + while (ap != NULL) { + alias_t *nextp; + + nextp = ap->next; + cache_free_alias(ap); + ap = nextp; + } + + free(dp); +} + +void +cache_free_path(path_t *pp) +{ + int i; + + free(pp->name); + free(pp->disks); + free(pp->states); + + for (i = 0; pp->wwns[i]; i++) { + free(pp->wwns[i]); + } + free(pp->wwns); + + free(pp); +} + +bus_t * +cache_get_buslist() +{ + if (initialize() != 0) { + return (NULL); + } + + return (bus_listp); +} + +controller_t * +cache_get_controllerlist() +{ + if (initialize() != 0) { + return (NULL); + } + + return (controller_listp); +} + +/* + * This routine will either get the existing descriptor from the descriptor + * cache or make make a new descriptor and put it in the descriptor cache and + * return a pointer to that descriptor. We increment the refcnt when we hand + * out the descriptor. + */ +descriptor_t * +cache_get_desc(int type, void *gp, char *name, char *secondary_name, int *errp) +{ + descriptor_t *dp; + + *errp = 0; + if ((dp = have_desc(type, gp, name, secondary_name)) == NULL) { + /* make a new desc */ + if ((dp = new_descriptor(type, gp, name, secondary_name)) == NULL) { + *errp = ENOMEM; + } + } + + if (dp != NULL) { + dp->refcnt++; + } + + return (dp); +} + +descriptor_t ** +cache_get_descriptors(int type, int *errp) +{ + descriptor_t **descs; + descriptor_t *descp; + int cnt = 0; + int pos; + + if ((*errp = make_descriptors(type)) != 0) { + return (NULL); + } + + /* count the number of active descriptors in the descriptor cache */ + descp = desc_listp; + while (descp != NULL) { + if (descp->type == type && descp->p.generic != NULL) { + cnt++; + } + descp = descp->next; + } + + descs = (descriptor_t **)calloc(cnt + 1, sizeof (descriptor_t *)); + if (descs == NULL) { + *errp = ENOMEM; + return (NULL); + } + + pos = 0; + descp = desc_listp; + while (descp != NULL) { + if (descp->type == type && descp->p.generic != NULL) { + /* update refcnts before handing out the descriptors */ + descp->refcnt++; + descs[pos++] = descp; + } + descp = descp->next; + } + descs[pos] = NULL; + + *errp = 0; + return (descs); +} + +disk_t * +cache_get_disklist() +{ + if (initialize() != 0) { + return (NULL); + } + + return (disk_listp); +} + +int +cache_is_valid_desc(descriptor_t *d) +{ + descriptor_t *descp; + + for (descp = desc_listp; descp != NULL; descp = descp->next) { + if (descp == d) { + return (1); + } + } + + return (0); +} + +/* + * This function is called by the *_make_descriptors function + * (e.g. drive_make_descriptors) within each of the objects. This function + * makes sure that the descriptor is built in the descriptor cache but + * it does not hand out the descriptors, so the refcnt is never incremented. + */ +void +cache_load_desc(int type, void *gp, char *name, char *secondary_name, int *errp) +{ + *errp = 0; + if (have_desc(type, gp, name, secondary_name) == NULL) { + /* make a new desc */ + if (new_descriptor(type, gp, name, secondary_name) == NULL) { + *errp = ENOMEM; + } + } +} + +void +cache_rlock() +{ + (void) rw_rdlock(&cache_lock); +} + +void +cache_unlock() +{ + (void) rw_unlock(&cache_lock); +} + +/* + * This function is called when we get a devtree event. Type is either add + * or delete of a drive. + * + * For delete, we need to clean up the 2nd level structures and clean up + * the pointers between the them. We also clear the descriptor ptr. + */ +void +cache_update(dm_event_type_t ev_type, char *devname) +{ + char *orig_name; + + cache_wlock(); + + /* update the cache */ + switch (ev_type) { + case DM_EV_DISK_ADD: + rewalk_tree(); + events_new_event(devname, DM_DRIVE, DM_EV_TADD); + break; + case DM_EV_DISK_DELETE: + orig_name = devname; + devname = basename(devname); + del_drive_by_name(devname); + events_new_event(orig_name, DM_DRIVE, DM_EV_TREMOVE); + break; + } + + cache_unlock(); +} + +void +cache_wlock() +{ + (void) rw_wrlock(&cache_lock); +} + +/* + * Clear any descriptors that point at the specified cached object. + * We must go through the whole list since there can be multiple descriptors + * referencing the same object (i.e. drive/media/slice descriptors all point + * to the same drive object). The list is usually small (0 size) so this + * is not a big deal. + */ +static void +clear_descriptors(void *gp) +{ + descriptor_t *descp; + + for (descp = desc_listp; descp != NULL; descp = descp->next) { + if (descp->p.generic == gp) { + /* clear descriptor */ + descp->p.generic = NULL; + } + } +} + +/* remove the ptr from the controller to the specified disk */ +static void +clr_ctrl_disk_ptr(controller_t *cp, disk_t *dp) +{ + int i; + + for (i = 0; cp->disks[i]; i++) { + if (dp == cp->disks[i]) { + int j; + + for (j = i; cp->disks[j]; j++) { + cp->disks[j] = cp->disks[j + 1]; + } + return; + } + } +} + +/* remove the ptr from the path to the specified disk */ +static void +clr_path_disk_ptr(path_t *pp, disk_t *dp) +{ + int i; + + for (i = 0; pp->disks[i]; i++) { + if (dp == pp->disks[i]) { + int j; + + for (j = i; pp->disks[j]; j++) { + pp->disks[j] = pp->disks[j + 1]; + } + return; + } + } +} + +static void +del_drive(disk_t *dp) +{ + int i; + disk_t *listp; + disk_t *prev = NULL; + + clear_descriptors(dp); + + /* clear any ptrs from controllers to this drive */ + if (dp->controllers != NULL) { + for (i = 0; dp->controllers[i]; i++) { + clr_ctrl_disk_ptr(dp->controllers[i], dp); + } + } + + /* clear any ptrs from paths to this drive */ + if (dp->paths != NULL) { + for (i = 0; dp->paths[i]; i++) { + clr_path_disk_ptr(dp->paths[i], dp); + } + } + + /* clear drive from disk list */ + for (listp = disk_listp; listp != NULL; listp = listp->next) { + if (dp == listp) { + if (prev == NULL) { + disk_listp = dp->next; + } else { + prev->next = dp->next; + } + + break; + } + + if (prev == NULL) { + prev = disk_listp; + } else { + prev = prev->next; + } + } + + cache_free_disk(dp); +} + +/* + * Delete cached drive info when we get a devtree drive delete event. + */ +static void +del_drive_by_name(char *name) +{ + disk_t *listp; + + for (listp = disk_listp; listp != NULL; listp = listp->next) { + alias_t *ap; + + for (ap = listp->aliases; ap; ap = ap->next) { + if (libdiskmgt_str_eq(name, ap->alias)) { + del_drive(listp); + return; + } + } + } +} + +static descriptor_t * +have_desc(int type, void *gp, char *name, char *secondary_name) +{ + descriptor_t *descp; + + if (name != NULL && name[0] == 0) { + name = NULL; + } + + if (secondary_name != NULL && secondary_name[0] == 0) { + secondary_name = NULL; + } + + descp = desc_listp; + while (descp != NULL) { + if (descp->type == type && descp->p.generic == gp && + libdiskmgt_str_eq(descp->name, name)) { + if (type == DM_SLICE || type == DM_PARTITION || + type == DM_PATH) { + if (libdiskmgt_str_eq(descp->secondary_name, + secondary_name)) { + return (descp); + } + } else { + return (descp); + } + } + descp = descp->next; + } + + return (NULL); +} + +static int +initialize() +{ + struct search_args args; + int status; + + if (cache_loaded) { + return (0); + } + + libdiskmgt_init_debug(); + + findevs(&args); + + if (args.dev_walk_status != 0) { + return (args.dev_walk_status); + } + + disk_listp = args.disk_listp; + controller_listp = args.controller_listp; + bus_listp = args.bus_listp; + + cache_loaded = 1; + + if ((status = events_start_event_watcher()) != 0) { + return (status); + } + + return (0); +} + +static int +make_descriptors(int type) +{ + int error; + + if ((error = initialize()) != 0) { + return (error); + } + + switch (type) { + case DM_DRIVE: + error = drive_make_descriptors(); + break; + case DM_BUS: + error = bus_make_descriptors(); + break; + case DM_CONTROLLER: + error = controller_make_descriptors(); + break; + case DM_PATH: + error = path_make_descriptors(); + break; + case DM_ALIAS: + error = alias_make_descriptors(); + break; + case DM_MEDIA: + error = media_make_descriptors(); + break; + case DM_PARTITION: + error = partition_make_descriptors(); + break; + case DM_SLICE: + error = slice_make_descriptors(); + break; + } + + return (error); +} + +static int +match_alias(alias_t *ap, alias_t *listp) +{ + if (ap->alias == NULL) { + return (0); + } + + while (listp != NULL) { + if (libdiskmgt_str_eq(ap->alias, listp->alias)) { + return (1); + } + listp = listp->next; + } + + return (0); +} + +static int +match_aliases(disk_t *d1p, disk_t *d2p) +{ + alias_t *ap; + + if (d1p->aliases == NULL || d2p->aliases == NULL) { + return (0); + } + + ap = d1p->aliases; + while (ap != NULL) { + if (match_alias(ap, d2p->aliases)) { + return (1); + } + ap = ap->next; + } + + return (0); +} + +static int +match_disk(disk_t *oldp, disk_t *newp) +{ + if (oldp->devid != NULL) { + if (newp->devid != NULL && + devid_compare(oldp->devid, newp->devid) == 0) { + return (1); + } + + } else { + /* oldp device id is null */ + if (newp->devid == NULL) { + /* both disks have no device id, check aliases */ + if (match_aliases(oldp, newp)) { + return (1); + } + } + } + + return (0); +} + +static descriptor_t * +new_descriptor(dm_desc_type_t type, void *op, char *name, char *secondary_name) +{ + descriptor_t *d; + + if (name != NULL && name[0] == 0) { + name = NULL; + } + + if (secondary_name != NULL && secondary_name[0] == 0) { + secondary_name = NULL; + } + + d = (descriptor_t *)malloc(sizeof (descriptor_t)); + if (d == NULL) { + return (NULL); + } + d->type = type; + switch (type) { + case DM_CONTROLLER: + d->p.controller = op; + break; + case DM_BUS: + d->p.bus = op; + break; + default: + d->p.disk = op; + break; + } + if (name != NULL) { + d->name = strdup(name); + if (d->name == NULL) { + free(d); + return (NULL); + } + } else { + d->name = NULL; + } + + if (type == DM_SLICE || type == DM_PARTITION) { + if (secondary_name != NULL) { + d->secondary_name = strdup(secondary_name); + if (d->secondary_name == NULL) { + free(d->name); + free(d); + return (NULL); + } + } else { + d->secondary_name = NULL; + } + } else { + d->secondary_name = NULL; + } + + d->refcnt = 0; + + /* add this descriptor to the head of the list */ + if (desc_listp != NULL) { + desc_listp->prev = d; + } + d->prev = NULL; + d->next = desc_listp; + desc_listp = d; + + return (d); +} + +static void +rewalk_tree() +{ + struct search_args args; + disk_t *free_disklistp; + controller_t *free_controllerlistp; + bus_t *free_buslistp; + + findevs(&args); + + if (args.dev_walk_status == 0) { + descriptor_t *descp; + + /* walk the existing descriptors and update the ptrs */ + descp = desc_listp; + while (descp != NULL) { + update_desc(descp, args.disk_listp, args.controller_listp, + args.bus_listp); + descp = descp->next; + } + + /* update the cached object ptrs */ + free_disklistp = disk_listp; + free_controllerlistp = controller_listp; + free_buslistp = bus_listp; + disk_listp = args.disk_listp; + controller_listp = args.controller_listp; + bus_listp = args.bus_listp; + + } else { + free_disklistp = args.disk_listp; + free_controllerlistp = args.controller_listp; + free_buslistp = args.bus_listp; + } + + /* + * Free the memory from either the old cached objects or the failed + * update objects. + */ + while (free_disklistp != NULL) { + disk_t *nextp; + + nextp = free_disklistp->next; + cache_free_disk(free_disklistp); + free_disklistp = nextp; + } + while (free_controllerlistp != NULL) { + controller_t *nextp; + + nextp = free_controllerlistp->next; + cache_free_controller(free_controllerlistp); + free_controllerlistp = nextp; + } + while (free_buslistp != NULL) { + bus_t *nextp; + + nextp = free_buslistp->next; + cache_free_bus(free_buslistp); + free_buslistp = nextp; + } +} + +/* + * Walk the new set of cached objects and update the descriptor ptr to point + * to the correct new object. If there is no object any more, set the desc + * ptr to null. + */ +static void +update_desc(descriptor_t *descp, disk_t *newdisksp, controller_t *newctrlp, + bus_t *newbusp) +{ + /* if the descriptor is already dead, we're done */ + if (descp->p.generic == NULL) { + return; + } + + /* + * All descriptors use a disk ptr except for controller descriptors + * and path descriptors. + */ + + switch (descp->type) { + case DM_BUS: + update_desc_busp(descp, newbusp); + break; + case DM_CONTROLLER: + update_desc_ctrlp(descp, newctrlp); + break; + case DM_PATH: + update_desc_pathp(descp, newctrlp); + break; + default: + update_desc_diskp(descp, newdisksp); + break; + } +} + +static void +update_desc_busp(descriptor_t *descp, bus_t *busp) +{ + /* walk the new objects and find the correct bus */ + for (; busp; busp = busp->next) { + if (libdiskmgt_str_eq(descp->p.bus->name, busp->name)) { + descp->p.bus = busp; + return; + } + } + + /* we did not find the controller any more, clear the ptr in the desc */ + descp->p.bus = NULL; +} + +static void +update_desc_ctrlp(descriptor_t *descp, controller_t *newctrlp) +{ + /* walk the new objects and find the correct controller */ + for (; newctrlp; newctrlp = newctrlp->next) { + if (libdiskmgt_str_eq(descp->p.controller->name, newctrlp->name)) { + descp->p.controller = newctrlp; + return; + } + } + + /* we did not find the controller any more, clear the ptr in the desc */ + descp->p.controller = NULL; +} + +static void +update_desc_diskp(descriptor_t *descp, disk_t *newdisksp) +{ + /* walk the new objects and find the correct disk */ + for (; newdisksp; newdisksp = newdisksp->next) { + if (match_disk(descp->p.disk, newdisksp)) { + descp->p.disk = newdisksp; + return; + } + } + + /* we did not find the disk any more, clear the ptr in the descriptor */ + descp->p.disk = NULL; +} + +static void +update_desc_pathp(descriptor_t *descp, controller_t *newctrlp) +{ + /* walk the new objects and find the correct path */ + for (; newctrlp; newctrlp = newctrlp->next) { + path_t **pp; + + pp = newctrlp->paths; + if (pp != NULL) { + int i; + + for (i = 0; pp[i]; i++) { + if (libdiskmgt_str_eq(descp->p.path->name, pp[i]->name)) { + descp->p.path = pp[i]; + return; + } + } + } + } + + /* we did not find the path any more, clear the ptr in the desc */ + descp->p.path = NULL; +} diff --git a/usr/src/lib/libdiskmgt/common/controller.c b/usr/src/lib/libdiskmgt/common/controller.c new file mode 100644 index 0000000000..bde953f724 --- /dev/null +++ b/usr/src/lib/libdiskmgt/common/controller.c @@ -0,0 +1,309 @@ +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <fcntl.h> +#include <libdevinfo.h> +#include <stdlib.h> +#include <sys/sunddi.h> +#include <sys/types.h> +#include <sys/scsi/conf/autoconf.h> + +#include "libdiskmgt.h" +#include "disks_private.h" + +static descriptor_t **get_assoc_buses(descriptor_t *desc, int *errp); +static descriptor_t **get_assoc_drives(descriptor_t *desc, int *errp); +static descriptor_t **get_assoc_paths(descriptor_t *desc, int *errp); + +descriptor_t ** +controller_get_assoc_descriptors(descriptor_t *desc, dm_desc_type_t type, + int *errp) +{ + switch (type) { + case DM_DRIVE: + return (get_assoc_drives(desc, errp)); + case DM_PATH: + return (get_assoc_paths(desc, errp)); + case DM_BUS: + return (get_assoc_buses(desc, errp)); + } + + *errp = EINVAL; + return (NULL); +} + +nvlist_t * +controller_get_attributes(descriptor_t *dp, int *errp) +{ + controller_t *cp; + nvlist_t *attrs; + + if (nvlist_alloc(&attrs, NVATTRS, 0) != 0) { + *errp = ENOMEM; + return (NULL); + } + + cp = dp->p.controller; + + if (nvlist_add_string(attrs, DM_CTYPE, cp->ctype) != 0) { + nvlist_free(attrs); + *errp = ENOMEM; + return (NULL); + } + + if (cp->multiplex) { + if (nvlist_add_boolean(attrs, DM_MULTIPLEX) != 0) { + nvlist_free(attrs); + *errp = ENOMEM; + return (NULL); + } + } + + if (cp->scsi_options != -1) { + if (cp->scsi_options & SCSI_OPTIONS_FAST) { + if (nvlist_add_boolean(attrs, DM_FAST) != 0) { + nvlist_free(attrs); + *errp = ENOMEM; + return (NULL); + } + } + if (cp->scsi_options & SCSI_OPTIONS_WIDE) { + if (nvlist_add_boolean(attrs, DM_WIDE) != 0) { + nvlist_free(attrs); + *errp = ENOMEM; + return (NULL); + } + } + if (cp->scsi_options & SCSI_OPTIONS_FAST20) { + if (nvlist_add_boolean(attrs, DM_FAST20) != 0) { + nvlist_free(attrs); + *errp = ENOMEM; + return (NULL); + } + } + if (cp->scsi_options & SCSI_OPTIONS_FAST40) { + if (nvlist_add_boolean(attrs, DM_FAST40) != 0) { + nvlist_free(attrs); + *errp = ENOMEM; + return (NULL); + } + } + if (cp->scsi_options & SCSI_OPTIONS_FAST80) { + if (nvlist_add_boolean(attrs, DM_FAST80) != 0) { + nvlist_free(attrs); + *errp = ENOMEM; + return (NULL); + } + } + } + + if (cp->freq != 0) { + if (nvlist_add_uint32(attrs, DM_CLOCK, cp->freq) != 0) { + nvlist_free(attrs); + *errp = ENOMEM; + return (NULL); + } + } + + *errp = 0; + return (attrs); +} + +descriptor_t * +controller_get_descriptor_by_name(char *name, int *errp) +{ + descriptor_t **controllers; + int i; + descriptor_t *controller = NULL; + + controllers = cache_get_descriptors(DM_CONTROLLER, errp); + if (*errp != 0) { + return (NULL); + } + + for (i = 0; controllers[i]; i++) { + if (libdiskmgt_str_eq(name, controllers[i]->p.controller->name)) { + controller = controllers[i]; + } else { + /* clean up the unused descriptors */ + cache_free_descriptor(controllers[i]); + } + } + free(controllers); + + if (controller == NULL) { + *errp = ENODEV; + } + + return (controller); +} + +/* ARGSUSED */ +descriptor_t ** +controller_get_descriptors(int filter[], int *errp) +{ + return (cache_get_descriptors(DM_CONTROLLER, errp)); +} + +char * +controller_get_name(descriptor_t *desc) +{ + return (desc->p.controller->name); +} + +/* ARGSUSED */ +nvlist_t * +controller_get_stats(descriptor_t *dp, int stat_type, int *errp) +{ + /* There are no stat types defined for controllers */ + *errp = EINVAL; + return (NULL); +} + +int +controller_make_descriptors() +{ + int error; + controller_t *cp; + + cp = cache_get_controllerlist(); + while (cp != NULL) { + cache_load_desc(DM_CONTROLLER, cp, NULL, NULL, &error); + if (error != 0) { + return (error); + } + cp = cp->next; + } + + return (0); +} + +static descriptor_t ** +get_assoc_buses(descriptor_t *desc, int *errp) +{ + controller_t *cp; + descriptor_t **buses; + int pos = 0; + + cp = desc->p.controller; + + /* make the snapshot */ + buses = (descriptor_t **)calloc(2, sizeof (descriptor_t *)); + if (buses == NULL) { + *errp = ENOMEM; + return (NULL); + } + + if (cp->bus != NULL) { + buses[pos++] = cache_get_desc(DM_BUS, cp->bus, NULL, NULL, errp); + if (*errp != 0) { + cache_free_descriptors(buses); + return (NULL); + } + } + buses[pos] = NULL; + + *errp = 0; + return (buses); +} + +static descriptor_t ** +get_assoc_drives(descriptor_t *desc, int *errp) +{ + controller_t *cp; + descriptor_t **drives; + int cnt; + int i; + + cp = desc->p.controller; + + /* Count how many we have. */ + for (cnt = 0; cp->disks[cnt]; cnt++); + + /* make the snapshot */ + drives = (descriptor_t **)calloc(cnt + 1, sizeof (descriptor_t *)); + if (drives == NULL) { + *errp = ENOMEM; + return (NULL); + } + + for (i = 0; cp->disks[i]; i++) { + drives[i] = cache_get_desc(DM_DRIVE, cp->disks[i], NULL, NULL, + errp); + if (*errp != 0) { + cache_free_descriptors(drives); + return (NULL); + } + } + drives[i] = NULL; + + *errp = 0; + return (drives); +} + +static descriptor_t ** +get_assoc_paths(descriptor_t *desc, int *errp) +{ + path_t **pp; + int cnt; + descriptor_t **paths; + int i; + + pp = desc->p.controller->paths; + + /* Count how many we have. */ + cnt = 0; + if (pp != NULL) { + for (; pp[cnt]; cnt++); + } + + /* make the snapshot */ + paths = (descriptor_t **)calloc(cnt + 1, sizeof (descriptor_t *)); + if (paths == NULL) { + *errp = ENOMEM; + return (NULL); + } + + /* + * The name field of the descriptor is not filled in. Thus, we + * know not to try to lookup drive-path state information within + * the path code if we try to get attributes for this descriptor. + */ + for (i = 0; i < cnt; i++) { + paths[i] = cache_get_desc(DM_PATH, pp[i], NULL, NULL, errp); + if (*errp != 0) { + cache_free_descriptors(paths); + return (NULL); + } + } + + paths[i] = NULL; + + *errp = 0; + return (paths); +} diff --git a/usr/src/lib/libdiskmgt/common/disks_private.h b/usr/src/lib/libdiskmgt/common/disks_private.h new file mode 100644 index 0000000000..3239925853 --- /dev/null +++ b/usr/src/lib/libdiskmgt/common/disks_private.h @@ -0,0 +1,273 @@ +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _DISKS_PRIVATE_H +#define _DISKS_PRIVATE_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include <libdevinfo.h> +#include <sys/dkio.h> +#include <devid.h> + +#define DM_DEBUG "DM_LIBDISKMGT_DEBUG" +extern int dm_debug; + +#define NVATTRS NV_UNIQUE_NAME | NV_UNIQUE_NAME_TYPE +#define NVATTRS_STAT 0x0 + +typedef struct slice_info { + char *devpath; + int slice_num; + struct slice_info *next; +} slice_t; + +typedef struct alias_info { + char *kstat_name; + char *alias; + slice_t *devpaths; + slice_t *orig_paths; + char *wwn; + int cluster; + int lun; + int target; + struct alias_info *next; +} alias_t; + +typedef struct path { + char *name; + char *ctype; + int *states; + char **wwns; + struct disk **disks; + struct controller_info *controller; + struct path *next; +} path_t; + +typedef struct bus_info { + char *name; + char *kstat_name; + char *btype; + char *pname; + int freq; + struct controller_info **controllers; + struct bus_info *next; +} bus_t; + +typedef struct controller_info { + char *name; + char *kstat_name; + char *ctype; + int freq; + struct disk **disks; + struct path **paths; + struct bus_info *bus; + struct controller_info *next; + int multiplex; + int scsi_options; +} controller_t; + +typedef struct disk { + char *device_id; /* string encoded device id */ + ddi_devid_t devid; /* decoded device id */ + char *kernel_name; /* handles drives w/ no devlinks */ + char *volm_path; + char *product_id; + char *vendor_id; + controller_t **controllers; + path_t **paths; + alias_t *aliases; + struct disk *next; + int drv_type; + int removable; + int sync_speed; + int rpm; + int wide; + int cd_rom; + int volm_path_set; +} disk_t; + +typedef struct descriptor { + union { + void *generic; + disk_t *disk; + controller_t *controller; + bus_t *bus; + path_t *path; + } p; + char *name; + char *secondary_name; + struct descriptor *next; + struct descriptor *prev; + dm_desc_type_t type; + int refcnt; +} descriptor_t; + +struct search_args { + disk_t *disk_listp; + controller_t *controller_listp; + bus_t *bus_listp; + di_devlink_handle_t handle; + di_prom_handle_t ph; + di_node_t node; + di_minor_t minor; + int dev_walk_status; +}; + +typedef enum { + DM_EV_DISK_ADD = 0, + DM_EV_DISK_DELETE +} dm_event_type_t; + + +/* private internal functions */ +descriptor_t **alias_get_descriptors(int filter[], int *errp); +descriptor_t **alias_get_assoc_descriptors(descriptor_t *desc, + dm_desc_type_t type, int *errp); +descriptor_t *alias_get_descriptor_by_name(char *name, int *errp); +char *alias_get_name(descriptor_t *desc); +nvlist_t *alias_get_attributes(descriptor_t *desc, int *errp); +nvlist_t *alias_get_stats(descriptor_t *desc, int stat_type, int *errp); +int alias_make_descriptors(); + +descriptor_t **bus_get_descriptors(int filter[], int *errp); +descriptor_t **bus_get_assoc_descriptors(descriptor_t *desc, + dm_desc_type_t type, int *errp); +descriptor_t *bus_get_descriptor_by_name(char *name, int *errp); +char *bus_get_name(descriptor_t *desc); +nvlist_t *bus_get_attributes(descriptor_t *desc, int *errp); +nvlist_t *bus_get_stats(descriptor_t *desc, int stat_type, + int *errp); +int bus_make_descriptors(); + +descriptor_t **controller_get_descriptors(int filter[], int *errp); +descriptor_t **controller_get_assoc_descriptors(descriptor_t *desc, + dm_desc_type_t type, int *errp); +descriptor_t *controller_get_descriptor_by_name(char *name, int *errp); +char *controller_get_name(descriptor_t *desc); +nvlist_t *controller_get_attributes(descriptor_t *desc, int *errp); +nvlist_t *controller_get_stats(descriptor_t *desc, int stat_type, + int *errp); +int controller_make_descriptors(); + +descriptor_t **drive_get_descriptors(int filter[], int *errp); +descriptor_t **drive_get_assoc_descriptors(descriptor_t *desc, + dm_desc_type_t type, int *errp); +descriptor_t **drive_get_assocs(descriptor_t *desc, int *errp); +descriptor_t *drive_get_descriptor_by_name(char *name, int *errp); +char *drive_get_name(descriptor_t *desc); +nvlist_t *drive_get_attributes(descriptor_t *desc, int *errp); +nvlist_t *drive_get_stats(descriptor_t *desc, int stat_type, int *errp); +int drive_make_descriptors(); +int drive_open_disk(disk_t *diskp, char *opath, int len); + +descriptor_t **media_get_descriptors(int filter[], int *errp); +descriptor_t **media_get_assoc_descriptors(descriptor_t *desc, + dm_desc_type_t type, int *errp); +descriptor_t **media_get_assocs(descriptor_t *desc, int *errp); +descriptor_t *media_get_descriptor_by_name(char *name, int *errp); +char *media_get_name(descriptor_t *desc); +nvlist_t *media_get_attributes(descriptor_t *desc, int *errp); +nvlist_t *media_get_stats(descriptor_t *desc, int stat_type, int *errp); +int media_get_volm_path(disk_t *diskp, char *mediapath, int size); +int media_make_descriptors(); +int media_read_info(int fd, struct dk_minfo *minfo); +int media_read_name(disk_t *dp, char *mname, int size); + +descriptor_t **path_get_descriptors(int filter[], int *errp); +descriptor_t **path_get_assoc_descriptors(descriptor_t *desc, + dm_desc_type_t type, int *errp); +descriptor_t *path_get_descriptor_by_name(char *name, int *errp); +char *path_get_name(descriptor_t *desc); +nvlist_t *path_get_attributes(descriptor_t *desc, int *errp); +nvlist_t *path_get_stats(descriptor_t *desc, int stat_type, int *errp); +int path_make_descriptors(); + +descriptor_t **slice_get_descriptors(int filter[], int *errp); +descriptor_t **slice_get_assoc_descriptors(descriptor_t *desc, + dm_desc_type_t type, int *errp); +descriptor_t **slice_get_assocs(descriptor_t *desc, int *errp); +descriptor_t *slice_get_descriptor_by_name(char *name, int *errp); +char *slice_get_name(descriptor_t *desc); +nvlist_t *slice_get_attributes(descriptor_t *desc, int *errp); +nvlist_t *slice_get_stats(descriptor_t *desc, int stat_type, int *errp); +int slice_make_descriptors(); +void slice_rdsk2dsk(char *rdsk, char *dsk, int size); + +/* cache.c */ +void cache_free_alias(alias_t *aliasp); +void cache_free_bus(bus_t *bp); +void cache_free_controller(controller_t *cp); +void cache_free_descriptor(descriptor_t *desc); +void cache_free_descriptors(descriptor_t **desc_list); +void cache_free_disk(disk_t *dp); +void cache_free_path(path_t *pp); +bus_t *cache_get_buslist(); +controller_t *cache_get_controllerlist(); +descriptor_t *cache_get_desc(int type, void *gp, char *name, + char *secondary_name, int *errp); +descriptor_t **cache_get_descriptors(int type, int *errp); +disk_t *cache_get_disklist(); +int cache_is_valid_desc(descriptor_t *d); +void cache_load_desc(int type, void *gp, char *name, + char *secondary_name, int *errp); +void cache_rlock(); +void cache_unlock(); +void cache_update(dm_event_type_t ev_type, char *devname); +void cache_wlock(); + +/* findevs.c */ +void findevs(struct search_args *args); + +/* events.c */ +int events_start_event_watcher(); +void events_new_event(char *name, int dtype, char *etype); +void events_new_slice_event(char *dev, char *type); + +/* entry.c */ +void libdiskmgt_add_str(nvlist_t *attrs, char *name, char *val, + int *errp); +descriptor_t **libdiskmgt_empty_desc_array(int *errp); +void libdiskmgt_init_debug(); +int libdiskmgt_str_eq(char *nm1, char *nm2); + +/* in-use detectors */ +int inuse_mnt(char *slice, nvlist_t *attrs, int *errp); +int inuse_svm(char *slice, nvlist_t *attrs, int *errp); +int inuse_lu(char *slice, nvlist_t *attrs, int *errp); +int inuse_dump(char *slice, nvlist_t *attrs, int *errp); +int inuse_vxvm(char *slice, nvlist_t *attrs, int *errp); +int inuse_fs(char *slice, nvlist_t *attrs, int *errp); + +#ifdef __cplusplus +} +#endif + +#endif /* _DISKS_PRIVATE_H */ diff --git a/usr/src/lib/libdiskmgt/common/drive.c b/usr/src/lib/libdiskmgt/common/drive.c new file mode 100644 index 0000000000..00fd979ae3 --- /dev/null +++ b/usr/src/lib/libdiskmgt/common/drive.c @@ -0,0 +1,1575 @@ +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <fcntl.h> +#include <libdevinfo.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stropts.h> +#include <sys/dkio.h> +#include <sys/sunddi.h> +#include <sys/types.h> +#include <unistd.h> +#include <kstat.h> +#include <errno.h> +#include <devid.h> +#include <dirent.h> + +/* included for uscsi */ +#include <strings.h> +#include <sys/stat.h> +#include <sys/scsi/impl/types.h> +#include <sys/scsi/impl/uscsi.h> +#include <sys/scsi/generic/commands.h> +#include <sys/scsi/impl/commands.h> +#include <sys/scsi/generic/mode.h> +#include <sys/byteorder.h> + +#include "libdiskmgt.h" +#include "disks_private.h" + +#define KSTAT_CLASS_DISK "disk" +#define KSTAT_CLASS_ERROR "device_error" + +#define SCSIBUFLEN 0xffff + +/* byte get macros */ +#define b3(a) (((a)>>24) & 0xFF) +#define b2(a) (((a)>>16) & 0xFF) +#define b1(a) (((a)>>8) & 0xFF) +#define b0(a) (((a)>>0) & 0xFF) + +static char *kstat_err_names[] = { + "Soft Errors", + "Hard Errors", + "Transport Errors", + "Media Error", + "Device Not Ready", + "No Device", + "Recoverable", + "Illegal Request", + "Predictive Failure Analysis", + NULL +}; + +static char *err_attr_names[] = { + DM_NSOFTERRS, + DM_NHARDERRS, + DM_NTRANSERRS, + DM_NMEDIAERRS, + DM_NDNRERRS, + DM_NNODEVERRS, + DM_NRECOVERRS, + DM_NILLREQERRS, + DM_FAILING, + NULL +}; + +/* + * **************** begin uscsi stuff **************** + */ + +#if defined(_BIT_FIELDS_LTOH) +#elif defined(_BIT_FIELDS_HTOL) +#else +#error One of _BIT_FIELDS_LTOH or _BIT_FIELDS_HTOL must be defined +#endif + +struct conf_feature { + uchar_t feature[2]; /* common to all */ +#if defined(_BIT_FIELDS_LTOH) + uchar_t current : 1; + uchar_t persist : 1; + uchar_t version : 4; + uchar_t reserved: 2; +#else + uchar_t reserved: 2; + uchar_t version : 4; + uchar_t persist : 1; + uchar_t current : 1; +#endif /* _BIT_FIELDS_LTOH */ + uchar_t len; + union features { + struct generic { + uchar_t data[1]; + } gen; + uchar_t data[1]; + struct profile_list { + uchar_t profile[2]; +#if defined(_BIT_FIELDS_LTOH) + uchar_t current_p : 1; + uchar_t reserved1 : 7; +#else + uchar_t reserved1 : 7; + uchar_t current_p : 1; +#endif /* _BIT_FIELDS_LTOH */ + uchar_t reserved2; + } plist[1]; + struct core { + uchar_t phys[4]; + } core; + struct morphing { +#if defined(_BIT_FIELDS_LTOH) + uchar_t async : 1; + uchar_t reserved1 : 7; +#else + uchar_t reserved1 : 7; + uchar_t async : 1; +#endif /* _BIT_FIELDS_LTOH */ + uchar_t reserved[3]; + } morphing; + struct removable { +#if defined(_BIT_FIELDS_LTOH) + uchar_t lock : 1; + uchar_t resv1 : 1; + uchar_t pvnt : 1; + uchar_t eject : 1; + uchar_t resv2 : 1; + uchar_t loading : 3; +#else + uchar_t loading : 3; + uchar_t resv2 : 1; + uchar_t eject : 1; + uchar_t pvnt : 1; + uchar_t resv1 : 1; + uchar_t lock : 1; +#endif /* _BIT_FIELDS_LTOH */ + uchar_t reserved[3]; + } removable; + struct random_readable { + uchar_t lbsize[4]; + uchar_t blocking[2]; +#if defined(_BIT_FIELDS_LTOH) + uchar_t pp : 1; + uchar_t reserved1 : 7; +#else + uchar_t reserved1 : 7; + uchar_t pp : 1; +#endif /* _BIT_FIELDS_LTOH */ + uchar_t reserved; + } rread; + struct cd_read { +#if defined(_BIT_FIELDS_LTOH) + uchar_t cdtext : 1; + uchar_t c2flag : 1; + uchar_t reserved1 : 6; +#else + uchar_t reserved1 : 6; + uchar_t c2flag : 1; + uchar_t cdtext : 1; +#endif /* _BIT_FIELDS_LTOH */ + } cdread; + struct cd_audio { +#if defined(_BIT_FIELDS_LTOH) + uchar_t sv : 1; + uchar_t scm : 1; + uchar_t scan : 1; + uchar_t resv : 5; +#else + uchar_t resv : 5; + uchar_t scan : 1; + uchar_t scm : 1; + uchar_t sv : 1; +#endif /* _BIT_FIELDS_LTOH */ + uchar_t reserved; + uchar_t numlevels[2]; + } audio; + struct dvd_css { + uchar_t reserved[3]; + uchar_t version; + } dvdcss; + } features; +}; + +#define PROF_NON_REMOVABLE 0x0001 +#define PROF_REMOVABLE 0x0002 +#define PROF_MAGNETO_OPTICAL 0x0003 +#define PROF_OPTICAL_WO 0x0004 +#define PROF_OPTICAL_ASMO 0x0005 +#define PROF_CDROM 0x0008 +#define PROF_CDR 0x0009 +#define PROF_CDRW 0x000a +#define PROF_DVDROM 0x0010 +#define PROF_DVDR 0x0011 +#define PROF_DVDRAM 0x0012 +#define PROF_DVDRW_REST 0x0013 +#define PROF_DVDRW_SEQ 0x0014 +#define PROF_DVDRW 0x001a +#define PROF_DDCD_ROM 0x0020 +#define PROF_DDCD_R 0x0021 +#define PROF_DDCD_RW 0x0022 +#define PROF_NON_CONFORMING 0xffff + +struct get_configuration { + uchar_t len[4]; + uchar_t reserved[2]; + uchar_t curprof[2]; + struct conf_feature feature; +}; + +struct capabilities { +#if defined(_BIT_FIELDS_LTOH) + uchar_t pagecode : 6; + uchar_t resv1 : 1; + uchar_t ps : 1; +#else + uchar_t ps : 1; + uchar_t resv1 : 1; + uchar_t pagecode : 6; +#endif /* _BIT_FIELDS_LTOH */ + uchar_t pagelen; +#if defined(_BIT_FIELDS_LTOH) + /* read capabilities */ + uchar_t cdr_read : 1; + uchar_t cdrw_read : 1; + uchar_t method2 : 1; + uchar_t dvdrom_read : 1; + uchar_t dvdr_read : 1; + uchar_t dvdram_read : 1; + uchar_t resv2 : 2; +#else + uchar_t resv2 : 2; + uchar_t dvdram_read : 1; + uchar_t dvdr_read : 1; + uchar_t dvdrom_read : 1; + uchar_t method2 : 1; + uchar_t cdrw_read : 1; + uchar_t cdr_read : 1; +#endif /* _BIT_FIELDS_LTOH */ +#if defined(_BIT_FIELDS_LTOH) + /* write capabilities */ + uchar_t cdr_write : 1; + uchar_t cdrw_write : 1; + uchar_t testwrite : 1; + uchar_t resv3 : 1; + uchar_t dvdr_write : 1; + uchar_t dvdram_write : 1; + uchar_t resv4 : 2; +#else + /* write capabilities */ + uchar_t resv4 : 2; + uchar_t dvdram_write : 1; + uchar_t dvdr_write : 1; + uchar_t resv3 : 1; + uchar_t testwrite : 1; + uchar_t cdrw_write : 1; + uchar_t cdr_write : 1; +#endif /* _BIT_FIELDS_LTOH */ + uchar_t misc0; + uchar_t misc1; + uchar_t misc2; + uchar_t misc3; + uchar_t obsolete0[2]; + uchar_t numvlevels[2]; + uchar_t bufsize[2]; + uchar_t obsolete1[4]; + uchar_t resv5; + uchar_t misc4; + uchar_t obsolete2; + uchar_t copymgt[2]; + /* there is more to this page, but nothing we care about */ +}; + +struct mode_header_g2 { + uchar_t modelen[2]; + uchar_t obsolete; + uchar_t reserved[3]; + uchar_t desclen[2]; +}; + +/* + * Mode sense/select page header information + */ +struct scsi_ms_header { + struct mode_header mode_header; + struct block_descriptor block_descriptor; +}; + +#define MODESENSE_PAGE_LEN(p) (((int)((struct mode_page *)p)->length) + \ + sizeof (struct mode_page)) + +#define MODE_SENSE_PC_CURRENT (0 << 6) +#define MODE_SENSE_PC_DEFAULT (2 << 6) +#define MODE_SENSE_PC_SAVED (3 << 6) + +#define MAX_MODE_SENSE_SIZE 255 +#define IMPOSSIBLE_SCSI_STATUS 0xff + +/* + * ********** end of uscsi stuff ************ + */ + +static descriptor_t **apply_filter(descriptor_t **drives, int filter[], + int *errp); +static int check_atapi(int fd); +static int conv_drive_type(uint_t drive_type); +static uint64_t convnum(uchar_t *nptr, int len); +static void fill_command_g1(struct uscsi_cmd *cmd, + union scsi_cdb *cdb, caddr_t buff, int blen); +static void fill_general_page_cdb_g1(union scsi_cdb *cdb, + int command, int lun, uchar_t c0, uchar_t c1); +static void fill_mode_page_cdb(union scsi_cdb *cdb, int page); +static descriptor_t **get_assoc_alias(disk_t *diskp, int *errp); +static descriptor_t **get_assoc_controllers(descriptor_t *dp, int *errp); +static descriptor_t **get_assoc_paths(descriptor_t *dp, int *errp); +static int get_attrs(disk_t *diskp, int fd, char *opath, + nvlist_t *nvp); +static int get_cdrom_drvtype(int fd); +static int get_disk_kstats(kstat_ctl_t *kc, char *diskname, + char *classname, nvlist_t *stats); +static void get_drive_type(disk_t *dp, int fd); +static int get_err_kstats(kstat_ctl_t *kc, char *diskname, + nvlist_t *stats); +static int get_io_kstats(kstat_ctl_t *kc, char *diskname, + nvlist_t *stats); +static int get_kstat_vals(kstat_t *ksp, nvlist_t *stats); +static char *get_err_attr_name(char *kstat_name); +static int get_rpm(disk_t *dp, int fd); +static int update_stat64(nvlist_t *stats, char *attr, + uint64_t value); +static int update_stat32(nvlist_t *stats, char *attr, + uint32_t value); +static int uscsi_mode_sense(int fd, int page_code, + int page_control, caddr_t page_data, int page_size, + struct scsi_ms_header *header); + +descriptor_t ** +drive_get_assoc_descriptors(descriptor_t *dp, dm_desc_type_t type, + int *errp) +{ + switch (type) { + case DM_CONTROLLER: + return (get_assoc_controllers(dp, errp)); + case DM_PATH: + return (get_assoc_paths(dp, errp)); + case DM_ALIAS: + return (get_assoc_alias(dp->p.disk, errp)); + case DM_MEDIA: + return (media_get_assocs(dp, errp)); + } + + *errp = EINVAL; + return (NULL); +} + +/* + * Get the drive descriptors for the given media/alias/devpath. + */ +descriptor_t ** +drive_get_assocs(descriptor_t *desc, int *errp) +{ + descriptor_t **drives; + + /* at most one drive is associated with these descriptors */ + + drives = (descriptor_t **)calloc(2, sizeof (descriptor_t *)); + if (drives == NULL) { + *errp = ENOMEM; + return (NULL); + } + + drives[0] = cache_get_desc(DM_DRIVE, desc->p.disk, NULL, NULL, errp); + if (*errp != 0) { + cache_free_descriptors(drives); + return (NULL); + } + + drives[1] = NULL; + + return (drives); +} + +nvlist_t * +drive_get_attributes(descriptor_t *dp, int *errp) +{ + nvlist_t *attrs = NULL; + int fd; + char opath[MAXPATHLEN]; + + if (nvlist_alloc(&attrs, NVATTRS, 0) != 0) { + *errp = ENOMEM; + return (NULL); + } + + opath[0] = 0; + fd = drive_open_disk(dp->p.disk, opath, sizeof (opath)); + + if ((*errp = get_attrs(dp->p.disk, fd, opath, attrs)) != 0) { + nvlist_free(attrs); + attrs = NULL; + } + + if (fd >= 0) { + (void) close(fd); + } + + return (attrs); +} + +/* + * Check if we have the drive in our list, based upon the device id. + * We got the device id from the dev tree walk. This is encoded + * using devid_str_encode(3DEVID). In order to check the device ids we need + * to use the devid_compare(3DEVID) function, so we need to decode the + * string representation of the device id. + */ +descriptor_t * +drive_get_descriptor_by_name(char *name, int *errp) +{ + ddi_devid_t devid; + descriptor_t **drives; + descriptor_t *drive = NULL; + int i; + + if (name == NULL || devid_str_decode(name, &devid, NULL) != 0) { + *errp = EINVAL; + return (NULL); + } + + drives = cache_get_descriptors(DM_DRIVE, errp); + if (*errp != 0) { + devid_free(devid); + return (NULL); + } + + /* + * We have to loop through all of them, freeing the ones we don't + * want. Once drive is set, we don't need to compare any more. + */ + for (i = 0; drives[i]; i++) { + if (drive == NULL && drives[i]->p.disk->devid != NULL && + devid_compare(devid, drives[i]->p.disk->devid) == 0) { + drive = drives[i]; + + } else { + /* clean up the unused descriptor */ + cache_free_descriptor(drives[i]); + } + } + free(drives); + devid_free(devid); + + if (drive == NULL) { + *errp = ENODEV; + } + + return (drive); +} + +descriptor_t ** +drive_get_descriptors(int filter[], int *errp) +{ + descriptor_t **drives; + + drives = cache_get_descriptors(DM_DRIVE, errp); + if (*errp != 0) { + return (NULL); + } + + if (filter != NULL && filter[0] != DM_FILTER_END) { + descriptor_t **found; + found = apply_filter(drives, filter, errp); + if (*errp != 0) { + drives = NULL; + } else { + drives = found; + } + } + + return (drives); +} + +char * +drive_get_name(descriptor_t *dp) +{ + return (dp->p.disk->device_id); +} + +nvlist_t * +drive_get_stats(descriptor_t *dp, int stat_type, int *errp) +{ + disk_t *diskp; + nvlist_t *stats; + + diskp = dp->p.disk; + + if (nvlist_alloc(&stats, NVATTRS, 0) != 0) { + *errp = ENOMEM; + return (NULL); + } + + if (stat_type == DM_DRV_STAT_PERFORMANCE || + stat_type == DM_DRV_STAT_DIAGNOSTIC) { + + alias_t *ap; + kstat_ctl_t *kc; + + ap = diskp->aliases; + if (ap == NULL || ap->kstat_name == NULL) { + nvlist_free(stats); + *errp = EACCES; + return (NULL); + } + + if ((kc = kstat_open()) == NULL) { + nvlist_free(stats); + *errp = EACCES; + return (NULL); + } + + while (ap != NULL) { + int status; + + if (ap->kstat_name == NULL) { + continue; + } + + if (stat_type == DM_DRV_STAT_PERFORMANCE) { + status = get_io_kstats(kc, ap->kstat_name, stats); + } else { + status = get_err_kstats(kc, ap->kstat_name, stats); + } + + if (status != 0) { + nvlist_free(stats); + (void) kstat_close(kc); + *errp = ENOMEM; + return (NULL); + } + + ap = ap->next; + } + + (void) kstat_close(kc); + + *errp = 0; + return (stats); + } + + if (stat_type == DM_DRV_STAT_TEMPERATURE) { + int fd; + + if ((fd = drive_open_disk(diskp, NULL, 0)) >= 0) { + struct dk_temperature temp; + + if (ioctl(fd, DKIOCGTEMPERATURE, &temp) >= 0) { + if (nvlist_add_uint32(stats, DM_TEMPERATURE, + temp.dkt_cur_temp) != 0) { + *errp = ENOMEM; + nvlist_free(stats); + return (NULL); + } + } else { + *errp = errno; + nvlist_free(stats); + return (NULL); + } + (void) close(fd); + } else { + *errp = errno; + nvlist_free(stats); + return (NULL); + } + + *errp = 0; + return (stats); + } + + nvlist_free(stats); + *errp = EINVAL; + return (NULL); +} + +int +drive_make_descriptors() +{ + int error; + disk_t *dp; + + dp = cache_get_disklist(); + while (dp != NULL) { + cache_load_desc(DM_DRIVE, dp, NULL, NULL, &error); + if (error != 0) { + return (error); + } + dp = dp->next; + } + + return (0); +} + +/* + * This function opens the disk generically (any slice). + * + * Opening the disk could fail because the disk is managed by the volume + * manager. Handle this if that is the case. Note that the media APIs don't + * always return a device. If the media has slices (e.g. a solaris install + * CD-ROM) then media_findname(volname) returns a directory with per slice + * devices underneath. We need to open one of those devices in this case. + */ +int +drive_open_disk(disk_t *diskp, char *opath, int len) +{ + char rmmedia_devpath[MAXPATHLEN]; + + if (diskp->removable && media_get_volm_path(diskp, rmmedia_devpath, + sizeof (rmmedia_devpath))) { + + int fd; + struct stat buf; + + if (rmmedia_devpath[0] == 0) { + /* removable but no media */ + return (-1); + } + + if ((fd = open(rmmedia_devpath, O_RDONLY|O_NDELAY)) < 0) { + return (-1); + } + + if (fstat(fd, &buf) != 0) { + (void) close(fd); + return (-1); + } + + if (buf.st_mode & S_IFCHR) { + /* opened, is device, so done */ + if (opath != NULL) { + (void) strlcpy(opath, rmmedia_devpath, len); + } + return (fd); + + } else if (buf.st_mode & S_IFDIR) { + /* disk w/ slices so handle the directory */ + DIR *dirp; + struct dirent *dentp; + int dfd; +#ifdef _LP64 + struct dirent *result; +#endif + + /* each device file in the dir represents a slice */ + + if ((dirp = fdopendir(fd)) == NULL) { + (void) close(fd); + return (-1); + } + + if ((dentp = (struct dirent *)malloc(sizeof (struct dirent) + + _PC_NAME_MAX + 1)) == NULL) { + /* out of memory */ + (void) close(fd); + return (-1); + } +#ifdef _LP64 + while (readdir_r(dirp, dentp, &result) != NULL) { +#else + while (readdir_r(dirp, dentp) != NULL) { +#endif + char slice_path[MAXPATHLEN]; + + if (libdiskmgt_str_eq(".", dentp->d_name) || + libdiskmgt_str_eq("..", dentp->d_name)) { + continue; + } + + (void) snprintf(slice_path, sizeof (slice_path), "%s/%s", + rmmedia_devpath, dentp->d_name); + + if ((dfd = open(slice_path, O_RDONLY|O_NDELAY)) < 0) { + continue; + } + + if (fstat(dfd, &buf) == 0 && (buf.st_mode & S_IFCHR)) { + /* opened, is device, so done */ + free(dentp); + (void) close(fd); + if (opath != NULL) { + (void) strlcpy(opath, slice_path, len); + } + return (dfd); + } + + /* not a device, keep looking */ + (void) close(dfd); + } + + /* did not find a device under the rmmedia_path */ + free(dentp); + (void) close(fd); + } + + /* didn't find a device under volume management control */ + return (-1); + } + + /* + * Not removable media under volume management control so just open the + * first devpath. + */ + if (diskp->aliases != NULL && diskp->aliases->devpaths != NULL) { + if (opath != NULL) { + (void) strlcpy(opath, diskp->aliases->devpaths->devpath, len); + } + return (open(diskp->aliases->devpaths->devpath, O_RDONLY|O_NDELAY)); + } + + return (-1); +} + +static descriptor_t ** +apply_filter(descriptor_t **drives, int filter[], int *errp) +{ + int i; + descriptor_t **found; + int cnt; + int pos; + + /* count the number of drives in the snapshot */ + for (cnt = 0; drives[cnt]; cnt++); + + found = (descriptor_t **)calloc(cnt + 1, sizeof (descriptor_t *)); + if (found == NULL) { + *errp = ENOMEM; + cache_free_descriptors(drives); + return (NULL); + } + + pos = 0; + for (i = 0; drives[i]; i++) { + int j; + int match; + + /* Make sure the drive type is set */ + get_drive_type(drives[i]->p.disk, -1); + + match = 0; + for (j = 0; filter[j] != DM_FILTER_END; j++) { + if (drives[i]->p.disk->drv_type == filter[j]) { + found[pos++] = drives[i]; + match = 1; + break; + } + } + + if (!match) { + cache_free_descriptor(drives[i]); + } + } + found[pos] = NULL; + free(drives); + + *errp = 0; + return (found); +} + +static int +conv_drive_type(uint_t drive_type) +{ + switch (drive_type) { + case DK_UNKNOWN: + return (DM_DT_UNKNOWN); + case DK_MO_ERASABLE: + return (DM_DT_MO_ERASABLE); + case DK_MO_WRITEONCE: + return (DM_DT_MO_WRITEONCE); + case DK_AS_MO: + return (DM_DT_AS_MO); + case DK_CDROM: + return (DM_DT_CDROM); + case DK_CDR: + return (DM_DT_CDR); + case DK_CDRW: + return (DM_DT_CDRW); + case DK_DVDROM: + return (DM_DT_DVDROM); + case DK_DVDR: + return (DM_DT_DVDR); + case DK_DVDRAM: + return (DM_DT_DVDRAM); + case DK_FIXED_DISK: + return (DM_DT_FIXED); + case DK_FLOPPY: + return (DM_DT_FLOPPY); + case DK_ZIP: + return (DM_DT_ZIP); + case DK_JAZ: + return (DM_DT_JAZ); + default: + return (DM_DT_UNKNOWN); + } +} + +static descriptor_t ** +get_assoc_alias(disk_t *diskp, int *errp) +{ + alias_t *aliasp; + uint_t cnt; + descriptor_t **out_array; + int pos; + + *errp = 0; + + aliasp = diskp->aliases; + cnt = 0; + + while (aliasp != NULL) { + if (aliasp->alias != NULL) { + cnt++; + } + aliasp = aliasp->next; + } + + /* set up the new array */ + out_array = (descriptor_t **)calloc(cnt + 1, sizeof (descriptor_t)); + if (out_array == NULL) { + *errp = ENOMEM; + return (NULL); + } + + aliasp = diskp->aliases; + pos = 0; + while (aliasp != NULL) { + if (aliasp->alias != NULL) { + out_array[pos++] = cache_get_desc(DM_ALIAS, diskp, + aliasp->alias, NULL, errp); + if (*errp != 0) { + cache_free_descriptors(out_array); + return (NULL); + } + } + + aliasp = aliasp->next; + } + + out_array[pos] = NULL; + + return (out_array); +} + +static descriptor_t ** +get_assoc_controllers(descriptor_t *dp, int *errp) +{ + disk_t *diskp; + int cnt; + descriptor_t **controllers; + int i; + + diskp = dp->p.disk; + + /* Count how many we have. */ + for (cnt = 0; diskp->controllers[cnt]; cnt++); + + /* make the snapshot */ + controllers = (descriptor_t **)calloc(cnt + 1, sizeof (descriptor_t *)); + if (controllers == NULL) { + *errp = ENOMEM; + return (NULL); + } + + for (i = 0; diskp->controllers[i]; i++) { + controllers[i] = cache_get_desc(DM_CONTROLLER, + diskp->controllers[i], NULL, NULL, errp); + if (*errp != 0) { + cache_free_descriptors(controllers); + return (NULL); + } + } + + controllers[i] = NULL; + + *errp = 0; + return (controllers); +} + +static descriptor_t ** +get_assoc_paths(descriptor_t *dp, int *errp) +{ + path_t **pp; + int cnt; + descriptor_t **paths; + int i; + + pp = dp->p.disk->paths; + + /* Count how many we have. */ + cnt = 0; + if (pp != NULL) { + for (; pp[cnt]; cnt++); + } + + /* make the snapshot */ + paths = (descriptor_t **)calloc(cnt + 1, sizeof (descriptor_t *)); + if (paths == NULL) { + *errp = ENOMEM; + return (NULL); + } + + /* + * We fill in the name field of the descriptor with the device_id + * when we deal with path descriptors originating from a drive. + * In that way we can use the device id within the path code to + * lookup the path state for this drive. + */ + for (i = 0; i < cnt; i++) { + paths[i] = cache_get_desc(DM_PATH, pp[i], dp->p.disk->device_id, + NULL, errp); + if (*errp != 0) { + cache_free_descriptors(paths); + return (NULL); + } + } + + paths[i] = NULL; + + *errp = 0; + return (paths); +} + +static int +get_attrs(disk_t *diskp, int fd, char *opath, nvlist_t *attrs) +{ + if (diskp->removable) { + struct dk_minfo minfo; + + if (nvlist_add_boolean(attrs, DM_REMOVABLE) != 0) { + return (ENOMEM); + } + + /* Make sure media is inserted and spun up. */ + if (fd >= 0 && media_read_info(fd, &minfo)) { + if (nvlist_add_boolean(attrs, DM_LOADED) != 0) { + return (ENOMEM); + } + } + + /* can't tell diff between dead & no media on removable drives */ + if (nvlist_add_uint32(attrs, DM_STATUS, DM_DISK_UP) != 0) { + return (ENOMEM); + } + + get_drive_type(diskp, fd); + + } else { + struct dk_minfo minfo; + + /* check if the fixed drive is up or not */ + if (fd >= 0 && media_read_info(fd, &minfo)) { + if (nvlist_add_uint32(attrs, DM_STATUS, DM_DISK_UP) != 0) { + return (ENOMEM); + } + } else { + if (nvlist_add_uint32(attrs, DM_STATUS, DM_DISK_DOWN) != 0) { + return (ENOMEM); + } + } + + get_drive_type(diskp, fd); + } + + if (nvlist_add_uint32(attrs, DM_DRVTYPE, diskp->drv_type) != 0) { + return (ENOMEM); + } + + if (diskp->product_id != NULL) { + if (nvlist_add_string(attrs, DM_PRODUCT_ID, diskp->product_id) + != 0) { + return (ENOMEM); + } + } + if (diskp->vendor_id != NULL) { + if (nvlist_add_string(attrs, DM_VENDOR_ID, diskp->vendor_id) != 0) { + return (ENOMEM); + } + } + + if (diskp->sync_speed != -1) { + if (nvlist_add_uint32(attrs, DM_SYNC_SPEED, diskp->sync_speed) + != 0) { + return (ENOMEM); + } + } + + if (diskp->wide == 1) { + if (nvlist_add_boolean(attrs, DM_WIDE) != 0) { + return (ENOMEM); + } + } + + if (diskp->rpm == 0) { + diskp->rpm = get_rpm(diskp, fd); + } + + if (diskp->rpm > 0) { + if (nvlist_add_uint32(attrs, DM_RPM, diskp->rpm) != 0) { + return (ENOMEM); + } + } + + if (diskp->aliases != NULL && diskp->aliases->cluster) { + if (nvlist_add_boolean(attrs, DM_CLUSTERED) != 0) { + return (ENOMEM); + } + } + + if (strlen(opath) > 0) { + if (nvlist_add_string(attrs, DM_OPATH, opath) != 0) { + return (ENOMEM); + } + } + + return (0); +} + +static int +get_disk_kstats(kstat_ctl_t *kc, char *diskname, char *classname, + nvlist_t *stats) +{ + kstat_t *ksp; + size_t class_len; + int err = 0; + + class_len = strlen(classname); + for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) { + if (strncmp(ksp->ks_class, classname, class_len) == 0) { + char kstat_name[KSTAT_STRLEN]; + char *dname = kstat_name; + char *ename = ksp->ks_name; + + /* names are format: "sd0,err" - copy chars up to comma */ + while (*ename && *ename != ',') { + *dname++ = *ename++; + } + *dname = NULL; + + if (libdiskmgt_str_eq(diskname, kstat_name)) { + (void) kstat_read(kc, ksp, NULL); + err = get_kstat_vals(ksp, stats); + break; + } + } + } + + return (err); +} + +/* + * Getting the drive type depends on if the dev tree walk indicated that the + * drive was a CD-ROM or not. The kernal lumps all of the removable multi-media + * drives (e.g. CD, DVD, MO, etc.) together as CD-ROMS, so we need to use + * a uscsi cmd to check the drive type. + */ +static void +get_drive_type(disk_t *dp, int fd) +{ + if (dp->drv_type == DM_DT_UNKNOWN) { + int opened_here = 0; + + /* We may have already opened the device. */ + if (fd < 0) { + fd = drive_open_disk(dp, NULL, 0); + opened_here = 1; + } + + if (fd >= 0) { + if (dp->cd_rom) { + /* use uscsi to determine drive type */ + dp->drv_type = get_cdrom_drvtype(fd); + + /* if uscsi fails, just call it a cd-rom */ + if (dp->drv_type == DM_DT_UNKNOWN) { + dp->drv_type = DM_DT_CDROM; + } + + } else { + struct dk_minfo minfo; + + if (media_read_info(fd, &minfo)) { + dp->drv_type = conv_drive_type(minfo.dki_media_type); + } + } + + if (opened_here) { + (void) close(fd); + } + + } else { + /* couldn't open */ + if (dp->cd_rom) { + dp->drv_type = DM_DT_CDROM; + } + } + } +} + +static char * +get_err_attr_name(char *kstat_name) +{ + int i; + + for (i = 0; kstat_err_names[i] != NULL; i++) { + if (libdiskmgt_str_eq(kstat_name, kstat_err_names[i])) { + return (err_attr_names[i]); + } + } + + return (NULL); +} + +static int +get_err_kstats(kstat_ctl_t *kc, char *diskname, nvlist_t *stats) +{ + return (get_disk_kstats(kc, diskname, KSTAT_CLASS_ERROR, stats)); +} + +static int +get_io_kstats(kstat_ctl_t *kc, char *diskname, nvlist_t *stats) +{ + return (get_disk_kstats(kc, diskname, KSTAT_CLASS_DISK, stats)); +} + +static int +get_kstat_vals(kstat_t *ksp, nvlist_t *stats) +{ + if (ksp->ks_type == KSTAT_TYPE_IO) { + kstat_io_t *kiop; + + kiop = KSTAT_IO_PTR(ksp); + + /* see sys/kstat.h kstat_io_t struct for more fields */ + + if (update_stat64(stats, DM_NBYTESREAD, kiop->nread) != 0) { + return (ENOMEM); + } + if (update_stat64(stats, DM_NBYTESWRITTEN, kiop->nwritten) != 0) { + return (ENOMEM); + } + if (update_stat64(stats, DM_NREADOPS, kiop->reads) != 0) { + return (ENOMEM); + } + if (update_stat64(stats, DM_NWRITEOPS, kiop->writes) != 0) { + return (ENOMEM); + } + + } else if (ksp->ks_type == KSTAT_TYPE_NAMED) { + kstat_named_t *knp; + int i; + + knp = KSTAT_NAMED_PTR(ksp); + for (i = 0; i < ksp->ks_ndata; i++) { + char *attr_name; + + if (knp[i].name[0] == 0) + continue; + + if ((attr_name = get_err_attr_name(knp[i].name)) == NULL) { + continue; + + } + + switch (knp[i].data_type) { + case KSTAT_DATA_UINT32: + if (update_stat32(stats, attr_name, knp[i].value.ui32) + != 0) { + return (ENOMEM); + } + break; + + default: + /* Right now all of the error types are uint32 */ + break; + } + } + } + return (0); +} + +static int +update_stat32(nvlist_t *stats, char *attr, uint32_t value) +{ + int32_t currval; + + if (nvlist_lookup_int32(stats, attr, &currval) == 0) { + value += currval; + } + + return (nvlist_add_uint32(stats, attr, value)); +} + +/* + * There can be more than one kstat value when we have multi-path drives + * that are not under mpxio (since there is more than one kstat name for + * the drive in this case). So, we may have merge all of the kstat values + * to give an accurate set of stats for the drive. + */ +static int +update_stat64(nvlist_t *stats, char *attr, uint64_t value) +{ + int64_t currval; + + if (nvlist_lookup_int64(stats, attr, &currval) == 0) { + value += currval; + } + return (nvlist_add_uint64(stats, attr, value)); +} + +/* + * uscsi function to get the rpm of the drive + */ +static int +get_rpm(disk_t *dp, int fd) +{ + int opened_here = 0; + int rpm = -1; + + /* We may have already opened the device. */ + if (fd < 0) { + fd = drive_open_disk(dp, NULL, 0); + opened_here = 1; + } + + if (fd >= 0) { + int status; + struct mode_geometry *page4; + struct scsi_ms_header header; + union { + struct mode_geometry page4; + char rawbuf[MAX_MODE_SENSE_SIZE]; + } u_page4; + + page4 = &u_page4.page4; + (void) memset(&u_page4, 0, sizeof (u_page4)); + + status = uscsi_mode_sense(fd, DAD_MODE_GEOMETRY, + MODE_SENSE_PC_DEFAULT, (caddr_t)page4, MAX_MODE_SENSE_SIZE, + &header); + + if (status) { + status = uscsi_mode_sense(fd, DAD_MODE_GEOMETRY, + MODE_SENSE_PC_SAVED, (caddr_t)page4, MAX_MODE_SENSE_SIZE, + &header); + } + + if (status) { + status = uscsi_mode_sense(fd, DAD_MODE_GEOMETRY, + MODE_SENSE_PC_CURRENT, (caddr_t)page4, MAX_MODE_SENSE_SIZE, + &header); + } + + if (!status) { +#ifdef _LITTLE_ENDIAN + page4->rpm = ntohs(page4->rpm); +#endif /* _LITTLE_ENDIAN */ + + rpm = page4->rpm; + } + + if (opened_here) { + (void) close(fd); + } + } + + return (rpm); +} + +/* + * ******** the rest of this is uscsi stuff for the drv type ******** + */ + +/* + * We try a get_configuration uscsi cmd. If that fails, try a + * atapi_capabilities cmd. If both fail then this is an older CD-ROM. + */ +static int +get_cdrom_drvtype(int fd) +{ + union scsi_cdb cdb; + struct uscsi_cmd cmd; + uchar_t buff[SCSIBUFLEN]; + + fill_general_page_cdb_g1(&cdb, SCMD_GET_CONFIGURATION, 0, + b0(sizeof (buff)), b1(sizeof (buff))); + fill_command_g1(&cmd, &cdb, (caddr_t)buff, sizeof (buff)); + + if (ioctl(fd, USCSICMD, &cmd) >= 0) { + struct get_configuration *config; + struct conf_feature *feature; + int flen; + + /* The first profile is the preferred one for the drive. */ + config = (struct get_configuration *)buff; + feature = &config->feature; + flen = feature->len / sizeof (struct profile_list); + if (flen > 0) { + int prof_num; + + prof_num = (int)convnum(feature->features.plist[0].profile, 2); + + if (dm_debug > 1) { + (void) fprintf(stderr, "INFO: uscsi get_configuration %d\n", + prof_num); + } + + switch (prof_num) { + case PROF_MAGNETO_OPTICAL: + return (DM_DT_MO_ERASABLE); + case PROF_OPTICAL_WO: + return (DM_DT_MO_WRITEONCE); + case PROF_OPTICAL_ASMO: + return (DM_DT_AS_MO); + case PROF_CDROM: + return (DM_DT_CDROM); + case PROF_CDR: + return (DM_DT_CDR); + case PROF_CDRW: + return (DM_DT_CDRW); + case PROF_DVDROM: + return (DM_DT_DVDROM); + case PROF_DVDRAM: + return (DM_DT_DVDRAM); + case PROF_DVDRW_REST: + return (DM_DT_DVDRW); + case PROF_DVDRW_SEQ: + return (DM_DT_DVDRW); + case PROF_DVDRW: + return (DM_DT_DVDRW); + case PROF_DDCD_ROM: + return (DM_DT_DDCDROM); + case PROF_DDCD_R: + return (DM_DT_DDCDR); + case PROF_DDCD_RW: + return (DM_DT_DDCDRW); + } + } + } + + /* see if the atapi capabilities give anything */ + return (check_atapi(fd)); +} + +static int +check_atapi(int fd) +{ + union scsi_cdb cdb; + struct uscsi_cmd cmd; + uchar_t buff[SCSIBUFLEN]; + + fill_mode_page_cdb(&cdb, ATAPI_CAPABILITIES); + fill_command_g1(&cmd, &cdb, (caddr_t)buff, sizeof (buff)); + + if (ioctl(fd, USCSICMD, &cmd) >= 0) { + int bdesclen; + struct capabilities *cap; + struct mode_header_g2 *mode; + + mode = (struct mode_header_g2 *)buff; + + bdesclen = (int)convnum(mode->desclen, 2); + cap = (struct capabilities *) + &buff[sizeof (struct mode_header_g2) + bdesclen]; + + if (dm_debug > 1) { + (void) fprintf(stderr, "INFO: uscsi atapi capabilities\n"); + } + + /* These are in order of how we want to report the drv type. */ + if (cap->dvdram_write) { + return (DM_DT_DVDRAM); + } + if (cap->dvdr_write) { + return (DM_DT_DVDR); + } + if (cap->dvdrom_read) { + return (DM_DT_DVDROM); + } + if (cap->cdrw_write) { + return (DM_DT_CDRW); + } + if (cap->cdr_write) { + return (DM_DT_CDR); + } + if (cap->cdr_read) { + return (DM_DT_CDROM); + } + } + + /* everything failed, so this is an older CD-ROM */ + if (dm_debug > 1) { + (void) fprintf(stderr, "INFO: uscsi failed\n"); + } + + return (DM_DT_CDROM); +} + +static uint64_t +convnum(uchar_t *nptr, int len) +{ + uint64_t value; + + for (value = 0; len > 0; len--, nptr++) + value = (value << 8) | *nptr; + return (value); +} + +static void +fill_command_g1(struct uscsi_cmd *cmd, union scsi_cdb *cdb, + caddr_t buff, int blen) +{ + bzero((caddr_t)cmd, sizeof (struct uscsi_cmd)); + bzero(buff, blen); + + cmd->uscsi_cdb = (caddr_t)cdb; + cmd->uscsi_cdblen = CDB_GROUP1; + + cmd->uscsi_bufaddr = buff; + cmd->uscsi_buflen = blen; + + cmd->uscsi_flags = USCSI_DIAGNOSE|USCSI_ISOLATE|USCSI_READ; +} + +static void +fill_general_page_cdb_g1(union scsi_cdb *cdb, int command, int lun, + uchar_t c0, uchar_t c1) +{ + bzero((caddr_t)cdb, sizeof (union scsi_cdb)); + cdb->scc_cmd = command; + cdb->scc_lun = lun; + cdb->g1_count0 = c0; /* max length for page */ + cdb->g1_count1 = c1; /* max length for page */ +} + +static void +fill_mode_page_cdb(union scsi_cdb *cdb, int page) +{ + /* group 1 mode page */ + bzero((caddr_t)cdb, sizeof (union scsi_cdb)); + cdb->scc_cmd = SCMD_MODE_SENSE_G1; + cdb->g1_count0 = 0xff; /* max length for mode page */ + cdb->g1_count1 = 0xff; /* max length for mode page */ + cdb->g1_addr3 = page; +} + +static int +uscsi_mode_sense(int fd, int page_code, int page_control, caddr_t page_data, + int page_size, struct scsi_ms_header *header) +{ + caddr_t mode_sense_buf; + struct mode_header *hdr; + struct mode_page *pg; + int nbytes; + struct uscsi_cmd ucmd; + union scsi_cdb cdb; + int status; + int maximum; + char rqbuf[255]; + + /* + * Allocate a buffer for the mode sense headers + * and mode sense data itself. + */ + nbytes = sizeof (struct block_descriptor) + + sizeof (struct mode_header) + page_size; + nbytes = page_size; + if ((mode_sense_buf = malloc((uint_t)nbytes)) == NULL) { + return (-1); + } + + /* + * Build and execute the uscsi ioctl + */ + (void) memset(mode_sense_buf, 0, nbytes); + (void) memset((char *)&ucmd, 0, sizeof (ucmd)); + (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb)); + + cdb.scc_cmd = SCMD_MODE_SENSE; + FORMG0COUNT(&cdb, (uchar_t)nbytes); + cdb.cdb_opaque[2] = page_control | page_code; + ucmd.uscsi_cdb = (caddr_t)&cdb; + ucmd.uscsi_cdblen = CDB_GROUP0; + ucmd.uscsi_bufaddr = mode_sense_buf; + ucmd.uscsi_buflen = nbytes; + + ucmd.uscsi_flags |= USCSI_SILENT; + ucmd.uscsi_flags |= USCSI_READ; + ucmd.uscsi_timeout = 30; + ucmd.uscsi_flags |= USCSI_RQENABLE; + if (ucmd.uscsi_rqbuf == NULL) { + ucmd.uscsi_rqbuf = rqbuf; + ucmd.uscsi_rqlen = sizeof (rqbuf); + ucmd.uscsi_rqresid = sizeof (rqbuf); + } + ucmd.uscsi_rqstatus = IMPOSSIBLE_SCSI_STATUS; + + status = ioctl(fd, USCSICMD, &ucmd); + + if (status || ucmd.uscsi_status != 0) { + free(mode_sense_buf); + return (-1); + } + + /* + * Verify that the returned data looks reasonabled, + * find the actual page data, and copy it into the + * user's buffer. Copy the mode_header and block_descriptor + * into the header structure, which can then be used to + * return the same data to the drive when issuing a mode select. + */ + hdr = (struct mode_header *)mode_sense_buf; + (void) memset((caddr_t)header, 0, sizeof (struct scsi_ms_header)); + if (hdr->bdesc_length != sizeof (struct block_descriptor) && + hdr->bdesc_length != 0) { + free(mode_sense_buf); + return (-1); + } + (void) memcpy((caddr_t)header, mode_sense_buf, + (int) (sizeof (struct mode_header) + hdr->bdesc_length)); + pg = (struct mode_page *)((ulong_t)mode_sense_buf + + sizeof (struct mode_header) + hdr->bdesc_length); + if (pg->code != page_code) { + free(mode_sense_buf); + return (-1); + } + + /* + * Accept up to "page_size" bytes of mode sense data. + * This allows us to accept both CCS and SCSI-2 + * structures, as long as we request the greater + * of the two. + */ + maximum = page_size - sizeof (struct mode_page) - hdr->bdesc_length; + if (((int)pg->length) > maximum) { + free(mode_sense_buf); + return (-1); + } + + (void) memcpy(page_data, (caddr_t)pg, MODESENSE_PAGE_LEN(pg)); + + free(mode_sense_buf); + return (0); +} diff --git a/usr/src/lib/libdiskmgt/common/entry.c b/usr/src/lib/libdiskmgt/common/entry.c new file mode 100644 index 0000000000..e898e30a15 --- /dev/null +++ b/usr/src/lib/libdiskmgt/common/entry.c @@ -0,0 +1,599 @@ +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <fcntl.h> +#include <libdevinfo.h> +#include <stdio.h> +#include <sys/sunddi.h> +#include <sys/types.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> + +#include "libdiskmgt.h" +#include "disks_private.h" +#include "partition.h" + + +extern dm_desc_type_t drive_assoc_types[]; +extern dm_desc_type_t bus_assoc_types[]; +extern dm_desc_type_t controller_assoc_types[]; +extern dm_desc_type_t media_assoc_types[]; +extern dm_desc_type_t slice_assoc_types[]; +extern dm_desc_type_t partition_assoc_types[]; +extern dm_desc_type_t path_assoc_types[]; +extern dm_desc_type_t alias_assoc_types[]; + +static dm_descriptor_t *ptr_array_to_desc_array(descriptor_t **ptrs, int *errp); +static descriptor_t **desc_array_to_ptr_array(dm_descriptor_t *da, int *errp); + +void +dm_free_descriptor(dm_descriptor_t desc) +{ + descriptor_t *dp; + + if (desc == NULL) { + return; + } + dp = (descriptor_t *)desc; + + cache_wlock(); + cache_free_descriptor(dp); + cache_unlock(); +} + +void +dm_free_descriptors(dm_descriptor_t *desc_list) +{ + descriptor_t **dp; + int error; + + if (desc_list == NULL) { + return; + } + dp = desc_array_to_ptr_array(desc_list, &error); + if (error != 0) { + free(desc_list); + return; + } + + cache_wlock(); + cache_free_descriptors(dp); + cache_unlock(); +} + +/*ARGSUSED*/ +void +dm_free_name(char *name) +{ + free(name); +} + +dm_descriptor_t * +dm_get_associated_descriptors(dm_descriptor_t desc, dm_desc_type_t type, + int *errp) +{ + descriptor_t **descs = NULL; + descriptor_t *dp; + + + dp = (descriptor_t *)desc; + + cache_wlock(); + + if (!cache_is_valid_desc(dp)) { + cache_unlock(); + *errp = EBADF; + return (NULL); + } + + /* verify that the descriptor is still valid */ + if (dp->p.generic == NULL) { + cache_unlock(); + *errp = ENODEV; + return (NULL); + } + + switch (dp->type) { + case DM_DRIVE: + descs = drive_get_assoc_descriptors(dp, type, errp); + break; + case DM_BUS: + descs = bus_get_assoc_descriptors(dp, type, errp); + break; + case DM_CONTROLLER: + descs = controller_get_assoc_descriptors(dp, type, errp); + break; + case DM_MEDIA: + descs = media_get_assoc_descriptors(dp, type, errp); + break; + case DM_SLICE: + descs = slice_get_assoc_descriptors(dp, type, errp); + break; + case DM_PARTITION: + descs = partition_get_assoc_descriptors(dp, type, errp); + break; + case DM_PATH: + descs = path_get_assoc_descriptors(dp, type, errp); + break; + case DM_ALIAS: + descs = alias_get_assoc_descriptors(dp, type, errp); + break; + default: + *errp = EINVAL; + break; + } + + cache_unlock(); + + return (ptr_array_to_desc_array(descs, errp)); +} + +dm_desc_type_t * +dm_get_associated_types(dm_desc_type_t type) +{ + switch (type) { + case DM_DRIVE: + return (drive_assoc_types); + case DM_BUS: + return (bus_assoc_types); + case DM_CONTROLLER: + return (controller_assoc_types); + case DM_MEDIA: + return (media_assoc_types); + case DM_SLICE: + return (slice_assoc_types); + case DM_PARTITION: + return (partition_assoc_types); + case DM_PATH: + return (path_assoc_types); + case DM_ALIAS: + return (alias_assoc_types); + } + + return (NULL); +} + +nvlist_t * +dm_get_attributes(dm_descriptor_t desc, int *errp) +{ + descriptor_t *dp; + nvlist_t *attrs = NULL; + + + dp = (descriptor_t *)desc; + + cache_rlock(); + + if (!cache_is_valid_desc(dp)) { + cache_unlock(); + *errp = EBADF; + return (NULL); + } + + /* verify that the descriptor is still valid */ + if (dp->p.generic == NULL) { + cache_unlock(); + *errp = ENODEV; + return (NULL); + } + + switch (dp->type) { + case DM_DRIVE: + attrs = drive_get_attributes(dp, errp); + break; + case DM_BUS: + attrs = bus_get_attributes(dp, errp); + break; + case DM_CONTROLLER: + attrs = controller_get_attributes(dp, errp); + break; + case DM_MEDIA: + attrs = media_get_attributes(dp, errp); + break; + case DM_SLICE: + attrs = slice_get_attributes(dp, errp); + break; + case DM_PARTITION: + attrs = partition_get_attributes(dp, errp); + break; + case DM_PATH: + attrs = path_get_attributes(dp, errp); + break; + case DM_ALIAS: + attrs = alias_get_attributes(dp, errp); + break; + default: + *errp = EINVAL; + break; + } + + cache_unlock(); + + return (attrs); +} + +dm_descriptor_t +dm_get_descriptor_by_name(dm_desc_type_t desc_type, char *name, int *errp) +{ + dm_descriptor_t desc = NULL; + + + cache_wlock(); + + switch (desc_type) { + case DM_DRIVE: + desc = (uintptr_t)drive_get_descriptor_by_name(name, errp); + break; + case DM_BUS: + desc = (uintptr_t)bus_get_descriptor_by_name(name, errp); + break; + case DM_CONTROLLER: + desc = (uintptr_t)controller_get_descriptor_by_name(name, + errp); + break; + case DM_MEDIA: + desc = (uintptr_t)media_get_descriptor_by_name(name, errp); + break; + case DM_SLICE: + desc = (uintptr_t)slice_get_descriptor_by_name(name, errp); + break; + case DM_PARTITION: + desc = (uintptr_t)partition_get_descriptor_by_name(name, + errp); + break; + case DM_PATH: + desc = (uintptr_t)path_get_descriptor_by_name(name, errp); + break; + case DM_ALIAS: + desc = (uintptr_t)alias_get_descriptor_by_name(name, errp); + break; + default: + *errp = EINVAL; + break; + } + + cache_unlock(); + + return (desc); +} + +dm_descriptor_t * +dm_get_descriptors(dm_desc_type_t type, int filter[], int *errp) +{ + descriptor_t **descs = NULL; + + + cache_wlock(); + + switch (type) { + case DM_DRIVE: + descs = drive_get_descriptors(filter, errp); + break; + case DM_BUS: + descs = bus_get_descriptors(filter, errp); + break; + case DM_CONTROLLER: + descs = controller_get_descriptors(filter, errp); + break; + case DM_MEDIA: + descs = media_get_descriptors(filter, errp); + break; + case DM_SLICE: + descs = slice_get_descriptors(filter, errp); + break; + case DM_PARTITION: + descs = partition_get_descriptors(filter, errp); + break; + case DM_PATH: + descs = path_get_descriptors(filter, errp); + break; + case DM_ALIAS: + descs = alias_get_descriptors(filter, errp); + break; + default: + *errp = EINVAL; + break; + } + + cache_unlock(); + + return (ptr_array_to_desc_array(descs, errp)); +} + +char * +dm_get_name(dm_descriptor_t desc, int *errp) +{ + descriptor_t *dp; + char *nm = NULL; + char *name = NULL; + + dp = (descriptor_t *)desc; + + cache_rlock(); + + if (!cache_is_valid_desc(dp)) { + cache_unlock(); + *errp = EBADF; + return (NULL); + } + + /* verify that the descriptor is still valid */ + if (dp->p.generic == NULL) { + cache_unlock(); + *errp = ENODEV; + return (NULL); + } + + switch (dp->type) { + case DM_DRIVE: + nm = (drive_get_name(dp)); + break; + case DM_BUS: + nm = (bus_get_name(dp)); + break; + case DM_CONTROLLER: + nm = (controller_get_name(dp)); + break; + case DM_MEDIA: + nm = (media_get_name(dp)); + break; + case DM_SLICE: + nm = (slice_get_name(dp)); + break; + case DM_PARTITION: + nm = (partition_get_name(dp)); + break; + case DM_PATH: + nm = (path_get_name(dp)); + break; + case DM_ALIAS: + nm = (alias_get_name(dp)); + break; + } + + cache_unlock(); + + *errp = 0; + if (nm != NULL) { + name = strdup(nm); + if (name == NULL) { + *errp = ENOMEM; + return (NULL); + } + return (name); + } + return (NULL); +} + +nvlist_t * +dm_get_stats(dm_descriptor_t desc, int stat_type, int *errp) +{ + descriptor_t *dp; + nvlist_t *stats = NULL; + + + dp = (descriptor_t *)desc; + + cache_rlock(); + + if (!cache_is_valid_desc(dp)) { + cache_unlock(); + *errp = EBADF; + return (NULL); + } + + /* verify that the descriptor is still valid */ + if (dp->p.generic == NULL) { + cache_unlock(); + *errp = ENODEV; + return (NULL); + } + + switch (dp->type) { + case DM_DRIVE: + stats = drive_get_stats(dp, stat_type, errp); + break; + case DM_BUS: + stats = bus_get_stats(dp, stat_type, errp); + break; + case DM_CONTROLLER: + stats = controller_get_stats(dp, stat_type, errp); + break; + case DM_MEDIA: + stats = media_get_stats(dp, stat_type, errp); + break; + case DM_SLICE: + stats = slice_get_stats(dp, stat_type, errp); + break; + case DM_PARTITION: + stats = partition_get_stats(dp, stat_type, errp); + break; + case DM_PATH: + stats = path_get_stats(dp, stat_type, errp); + break; + case DM_ALIAS: + stats = alias_get_stats(dp, stat_type, errp); + break; + default: + *errp = EINVAL; + break; + } + + cache_unlock(); + + return (stats); +} + +dm_desc_type_t +dm_get_type(dm_descriptor_t desc) +{ + descriptor_t *dp; + + dp = (descriptor_t *)desc; + + cache_rlock(); + + if (!cache_is_valid_desc(dp)) { + cache_unlock(); + return (-1); + } + + cache_unlock(); + + return (dp->type); +} + +void +libdiskmgt_add_str(nvlist_t *attrs, char *name, char *val, int *errp) +{ + if (*errp == 0) { + *errp = nvlist_add_string(attrs, name, val); + } +} + +descriptor_t ** +libdiskmgt_empty_desc_array(int *errp) +{ + descriptor_t **empty; + + empty = (descriptor_t **)calloc(1, sizeof (descriptor_t *)); + if (empty == NULL) { + *errp = ENOMEM; + return (NULL); + } + empty[0] = NULL; + + *errp = 0; + return (empty); +} + +void +libdiskmgt_init_debug() +{ + char *valp; + + if ((valp = getenv(DM_DEBUG)) != NULL) { + dm_debug = atoi(valp); + } +} + +int +libdiskmgt_str_eq(char *nm1, char *nm2) +{ + if (nm1 == NULL) { + if (dm_debug) { + (void) fprintf(stderr, "WARNING: str_eq nm1 NULL\n"); + } + + if (nm2 == NULL) { + return (1); + } else { + return (0); + } + } + + /* nm1 != NULL */ + + if (nm2 == NULL) { + if (dm_debug) { + (void) fprintf(stderr, "WARNING: str_eq nm2 NULL\n"); + } + return (0); + } + + if (strcmp(nm1, nm2) == 0) { + return (1); + } + + return (0); +} + +/*ARGSUSED*/ +static descriptor_t ** +desc_array_to_ptr_array(dm_descriptor_t *descs, int *errp) +{ +#ifdef _LP64 + return ((descriptor_t **)descs); +#else + /* convert the 64 bit descriptors to 32 bit ptrs */ + int cnt; + int i; + descriptor_t **da; + + for (cnt = 0; descs[cnt]; cnt++); + + da = (descriptor_t **)calloc(cnt + 1, sizeof (descriptor_t *)); + if (da == NULL) { + *errp = ENOMEM; + return (NULL); + } + + for (i = 0; descs[i]; i++) { + da[i] = (descriptor_t *)descs[i]; + } + *errp = 0; + free(descs); + + return (da); +#endif +} + +/*ARGSUSED*/ +static dm_descriptor_t * +ptr_array_to_desc_array(descriptor_t **ptrs, int *errp) +{ +#ifdef _LP64 + return ((dm_descriptor_t *)ptrs); +#else + /* convert the 32 bit ptrs to the 64 bit descriptors */ + int cnt; + int i; + dm_descriptor_t *da; + + if (*errp != 0 || ptrs == NULL) { + return (NULL); + } + + for (cnt = 0; ptrs[cnt]; cnt++); + + da = (dm_descriptor_t *)calloc(cnt + 1, sizeof (dm_descriptor_t)); + if (da == NULL) { + *errp = ENOMEM; + return (NULL); + } + + for (i = 0; ptrs[i]; i++) { + da[i] = (uintptr_t)ptrs[i]; + } + *errp = 0; + free(ptrs); + + return (da); +#endif +} diff --git a/usr/src/lib/libdiskmgt/common/events.c b/usr/src/lib/libdiskmgt/common/events.c new file mode 100644 index 0000000000..e6dc869d2a --- /dev/null +++ b/usr/src/lib/libdiskmgt/common/events.c @@ -0,0 +1,496 @@ +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <stdlib.h> +#include <stropts.h> +#include <synch.h> +#include <thread.h> +#include <libsysevent.h> +#include <sys/sysevent/eventdefs.h> +#include <sys/sysevent/dev.h> +#include <errno.h> +#include <libgen.h> +#include <unistd.h> + +#include "libdiskmgt.h" +#include "disks_private.h" + +struct event_list { + struct event_list *next; + nvlist_t *event; +}; + +static struct event_list *events = NULL; +static int event_error = 0; +static int event_break = 0; +static mutex_t queue_lock; +static sema_t semaphore; + +/* + * When we add a controller we get an add event for each drive on the + * controller. We don't want to walk the devtree for each drive since + * we will get the same information each time. So, the solution is to + * wait for a few seconds for all of the add events to come in and then + * do a single walk. If an add event comes in after we start the walk, we + * need to do another walk since we might have missed that drive. + * + * State: 0 - no walker; 1 - walker waiting; 2 - walker running + * 0 -> 1; wait a few seconds + * 1 -> 2; walking the devtree + * 2 -> either 0 or 1 (see below) + * While running (state 2), if event comes in, go back to waiting (state 1) + * after the walk otherwise go back to none (state 0). + * + * walker_lock protects walker_state & events_pending + */ +#define WALK_NONE 0 +#define WALK_WAITING 1 +#define WALK_RUNNING 2 +#define WALK_WAIT_TIME 60 /* wait 60 seconds */ + +static mutex_t walker_lock; +static int walker_state = WALK_NONE; +static int events_pending = 0; + +static int sendevents = 0; + +static void add_event_to_queue(nvlist_t *event); +static void cb_watch_events(); +static void event_handler(sysevent_t *ev); +static void print_nvlist(char *prefix, nvlist_t *list); +static void walk_devtree(); +static void walker(void *arg); + +static void(*callback)(nvlist_t *, int) = NULL; + +nvlist_t * +dm_get_event(int *errp) +{ + nvlist_t *event = NULL; + + *errp = 0; + + /* wait until there is an event in the queue */ + /*CONSTCOND*/ + while (1) { + (void) sema_wait(&semaphore); + + if (event_break) { + event_break = 0; + *errp = EINTR; + break; + } + + (void) mutex_lock(&queue_lock); + + /* first see if we ran out of memory since the last call */ + if (event_error != 0) { + *errp = event_error; + event_error = 0; + + } else if (events != NULL) { + struct event_list *tmpp; + + event = events->event; + tmpp = events->next; + free(events); + events = tmpp; + } + + (void) mutex_unlock(&queue_lock); + + if (*errp != 0 || event != NULL) { + break; + } + } + + return (event); +} + +void +dm_init_event_queue(void (*cb)(nvlist_t *, int), int *errp) +{ + if (sendevents == 1) { + /* we were already initialized, see what changes to make */ + *errp = 0; + if (cb != callback) { + + callback = cb; + if (cb == NULL) { + /* clearing the cb so shutdown the internal cb thread */ + event_break = 1; + (void) sema_post(&semaphore); + + } else { + /* installing a cb; we didn't have one before */ + thread_t watch_thread; + + *errp = thr_create(NULL, NULL, + (void *(*)(void *))cb_watch_events, NULL, THR_DAEMON, + &watch_thread); + } + } + + } else { + /* first time to initialize */ + sendevents = 1; + + *errp = sema_init(&semaphore, 0, USYNC_THREAD, NULL); + if (*errp != 0) { + return; + } + + if (cb != NULL) { + thread_t watch_thread; + + callback = cb; + + *errp = thr_create(NULL, NULL, + (void *(*)(void *))cb_watch_events, NULL, THR_DAEMON, + &watch_thread); + } + } +} + +void +events_new_event(char *name, int dtype, char *etype) +{ + nvlist_t *event = NULL; + + if (!sendevents) { + return; + } + + if (nvlist_alloc(&event, NVATTRS, 0) != 0) { + event = NULL; + + } else { + int error = 0; + + if (name != NULL && + nvlist_add_string(event, DM_EV_NAME, name) != 0) { + error = ENOMEM; + } + + if (dtype != -1 && + nvlist_add_uint32(event, DM_EV_DTYPE, dtype) != 0) { + error = ENOMEM; + } + + if (nvlist_add_string(event, DM_EV_TYPE, etype) != 0) { + error = ENOMEM; + } + + if (error != 0) { + nvlist_free(event); + event = NULL; + } + } + + add_event_to_queue(event); +} + +void +events_new_slice_event(char *dev, char *type) +{ + events_new_event(basename(dev), DM_SLICE, type); +} + +int +events_start_event_watcher() +{ + sysevent_handle_t *shp; + const char *subclass_list[1]; + + /* Bind event handler and create subscriber handle */ + shp = sysevent_bind_handle(event_handler); + if (shp == NULL) { + if (dm_debug) { + /* keep going when we're debugging */ + (void) fprintf(stderr, "ERROR: bind failed %d\n", errno); + return (0); + } + return (errno); + } + + subclass_list[0] = ESC_DISK; + if (sysevent_subscribe_event(shp, EC_DEV_ADD, subclass_list, 1) != 0) { + if (dm_debug) { + /* keep going when we're debugging */ + (void) fprintf(stderr, "ERROR: subscribe failed\n"); + return (0); + } + return (errno); + } + if (sysevent_subscribe_event(shp, EC_DEV_REMOVE, subclass_list, 1) + != 0) { + if (dm_debug) { + /* keep going when we're debugging */ + (void) fprintf(stderr, "ERROR: subscribe failed\n"); + return (0); + } + return (errno); + } + + return (0); +} + +static void +add_event_to_queue(nvlist_t *event) +{ + (void) mutex_lock(&queue_lock); + + if (event == NULL) { + event_error = ENOMEM; + (void) mutex_unlock(&queue_lock); + return; + } + + if (events == NULL) { + + events = (struct event_list *)malloc(sizeof (struct event_list)); + if (events == NULL) { + event_error = ENOMEM; + nvlist_free(event); + } else { + events->next = NULL; + events->event = event; + } + + } else { + /* already have events in the queue */ + struct event_list *ep; + struct event_list *new_event; + + /* find the last element in the list */ + for (ep = events; ep->next != NULL; ep = ep->next); + + new_event = (struct event_list *)malloc(sizeof (struct event_list)); + if (new_event == NULL) { + event_error = ENOMEM; + nvlist_free(event); + } else { + new_event->next = NULL; + new_event->event = event; + ep->next = new_event; + } + } + + (void) mutex_unlock(&queue_lock); + + (void) sema_post(&semaphore); +} + +static void +cb_watch_events() +{ + nvlist_t *event; + int error; + + /*CONSTCOND*/ + while (1) { + event = dm_get_event(&error); + if (callback == NULL) { + /* end the thread */ + return; + } + callback(event, error); + } +} + +static void +event_handler(sysevent_t *ev) +{ + char *class_name; + char *pub; + + class_name = sysevent_get_class_name(ev); + if (dm_debug) { + (void) fprintf(stderr, "****EVENT: %s %s ", class_name, + sysevent_get_subclass_name(ev)); + if ((pub = sysevent_get_pub_name(ev)) != NULL) { + (void) fprintf(stderr, "%s\n", pub); + free(pub); + } else { + (void) fprintf(stderr, "\n"); + } + } + + if (libdiskmgt_str_eq(class_name, EC_DEV_ADD)) { + /* batch up the adds into a single devtree walk */ + walk_devtree(); + + } else if (libdiskmgt_str_eq(class_name, EC_DEV_REMOVE)) { + nvlist_t *nvlist = NULL; + char *dev_name = NULL; + + (void) sysevent_get_attr_list(ev, &nvlist); + if (nvlist != NULL) { + (void) nvlist_lookup_string(nvlist, DEV_NAME, &dev_name); + + if (dm_debug) { + print_nvlist("**** ", nvlist); + } + } + + if (dev_name != NULL) { + cache_update(DM_EV_DISK_DELETE, dev_name); + } + + if (nvlist != NULL) { + nvlist_free(nvlist); + } + } +} + +/* + * This is a debugging function only. + */ +static void +print_nvlist(char *prefix, nvlist_t *list) +{ + nvpair_t *nvp; + + nvp = nvlist_next_nvpair(list, NULL); + while (nvp != NULL) { + char *attrname; + char *str; + uint32_t ui32; + uint64_t ui64; + char **str_array; + uint_t cnt; + int i; + + attrname = nvpair_name(nvp); + switch (nvpair_type(nvp)) { + case DATA_TYPE_STRING: + (void) nvpair_value_string(nvp, &str); + (void) fprintf(stderr, "%s%s: %s\n", prefix, attrname, str); + break; + + case DATA_TYPE_STRING_ARRAY: + (void) nvpair_value_string_array(nvp, &str_array, &cnt); + (void) fprintf(stderr, "%s%s:\n", prefix, attrname); + for (i = 0; i < cnt; i++) { + (void) fprintf(stderr, "%s %s\n", prefix, str_array[i]); + } + break; + + case DATA_TYPE_UINT32: + (void) nvpair_value_uint32(nvp, &ui32); + (void) fprintf(stderr, "%s%s: %u\n", prefix, attrname, ui32); + break; + + case DATA_TYPE_UINT64: + (void) nvpair_value_uint64(nvp, &ui64); +#ifdef _LP64 + (void) fprintf(stderr, "%s%s: %lu\n", prefix, attrname, ui64); +#else + (void) fprintf(stderr, "%s%s: %llu\n", prefix, attrname, ui64); +#endif + break; + + + case DATA_TYPE_BOOLEAN: + (void) fprintf(stderr, "%s%s: true\n", prefix, attrname); + break; + + default: + (void) fprintf(stderr, "%s%s: UNSUPPORTED TYPE\n", prefix, + attrname); + break; + } + + nvp = nvlist_next_nvpair(list, nvp); + } +} + +/* + * Batch up the adds into a single devtree walk. We can get a bunch of + * adds when we add a controller since we will get an add event for each + * drive. + */ +static void +walk_devtree() +{ + thread_t walk_thread; + + (void) mutex_lock(&walker_lock); + + switch (walker_state) { + case WALK_NONE: + if (thr_create(NULL, NULL, (void *(*)(void *))walker, NULL, + THR_DAEMON, &walk_thread) == 0) { + walker_state = WALK_WAITING; + } + break; + + case WALK_WAITING: + /* absorb the event and do nothing */ + break; + + case WALK_RUNNING: + events_pending = 1; + break; + } + + (void) mutex_unlock(&walker_lock); +} + +/*ARGSUSED*/ +static void +walker(void *arg) +{ + int walk_again = 0; + + do { + /* start by wating for a few seconds to absorb extra events */ + (void) sleep(WALK_WAIT_TIME); + + (void) mutex_lock(&walker_lock); + walker_state = WALK_RUNNING; + (void) mutex_unlock(&walker_lock); + + cache_update(DM_EV_DISK_ADD, NULL); + + (void) mutex_lock(&walker_lock); + + if (events_pending) { + events_pending = 0; + walker_state = WALK_WAITING; + walk_again = 1; + } else { + walker_state = WALK_NONE; + walk_again = 0; + } + + (void) mutex_unlock(&walker_lock); + + } while (walk_again); +} diff --git a/usr/src/lib/libdiskmgt/common/findevs.c b/usr/src/lib/libdiskmgt/common/findevs.c new file mode 100644 index 0000000000..39a9ddfd16 --- /dev/null +++ b/usr/src/lib/libdiskmgt/common/findevs.c @@ -0,0 +1,1885 @@ +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <fcntl.h> +#include <libdevinfo.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/sunddi.h> +#include <sys/types.h> +#include <ctype.h> +#include <libgen.h> +#include <unistd.h> +#include <devid.h> + +#include "libdiskmgt.h" +#include "disks_private.h" + +#define CLUSTER_DEV "did" + +/* specify which disk links to use in the /dev directory */ +#define DEVLINK_REGEX "rdsk/.*" +#define DEVLINK_FLOPPY_REGEX "rdiskette[0-9]" +#define DEVLINK_DID_REGEX "did/rdsk/.*" + +#define FLOPPY_NAME "rdiskette" + +#define MAXPROPLEN 1024 +#define DEVICE_ID_PROP "devid" +#define PROD_ID_PROP "inquiry-product-id" +#define PROD_ID_USB_PROP "usb-product-name" +#define REMOVABLE_PROP "removable-media" +#define SCSI_OPTIONS_PROP "scsi-options" +#define VENDOR_ID_PROP "inquiry-vendor-id" +#define VENDOR_ID_USB_PROP "usb-vendor-name" +#define WWN_PROP "node-wwn" + +/* The list of names of possible disk types used by libdevinfo. */ +static char *disktypes[] = { + DDI_NT_BLOCK, + DDI_NT_BLOCK_CHAN, + DDI_NT_BLOCK_WWN, + DDI_NT_BLOCK_FABRIC, + DDI_NT_CD_CHAN, + DDI_NT_CD, + DDI_NT_FD, + NULL +}; + +/* + * Most of the removable media will be lumped under here; CD, DVD, MO, etc. + */ +static char *cdromtypes[] = { + DDI_NT_CD_CHAN, + DDI_NT_CD, + NULL +}; + +static char *ctrltypes[] = { + DDI_NT_SCSI_NEXUS, + DDI_NT_SCSI_ATTACHMENT_POINT, + DDI_NT_FC_ATTACHMENT_POINT, + NULL +}; + +static char *bustypes[] = { + "sbus", + "pci", + "usb", + NULL +}; + +static bus_t *add_bus(struct search_args *args, di_node_t node, + di_minor_t minor, controller_t *cp); +static int add_cluster_devs(di_node_t node, di_minor_t minor, + void *arg); +static controller_t *add_controller(struct search_args *args, + di_node_t node, di_minor_t minor); +static int add_devpath(di_devlink_t devlink, void *arg); +static int add_devs(di_node_t node, di_minor_t minor, void *arg); +static int add_disk2controller(disk_t *diskp, + struct search_args *args); +static int add_disk2path(disk_t *dp, path_t *pp, + di_path_state_t st, char *wwn); +static int add_int2array(int p, int **parray); +static int add_ptr2array(void *p, void ***parray); +static char *bus_type(di_node_t node, di_minor_t minor, + di_prom_handle_t ph); +static int can_remove_controller(controller_t *cp, + controller_t *currp); +static void clean_paths(struct search_args *args); +static disk_t *create_disk(char *deviceid, char *kernel_name, + struct search_args *args); +static char *ctype(di_node_t node, di_minor_t minor); +static boolean_t disk_is_cdrom(char *type); +static alias_t *find_alias(disk_t *diskp, char *kernel_name); +static bus_t *find_bus(struct search_args *args, char *name); +static controller_t *find_controller(struct search_args *args, char *name); +static int fix_cluster_devpath(di_devlink_t devlink, void *arg); +static disk_t *get_disk_by_deviceid(disk_t *listp, char *devid); +static void get_disk_name_from_path(char *path, char *name, + int size); +static char *get_byte_prop(char *prop_name, di_node_t node); +static di_node_t get_parent_bus(di_node_t node, + struct search_args *args); +static int get_prom_int(char *prop_name, di_node_t node, + di_prom_handle_t ph); +static char *get_prom_str(char *prop_name, di_node_t node, + di_prom_handle_t ph); +static int get_prop(char *prop_name, di_node_t node); +static char *get_str_prop(char *prop_name, di_node_t node); +static int have_disk(struct search_args *args, char *devid, + char *kernel_name, disk_t **diskp); +static int is_cluster_disk(di_node_t node, di_minor_t minor); +static int is_ctds(char *name); +static int is_drive(di_minor_t minor); +static int is_HBA(di_node_t node, di_minor_t minor); +static int new_alias(disk_t *diskp, char *kernel_path, + char *devlink_path, struct search_args *args); +static int new_devpath(alias_t *ap, char *devpath); +static path_t *new_path(controller_t *cp, disk_t *diskp, + di_node_t node, di_path_state_t st, char *wwn); +static void remove_invalid_controller(char *name, + controller_t *currp, struct search_args *args); +static char *str_case_index(register char *s1, register char *s2); + +/* + * The functions in this file do a dev tree walk to build up a model of the + * disks, controllers and paths on the system. This model is returned in the + * args->disk_listp and args->controller_listp members of the args param. + * There is no global data for this file so it is thread safe. It is up to + * the caller to merge the resulting model with any existing model that is + * cached. The caller must also free the memory for this model when it is + * no longer needed. + */ +void +findevs(struct search_args *args) +{ + uint_t flags; + di_node_t di_root; + + args->dev_walk_status = 0; + args->disk_listp = NULL; + args->controller_listp = NULL; + args->bus_listp = NULL; + + args->handle = di_devlink_init(NULL, 0); + + /* + * Have to make several passes at this with the new devfs caching. + * First, we find non-mpxio devices. Then we find mpxio/multipath + * devices. Finally, we get cluster devices. + */ + flags = DINFOCACHE; + di_root = di_init("/", flags); + args->ph = di_prom_init(); + (void) di_walk_minor(di_root, NULL, 0, args, add_devs); + di_fini(di_root); + + flags = DINFOCPYALL | DINFOPATH; + di_root = di_init("/", flags); + (void) di_walk_minor(di_root, NULL, 0, args, add_devs); + di_fini(di_root); + + /* do another pass to clean up cluster devpaths */ + flags = DINFOCACHE; + di_root = di_init("/", flags); + (void) di_walk_minor(di_root, DDI_PSEUDO, 0, args, add_cluster_devs); + if (args->ph != DI_PROM_HANDLE_NIL) { + (void) di_prom_fini(args->ph); + } + di_fini(di_root); + + (void) di_devlink_fini(&(args->handle)); + + clean_paths(args); +} + +/* + * Definitions of private functions + */ + +static bus_t * +add_bus(struct search_args *args, di_node_t node, di_minor_t minor, + controller_t *cp) +{ + char *btype; + char *devpath; + bus_t *bp; + char kstat_name[MAXPATHLEN]; + di_node_t pnode; + + if (node == DI_NODE_NIL) { + return (NULL); + } + + if ((btype = bus_type(node, minor, args->ph)) == NULL) { + return (add_bus(args, di_parent_node(node), + di_minor_next(di_parent_node(node), NULL), cp)); + } + + devpath = di_devfs_path(node); + + if ((bp = find_bus(args, devpath)) != NULL) { + di_devfs_path_free((void *) devpath); + + if (cp != NULL) { + if (add_ptr2array(cp, (void ***)&bp->controllers) != 0) { + args->dev_walk_status = ENOMEM; + return (NULL); + } + } + return (bp); + } + + /* Special handling for root node. */ + if (strcmp(devpath, "/") == 0) { + di_devfs_path_free((void *) devpath); + return (NULL); + } + + if (dm_debug) { + (void) fprintf(stderr, "INFO: add_bus %s\n", devpath); + } + + bp = (bus_t *)calloc(1, sizeof (bus_t)); + if (bp == NULL) { + return (NULL); + } + + bp->name = strdup(devpath); + di_devfs_path_free((void *) devpath); + if (bp->name == NULL) { + args->dev_walk_status = ENOMEM; + cache_free_bus(bp); + return (NULL); + } + + bp->btype = strdup(btype); + if (bp->btype == NULL) { + args->dev_walk_status = ENOMEM; + cache_free_bus(bp); + return (NULL); + } + + (void) snprintf(kstat_name, sizeof (kstat_name), "%s%d", + di_node_name(node), di_instance(node)); + + if ((bp->kstat_name = strdup(kstat_name)) == NULL) { + args->dev_walk_status = ENOMEM; + cache_free_bus(bp); + return (NULL); + } + + /* if parent node is a bus, get its name */ + if ((pnode = get_parent_bus(node, args)) != NULL) { + devpath = di_devfs_path(pnode); + bp->pname = strdup(devpath); + di_devfs_path_free((void *) devpath); + if (bp->pname == NULL) { + args->dev_walk_status = ENOMEM; + cache_free_bus(bp); + return (NULL); + } + + } else { + bp->pname = NULL; + } + + bp->freq = get_prom_int("clock-frequency", node, args->ph); + + bp->controllers = (controller_t **)calloc(1, sizeof (controller_t *)); + if (bp->controllers == NULL) { + args->dev_walk_status = ENOMEM; + cache_free_bus(bp); + return (NULL); + } + bp->controllers[0] = NULL; + + if (cp != NULL) { + if (add_ptr2array(cp, (void ***)&bp->controllers) != 0) { + args->dev_walk_status = ENOMEM; + return (NULL); + } + } + + bp->next = args->bus_listp; + args->bus_listp = bp; + + return (bp); +} + +static int +add_cluster_devs(di_node_t node, di_minor_t minor, void *arg) +{ + struct search_args *args; + char *devpath; + char slice_path[MAXPATHLEN]; + int result = DI_WALK_CONTINUE; + + if (!is_cluster_disk(node, minor)) { + return (DI_WALK_CONTINUE); + } + + args = (struct search_args *)arg; + + if (dm_debug > 1) { + /* This is all just debugging code */ + char *devpath; + char dev_name[MAXPATHLEN]; + + devpath = di_devfs_path(node); + (void) snprintf(dev_name, sizeof (dev_name), "%s:%s", devpath, + di_minor_name(minor)); + di_devfs_path_free((void *) devpath); + + (void) fprintf(stderr, "INFO: cluster dev: %s\n", dev_name); + } + + args->node = node; + args->minor = minor; + args->dev_walk_status = 0; + + /* + * Fix the devpaths for the cluster drive. + * + * We will come through here once for each raw slice device name. + */ + devpath = di_devfs_path(node); + (void) snprintf(slice_path, sizeof (slice_path), "%s:%s", devpath, + di_minor_name(minor)); + di_devfs_path_free((void *) devpath); + + /* Walk the /dev tree to get the cluster devlinks. */ + (void) di_devlink_walk(args->handle, DEVLINK_DID_REGEX, slice_path, + DI_PRIMARY_LINK, arg, fix_cluster_devpath); + + if (args->dev_walk_status != 0) { + result = DI_WALK_TERMINATE; + } + + return (result); +} + +static controller_t * +add_controller(struct search_args *args, di_node_t node, di_minor_t minor) +{ + char *devpath; + controller_t *cp; + char kstat_name[MAXPATHLEN]; + char *c_type = DM_CTYPE_UNKNOWN; + + devpath = di_devfs_path(node); + + if ((cp = find_controller(args, devpath)) != NULL) { + di_devfs_path_free((void *) devpath); + return (cp); + } + + /* Special handling for fp attachment node. */ + if (strcmp(di_node_name(node), "fp") == 0) { + di_node_t pnode; + + pnode = di_parent_node(node); + if (pnode != DI_NODE_NIL) { + di_devfs_path_free((void *) devpath); + devpath = di_devfs_path(pnode); + + if ((cp = find_controller(args, devpath)) != NULL) { + di_devfs_path_free((void *) devpath); + return (cp); + } + + /* not in the list, create it */ + node = pnode; + c_type = DM_CTYPE_FIBRE; + } + } + + if (dm_debug) { + (void) fprintf(stderr, "INFO: add_controller %s\n", devpath); + } + + cp = (controller_t *)calloc(1, sizeof (controller_t)); + if (cp == NULL) { + return (NULL); + } + + cp->name = strdup(devpath); + di_devfs_path_free((void *) devpath); + if (cp->name == NULL) { + cache_free_controller(cp); + return (NULL); + } + + if (strcmp(c_type, DM_CTYPE_UNKNOWN) == 0) { + c_type = ctype(node, minor); + } + cp->ctype = c_type; + + (void) snprintf(kstat_name, sizeof (kstat_name), "%s%d", + di_node_name(node), di_instance(node)); + + if ((cp->kstat_name = strdup(kstat_name)) == NULL) { + cache_free_controller(cp); + return (NULL); + } + + if (libdiskmgt_str_eq(cp->ctype, "scsi")) { + cp->scsi_options = get_prop(SCSI_OPTIONS_PROP, node); + } + + if (libdiskmgt_str_eq(di_node_name(node), "scsi_vhci")) { + cp->multiplex = 1; + } else { + cp->multiplex = 0; + } + + cp->freq = get_prom_int("clock-frequency", node, args->ph); + + cp->disks = (disk_t **)calloc(1, sizeof (disk_t *)); + if (cp->disks == NULL) { + cache_free_controller(cp); + return (NULL); + } + cp->disks[0] = NULL; + + cp->next = args->controller_listp; + args->controller_listp = cp; + + cp->bus = add_bus(args, di_parent_node(node), + di_minor_next(di_parent_node(node), NULL), cp); + + return (cp); +} + +static int +add_devpath(di_devlink_t devlink, void *arg) +{ + struct search_args *args; + char *devidstr; + disk_t *diskp; + char kernel_name[MAXPATHLEN]; + + args = (struct search_args *)arg; + + /* + * Get the diskp value from calling have_disk. Can either be found + * by kernel name or devid. + */ + + diskp = NULL; + devidstr = get_str_prop(DEVICE_ID_PROP, args->node); + (void) snprintf(kernel_name, sizeof (kernel_name), "%s%d", + di_node_name(args->node), di_instance(args->node)); + + (void) have_disk(args, devidstr, kernel_name, &diskp); + + /* + * The devlink_path is usually of the form /dev/rdsk/c0t0d0s0. + * For diskettes it is /dev/rdiskette*. + * On Intel we would also get each fdisk partition as well + * (e.g. /dev/rdsk/c0t0d0p0). + */ + if (diskp != NULL) { + alias_t *ap; + char *devlink_path; + + if (diskp->drv_type != DM_DT_FLOPPY) { + /* + * Add other controllers for multipath disks. This will have + * no effect if the controller relationship is already set up. + */ + if (add_disk2controller(diskp, args) != 0) { + args->dev_walk_status = ENOMEM; + } + } + + (void) snprintf(kernel_name, sizeof (kernel_name), "%s%d", + di_node_name(args->node), di_instance(args->node)); + devlink_path = (char *)di_devlink_path(devlink); + + if (dm_debug > 1) { + (void) fprintf(stderr, "INFO: devpath %s\n", devlink_path); + } + + if ((ap = find_alias(diskp, kernel_name)) == NULL) { + if (new_alias(diskp, kernel_name, devlink_path, args) != 0) { + args->dev_walk_status = ENOMEM; + } + } else { + /* + * It is possible that we have already added this devpath. + * Do not add it again. new_devpath will return a 0 if + * found, and not add the path. + */ + if (new_devpath(ap, devlink_path) != 0) { + args->dev_walk_status = ENOMEM; + } + } + } + + return (DI_WALK_CONTINUE); +} + +static int +add_devs(di_node_t node, di_minor_t minor, void *arg) +{ + struct search_args *args; + int result = DI_WALK_CONTINUE; + + args = (struct search_args *)arg; + + if (dm_debug > 1) { + /* This is all just debugging code */ + char *devpath; + char dev_name[MAXPATHLEN]; + + devpath = di_devfs_path(node); + (void) snprintf(dev_name, sizeof (dev_name), "%s:%s", devpath, + di_minor_name(minor)); + di_devfs_path_free((void *) devpath); + + (void) fprintf(stderr, + "INFO: dev: %s, node: %s%d, minor: 0x%x, type: %s\n", + dev_name, + di_node_name(node), di_instance(node), + di_minor_spectype(minor), + (di_minor_nodetype(minor) != NULL ? + di_minor_nodetype(minor) : "NULL")); + } + + if (bus_type(node, minor, args->ph) != NULL) { + if (add_bus(args, node, minor, NULL) == NULL) { + args->dev_walk_status = ENOMEM; + result = DI_WALK_TERMINATE; + } + + } else if (is_HBA(node, minor)) { + if (add_controller(args, node, minor) == NULL) { + args->dev_walk_status = ENOMEM; + result = DI_WALK_TERMINATE; + } + + } else if (di_minor_spectype(minor) == S_IFCHR && is_drive(minor)) { + char *devidstr; + char kernel_name[MAXPATHLEN]; + disk_t *diskp; + + (void) snprintf(kernel_name, sizeof (kernel_name), "%s%d", + di_node_name(node), di_instance(node)); + devidstr = get_str_prop(DEVICE_ID_PROP, node); + + args->node = node; + args->minor = minor; + + /* Check if we already got this disk and this is another slice */ + if (!have_disk(args, devidstr, kernel_name, &diskp)) { + + args->dev_walk_status = 0; + /* This is a newly found disk, create the disk structure. */ + diskp = create_disk(devidstr, kernel_name, args); + if (diskp == NULL) { + args->dev_walk_status = ENOMEM; + } + + if (diskp->drv_type != DM_DT_FLOPPY) { + /* add the controller relationship */ + if (args->dev_walk_status == 0) { + if (add_disk2controller(diskp, args) != 0) { + args->dev_walk_status = ENOMEM; + } + } + } + } + + /* Add the devpaths for the drive. */ + if (args->dev_walk_status == 0) { + char *devpath; + char slice_path[MAXPATHLEN]; + char *pattern; + + /* + * We will come through here once for each of the raw slice + * device names. + */ + devpath = di_devfs_path(node); + (void) snprintf(slice_path, sizeof (slice_path), "%s:%s", + devpath, di_minor_name(minor)); + di_devfs_path_free((void *) devpath); + + if (libdiskmgt_str_eq(di_minor_nodetype(minor), DDI_NT_FD)) { + pattern = DEVLINK_FLOPPY_REGEX; + } else { + pattern = DEVLINK_REGEX; + } + + /* Walk the /dev tree to get the devlinks. */ + (void) di_devlink_walk(args->handle, pattern, slice_path, + DI_PRIMARY_LINK, arg, add_devpath); + } + + if (args->dev_walk_status != 0) { + result = DI_WALK_TERMINATE; + } + } + + return (result); +} + +static int +add_disk2controller(disk_t *diskp, struct search_args *args) +{ + di_node_t pnode; + controller_t *cp; + di_minor_t minor; + di_node_t node; + int i; + + node = args->node; + + pnode = di_parent_node(node); + if (pnode == DI_NODE_NIL) { + return (0); + } + + minor = di_minor_next(pnode, NULL); + if (minor == NULL) { + return (0); + } + + if ((cp = add_controller(args, pnode, minor)) == NULL) { + return (ENOMEM); + } + + /* check if the disk <-> ctrl assoc is already there */ + for (i = 0; diskp->controllers[i]; i++) { + if (cp == diskp->controllers[i]) { + return (0); + } + } + + /* this is a new controller for this disk */ + + /* add the disk to the controlller */ + if (add_ptr2array(diskp, (void ***)&cp->disks) != 0) { + return (ENOMEM); + } + + /* add the controlller to the disk */ + if (add_ptr2array(cp, (void ***)&diskp->controllers) != 0) { + return (ENOMEM); + } + + /* + * Set up paths for mpxio controlled drives. + */ + if (libdiskmgt_str_eq(di_node_name(pnode), "scsi_vhci")) { + /* note: mpxio di_path stuff is all consolidation private */ + di_path_t pi = DI_PATH_NIL; + + while ((pi = di_path_next_phci(node, pi)) != DI_PATH_NIL) { + int cnt; + uchar_t *bytes; + char str[MAXPATHLEN]; + char *wwn; + + di_node_t phci_node = di_path_phci_node(pi); + + /* get the node wwn */ + cnt = di_path_prop_lookup_bytes(pi, WWN_PROP, &bytes); + wwn = NULL; + if (cnt > 0) { + int i; + + str[0] = 0; + for (i = 0; i < cnt; i++) { + char bstr[8]; /* a byte is only 2 hex chars + null */ + + (void) snprintf(bstr, sizeof (bstr), "%.2x", bytes[i]); + (void) strlcat(str, bstr, sizeof (str)); + } + wwn = str; + } + + if (new_path(cp, diskp, phci_node, di_path_state(pi), wwn) + == NULL) { + return (ENOMEM); + } + } + } + + return (0); +} + +static int +add_disk2path(disk_t *dp, path_t *pp, di_path_state_t st, char *wwn) +{ + /* add the disk to the path */ + if (add_ptr2array(dp, (void ***)&pp->disks) != 0) { + cache_free_path(pp); + return (0); + } + + /* add the path to the disk */ + if (add_ptr2array(pp, (void ***)&dp->paths) != 0) { + cache_free_path(pp); + return (0); + } + + /* add the path state for this disk */ + if (add_int2array(st, &pp->states) != 0) { + cache_free_path(pp); + return (0); + } + + /* add the path state for this disk */ + if (wwn != NULL) { + char *wp; + + if ((wp = strdup(wwn)) != NULL) { + if (add_ptr2array(wp, (void ***)(&pp->wwns)) != 0) { + cache_free_path(pp); + return (0); + } + } + } + + return (1); +} + +static int +add_int2array(int p, int **parray) +{ + int i; + int cnt; + int *pa; + int *new_array; + + pa = *parray; + + cnt = 0; + if (pa != NULL) { + for (; pa[cnt] != -1; cnt++); + } + + new_array = (int *)calloc(cnt + 2, sizeof (int *)); + if (new_array == NULL) { + return (ENOMEM); + } + + /* copy the existing array */ + for (i = 0; i < cnt; i++) { + new_array[i] = pa[i]; + } + + new_array[i] = p; + new_array[i + 1] = -1; + + free(pa); + *parray = new_array; + + return (0); +} + +static int +add_ptr2array(void *p, void ***parray) +{ + int i; + int cnt; + void **pa; + void **new_array; + + pa = *parray; + + cnt = 0; + if (pa != NULL) { + for (; pa[cnt]; cnt++); + } + + new_array = (void **)calloc(cnt + 2, sizeof (void *)); + if (new_array == NULL) { + return (ENOMEM); + } + + /* copy the existing array */ + for (i = 0; i < cnt; i++) { + new_array[i] = pa[i]; + } + + new_array[i] = p; + new_array[i + 1] = NULL; + + free(pa); + *parray = new_array; + + return (0); +} + +/* + * This double checks that we aren't going to get into a bad situation. + * This function should never fail, but I just want to double check things. + */ +static int +can_remove_controller(controller_t *cp, controller_t *currp) +{ + if (dm_debug) { + if (cp == currp) { + (void) fprintf(stderr, "ERROR: remove current controller\n"); + } + + if (cp->disks != NULL && cp->disks[0] != NULL) { + (void) fprintf(stderr, + "ERROR: remove controller with disk ptrs\n"); + } + + if (cp->paths != NULL && cp->paths[0] != NULL) { + (void) fprintf(stderr, + "ERROR: remove controller with path ptrs\n"); + } + } + + return (1); +} + +/* + * If we have a controller in the list that is really a path then we need to + * take that controller out of the list since nodes that are paths are not + * considered to be controllers. + */ +static void +clean_paths(struct search_args *args) +{ + controller_t *cp; + + cp = args->controller_listp; + while (cp != NULL) { + path_t **pp; + + pp = cp->paths; + if (pp != NULL) { + int i; + + for (i = 0; pp[i]; i++) { + remove_invalid_controller(pp[i]->name, cp, args); + } + } + cp = cp->next; + } +} + +static disk_t * +create_disk(char *deviceid, char *kernel_name, struct search_args *args) +{ + disk_t *diskp; + char *type; + char *prod_id; + char *vendor_id; + + if (dm_debug) { + (void) fprintf(stderr, "INFO: create_disk %s\n", kernel_name); + } + + diskp = calloc(1, sizeof (disk_t)); + if (diskp == NULL) { + return (NULL); + } + + diskp->controllers = (controller_t **) + calloc(1, sizeof (controller_t *)); + if (diskp->controllers == NULL) { + cache_free_disk(diskp); + return (NULL); + } + diskp->controllers[0] = NULL; + + diskp->devid = NULL; + if (deviceid != NULL) { + if ((diskp->device_id = strdup(deviceid)) == NULL) { + cache_free_disk(diskp); + return (NULL); + } + + (void) devid_str_decode(deviceid, &(diskp->devid), NULL); + } + + if (kernel_name != NULL) { + diskp->kernel_name = strdup(kernel_name); + if (diskp->kernel_name == NULL) { + cache_free_disk(diskp); + return (NULL); + } + } + + diskp->paths = NULL; + diskp->aliases = NULL; + + diskp->cd_rom = 0; + diskp->rpm = 0; + type = di_minor_nodetype(args->minor); + + prod_id = get_str_prop(PROD_ID_PROP, args->node); + if (prod_id != NULL) { + if ((diskp->product_id = strdup(prod_id)) == NULL) { + cache_free_disk(diskp); + return (NULL); + } + } else { + prod_id = get_str_prop(PROD_ID_USB_PROP, args->node); + if (prod_id != NULL) { + if ((diskp->product_id = strdup(prod_id)) == NULL) { + cache_free_disk(diskp); + return (NULL); + } + } + } + + vendor_id = get_str_prop(VENDOR_ID_PROP, args->node); + if (vendor_id != NULL) { + if ((diskp->vendor_id = strdup(vendor_id)) == NULL) { + cache_free_disk(diskp); + return (NULL); + } + } else { + vendor_id = get_str_prop(VENDOR_ID_PROP, args->node); + if (vendor_id != NULL) { + if ((diskp->vendor_id = strdup(vendor_id)) == NULL) { + cache_free_disk(diskp); + return (NULL); + } + } + } + + /* + * DVD, CD-ROM, CD-RW, MO, etc. are all reported as CD-ROMS. + * We try to use uscsi later to determine the real type. + * The cd_rom flag tells us that the kernel categorized the drive + * as a CD-ROM. We leave the drv_type as UKNOWN for now. + * The combination of the cd_rom flag being set with the drv_type of + * unknown is what triggers the uscsi probe in drive.c. + */ + if (disk_is_cdrom(type)) { + diskp->drv_type = DM_DT_UNKNOWN; + diskp->cd_rom = 1; + diskp->removable = 1; + } else if (libdiskmgt_str_eq(type, DDI_NT_FD)) { + diskp->drv_type = DM_DT_FLOPPY; + diskp->removable = 1; + } else { + /* not a "CD-ROM" or Floppy */ + + diskp->removable = get_prop(REMOVABLE_PROP, args->node); + if (diskp->removable == -1) { + diskp->removable = 0; +#ifdef i386 + /* + * x86 does not have removable property. Check for common + * removable drives, zip & jaz, and mark those correctly. + */ + if (vendor_id != NULL && prod_id != NULL) { + if (str_case_index(vendor_id, "iomega") != NULL) { + if (str_case_index(prod_id, "jaz") != NULL) { + diskp->removable = 1; + } else if (str_case_index(prod_id, "zip") != NULL) { + diskp->removable = 1; + } + } + } +#endif + } + + if (diskp->removable) { + /* + * For removable jaz or zip drives there is no way + * to get the drive type unless media is inserted, so we + * look at the product-id for a hint. + */ + + diskp->drv_type = DM_DT_UNKNOWN; + + if (prod_id != NULL) { + if (str_case_index(prod_id, "jaz") != NULL) { + diskp->drv_type = DM_DT_JAZ; + } else if (str_case_index(prod_id, "zip") != NULL) { + diskp->drv_type = DM_DT_ZIP; + } + } + } else { + diskp->drv_type = DM_DT_FIXED; + } + } + diskp->volm_path_set = 0; + diskp->volm_path = NULL; + + diskp->next = args->disk_listp; + args->disk_listp = diskp; + + return (diskp); +} + +static char * +ctype(di_node_t node, di_minor_t minor) +{ + char *type; + char *name; + + type = di_minor_nodetype(minor); + name = di_node_name(node); + + /* IDE disks use SCSI nexus as the type, so handle this special case */ + if (libdiskmgt_str_eq(name, "ide")) { + return (DM_CTYPE_ATA); + } + + if (libdiskmgt_str_eq(di_minor_name(minor), "scsa2usb")) { + return (DM_CTYPE_USB); + } + + if (libdiskmgt_str_eq(type, DDI_NT_SCSI_NEXUS) || + libdiskmgt_str_eq(type, DDI_NT_SCSI_ATTACHMENT_POINT)) { + return (DM_CTYPE_SCSI); + } + + if (libdiskmgt_str_eq(type, DDI_NT_FC_ATTACHMENT_POINT)) { + return (DM_CTYPE_FIBRE); + } + + if (libdiskmgt_str_eq(type, DDI_NT_NEXUS) && + libdiskmgt_str_eq(name, "fp")) { + return (DM_CTYPE_FIBRE); + } + + if (libdiskmgt_str_eq(type, DDI_PSEUDO) && + libdiskmgt_str_eq(name, "ide")) { + return (DM_CTYPE_ATA); + } + + return (DM_CTYPE_UNKNOWN); +} + +static boolean_t +disk_is_cdrom(char *type) +{ + + int type_index; + + for (type_index = 0; cdromtypes[type_index] != NULL; type_index++) { + if (libdiskmgt_str_eq(type, cdromtypes[type_index])) { + return (B_TRUE); + } + } + + return (B_FALSE); +} + +static alias_t * +find_alias(disk_t *diskp, char *kernel_name) +{ + alias_t *ap; + + ap = diskp->aliases; + while (ap != NULL) { + if (libdiskmgt_str_eq(ap->kstat_name, kernel_name)) { + return (ap); + } + ap = ap->next; + } + + return (NULL); +} + +static bus_t * +find_bus(struct search_args *args, char *name) +{ + bus_t *listp; + + listp = args->bus_listp; + while (listp != NULL) { + if (libdiskmgt_str_eq(listp->name, name)) { + return (listp); + } + + listp = listp->next; + } + + return (NULL); +} + +static controller_t * +find_controller(struct search_args *args, char *name) +{ + controller_t *listp; + + listp = args->controller_listp; + while (listp != NULL) { + if (libdiskmgt_str_eq(listp->name, name)) { + return (listp); + } + + listp = listp->next; + } + + return (NULL); +} + +static int +fix_cluster_devpath(di_devlink_t devlink, void *arg) +{ + int fd; + struct search_args *args; + char *devlink_path; + disk_t *diskp = NULL; + alias_t *ap = NULL; + + /* + * The devlink_path is of the form /dev/did/rdsk/d1s0. + */ + + args = (struct search_args *)arg; + + /* Find the disk by the deviceid we read from the cluster disk. */ + devlink_path = (char *)di_devlink_path(devlink); + if (devlink_path == NULL) { + return (DI_WALK_CONTINUE); + } + + if ((fd = open(devlink_path, O_RDONLY|O_NDELAY)) >= 0) { + ddi_devid_t devid; + + if (dm_debug > 1) { + (void) fprintf(stderr, "INFO: cluster devpath %s\n", + devlink_path); + } + + if (devid_get(fd, &devid) == 0) { + char *minor; + char *devidstr; + + minor = di_minor_name(args->minor); + + if ((devidstr = devid_str_encode(devid, minor)) != NULL) { + diskp = get_disk_by_deviceid(args->disk_listp, devidstr); + + /* + * This really shouldn't happen, since we should have + * found all of the disks during our first pass through + * the dev tree, but just in case... + */ + if (diskp == NULL) { + if (dm_debug > 1) { + (void) fprintf(stderr, + "INFO: cluster create disk\n"); + } + + diskp = create_disk(devidstr, NULL, args); + if (diskp == NULL) { + args->dev_walk_status = ENOMEM; + } + + /* add the controller relationship */ + if (args->dev_walk_status == 0) { + if (add_disk2controller(diskp, args) != 0) { + args->dev_walk_status = ENOMEM; + } + } + + if (new_alias(diskp, NULL, devlink_path, args) + != 0) { + args->dev_walk_status = ENOMEM; + } + } + + devid_str_free(devidstr); + } + + devid_free(devid); + } + (void) close(fd); + } + + + if (diskp != NULL) { + if (dm_debug > 1) { + (void) fprintf(stderr, "INFO: cluster found disk\n"); + } + + ap = diskp->aliases; + } + + if (ap != NULL) { + /* NOTE: if ap->next != NULL have cluster disks w/ multiple paths */ + + if (!ap->cluster) { + char *basep; + char *namep; + int cnt = 0; + int size; + char alias[MAXPATHLEN]; + + /* + * First time; save the /dev/rdsk devpaths and update the + * alias info with the new alias name. + */ + ap->orig_paths = ap->devpaths; + ap->devpaths = NULL; + + free(ap->alias); + + /* get the new cluster alias name */ + basep = strrchr(devlink_path, '/'); + if (basep == NULL) { + basep = devlink_path; + } else { + basep++; + } + + size = sizeof (alias) - 1; + namep = alias; + while (*basep != 0 && *basep != 's' && cnt < size) { + *namep++ = *basep++; + cnt++; + } + *namep = 0; + + if ((ap->alias = strdup(alias)) == NULL) { + args->dev_walk_status = ENOMEM; + } + + ap->cluster = 1; + } + + if (new_devpath(ap, devlink_path) != 0) { + args->dev_walk_status = ENOMEM; + } + } + + return (DI_WALK_CONTINUE); +} + +/* + * Check if we have the drive in our list, based upon the device id. + * We got the device id from the dev tree walk. This is encoded + * using devid_str_encode(3DEVID). In order to check the device ids we need + * to use the devid_compare(3DEVID) function, so we need to decode the + * string representation of the device id. + */ +static disk_t * +get_disk_by_deviceid(disk_t *listp, char *devidstr) +{ + ddi_devid_t devid; + + if (devidstr == NULL || devid_str_decode(devidstr, &devid, NULL) != 0) { + return (NULL); + } + + while (listp != NULL) { + if (listp->devid != NULL && + devid_compare(listp->devid, devid) == 0) { + break; + } + + listp = listp->next; + } + + devid_free(devid); + + return (listp); +} + +/* + * Get the base disk name with no path prefix and no slice (if there is one). + * The name parameter should be big enough to hold the name. + * This handles diskette names ok (/dev/rdiskette0) since there is no slice, + * and converts the raw diskette name. + * But, we don't know how to strip off the slice from third party drive + * names. That just means that their drive name will include a slice on + * it. + */ +static void +get_disk_name_from_path(char *path, char *name, int size) +{ + char *basep; + int cnt = 0; + + basep = strrchr(path, '/'); + if (basep == NULL) { + basep = path; + } else { + basep++; + } + + size = size - 1; /* leave room for terminating 0 */ + + if (is_ctds(basep)) { + while (*basep != 0 && *basep != 's' && cnt < size) { + *name++ = *basep++; + cnt++; + } + *name = 0; + } else { + if (strncmp(basep, FLOPPY_NAME, sizeof (FLOPPY_NAME) - 1) == 0) { + /* + * a floppy, convert rdiskette name to diskette name, + * by skipping over the 'r' for raw diskette + */ + basep++; + } + + /* not a ctds name, just copy it */ + (void) strlcpy(name, basep, size); + } +} + +static char * +get_byte_prop(char *prop_name, di_node_t node) +{ + int cnt; + uchar_t *bytes; + int i; + char str[MAXPATHLEN]; + + cnt = di_prop_lookup_bytes(DDI_DEV_T_ANY, node, prop_name, &bytes); + if (cnt < 1) { + return (NULL); + } + + str[0] = 0; + for (i = 0; i < cnt; i++) { + char bstr[8]; /* a byte is only 2 hex chars + null */ + + (void) snprintf(bstr, sizeof (bstr), "%.2x", bytes[i]); + (void) strlcat(str, bstr, sizeof (str)); + } + return (strdup(str)); +} + +static di_node_t +get_parent_bus(di_node_t node, struct search_args *args) +{ + di_node_t pnode; + + pnode = di_parent_node(node); + if (pnode == DI_NODE_NIL) { + return (NULL); + } + + if (bus_type(pnode, di_minor_next(pnode, NULL), args->ph) != NULL) { + return (pnode); + } + + return (get_parent_bus(pnode, args)); +} + +static int +get_prom_int(char *prop_name, di_node_t node, di_prom_handle_t ph) +{ + int *n; + + if (di_prom_prop_lookup_ints(ph, node, prop_name, &n) == 1) { + return (*n); + } + + return (0); +} + +static char * +get_prom_str(char *prop_name, di_node_t node, di_prom_handle_t ph) +{ + char *str; + + if (di_prom_prop_lookup_strings(ph, node, prop_name, &str) == 1) { + return (str); + } + + return (NULL); +} + +/* + * Get one of the positive int or boolean properties. + */ +static int +get_prop(char *prop_name, di_node_t node) +{ + int num; + int *ip; + + if ((num = di_prop_lookup_ints(DDI_DEV_T_ANY, node, prop_name, &ip)) + >= 0) { + if (num == 0) { + /* boolean */ + return (1); + } else if (num == 1) { + /* single int */ + return (*ip); + } + } + + return (-1); +} + +static char * +get_str_prop(char *prop_name, di_node_t node) +{ + char *str; + + if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, prop_name, &str) == 1) { + return (str); + } + + return (NULL); +} + +/* + * Check if we have the drive in our list, based upon the device id, if the + * drive has a device id, or the kernel name, if it doesn't have a device id. + */ +static int +have_disk(struct search_args *args, char *devidstr, char *kernel_name, + disk_t **diskp) +{ + disk_t *listp; + + *diskp = NULL; + listp = args->disk_listp; + if (devidstr != NULL) { + if ((*diskp = get_disk_by_deviceid(listp, devidstr)) != NULL) { + return (1); + } + + } else { + /* no devid, try matching the kernel names on the drives */ + while (listp != NULL) { + if (libdiskmgt_str_eq(kernel_name, listp->kernel_name)) { + *diskp = listp; + return (1); + } + listp = listp->next; + } + } + + return (0); +} + +static char * +bus_type(di_node_t node, di_minor_t minor, di_prom_handle_t ph) +{ + char *type; + int i; + + type = get_prom_str("device_type", node, ph); + if (type == NULL) { + type = di_node_name(node); + } + + for (i = 0; bustypes[i]; i++) { + if (libdiskmgt_str_eq(type, bustypes[i])) { + return (type); + } + } + + if (minor != NULL && strcmp(di_minor_nodetype(minor), + DDI_NT_USB_ATTACHMENT_POINT) == 0) { + return ("usb"); + } + + return (NULL); +} + +static int +is_cluster_disk(di_node_t node, di_minor_t minor) +{ + if (di_minor_spectype(minor) == S_IFCHR && + libdiskmgt_str_eq(di_minor_nodetype(minor), DDI_PSEUDO) && + libdiskmgt_str_eq(di_node_name(node), CLUSTER_DEV)) { + return (1); + } + + return (0); +} + +/* + * If the input name is in c[t]ds format then return 1, otherwise return 0. + */ +static int +is_ctds(char *name) +{ + char *p; + + p = name; + + if (*p++ != 'c') { + return (0); + } + /* skip controller digits */ + while (isdigit(*p)) { + p++; + } + + /* handle optional target */ + if (*p == 't') { + p++; + /* skip over target */ + while (isdigit(*p) || isupper(*p)) { + p++; + } + } + + if (*p++ != 'd') { + return (0); + } + while (isdigit(*p)) { + p++; + } + + if (*p++ != 's') { + return (0); + } + + /* check the slice number */ + while (isdigit(*p)) { + p++; + } + + if (*p != 0) { + return (0); + } + + return (1); +} + +static int +is_drive(di_minor_t minor) +{ + char *type; + int type_index; + + type = di_minor_nodetype(minor); + type_index = 0; + + while (disktypes[type_index] != NULL) { + if (libdiskmgt_str_eq(type, disktypes[type_index])) { + return (1); + } + type_index++; + } + + return (0); +} + +static int +is_HBA(di_node_t node, di_minor_t minor) +{ + char *type; + char *name; + int type_index; + + type = di_minor_nodetype(minor); + type_index = 0; + + while (ctrltypes[type_index] != NULL) { + if (libdiskmgt_str_eq(type, ctrltypes[type_index])) { + return (1); + } + type_index++; + } + + name = di_node_name(node); + if (libdiskmgt_str_eq(type, DDI_PSEUDO) && + libdiskmgt_str_eq(name, "ide")) { + return (1); + } + + return (0); +} + +static int +new_alias(disk_t *diskp, char *kernel_name, char *devlink_path, + struct search_args *args) +{ + alias_t *aliasp; + char alias[MAXPATHLEN]; + di_node_t pnode; + + aliasp = malloc(sizeof (alias_t)); + if (aliasp == NULL) { + return (ENOMEM); + } + + aliasp->alias = NULL; + aliasp->kstat_name = NULL; + aliasp->wwn = NULL; + aliasp->devpaths = NULL; + aliasp->orig_paths = NULL; + + get_disk_name_from_path(devlink_path, alias, sizeof (alias)); + + aliasp->alias = strdup(alias); + if (aliasp->alias == NULL) { + cache_free_alias(aliasp); + return (ENOMEM); + } + + if (kernel_name != NULL) { + aliasp->kstat_name = strdup(kernel_name); + if (aliasp->kstat_name == NULL) { + cache_free_alias(aliasp); + return (ENOMEM); + } + } else { + aliasp->kstat_name = NULL; + } + + aliasp->cluster = 0; + aliasp->lun = get_prop(DM_LUN, args->node); + aliasp->target = get_prop(DM_TARGET, args->node); + aliasp->wwn = get_byte_prop(WWN_PROP, args->node); + + pnode = di_parent_node(args->node); + if (pnode != DI_NODE_NIL) { + char prop_name[MAXPROPLEN]; + + (void) snprintf(prop_name, sizeof (prop_name), + "target%d-sync-speed", aliasp->target); + diskp->sync_speed = get_prop(prop_name, pnode); + (void) snprintf(prop_name, sizeof (prop_name), "target%d-wide", + aliasp->target); + diskp->wide = get_prop(prop_name, pnode); + } + + if (new_devpath(aliasp, devlink_path) != 0) { + cache_free_alias(aliasp); + return (ENOMEM); + } + + aliasp->next = diskp->aliases; + diskp->aliases = aliasp; + + return (0); +} + +/* + * Append the new devpath to the end of the devpath list. This is important + * since we may want to use the order of the devpaths to match up the vtoc + * entries. + */ +static int +new_devpath(alias_t *ap, char *devpath) +{ + slice_t *newdp; + slice_t *alistp; + + /* + * First, search the alias list to be sure that this devpath is + * not already there. + */ + + for (alistp = ap->devpaths; alistp != NULL; alistp = alistp->next) { + if (libdiskmgt_str_eq(alistp->devpath, devpath)) { + return (0); + } + } + + /* + * Otherwise, not found so add this new devpath to the list. + */ + + newdp = malloc(sizeof (slice_t)); + if (newdp == NULL) { + return (ENOMEM); + } + + newdp->devpath = strdup(devpath); + if (newdp->devpath == NULL) { + free(newdp); + return (ENOMEM); + } + newdp->slice_num = -1; + newdp->next = NULL; + + if (ap->devpaths == NULL) { + ap->devpaths = newdp; + } else { + /* append the devpath to the end of the list */ + slice_t *dp; + + dp = ap->devpaths; + while (dp->next != NULL) { + dp = dp->next; + } + + dp->next = newdp; + } + + return (0); +} + +static path_t * +new_path(controller_t *cp, disk_t *dp, di_node_t node, di_path_state_t st, + char *wwn) +{ + char *devpath; + path_t *pp; + di_minor_t minor; + + /* Special handling for fp attachment node. */ + if (strcmp(di_node_name(node), "fp") == 0) { + di_node_t pnode; + + pnode = di_parent_node(node); + if (pnode != DI_NODE_NIL) { + node = pnode; + } + } + + devpath = di_devfs_path(node); + + /* check if the path is already there */ + pp = NULL; + if (cp->paths != NULL) { + int i; + + for (i = 0; cp->paths[i]; i++) { + if (libdiskmgt_str_eq(devpath, cp->paths[i]->name)) { + pp = cp->paths[i]; + break; + } + } + } + + if (pp != NULL) { + /* the path exists, add this disk to it */ + + di_devfs_path_free((void *) devpath); + + if (!add_disk2path(dp, pp, st, wwn)) { + return (NULL); + } + + return (pp); + } + + /* create a new path */ + + pp = calloc(1, sizeof (path_t)); + if (pp == NULL) { + di_devfs_path_free((void *) devpath); + return (NULL); + } + + pp->name = strdup(devpath); + di_devfs_path_free((void *) devpath); + if (pp->name == NULL) { + cache_free_path(pp); + return (NULL); + } + + /* add the disk to the path */ + if (!add_disk2path(dp, pp, st, wwn)) { + return (NULL); + } + + /* add the path to the controller */ + if (add_ptr2array(pp, (void ***)&cp->paths) != 0) { + cache_free_path(pp); + return (NULL); + } + + /* add the controller to the path */ + pp->controller = cp; + + minor = di_minor_next(node, NULL); + if (minor != NULL) { + pp->ctype = ctype(node, minor); + } else { + pp->ctype = DM_CTYPE_UNKNOWN; + } + + return (pp); +} + +/* + * We pass in the current controller pointer (currp) so we can double check + * that we aren't corrupting the list by removing the element we are on. This + * should never happen, but it doesn't hurt to double check. + */ +static void +remove_invalid_controller(char *name, controller_t *currp, + struct search_args *args) +{ + controller_t *cp; + bus_t *bp; + controller_t *prevp; + + bp = args->bus_listp; + while (bp != NULL) { + int i; + + for (i = 0; bp->controllers[i]; i++) { + if (libdiskmgt_str_eq(bp->controllers[i]->name, name)) { + int j; + + /* remove pointer to invalid controller (it is a path) */ + for (j = i; bp->controllers[j]; j++) { + bp->controllers[j] = bp->controllers[j + 1]; + } + } + } + bp = bp->next; + } + + if (args->controller_listp == NULL) { + return; + } + + cp = args->controller_listp; + if (libdiskmgt_str_eq(cp->name, name)) { + if (can_remove_controller(cp, currp)) { + args->controller_listp = cp->next; + cache_free_controller(cp); + } + return; + } + + prevp = cp; + cp = cp->next; + while (cp != NULL) { + if (libdiskmgt_str_eq(cp->name, name)) { + if (can_remove_controller(cp, currp)) { + prevp->next = cp->next; + cache_free_controller(cp); + } + return; + } + prevp = cp; + cp = cp->next; + } +} + +/* + * This is the standard strstr code modified for case independence. + */ +static char * +str_case_index(register char *s1, register char *s2) +{ + uint_t s2len = strlen(s2); /* length of the second string */ + + /* If the length of the second string is 0, return the first arg. */ + if (s2len == 0) { + return (s1); + } + + while (strlen(s1) >= s2len) { + if (strncasecmp(s1, s2, s2len) == 0) { + return (s1); + } + s1++; + } + return (NULL); +} diff --git a/usr/src/lib/libdiskmgt/common/inuse_dump.c b/usr/src/lib/libdiskmgt/common/inuse_dump.c new file mode 100644 index 0000000000..7725f42a70 --- /dev/null +++ b/usr/src/lib/libdiskmgt/common/inuse_dump.c @@ -0,0 +1,96 @@ +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Checks for a match with the the dump slice. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <synch.h> +#include <sys/param.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/dumpadm.h> + +#include "libdiskmgt.h" +#include "disks_private.h" + +/* Cached file descriptor for /dev/dump. */ +static int dump_fd = -1; + +static mutex_t dump_lock = DEFAULTMUTEX; + +/* + * Check the dump device against the input slice. + */ +int +inuse_dump(char *slice, nvlist_t *attrs, int *errp) +{ + int found = 0; + int fd; + char device[MAXPATHLEN]; + + *errp = 0; + if (slice == NULL) { + return (found); + } + + /* + * We only want to open /dev/dump once instead of for every + * slice so we cache the open file descriptor. The ioctl + * is cheap so we can do that for every slice. + */ + (void) mutex_lock(&dump_lock); + + if (dump_fd == -1) { + if ((dump_fd = open("/dev/dump", O_RDONLY)) >= 0) + (void) fcntl(dump_fd, F_SETFD, FD_CLOEXEC); + } + + fd = dump_fd; + + (void) mutex_unlock(&dump_lock); + + if (fd != -1) { + if (ioctl(fd, DIOCGETDEV, device) != -1) { + if (strcmp(slice, device) == 0) { + libdiskmgt_add_str(attrs, DM_USED_BY, + DM_USE_DUMP, errp); + libdiskmgt_add_str(attrs, DM_USED_NAME, + DM_USE_DUMP, errp); + found = 1; + } + } + } + + return (found); +} diff --git a/usr/src/lib/libdiskmgt/common/inuse_fs.c b/usr/src/lib/libdiskmgt/common/inuse_fs.c new file mode 100644 index 0000000000..7b37beb8c2 --- /dev/null +++ b/usr/src/lib/libdiskmgt/common/inuse_fs.c @@ -0,0 +1,344 @@ +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <dirent.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <synch.h> +#include <unistd.h> +#include <sys/errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/vfstab.h> +#include <fcntl.h> +#include <sys/wait.h> +#include <sys/fs/ufs_fs.h> + +#include "libdiskmgt.h" +#include "disks_private.h" + +/* + * The list of filesystem heuristic programs. + */ +struct heuristic { + struct heuristic *next; + char *prog; + char *type; +}; + +struct vfstab_list { + char *special; + char *mountp; + struct vfstab_list *next; +}; + +static struct vfstab_list *vfstab_listp = NULL; +static mutex_t vfstab_lock = DEFAULTMUTEX; + +static time_t timestamp = 0; + +static struct heuristic *hlist = NULL; +static int initialized = 0; +static mutex_t init_lock = DEFAULTMUTEX; + +static int has_fs(char *prog, char *slice); +static int load_heuristics(); +static int add_use_record(struct vfstab *vp); +static int load_vfstab(); +static void free_vfstab(struct vfstab_list *listp); + +/* + * Use the heuristics to check for a filesystem on the slice. + */ +int +inuse_fs(char *slice, nvlist_t *attrs, int *errp) +{ + struct heuristic *hp; + time_t curr_time; + int found = 0; + + + *errp = 0; + + if (slice == NULL) { + return (0); + } + + /* + * We get the list of heuristic programs one time. + */ + (void) mutex_lock(&init_lock); + if (!initialized) { + *errp = load_heuristics(); + + if (*errp == 0) { + initialized = 1; + } + } + (void) mutex_unlock(&init_lock); + + /* Run each of the heuristics. */ + for (hp = hlist; hp; hp = hp->next) { + if (has_fs(hp->prog, slice)) { + libdiskmgt_add_str(attrs, DM_USED_BY, DM_USE_FS, errp); + libdiskmgt_add_str(attrs, DM_USED_NAME, hp->type, errp); + found = 1; + } + } + + if (*errp != 0) + return (found); + + /* + * Second heuristic used is the check for an entry in vfstab + */ + + (void) mutex_lock(&vfstab_lock); + curr_time = time(NULL); + + if (timestamp < curr_time && (curr_time - timestamp) > 60) { + free_vfstab(vfstab_listp); + *errp = load_vfstab(); + timestamp = curr_time; + } + + if (*errp == 0) { + struct vfstab_list *listp; + listp = vfstab_listp; + + while (listp != NULL) { + if (strcmp(slice, listp->special) == 0) { + char *mountp = ""; + + if (listp->mountp != NULL) + mountp = listp->mountp; + + libdiskmgt_add_str(attrs, DM_USED_BY, DM_USE_VFSTAB, errp); + libdiskmgt_add_str(attrs, DM_USED_NAME, mountp, errp); + found = 1; + break; + } + listp = listp->next; + } + } + (void) mutex_unlock(&vfstab_lock); + return (found); +} + +static int +has_fs(char *prog, char *slice) +{ + pid_t pid; + int loc; + mode_t mode = S_IRUSR | S_IWUSR; + + switch ((pid = fork1())) { + case 0: + /* child process */ + + closefrom(1); + (void) open("/dev/null", O_WRONLY, mode); + (void) open("/dev/null", O_WRONLY, mode); + (void) execl(prog, "fstyp", slice, NULL); + _exit(1); + break; + + case -1: + return (0); + + default: + /* parent process */ + break; + } + + (void) waitpid(pid, &loc, 0); + + if (WIFEXITED(loc) && WEXITSTATUS(loc) == 0) { + return (1); + } + + return (0); +} + +/* + * Create a list of filesystem heuristic programs. + */ +static int +load_heuristics() +{ + DIR *dirp; + + if ((dirp = opendir("/usr/lib/fs")) != NULL) { + struct dirent *dp; + + while ((dp = readdir(dirp)) != NULL) { + char path[MAXPATHLEN]; + struct stat buf; + DIR *subdirp; + + /* skip known dirs */ + if (strcmp(dp->d_name, ".") == 0 || + strcmp(dp->d_name, "..") == 0) { + continue; + } + + (void) snprintf(path, sizeof (path), "/usr/lib/fs/%s", + dp->d_name); + + if (stat(path, &buf) != 0 || !(buf.st_mode & S_IFDIR)) { + continue; + } + + if ((subdirp = opendir(path)) != NULL) { + struct dirent *sdp; + + while ((sdp = readdir(subdirp)) != NULL) { + + if (strcmp(sdp->d_name, "fstyp") == 0) { + char progpath[MAXPATHLEN]; + + (void) snprintf(progpath, sizeof (progpath), + "/usr/lib/fs/%s/fstyp", dp->d_name); + + if (stat(progpath, &buf) == 0 && + buf.st_mode & S_IFREG) { + + struct heuristic *hp; + + hp = (struct heuristic *) + malloc(sizeof (struct heuristic)); + + if (hp == NULL) { + (void) closedir(subdirp); + (void) closedir(dirp); + return (ENOMEM); + } + + if ((hp->prog = strdup(progpath)) == NULL) { + (void) closedir(subdirp); + (void) closedir(dirp); + return (ENOMEM); + } + + if ((hp->type = strdup(dp->d_name)) == NULL) { + (void) closedir(subdirp); + (void) closedir(dirp); + return (ENOMEM); + } + + hp->next = hlist; + hlist = hp; + } + + break; + } + } + + (void) closedir(subdirp); + } + } + + (void) closedir(dirp); + } + + return (0); +} + +static int +load_vfstab() +{ + FILE *fp; + struct vfstab vp; + int status = 1; + + fp = fopen(VFSTAB, "r"); + if (fp != NULL) { + (void) memset(&vp, 0, sizeof (struct vfstab)); + while (getvfsent(fp, &vp) == 0) { + status = add_use_record(&vp); + if (status != 0) { + (void) fclose(fp); + return (status); + } + (void) memset(&vp, 0, sizeof (struct vfstab)); + } + (void) fclose(fp); + status = 0; + } + + return (status); +} + +static int +add_use_record(struct vfstab *vp) +{ + struct vfstab_list *vfsp; + + vfsp = (struct vfstab_list *)malloc(sizeof (struct vfstab_list)); + if (vfsp == NULL) { + return (ENOMEM); + } + + vfsp->special = strdup(vp->vfs_special); + if (vfsp->special == NULL) { + free(vfsp); + return (ENOMEM); + } + + if (vp->vfs_mountp != NULL) { + vfsp->mountp = strdup(vp->vfs_mountp); + if (vfsp->mountp == NULL) { + free(vfsp); + return (ENOMEM); + } + } else { + vfsp->mountp = NULL; + } + + vfsp->next = vfstab_listp; + vfstab_listp = vfsp; + + return (0); +} + +static void +free_vfstab(struct vfstab_list *listp) +{ + struct vfstab_list *nextp; + + while (listp != NULL) { + nextp = listp->next; + free((void *)listp->special); + free((void *)listp->mountp); + free((void *)listp); + listp = nextp; + } + + vfstab_listp = NULL; +} diff --git a/usr/src/lib/libdiskmgt/common/inuse_lu.c b/usr/src/lib/libdiskmgt/common/inuse_lu.c new file mode 100644 index 0000000000..48d2089de5 --- /dev/null +++ b/usr/src/lib/libdiskmgt/common/inuse_lu.c @@ -0,0 +1,343 @@ +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Creates and maintains a short-term cache of live upgrade slices. + */ + +#include <dirent.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <synch.h> +#include <sys/errno.h> +#include <sys/param.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/wait.h> + +#include "libdiskmgt.h" +#include "disks_private.h" + +#define TMPNM_SIZE 25 + +/* + * The list of live upgrade slices in use. + */ + +struct lu_list { + struct lu_list *next; + char *slice; + char *name; +}; + +static struct lu_list *lu_listp = NULL; +static time_t timestamp = 0; +static mutex_t lu_lock = DEFAULTMUTEX; + +static int add_use_record(char *devname, char *name); +static void free_lu(struct lu_list *listp); +static int load_lu(); +static int lustatus(int fd); +static int lufslist(int fd); +static int run_cmd(char *path, char *cmd, char *arg, int fd); + +/* + * Search the list of devices under live upgrade for the specified device. + */ +int +inuse_lu(char *slice, nvlist_t *attrs, int *errp) +{ + int found = 0; + time_t curr_time; + + *errp = 0; + + if (slice == NULL) { + return (found); + } + + /* + * We don't want to have to re-read the live upgrade config for + * every slice, but we can't just cache it since there is no event + * when this changes. So, we'll keep the config in memory for + * a short time (1 minute) before reloading it. + */ + (void) mutex_lock(&lu_lock); + + curr_time = time(NULL); + if (timestamp < curr_time && (curr_time - timestamp) > 60) { + free_lu(lu_listp); /* free old entries */ + lu_listp = NULL; + *errp = load_lu(); /* load the cache */ + timestamp = curr_time; + } + + if (*errp == 0) { + struct lu_list *listp; + + listp = lu_listp; + while (listp != NULL) { + if (strcmp(slice, listp->slice) == 0) { + libdiskmgt_add_str(attrs, DM_USED_BY, DM_USE_LU, errp); + libdiskmgt_add_str(attrs, DM_USED_NAME, listp->name, errp); + found = 1; + break; + } + listp = listp->next; + } + } + + (void) mutex_unlock(&lu_lock); + + return (found); +} + +static int +add_use_record(char *devname, char *name) +{ + struct lu_list *sp; + + sp = (struct lu_list *)malloc(sizeof (struct lu_list)); + if (sp == NULL) { + return (ENOMEM); + } + + if ((sp->slice = strdup(devname)) == NULL) { + free(sp); + return (ENOMEM); + } + + if ((sp->name = strdup(name)) == NULL) { + free(sp->slice); + free(sp); + return (ENOMEM); + } + + sp->next = lu_listp; + lu_listp = sp; + + return (0); +} + +/* + * Free the list of liveupgrade entries. + */ +static void +free_lu(struct lu_list *listp) { + + struct lu_list *nextp; + + while (listp != NULL) { + nextp = listp->next; + free((void *)listp->slice); + free((void *)listp->name); + free((void *)listp); + listp = nextp; + } +} + +/* + * Create a list of live upgrade devices. + */ +static int +load_lu() +{ + char tmpname[TMPNM_SIZE]; + int fd; + int status = 0; + + (void) strlcpy(tmpname, "/var/run/dm_lu_XXXXXX", TMPNM_SIZE); + if ((fd = mkstemp(tmpname)) != -1) { + (void) unlink(tmpname); + if (run_cmd("/usr/sbin/lustatus", "lustatus", NULL, fd)) { + status = lustatus(fd); + } else { + (void) close(fd); + } + } + + return (status); +} + +/* + * The XML generated by the live upgrade commands is not parseable by the + * standard Solaris XML parser, so we have to do it ourselves. + */ +static int +lufslist(int fd) +{ + FILE *fp; + char line[MAXPATHLEN]; + int status; + + if ((fp = fdopen(fd, "r")) == NULL) { + (void) close(fd); + return (0); + } + + (void) fseek(fp, 0L, SEEK_SET); + while (fgets(line, sizeof (line), fp) == line) { + char *devp; + char *nmp; + char *ep; + + if (strncmp(line, "<beFsComponent ", 15) != 0) { + continue; + } + + if ((devp = strstr(line, "fsDevice=\"")) == NULL) { + continue; + } + + devp = devp + 10; + + if ((ep = strchr(devp, '"')) == NULL) { + continue; + } + + *ep = 0; + + /* try to get the mountpoint name */ + if ((nmp = strstr(ep + 1, "mountPoint=\"")) != NULL) { + nmp = nmp + 12; + + if ((ep = strchr(nmp, '"')) != NULL) { + *ep = 0; + } else { + nmp = ""; + } + + } else { + nmp = ""; + } + + if ((status = add_use_record(devp, nmp)) != 0) { + break; + } + } + + (void) fclose(fp); + + return (status); +} + +static int +lustatus(int fd) +{ + FILE *fp; + char line[MAXPATHLEN]; + int status = 0; + + if ((fp = fdopen(fd, "r")) == NULL) { + (void) close(fd); + return (0); + } + + (void) fseek(fp, 0L, SEEK_SET); + while (fgets(line, sizeof (line), fp) == line) { + char *sp; + char *ep; + char tmpname[TMPNM_SIZE]; + int ffd; + + if (strncmp(line, "<beStatus ", 10) != 0) { + continue; + } + + if ((sp = strstr(line, "name=\"")) == NULL) { + continue; + } + + sp = sp + 6; + + if ((ep = strchr(sp, '"')) == NULL) { + continue; + } + + *ep = 0; + + (void) strlcpy(tmpname, "/var/run/dm_lu_XXXXXX", TMPNM_SIZE); + if ((ffd = mkstemp(tmpname)) != -1) { + (void) unlink(tmpname); + + if (run_cmd("/usr/sbin/lufslist", "lufslist", sp, ffd) == 0) { + (void) close(ffd); + break; + } + + if ((status = lufslist(ffd)) != 0) { + break; + } + } + } + + (void) fclose(fp); + + return (status); +} + +static int +run_cmd(char *path, char *cmd, char *arg, int fd) +{ + pid_t pid; + int loc; + + /* create the server process */ + switch ((pid = fork1())) { + case 0: + /* child process */ + (void) close(1); + (void) dup(fd); + (void) close(2); + (void) dup(fd); + closefrom(3); + (void) execl(path, cmd, "-X", arg, NULL); + _exit(1); + break; + + case -1: + return (0); + + default: + /* parent process */ + break; + } + + (void) waitpid(pid, &loc, 0); + + /* printf("got 0x%x %d %d\n", loc, WIFEXITED(loc), WEXITSTATUS(loc)); */ + + if (WIFEXITED(loc) && WEXITSTATUS(loc) == 0) { + return (1); + } + + return (0); +} diff --git a/usr/src/lib/libdiskmgt/common/inuse_mnt.c b/usr/src/lib/libdiskmgt/common/inuse_mnt.c new file mode 100644 index 0000000000..54001a46ce --- /dev/null +++ b/usr/src/lib/libdiskmgt/common/inuse_mnt.c @@ -0,0 +1,417 @@ +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Creates and maintains a cache of mount points. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <synch.h> +#include <thread.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/mnttab.h> +#include <sys/swap.h> + +#include "libdiskmgt.h" +#include "disks_private.h" + +/* + * The list of mount point entries in /etc/mnttab + */ + +struct mntpnt_list { + struct mntpnt_list *next; + char *special; + char *mountp; +}; + +static struct mntpnt_list *mntpoint_listp = NULL; +static rwlock_t mntpoint_lock = DEFAULTRWLOCK; +static int initialized = 0; +static mutex_t init_lock = DEFAULTMUTEX; + +static boolean_t diff_mnttab(int send_event, struct mntpnt_list *firstp, + struct mntpnt_list *secondp); +static void free_mnttab(struct mntpnt_list *listp); +static boolean_t in_list(struct mntpnt_list *elementp, + struct mntpnt_list *listp); +static int load_mnttab(int send_event); +static void watch_mnttab(); + +/* + * Search the list of devices from /etc/mnttab to find the mount point + * for the specified device. + */ +int +inuse_mnt(char *slice, nvlist_t *attrs, int *errp) +{ + struct mntpnt_list *listp; + int found = 0; + + *errp = 0; + if (slice == NULL) { + return (found); + } + + (void) mutex_lock(&init_lock); + if (!initialized) { + thread_t mnttab_thread; + + /* load the mntpnt cache */ + *errp = load_mnttab(B_FALSE); + + if (*errp == 0) { + /* start a thread to monitor the mnttab */ + *errp = thr_create(NULL, NULL, (void *(*)(void *))watch_mnttab, + NULL, THR_NEW_LWP | THR_DAEMON, &mnttab_thread); + } + + if (*errp == 0) { + initialized = 1; + } + } + (void) mutex_unlock(&init_lock); + + (void) rw_rdlock(&mntpoint_lock); + listp = mntpoint_listp; + while (listp != NULL) { + if (libdiskmgt_str_eq(slice, listp->special)) { + libdiskmgt_add_str(attrs, DM_USED_BY, DM_USE_MOUNT, errp); + libdiskmgt_add_str(attrs, DM_USED_NAME, listp->mountp, errp); + found = 1; + break; + } + listp = listp->next; + } + (void) rw_unlock(&mntpoint_lock); + + return (found); +} + +/* + * Return true if the lists are different. Send an event for each different + * device. + */ +static boolean_t +diff_mnttab(int send_event, struct mntpnt_list *firstp, + struct mntpnt_list *secondp) +{ + boolean_t different = B_FALSE; + struct mntpnt_list *listp; + + listp = firstp; + while (listp != NULL) { + if (! in_list(listp, secondp)) { + /* not in new list, so was mounted and now unmounted */ + if (send_event) { + events_new_slice_event(listp->special, DM_EV_TCHANGE); + } + different = B_TRUE; + } + listp = listp->next; + } + + listp = secondp; + while (listp != NULL) { + if (! in_list(listp, firstp)) { + /* not in orig list, so this is a new mount */ + if (send_event) { + events_new_slice_event(listp->special, DM_EV_TCHANGE); + } + different = B_TRUE; + } + listp = listp->next; + } + + return (different); +} + +/* + * free_mnttab() + * + * Free the list of metadevices from /etc/mnttab. + */ +static void +free_mnttab(struct mntpnt_list *listp) { + + struct mntpnt_list *nextp; + + while (listp != NULL) { + nextp = listp->next; + free((void *)listp->special); + free((void *)listp->mountp); + free((void *)listp); + listp = nextp; + } +} + +/* + * Return true if the element is in the list. + */ +static boolean_t +in_list(struct mntpnt_list *elementp, struct mntpnt_list *listp) +{ + while (listp != NULL) { + if (libdiskmgt_str_eq(elementp->special, listp->special) && + libdiskmgt_str_eq(elementp->mountp, listp->mountp)) { + return (B_TRUE); + } + listp = listp->next; + } + + return (B_FALSE); +} + +/* + * load_mnttab() + * + * Create a list of devices from /etc/mnttab and swap. + * return 1 if the list has changed, 0 if the list is still the same + */ +static int +load_mnttab(int send_event) +{ + + struct mntpnt_list *currp; + FILE *fp; + struct mntpnt_list *headp; + int num; + struct mntpnt_list *prevp; + + headp = NULL; + prevp = NULL; + + /* get the mnttab entries */ + if ((fp = fopen("/etc/mnttab", "r")) != NULL) { + + struct mnttab entry; + + while (getmntent(fp, &entry) == 0) { + + /* + * Ignore entries that are incomplete or that are not + * devices (skips network mounts, automounter entries, + * /proc, etc.). + */ + if (entry.mnt_special == NULL || + entry.mnt_mountp == NULL || + strncmp(entry.mnt_special, "/dev", 4) != 0) { + continue; + } + + currp = (struct mntpnt_list *)calloc((size_t)1, + (size_t)sizeof (struct mntpnt_list)); + + if (currp == NULL) { + /* + * out of memory, free what we have and return + */ + free_mnttab(headp); + (void) fclose(fp); + return (ENOMEM); + } + + if (headp == NULL) { + headp = currp; + } else { + prevp->next = currp; + } + + currp->next = NULL; + + currp->special = strdup(entry.mnt_special); + if (currp->special == NULL) { + /* + * out of memory, free what we have and return + */ + free_mnttab(headp); + (void) fclose(fp); + return (ENOMEM); + } + + currp->mountp = strdup(entry.mnt_mountp); + if (currp->mountp == NULL) { + /* + * out of memory, free what we have and return + */ + free_mnttab(headp); + (void) fclose(fp); + return (ENOMEM); + } + + prevp = currp; + } + + (void) fclose(fp); + } + + /* get the swap entries */ + if ((num = swapctl(SC_GETNSWP, NULL)) > -1) { + + struct swaptable *st; + struct swapent *swapent; + int i; + char *path; + char *pathstart; + char fullpath[MAXPATHLEN+1]; + + st = malloc((size_t)((num * sizeof (swapent_t)) + sizeof (int))); + if (st == NULL) { + /* out of memory, free what we have and return */ + free_mnttab(headp); + return (ENOMEM); + } + + path = malloc(num * MAXPATHLEN); + if (path == NULL) { + /* out of memory, free what we have and return */ + free(st); + free_mnttab(headp); + return (ENOMEM); + } + pathstart = path; + + swapent = st->swt_ent; + for (i = 0; i < num; i++, swapent++) { + swapent->ste_path = path; + path += MAXPATHLEN; + } + + st->swt_n = num; + if ((num = swapctl(SC_LIST, st)) >= 0) { + swapent = st->swt_ent; + for (i = 0; i < num; i++, swapent++) { + + currp = (struct mntpnt_list *) + calloc((size_t)1, (size_t)sizeof (struct mntpnt_list)); + + if (currp == NULL) { + /* out of memory, free what we have and return */ + free((void *)st); + free((void *)pathstart); + free_mnttab(headp); + return (ENOMEM); + } + + if (headp == NULL) { + headp = currp; + } else { + prevp->next = currp; + } + + currp->next = NULL; + + if (*swapent->ste_path != '/') { + (void) snprintf(fullpath, sizeof (fullpath), "/dev/%s", + swapent->ste_path); + } else { + (void) strlcpy(fullpath, swapent->ste_path, + sizeof (fullpath)); + } + + currp->special = strdup(fullpath); + if (currp->special == NULL) { + /* out of memory, free what we have and return */ + free(st); + free(pathstart); + free_mnttab(headp); + return (ENOMEM); + } + + currp->mountp = strdup("swap"); + if (currp->mountp == NULL) { + /* out of memory, free what we have and return */ + free(st); + free(pathstart); + free_mnttab(headp); + return (ENOMEM); + } + + prevp = currp; + } + } + + free(st); + free(pathstart); + } + + /* note that we unlock the mutex in both paths of this if statement */ + (void) rw_wrlock(&mntpoint_lock); + if (diff_mnttab(send_event, mntpoint_listp, headp) == B_TRUE) { + struct mntpnt_list *tmpp; + + tmpp = mntpoint_listp; + mntpoint_listp = headp; + (void) rw_unlock(&mntpoint_lock); + + /* free the old list */ + free_mnttab(tmpp); + } else { + (void) rw_unlock(&mntpoint_lock); + /* no change that we care about, so keep the current list */ + free_mnttab(headp); + } + return (0); +} + +/* + * This is a thread that runs forever, watching for changes in the mnttab + * that would cause us to flush and reload the cache of mnt entries. Only + * changes to /dev devices will cause the cache to be flushed and reloaded. + */ +static void +watch_mnttab() +{ + struct pollfd fds[1]; + int res; + + if ((fds[0].fd = open("/etc/mnttab", O_RDONLY)) != -1) { + + char buf[81]; + + /* do the initial read so we don't get the event right away */ + (void) read(fds[0].fd, buf, (size_t)(sizeof (buf) - 1)); + (void) lseek(fds[0].fd, 0, SEEK_SET); + + fds[0].events = POLLRDBAND; + while (res = poll(fds, (nfds_t)1, -1)) { + if (res <= 0) + continue; + + (void) load_mnttab(B_TRUE); + + (void) read(fds[0].fd, buf, (size_t)(sizeof (buf) - 1)); + (void) lseek(fds[0].fd, 0, SEEK_SET); + } + } +} diff --git a/usr/src/lib/libdiskmgt/common/inuse_svm.c b/usr/src/lib/libdiskmgt/common/inuse_svm.c new file mode 100644 index 0000000000..9298907af3 --- /dev/null +++ b/usr/src/lib/libdiskmgt/common/inuse_svm.c @@ -0,0 +1,639 @@ +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Creates and maintains a cache of slices used by SVM. + */ + +#include <meta.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <synch.h> +#include <thread.h> +#include <dlfcn.h> +#include <link.h> +#include <libsysevent.h> +#include <sys/types.h> +#include <sys/sysevent/eventdefs.h> + +#include "libdiskmgt.h" +#include "disks_private.h" + +/* + * The list of SVM slices in use. + */ + +struct svm_list { + struct svm_list *next; + char *slice; + char *name; + char *type; +}; + +static struct svm_list *svm_listp = NULL; +static rwlock_t svm_lock = DEFAULTRWLOCK; +static int initialized = 0; +static mutex_t init_lock = DEFAULTMUTEX; + +static int add_use_record(char *devname, char *type, char *mname); +static int diskset_info(mdsetname_t *sp); +static int drive_in_diskset(char *dpath, char *setname); +static void event_handler(); +static void free_names(mdnamelist_t *nlp); +static void free_svm(struct svm_list *listp); +static int init_svm(); +static int load_svm(); +static int new_entry(char *sname, char *type, char *mname, + mdsetname_t *sp); + +/* + * Pointers to libmeta functions that we dynamically resolve. + */ +static set_t (*mdl_get_max_sets)(md_error_t *ep); +static void (*mdl_mdclrerror)(md_error_t *ep); +static md_error_t *mdl_mdnullerror; +static void (*mdl_metaflushnames)(int flush_sr_cache); +static void (*mdl_metaflushsetname)(mdsetname_t *sp); +static void (*mdl_metafreenamelist)(mdnamelist_t *nlp); +static void (*mdl_metafreereplicalist)(md_replicalist_t *rlp); +static md_drive_desc *(*mdl_metaget_drivedesc)(mdsetname_t *sp, int flags, + md_error_t *ep); +static mdname_t *(*mdl_metaname)(mdsetname_t **spp, char *uname, + md_error_t *ep); +static int (*mdl_metareplicalist)(mdsetname_t *sp, int flags, + md_replicalist_t **rlpp, md_error_t *ep); +static mdsetname_t *(*mdl_metasetnosetname)(set_t setno, md_error_t *ep); +static int (*mdl_meta_get_hotspare_names)(mdsetname_t *sp, + mdnamelist_t **nlpp, int options, md_error_t *ep); +static md_raid_t *(*mdl_meta_get_raid)(mdsetname_t *sp, mdname_t *raidnp, + md_error_t *ep); +static int (*mdl_meta_get_raid_names)(mdsetname_t *sp, + mdnamelist_t **nlpp, int options, md_error_t *ep); +static md_sp_t *(*mdl_meta_get_sp)(mdsetname_t *sp, mdname_t *np, + md_error_t *ep); +static int (*mdl_meta_get_sp_names)(mdsetname_t *sp, + mdnamelist_t **nlpp, int options, md_error_t *ep); +static md_stripe_t *(*mdl_meta_get_stripe)(mdsetname_t *sp, + mdname_t *stripenp, md_error_t *ep); +static int (*mdl_meta_get_stripe_names)(mdsetname_t *sp, + mdnamelist_t **nlpp, int options, md_error_t *ep); +static int (*mdl_meta_get_trans_names)(mdsetname_t *sp, + mdnamelist_t **nlpp, int options, md_error_t *ep); +static void (*mdl_meta_invalidate_name)(mdname_t *np); +static void (*mdl_sdssc_bind_library)(); + +/* + * Search the list of devices under SVM for the specified device. + */ +int +inuse_svm(char *slice, nvlist_t *attrs, int *errp) +{ + struct svm_list *listp; + int found = 0; + + *errp = 0; + if (slice == NULL) { + return (found); + } + + (void) mutex_lock(&init_lock); + if (!initialized) { + /* dynamically load libmeta */ + if (init_svm()) { + /* need to initialize the cluster library to avoid seg faults */ + (mdl_sdssc_bind_library)(); + + /* load the SVM cache */ + *errp = load_svm(); + + if (*errp == 0) { + /* start a thread to monitor the svm config */ + sysevent_handle_t *shp; + const char *subclass_list[1]; + + shp = sysevent_bind_handle(event_handler); + if (shp != NULL) { + subclass_list[0] = EC_SUB_ALL; + if (sysevent_subscribe_event(shp, EC_SVM_CONFIG, + subclass_list, 1) != 0) { + *errp = errno; + } + } else { + *errp = errno; + } + } + } + + if (*errp == 0) { + initialized = 1; + } + } + (void) mutex_unlock(&init_lock); + + (void) rw_rdlock(&svm_lock); + listp = svm_listp; + while (listp != NULL) { + if (strcmp(slice, listp->slice) == 0) { + libdiskmgt_add_str(attrs, DM_USED_BY, DM_USE_SVM, errp); + if (strcmp(listp->type, "mdb") == 0 || + strcmp(listp->type, "hs") == 0) { + + libdiskmgt_add_str(attrs, DM_USED_NAME, listp->type, errp); + } else { + char name[MAXPATHLEN]; + (void) snprintf(name, MAXPATHLEN, "%s:%s", listp->type, + listp->name); + libdiskmgt_add_str(attrs, DM_USED_NAME, name, errp); + } + found = 1; + break; + } + listp = listp->next; + } + (void) rw_unlock(&svm_lock); + + return (found); +} + +static int +add_use_record(char *devname, char *type, char *mname) +{ + struct svm_list *sp; + + /* If prev. record is a dup, skip it. */ + if (svm_listp != NULL && strcmp(svm_listp->slice, devname) == 0 && + strcmp(svm_listp->type, type) == 0) { + return (0); + } + + sp = (struct svm_list *)malloc(sizeof (struct svm_list)); + if (sp == NULL) { + return (ENOMEM); + } + + if ((sp->slice = strdup(devname)) == NULL) { + free(sp); + return (ENOMEM); + } + + if ((sp->name = strdup(mname)) == NULL) { + free(sp->slice); + free(sp); + return (ENOMEM); + } + + if ((sp->type = strdup(type)) == NULL) { + free(sp->slice); + free(sp->name); + free(sp); + return (ENOMEM); + } + + sp->next = svm_listp; + svm_listp = sp; + + return (0); +} + +static int +diskset_info(mdsetname_t *sp) +{ + md_error_t error = *mdl_mdnullerror; + md_replicalist_t *replica_list = NULL; + mdnamelist_t *trans_list = NULL; + mdnamelist_t *raid_list = NULL; + mdnamelist_t *stripe_list = NULL; + mdnamelist_t *sp_list = NULL; + mdnamelist_t *spare_list = NULL; + + if ((mdl_metareplicalist)(sp, MD_BASICNAME_OK, &replica_list, &error) + >= 0) { + md_replicalist_t *nlp; + + for (nlp = replica_list; nlp != NULL; nlp = nlp->rl_next) { + if (new_entry(nlp->rl_repp->r_namep->bname, "mdb", + nlp->rl_repp->r_namep->cname, sp)) { + (mdl_metafreereplicalist)(replica_list); + return (ENOMEM); + } + } + (mdl_metafreereplicalist)(replica_list); + + } else { + (mdl_mdclrerror)(&error); + /* there are no metadb's; that is ok, no need to check the rest */ + return (0); + } + (mdl_mdclrerror)(&error); + + if ((mdl_meta_get_trans_names)(sp, &trans_list, 0, &error) >= 0) { + mdnamelist_t *nlp; + + for (nlp = trans_list; nlp != NULL; nlp = nlp->next) { + if (new_entry(nlp->namep->bname, "trans", nlp->namep->cname, + sp)) { + free_names(trans_list); + return (ENOMEM); + } + } + + free_names(trans_list); + } + (mdl_mdclrerror)(&error); + + if ((mdl_meta_get_raid_names)(sp, &raid_list, 0, &error) >= 0) { + mdnamelist_t *nlp; + + for (nlp = raid_list; nlp != NULL; nlp = nlp->next) { + mdname_t *mdn; + md_raid_t *raid; + + mdn = (mdl_metaname)(&sp, nlp->namep->cname, &error); + (mdl_mdclrerror)(&error); + if (mdn == NULL) { + continue; + } + + raid = (mdl_meta_get_raid)(sp, mdn, &error); + (mdl_mdclrerror)(&error); + + if (raid != NULL) { + int i; + + for (i = 0; i < raid->cols.cols_len; i++) { + if (new_entry(raid->cols.cols_val[i].colnamep->bname, + "raid", nlp->namep->cname, sp)) { + free_names(raid_list); + return (ENOMEM); + } + } + } + } + + free_names(raid_list); + } + (mdl_mdclrerror)(&error); + + if ((mdl_meta_get_stripe_names)(sp, &stripe_list, 0, &error) >= 0) { + mdnamelist_t *nlp; + + for (nlp = stripe_list; nlp != NULL; nlp = nlp->next) { + mdname_t *mdn; + md_stripe_t *stripe; + + mdn = (mdl_metaname)(&sp, nlp->namep->cname, &error); + (mdl_mdclrerror)(&error); + if (mdn == NULL) { + continue; + } + + stripe = (mdl_meta_get_stripe)(sp, mdn, &error); + (mdl_mdclrerror)(&error); + + if (stripe != NULL) { + int i; + + for (i = 0; i < stripe->rows.rows_len; i++) { + md_row_t *rowp; + int j; + + rowp = &stripe->rows.rows_val[i]; + + for (j = 0; j < rowp->comps.comps_len; j++) { + md_comp_t *component; + + component = &rowp->comps.comps_val[j]; + if (new_entry(component->compnamep->bname, "stripe", + nlp->namep->cname, sp)) { + free_names(stripe_list); + return (ENOMEM); + } + } + } + } + } + + free_names(stripe_list); + } + (mdl_mdclrerror)(&error); + + if ((mdl_meta_get_sp_names)(sp, &sp_list, 0, &error) >= 0) { + mdnamelist_t *nlp; + + for (nlp = sp_list; nlp != NULL; nlp = nlp->next) { + mdname_t *mdn; + md_sp_t *soft_part; + + mdn = (mdl_metaname)(&sp, nlp->namep->cname, &error); + (mdl_mdclrerror)(&error); + if (mdn == NULL) { + continue; + } + + soft_part = (mdl_meta_get_sp)(sp, mdn, &error); + (mdl_mdclrerror)(&error); + + if (soft_part != NULL) { + if (new_entry(soft_part->compnamep->bname, "sp", + nlp->namep->cname, sp)) { + free_names(sp_list); + return (ENOMEM); + } + } + } + + free_names(sp_list); + } + (mdl_mdclrerror)(&error); + + if ((mdl_meta_get_hotspare_names)(sp, &spare_list, 0, &error) >= 0) { + mdnamelist_t *nlp; + + for (nlp = spare_list; nlp != NULL; nlp = nlp->next) { + if (new_entry(nlp->namep->bname, "hs", nlp->namep->cname, sp)) { + free_names(spare_list); + return (ENOMEM); + } + } + + free_names(spare_list); + } + + (mdl_mdclrerror)(&error); + + return (0); +} + +/* + * SVM uses "drive names" (ctd name without trailing slice) for drives + * in disksets. Since it massages these names there is no direct correspondence + * with the slice device names in /dev. So, we need to massage these names + * back to something we can match on when a slice comes in. We create an + * entry for each possible slice since we don't know what slices actually + * exist. Slice 0 & 7 are probably enough, but the user could have + * repartitioned the drive after they added it to the diskset and removed the + * mdb. + */ +static int +drive_in_diskset(char *dpath, char *setname) +{ + int i; + char path[MAXPATHLEN]; + + (void) strlcpy(path, dpath, sizeof (path)); + if (strncmp(path, "/dev/rdsk/", 10) == 0) { + /* change rdsk to dsk */ + char *p; + + /* start p pointing to r in rdsk */ + for (p = path + 5; *p; p++) { + *p = *(p + 1); + } + } else if (strncmp(path, "/dev/did/rdsk/", 14) == 0) { + /* change rdsk to dsk */ + char *p; + + /* start p pointing to r in rdsk */ + for (p = path + 9; *p; p++) { + *p = *(p + 1); + } + } + + for (i = 0; i < 8; i++) { + char slice[MAXPATHLEN]; + + (void) snprintf(slice, sizeof (slice), "%ss%d", path, i); + if (add_use_record(slice, "diskset", setname)) { + return (ENOMEM); + } + } + + return (0); +} + +static void +event_handler() +{ + (void) rw_wrlock(&svm_lock); + free_svm(svm_listp); + svm_listp = NULL; + (mdl_metaflushnames)(0); + (void) load_svm(); + (void) rw_unlock(&svm_lock); +} + +static void +free_names(mdnamelist_t *nlp) +{ + mdnamelist_t *p; + + for (p = nlp; p != NULL; p = p->next) { + (mdl_meta_invalidate_name)(p->namep); + p->namep = NULL; + } + (mdl_metafreenamelist)(nlp); +} + +/* + * Free the list of SVM entries. + */ +static void +free_svm(struct svm_list *listp) { + + struct svm_list *nextp; + + while (listp != NULL) { + nextp = listp->next; + free((void *)listp->slice); + free((void *)listp->name); + free((void *)listp->type); + free((void *)listp); + listp = nextp; + } +} + +/* + * Try to dynamically link the libmeta functions we need. + */ +static int +init_svm() +{ + void *lh; + + if ((lh = dlopen("/usr/lib/libmeta.so", RTLD_NOW)) == NULL) { + return (0); + } + + mdl_get_max_sets = (set_t (*)(md_error_t *))dlsym(lh, "get_max_sets"); + + mdl_mdclrerror = (void(*)(md_error_t *))dlsym(lh, "mdclrerror"); + + mdl_mdnullerror = (md_error_t *)dlsym(lh, "mdnullerror"); + + mdl_metaflushnames = (void (*)(int))dlsym(lh, "metaflushnames"); + + mdl_metaflushsetname = (void (*)(mdsetname_t *))dlsym(lh, + "metaflushsetname"); + + mdl_metafreenamelist = (void (*)(mdnamelist_t *))dlsym(lh, + "metafreenamelist"); + + mdl_metafreereplicalist = (void (*)(md_replicalist_t *))dlsym(lh, + "metafreereplicalist"); + + mdl_metaget_drivedesc = (md_drive_desc *(*)(mdsetname_t *, int, + md_error_t *))dlsym(lh, "metaget_drivedesc"); + + mdl_metaname = (mdname_t *(*)(mdsetname_t **, char *, md_error_t *)) + dlsym(lh, "metaname"); + + mdl_metareplicalist = (int (*)(mdsetname_t *, int, md_replicalist_t **, + md_error_t *))dlsym(lh, "metareplicalist"); + + mdl_metasetnosetname = (mdsetname_t *(*)(set_t, md_error_t *))dlsym(lh, + "metasetnosetname"); + + mdl_meta_get_hotspare_names = (int (*)(mdsetname_t *, mdnamelist_t **, + int, md_error_t *))dlsym(lh, "meta_get_hotspare_names"); + + mdl_meta_get_raid = (md_raid_t *(*)(mdsetname_t *, mdname_t *, + md_error_t *))dlsym(lh, "meta_get_raid"); + + mdl_meta_get_raid_names = (int (*)(mdsetname_t *, mdnamelist_t **, + int, md_error_t *))dlsym(lh, "meta_get_raid_names"); + + mdl_meta_get_sp = (md_sp_t *(*)(mdsetname_t *, mdname_t *, + md_error_t *))dlsym(lh, "meta_get_sp"); + + mdl_meta_get_sp_names = (int (*)(mdsetname_t *, mdnamelist_t **, + int, md_error_t *))dlsym(lh, "meta_get_sp_names"); + + mdl_meta_get_stripe = (md_stripe_t *(*)(mdsetname_t *, mdname_t *, + md_error_t *))dlsym(lh, "meta_get_stripe"); + + mdl_meta_get_stripe_names = (int (*)(mdsetname_t *, mdnamelist_t **, + int, md_error_t *))dlsym(lh, "meta_get_stripe_names"); + + mdl_meta_get_trans_names = (int (*)(mdsetname_t *, mdnamelist_t **, + int, md_error_t *))dlsym(lh, "meta_get_trans_names"); + + mdl_meta_invalidate_name = (void (*)(mdname_t *))dlsym(lh, + "meta_invalidate_name"); + + mdl_sdssc_bind_library = (void (*)())dlsym(lh, "sdssc_bind_library"); + + return (1); +} + +/* + * Create a list of SVM devices + */ +static int +load_svm() +{ + int max_sets; + md_error_t error = *mdl_mdnullerror; + int i; + + if ((max_sets = (mdl_get_max_sets)(&error)) == 0) { + return (0); + } + + if (!mdisok(&error)) { + (mdl_mdclrerror)(&error); + return (0); + } + + /* for each possible set number, see if we really have a diskset */ + for (i = 0; i < max_sets; i++) { + mdsetname_t *sp; + + if ((sp = (mdl_metasetnosetname)(i, &error)) == NULL) { + + if (!mdisok(&error) && + mdisrpcerror(&error, RPC_PROGNOTREGISTERED)) { + /* metad rpc program not registered - no metasets */ + break; + } + + (mdl_mdclrerror)(&error); + continue; + } + (mdl_mdclrerror)(&error); + + /* pick up drives in disksets with no mdbs/metadevices */ + if (sp->setno != 0) { + md_drive_desc *dd; + + dd = (mdl_metaget_drivedesc)(sp, MD_BASICNAME_OK | PRINT_FAST, + &error); + (mdl_mdclrerror)(&error); + for (; dd != NULL; dd = dd->dd_next) { + if (drive_in_diskset(dd->dd_dnp->rname, sp->setname)) { + (mdl_metaflushsetname)(sp); + return (ENOMEM); + } + } + } + + if (diskset_info(sp)) { + (mdl_metaflushsetname)(sp); + return (ENOMEM); + } + + (mdl_metaflushsetname)(sp); + } + + (mdl_mdclrerror)(&error); + + return (0); +} + +static int +new_entry(char *sname, char *type, char *mname, mdsetname_t *sp) +{ + mdname_t *mdn; + md_error_t error = *mdl_mdnullerror; + + mdn = (mdl_metaname)(&sp, sname, &error); + if (!mdisok(&error)) { + (mdl_mdclrerror)(&error); + return (0); + } + + if (mdn != NULL && ( + mdn->drivenamep->type == MDT_ACCES || + mdn->drivenamep->type == MDT_COMP || + mdn->drivenamep->type == MDT_FAST_COMP)) { + + return (add_use_record(mdn->bname, type, mname)); + } + + return (0); +} diff --git a/usr/src/lib/libdiskmgt/common/inuse_vxvm.c b/usr/src/lib/libdiskmgt/common/inuse_vxvm.c new file mode 100644 index 0000000000..8b464dc70b --- /dev/null +++ b/usr/src/lib/libdiskmgt/common/inuse_vxvm.c @@ -0,0 +1,393 @@ +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Attempt to dynamically link in the Veritas libvxvmsc.so so that we can + * see if there are any Veritas volumes on any of the slices. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <strings.h> +#include <sys/param.h> +#include <sys/errno.h> +#include <thread.h> +#include <synch.h> +#include <dlfcn.h> +#include <link.h> +#include <ctype.h> + +#include "libdiskmgt.h" +#include "disks_private.h" + +#define VXVM_NAME_SIZE 1 +#define VXVM_PATH_SIZE 2 + +typedef char *vm_name_t; +typedef char *vm_path_t; + +/* + * Pointers to libvxvmsc.so functions that we dynamically resolve. + */ +static int (*vxdl_libvxvm_get_version)(int version); +static int (*vxdl_libvxvm_get_conf)(int param); +static int (*vxdl_libvxvm_get_dgs)(int len, vm_name_t namep[]); +static int (*vxdl_libvxvm_get_disks)(vm_name_t dgname, int len, + vm_path_t pathp[]); + +#define MAX_DISK_GROUPS 128 +#define MAX_DISKS_DG 1024 + +struct vxvm_list { + struct vxvm_list *next; + char *slice; +}; + +static struct vxvm_list *vxvm_listp = NULL; +static time_t timestamp = 0; +static mutex_t vxvm_lock = DEFAULTMUTEX; + +static int add_use_record(char *devname); +static void free_vxvm(); +static void *init_vxvm(); +static int is_ctds(char *name); +static int load_vxvm(); + +int +inuse_vxvm(char *slice, nvlist_t *attrs, int *errp) +{ + int found = 0; + time_t curr_time; + char *sp = NULL; + + *errp = 0; + if (slice == NULL) { + return (found); + } + + /* + * Since vxvm "encapsulates" the disk we need to match on any + * slice passed in. Strip the slice component from the devname. + */ + if (is_ctds(slice)) { + if ((sp = strrchr(slice, '/')) == NULL) + sp = slice; + + while (*sp && *sp != 's') + sp++; + + if (*sp) + *sp = 0; + else + sp = NULL; + } + + (void) mutex_lock(&vxvm_lock); + + curr_time = time(NULL); + if (timestamp < curr_time && (curr_time - timestamp) > 60) { + free_vxvm(); /* free old entries */ + *errp = load_vxvm(); /* load the cache */ + + timestamp = curr_time; + } + + if (*errp == 0) { + struct vxvm_list *listp; + + listp = vxvm_listp; + while (listp != NULL) { + if (strcmp(slice, listp->slice) == 0) { + libdiskmgt_add_str(attrs, DM_USED_BY, DM_USE_VXVM, errp); + libdiskmgt_add_str(attrs, DM_USED_NAME, "", errp); + found = 1; + break; + } + listp = listp->next; + } + } + + (void) mutex_unlock(&vxvm_lock); + + /* restore slice name to orignal value */ + if (sp != NULL) + *sp = 's'; + + return (found); +} + +static int +add_use_record(char *devname) +{ + struct vxvm_list *sp; + + sp = (struct vxvm_list *)malloc(sizeof (struct vxvm_list)); + if (sp == NULL) { + return (ENOMEM); + } + + if ((sp->slice = strdup(devname)) == NULL) { + free(sp); + return (ENOMEM); + } + + sp->next = vxvm_listp; + vxvm_listp = sp; + + /* + * Since vxvm "encapsulates" the disk we need to match on any + * slice passed in. Strip the slice component from the devname. + */ + if (is_ctds(sp->slice)) { + char *dp; + + if ((dp = strrchr(sp->slice, '/')) == NULL) + dp = sp->slice; + + while (*dp && *dp != 's') + dp++; + *dp = 0; + } + + return (0); +} + +/* + * If the input name is in c[t]ds format then return 1, otherwise return 0. + */ +static int +is_ctds(char *name) +{ + char *p; + + if ((p = strrchr(name, '/')) == NULL) + p = name; + else + p++; + + if (*p++ != 'c') { + return (0); + } + /* skip controller digits */ + while (isdigit(*p)) { + p++; + } + + /* handle optional target */ + if (*p == 't') { + p++; + /* skip over target */ + while (isdigit(*p) || isupper(*p)) { + p++; + } + } + + if (*p++ != 'd') { + return (0); + } + while (isdigit(*p)) { + p++; + } + + if (*p++ != 's') { + return (0); + } + + /* check the slice number */ + while (isdigit(*p)) { + p++; + } + + if (*p != 0) { + return (0); + } + + return (1); +} + +/* + * Free the list of vxvm entries. + */ +static void +free_vxvm() +{ + struct vxvm_list *listp = vxvm_listp; + struct vxvm_list *nextp; + + while (listp != NULL) { + nextp = listp->next; + free((void *)listp->slice); + free((void *)listp); + listp = nextp; + } + + vxvm_listp = NULL; +} + +/* + * Try to dynamically link the vxvm functions we need. + */ +static void * +init_vxvm() +{ + void *lh; + + if ((lh = dlopen("libvxvmsc.so", RTLD_NOW)) == NULL) { + return (lh); + } + + if ((vxdl_libvxvm_get_version = (int (*)(int))dlsym(lh, + "libvxvm_get_version")) == NULL) { + (void) dlclose(lh); + return (NULL); + } + + if ((vxdl_libvxvm_get_conf = (int (*)(int))dlsym(lh, + "libvxvm_get_conf")) == NULL) { + (void) dlclose(lh); + return (NULL); + } + + if ((vxdl_libvxvm_get_dgs = (int (*)(int, vm_name_t []))dlsym(lh, + "libvxvm_get_dgs")) == NULL) { + (void) dlclose(lh); + return (NULL); + } + + if ((vxdl_libvxvm_get_disks = (int (*)(vm_name_t, int, vm_path_t [])) + dlsym(lh, "libvxvm_get_disks")) == NULL) { + (void) dlclose(lh); + return (NULL); + } + + return (lh); +} + +static int +load_vxvm() +{ + void *lh; + int vers; + int nsize; + int psize; + int n_disk_groups; + vm_name_t *namep; + char *pnp; + vm_path_t *pathp; + int i; + + if ((lh = init_vxvm()) == NULL) { + /* No library. */ + return (0); + } + + vers = (vxdl_libvxvm_get_version)(1 << 8); + if (vers == -1) { + /* unsupported version */ + (void) dlclose(lh); + return (0); + } + + nsize = (vxdl_libvxvm_get_conf)(VXVM_NAME_SIZE); + psize = (vxdl_libvxvm_get_conf)(VXVM_PATH_SIZE); + + if (nsize == -1 || psize == -1) { + (void) dlclose(lh); + return (0); + } + + namep = (vm_name_t *)calloc(MAX_DISK_GROUPS, nsize); + if (namep == NULL) { + (void) dlclose(lh); + return (ENOMEM); + } + + pathp = (vm_path_t *)calloc(MAX_DISKS_DG, psize); + if (pathp == NULL) { + (void) dlclose(lh); + free(namep); + return (ENOMEM); + } + + n_disk_groups = (vxdl_libvxvm_get_dgs)(MAX_DISK_GROUPS, namep); + if (n_disk_groups < 0) { + (void) dlclose(lh); + free(namep); + free(pathp); + return (0); + } + + pnp = (char *)namep; + for (i = 0; i < n_disk_groups; i++) { + int n_disks; + + n_disks = (vxdl_libvxvm_get_disks)(pnp, MAX_DISKS_DG, pathp); + + if (n_disks >= 0) { + int j; + char *ppp; + + ppp = (char *)pathp; + for (j = 0; j < n_disks; j++) { + + if (strncmp(ppp, "/dev/vx/", 8) == 0) { + char *pslash; + char nm[MAXPATHLEN]; + + pslash = strrchr(ppp, '/'); + pslash++; + + (void) snprintf(nm, sizeof (nm), "/dev/dsk/%s", pslash); + if (add_use_record(nm)) { + (void) dlclose(lh); + free(pathp); + free(namep); + return (ENOMEM); + } + } else { + if (add_use_record(ppp)) { + (void) dlclose(lh); + free(pathp); + free(namep); + return (ENOMEM); + } + } + + ppp += psize; + } + } + + pnp += nsize; + } + + (void) dlclose(lh); + free(pathp); + free(namep); + + return (0); +} diff --git a/usr/src/lib/libdiskmgt/common/libdiskmgt.h b/usr/src/lib/libdiskmgt/common/libdiskmgt.h new file mode 100644 index 0000000000..d9c2909539 --- /dev/null +++ b/usr/src/lib/libdiskmgt/common/libdiskmgt.h @@ -0,0 +1,240 @@ +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _LIBDISKMGT_H +#define _LIBDISKMGT_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include <libnvpair.h> + +/* typedef void *dm_descriptor_t; */ +typedef uint64_t dm_descriptor_t; + +typedef enum { + DM_DRIVE = 0, + DM_CONTROLLER, + DM_MEDIA, + DM_SLICE, + DM_PARTITION, + DM_PATH, + DM_ALIAS, + DM_BUS +} dm_desc_type_t; + + +typedef enum { + DM_DT_UNKNOWN = 0, + DM_DT_FIXED, + DM_DT_ZIP, + DM_DT_JAZ, + DM_DT_FLOPPY, + DM_DT_MO_ERASABLE, + DM_DT_MO_WRITEONCE, + DM_DT_AS_MO, + DM_DT_CDROM, + DM_DT_CDR, + DM_DT_CDRW, + DM_DT_DVDROM, + DM_DT_DVDR, + DM_DT_DVDRAM, + DM_DT_DVDRW, + DM_DT_DDCDROM, + DM_DT_DDCDR, + DM_DT_DDCDRW +} dm_drive_type_t; + +typedef enum { + DM_MT_UNKNOWN = 0, + DM_MT_FIXED, + DM_MT_FLOPPY, + DM_MT_CDROM, + DM_MT_ZIP, + DM_MT_JAZ, + DM_MT_CDR, + DM_MT_CDRW, + DM_MT_DVDROM, + DM_MT_DVDR, + DM_MT_DVDRAM, + DM_MT_MO_ERASABLE, + DM_MT_MO_WRITEONCE, + DM_MT_AS_MO +} dm_media_type_t; + +#define DM_FILTER_END -1 + +/* drive stat name */ +typedef enum { + DM_DRV_STAT_PERFORMANCE = 0, + DM_DRV_STAT_DIAGNOSTIC, + DM_DRV_STAT_TEMPERATURE +} dm_drive_stat_t; + +/* slice stat name */ +typedef enum { + DM_SLICE_STAT_USE = 0 +} dm_slice_stat_t; + +/* attribute definitions */ + +/* drive */ +#define DM_DISK_UP 1 +#define DM_DISK_DOWN 0 + +#define DM_CLUSTERED "clustered" +#define DM_DRVTYPE "drvtype" +#define DM_FAILING "failing" +#define DM_LOADED "loaded" /* also in media */ +#define DM_NDNRERRS "ndevice_not_ready_errors" +#define DM_NBYTESREAD "nbytes_read" +#define DM_NBYTESWRITTEN "nbytes_written" +#define DM_NHARDERRS "nhard_errors" +#define DM_NILLREQERRS "nillegal_req_errors" +#define DM_NMEDIAERRS "nmedia_errors" +#define DM_NNODEVERRS "nno_dev_errors" +#define DM_NREADOPS "nread_ops" +#define DM_NRECOVERRS "nrecoverable_errors" +#define DM_NSOFTERRS "nsoft_errors" +#define DM_NTRANSERRS "ntransport_errors" +#define DM_NWRITEOPS "nwrite_ops" +#define DM_OPATH "opath" +#define DM_PRODUCT_ID "product_id" +#define DM_REMOVABLE "removable" /* also in media */ +#define DM_RPM "rpm" +#define DM_STATUS "status" +#define DM_SYNC_SPEED "sync_speed" +#define DM_TEMPERATURE "temperature" +#define DM_VENDOR_ID "vendor_id" +#define DM_WIDE "wide" /* also on controller */ +#define DM_WWN "wwn" + +/* bus */ +#define DM_BTYPE "btype" +#define DM_CLOCK "clock" /* also on controller */ +#define DM_PNAME "pname" + +/* controller */ +#define DM_FAST "fast" +#define DM_FAST20 "fast20" +#define DM_FAST40 "fast40" +#define DM_FAST80 "fast80" +#define DM_MULTIPLEX "multiplex" +#define DM_PATH_STATE "path_state" + +#define DM_CTYPE_ATA "ata" +#define DM_CTYPE_SCSI "scsi" +#define DM_CTYPE_FIBRE "fibre channel" +#define DM_CTYPE_USB "usb" +#define DM_CTYPE_UNKNOWN "unknown" + +/* media */ +#define DM_BLOCKSIZE "blocksize" +#define DM_FDISK "fdisk" +#define DM_MTYPE "mtype" +#define DM_NACTUALCYLINDERS "nactual_cylinders" +#define DM_NALTCYLINDERS "nalt_cylinders" +#define DM_NCYLINDERS "ncylinders" +#define DM_NHEADS "nheads" +#define DM_NPHYSCYLINDERS "nphys_cylinders" +#define DM_NSECTORS "nsectors" /* also in partition */ +#define DM_SIZE "size" /* also in slice */ +#define DM_NACCESSIBLE "naccessible" +#define DM_LABEL "label" + +/* partition */ +#define DM_BCYL "bcyl" +#define DM_BHEAD "bhead" +#define DM_BOOTID "bootid" +#define DM_BSECT "bsect" +#define DM_ECYL "ecyl" +#define DM_EHEAD "ehead" +#define DM_ESECT "esect" +#define DM_PTYPE "ptype" +#define DM_RELSECT "relsect" + +/* slice */ +#define DM_DEVICEID "deviceid" +#define DM_DEVT "devt" +#define DM_INDEX "index" +#define DM_EFI_NAME "name" +#define DM_MOUNTPOINT "mountpoint" +#define DM_LOCALNAME "localname" +#define DM_START "start" +#define DM_TAG "tag" +#define DM_FLAG "flag" +#define DM_EFI "efi" /* also on media */ +#define DM_USED_BY "used_by" +#define DM_USED_NAME "used_name" +#define DM_USE_MOUNT "mount" +#define DM_USE_SVM "svm" +#define DM_USE_LU "lu" +#define DM_USE_DUMP "dump" +#define DM_USE_VXVM "vxvm" +#define DM_USE_FS "fs" +#define DM_USE_VFSTAB "vfstab" + +/* event */ +#define DM_EV_NAME "name" +#define DM_EV_DTYPE "edtype" +#define DM_EV_TYPE "evtype" +#define DM_EV_TADD "add" +#define DM_EV_TREMOVE "remove" +#define DM_EV_TCHANGE "change" + +/* findisks */ +#define DM_CTYPE "ctype" +#define DM_LUN "lun" +#define DM_TARGET "target" + +void dm_free_descriptors(dm_descriptor_t *desc_list); +void dm_free_descriptor(dm_descriptor_t desc); +void dm_free_name(char *name); + +dm_descriptor_t *dm_get_descriptors(dm_desc_type_t type, int filter[], + int *errp); +dm_descriptor_t *dm_get_associated_descriptors(dm_descriptor_t desc, + dm_desc_type_t type, int *errp); +dm_desc_type_t *dm_get_associated_types(dm_desc_type_t type); +dm_descriptor_t dm_get_descriptor_by_name(dm_desc_type_t desc_type, + char *name, int *errp); +char *dm_get_name(dm_descriptor_t desc, int *errp); +dm_desc_type_t dm_get_type(dm_descriptor_t desc); +nvlist_t *dm_get_attributes(dm_descriptor_t desc, int *errp); +nvlist_t *dm_get_stats(dm_descriptor_t desc, int stat_type, + int *errp); +void dm_init_event_queue(void(*callback)(nvlist_t *, int), + int *errp); +nvlist_t *dm_get_event(int *errp); + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBDISKMGT_H */ diff --git a/usr/src/lib/libdiskmgt/common/llib-ldiskmgt b/usr/src/lib/libdiskmgt/common/llib-ldiskmgt new file mode 100644 index 0000000000..d4fd481b8f --- /dev/null +++ b/usr/src/lib/libdiskmgt/common/llib-ldiskmgt @@ -0,0 +1,33 @@ +/* + * 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 2002 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* LINTLIBRARY */ +/* PROTOLIB1 */ + +#include "libdiskmgt.h" +#include "disks_private.h" diff --git a/usr/src/lib/libdiskmgt/common/media.c b/usr/src/lib/libdiskmgt/common/media.c new file mode 100644 index 0000000000..364ff92d2a --- /dev/null +++ b/usr/src/lib/libdiskmgt/common/media.c @@ -0,0 +1,695 @@ +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <fcntl.h> +#include <libdevinfo.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <stropts.h> +#include <sys/dkio.h> +#include <sys/sunddi.h> +#include <sys/types.h> +#include <unistd.h> +#include <sys/vtoc.h> +#include <volmgt.h> +#include <sys/efi_partition.h> + +#include "libdiskmgt.h" +#include "disks_private.h" +#include "partition.h" + +#define IOCTLRETRIES 2 +#define IOCTLRETRYINTERVAL 1 + +static descriptor_t **apply_filter(descriptor_t **media, int filter[], + int *errp); +static int get_attrs(disk_t *dp, int fd, nvlist_t *attrs); +static int get_non_volm_name(disk_t *dp, char *mname, int size); +static int get_media_type(uint_t media_type); +static int desc_ok(descriptor_t *dp); + +/* + * This function gets the descriptors we are associated with. + */ +descriptor_t ** +media_get_assoc_descriptors(descriptor_t *desc, dm_desc_type_t type, + int *errp) +{ + if (!desc_ok(desc)) { + *errp = ENODEV; + return (NULL); + } + + switch (type) { + case DM_DRIVE: + return (drive_get_assocs(desc, errp)); + case DM_PARTITION: + return (partition_get_assocs(desc, errp)); + case DM_SLICE: + return (slice_get_assocs(desc, errp)); + } + + *errp = EINVAL; + return (NULL); +} + +/* + * Get the media descriptors for the given drive/partition/slice. + */ +descriptor_t ** +media_get_assocs(descriptor_t *dp, int *errp) +{ + descriptor_t **media; + char mname[MAXPATHLEN]; + + if (!media_read_name(dp->p.disk, mname, sizeof (mname))) { + /* For drives, this means no media but slice/part. require media. */ + if (dp->type == DM_DRIVE) { + return (libdiskmgt_empty_desc_array(errp)); + } else { + *errp = ENODEV; + return (NULL); + } + } + + /* make the snapshot */ + media = (descriptor_t **)calloc(2, sizeof (descriptor_t *)); + if (media == NULL) { + *errp = ENOMEM; + return (NULL); + } + + media[0] = cache_get_desc(DM_MEDIA, dp->p.disk, mname, NULL, errp); + if (*errp != 0) { + free(media); + return (NULL); + } + media[1] = NULL; + + *errp = 0; + return (media); +} + +nvlist_t * +media_get_attributes(descriptor_t *dp, int *errp) +{ + nvlist_t *attrs = NULL; + int fd; + + if (!desc_ok(dp)) { + *errp = ENODEV; + return (NULL); + } + + if (nvlist_alloc(&attrs, NVATTRS, 0) != 0) { + *errp = ENOMEM; + return (NULL); + } + + fd = drive_open_disk(dp->p.disk, NULL, 0); + + if ((*errp = get_attrs(dp->p.disk, fd, attrs)) != 0) { + nvlist_free(attrs); + attrs = NULL; + } + + if (fd >= 0) { + (void) close(fd); + } + + return (attrs); +} + +descriptor_t * +media_get_descriptor_by_name(char *name, int *errp) +{ + descriptor_t **media; + int i; + descriptor_t *medium = NULL; + + media = cache_get_descriptors(DM_MEDIA, errp); + if (*errp != 0) { + return (NULL); + } + + for (i = 0; media[i]; i++) { + if (libdiskmgt_str_eq(name, media[i]->name)) { + medium = media[i]; + } else { + /* clean up the unused descriptors */ + cache_free_descriptor(media[i]); + } + } + free(media); + + if (medium == NULL) { + *errp = ENODEV; + } + + return (medium); +} + +descriptor_t ** +media_get_descriptors(int filter[], int *errp) +{ + descriptor_t **media; + + media = cache_get_descriptors(DM_MEDIA, errp); + if (*errp != 0) { + return (NULL); + } + + if (filter != NULL && filter[0] != DM_FILTER_END) { + descriptor_t **found; + + found = apply_filter(media, filter, errp); + if (*errp != 0) { + media = NULL; + } else { + media = found; + } + } + + return (media); +} + +char * +media_get_name(descriptor_t *desc) +{ + return (desc->name); +} + +/* ARGSUSED */ +nvlist_t * +media_get_stats(descriptor_t *dp, int stat_type, int *errp) +{ + /* There are no stat types defined for media */ + *errp = EINVAL; + return (NULL); +} + +/* + * Get the removable media volume manager devpath for the disk. This is the + * name we need to open that will work with vold. + * Return 1 if under volm control, 0 if not under volm control. + * The string in mediapath will be empty if the drive is under volm control + * but there is no media loaded. + */ +int +media_get_volm_path(disk_t *diskp, char *mediapath, int size) +{ + char vname[MAXPATHLEN]; + char *volname; + char *media_name; + + if (!diskp->removable || !volmgt_running()) { + return (0); + } + + /* + * The volume manager is running, so we have to check if this removable + * drive is under volm control or not. + */ + + /* + * We must check if this drive is under volume management control and + * what devpath to use. + * Note that we have to do this every time for drives that are not + * under the control of the volume manager, since the volume manager + * might have taken control since the last time we checked. + */ + if (diskp->volm_path_set == 0) { + alias_t *ap; + slice_t *dp; + + if ((ap = diskp->aliases) == NULL) { + return (0); + } + + /* Check each devpath to see if it is under volm control. */ + dp = ap->devpaths; + while (dp != NULL) { + slice_rdsk2dsk(dp->devpath, vname, sizeof (vname)); + if (volmgt_inuse(vname)) { + break; + } + + dp = dp->next; + } + + if (dp != NULL) { + /* Volume manager is managing the devpath that dp points to. */ + diskp->volm_path = dp->devpath; + diskp->volm_path_set = 1; + } + } + + if (diskp->volm_path_set == 0) { + /* The volume manager is not managing any of the devpaths. */ + return (0); + } + + if (dm_debug > 1) { + (void) fprintf(stderr, "INFO: chk vol: %s\n", diskp->volm_path); + } + + slice_rdsk2dsk(diskp->volm_path, vname, sizeof (vname)); + volname = volmgt_symname(vname); + if (volname == NULL) { + mediapath[0] = 0; + return (1); + } + + media_name = media_findname(volname); + free(volname); + if (media_name == NULL) { + mediapath[0] = 0; + return (1); + } + + (void) strlcpy(mediapath, media_name, size); + free(media_name); + return (1); +} + +int +media_make_descriptors() +{ + int error; + disk_t *dp; + char mname[MAXPATHLEN]; + + dp = cache_get_disklist(); + while (dp != NULL) { + if (media_read_name(dp, mname, sizeof (mname))) { + cache_load_desc(DM_MEDIA, dp, mname, NULL, &error); + if (error != 0) { + return (error); + } + } + + dp = dp->next; + } + + return (0); +} + +/* + * Read the media information. + */ +int +media_read_info(int fd, struct dk_minfo *minfo) +{ + int status; + int tries = 0; + + minfo->dki_media_type = 0; + + /* + * This ioctl can fail if the media is not loaded or spun up. + * Retrying can sometimes succeed since the first ioctl will have + * started the media before the ioctl timed out so the media may be + * spun up on the subsequent attempt. + */ + while ((status = ioctl(fd, DKIOCGMEDIAINFO, minfo)) < 0) { + tries++; + if (tries >= IOCTLRETRIES) { + break; + } + (void) sleep(IOCTLRETRYINTERVAL); + } + + if (status < 0) { + return (0); + } + + return (1); +} + +/* return 1 if there is media, 0 if not. */ +int +media_read_name(disk_t *dp, char *mname, int size) +{ + int under_volm; + char rmmedia_devpath[MAXPATHLEN]; + + mname[0] = 0; + + if (!dp->removable) { + /* not removable, so media name is devid */ + if (dp->device_id != NULL) { + (void) strlcpy(mname, dp->device_id, size); + } + return (1); + } + + /* This is a removable media drive. */ + + /* Get 1 if under volm control, 0 if not */ + under_volm = media_get_volm_path(dp, rmmedia_devpath, + sizeof (rmmedia_devpath)); + + if (under_volm) { + /* under volm control */ + if (rmmedia_devpath[0] == 0) { + /* no media */ + return (0); + } + (void) strlcpy(mname, rmmedia_devpath, size); + return (1); + + } else { + /* not under volm control */ + return (get_non_volm_name(dp, mname, size)); + } +} + +static descriptor_t ** +apply_filter(descriptor_t **media, int filter[], int *errp) +{ + descriptor_t **found; + int i; + int cnt = 0; + int pos; + + /* count the number of media in the snapshot */ + for (i = 0; media[i]; i++) { + cnt++; + } + + found = (descriptor_t **)calloc(cnt + 1, sizeof (descriptor_t *)); + if (found == NULL) { + *errp = ENOMEM; + cache_free_descriptors(media); + return (NULL); + } + + pos = 0; + for (i = 0; media[i]; i++) { + int fd; + struct dk_minfo minfo; + + if ((fd = drive_open_disk(media[i]->p.disk, NULL, 0)) < 0) { + continue; + } + + if (media_read_info(fd, &minfo)) { + int mtype; + int j; + int match; + + mtype = get_media_type(minfo.dki_media_type); + + match = 0; + for (j = 0; filter[j] != DM_FILTER_END; j++) { + if (mtype == filter[j]) { + found[pos++] = media[i]; + match = 1; + break; + } + } + + if (!match) { + cache_free_descriptor(media[i]); + } + } +#ifdef i386 + /* XXX Work around bug 4725434 */ + else if (!media[i]->p.disk->removable) { + int j; + int match; + + match = 0; + for (j = 0; filter[j] != DM_FILTER_END; j++) { + if (DM_MT_FIXED == filter[j]) { + found[pos++] = media[i]; + match = 1; + break; + } + } + + if (!match) { + cache_free_descriptor(media[i]); + } + } +#endif + + (void) close(fd); + } + found[pos] = NULL; + free(media); + + *errp = 0; + return (found); +} + +/* return 1 if the media descriptor is still valid, 0 if not. */ +static int +desc_ok(descriptor_t *dp) +{ + /* First verify the media name for removable media */ + if (dp->p.disk->removable) { + char mname[MAXPATHLEN]; + + if (!media_read_name(dp->p.disk, mname, sizeof (mname))) { + return (0); + } + + if (mname[0] == 0) { + return (libdiskmgt_str_eq(dp->name, NULL)); + } else { + return (libdiskmgt_str_eq(dp->name, mname)); + } + } + + return (1); +} + +static int +get_attrs(disk_t *dp, int fd, nvlist_t *attrs) +{ + struct dk_minfo minfo; + struct dk_geom geometry; + + if (fd < 0) { + return (ENODEV); + } + + bzero(&minfo, sizeof (struct dk_minfo)); + + /* The first thing to do is read the media */ + if (!media_read_info(fd, &minfo)) { + /* XXX Work around bug 4725434 */ +#ifdef i386 + if (dp->removable) +#endif + return (ENODEV); + } + + if (partition_has_fdisk(dp, fd)) { + if (nvlist_add_boolean(attrs, DM_FDISK) != 0) { + return (ENOMEM); + } + } + + if (dp->removable) { + if (nvlist_add_boolean(attrs, DM_REMOVABLE) != 0) { + return (ENOMEM); + } + + if (nvlist_add_boolean(attrs, DM_LOADED) != 0) { + return (ENOMEM); + } + } + + if (nvlist_add_uint64(attrs, DM_SIZE, minfo.dki_capacity) != 0) { + return (ENOMEM); + } + + if (nvlist_add_uint32(attrs, DM_BLOCKSIZE, minfo.dki_lbsize) != 0) { + return (ENOMEM); + } + + if (nvlist_add_uint32(attrs, DM_MTYPE, + get_media_type(minfo.dki_media_type)) != 0) { + return (ENOMEM); + } + + /* only for disks < 1TB */ + if (ioctl(fd, DKIOCGGEOM, &geometry) >= 0) { + struct vtoc vtoc; + + if (nvlist_add_uint64(attrs, DM_START, 0) != 0) { + return (ENOMEM); + } + if (nvlist_add_uint64(attrs, DM_NACCESSIBLE, + geometry.dkg_ncyl * geometry.dkg_nhead * geometry.dkg_nsect) + != 0) { + return (ENOMEM); + } + if (nvlist_add_uint32(attrs, DM_NCYLINDERS, geometry.dkg_ncyl) + != 0) { + return (ENOMEM); + } + if (nvlist_add_uint32(attrs, DM_NPHYSCYLINDERS, geometry.dkg_pcyl) + != 0) { + return (ENOMEM); + } + if (nvlist_add_uint32(attrs, DM_NALTCYLINDERS, geometry.dkg_acyl) + != 0) { + return (ENOMEM); + } + if (nvlist_add_uint32(attrs, DM_NHEADS, geometry.dkg_nhead) != 0) { + return (ENOMEM); + } + if (nvlist_add_uint32(attrs, DM_NSECTORS, geometry.dkg_nsect) + != 0) { + return (ENOMEM); + } + + if (read_vtoc(fd, &vtoc) >= 0 && vtoc.v_volume[0] != 0) { + char label[LEN_DKL_VVOL + 1]; + + (void) snprintf(label, sizeof (label), "%.*s", LEN_DKL_VVOL, + vtoc.v_volume); + if (nvlist_add_string(attrs, DM_LABEL, label) != 0) { + return (ENOMEM); + } + } + + } else { + /* check for disks > 1TB for accessible size */ + struct dk_gpt *efip; + + if (efi_alloc_and_read(fd, &efip) >= 0) { + diskaddr_t p8size = 0; + + if (nvlist_add_boolean(attrs, DM_EFI) != 0) { + return (ENOMEM); + } + if (nvlist_add_uint64(attrs, DM_START, efip->efi_first_u_lba) + != 0) { + return (ENOMEM); + } + /* partition 8 is reserved on EFI labels */ + if (efip->efi_nparts >= 9) { + p8size = efip->efi_parts[8].p_size; + } + if (nvlist_add_uint64(attrs, DM_NACCESSIBLE, + (efip->efi_last_u_lba - p8size) - efip->efi_first_u_lba) + != 0) { + efi_free(efip); + return (ENOMEM); + } + efi_free(efip); + } + } + + /* This ioctl seems to be mainly for intel-based drives. */ + if (ioctl(fd, DKIOCG_PHYGEOM, &geometry) >= 0) { + if (nvlist_add_uint32(attrs, DM_NACTUALCYLINDERS, geometry.dkg_ncyl) + != 0) { + return (ENOMEM); + } + } + + return (0); +} + +static int +get_media_type(uint_t media_type) +{ + switch (media_type) { + case DK_UNKNOWN: + return (DM_MT_UNKNOWN); + case DK_MO_ERASABLE: + return (DM_MT_MO_ERASABLE); + case DK_MO_WRITEONCE: + return (DM_MT_MO_WRITEONCE); + case DK_AS_MO: + return (DM_MT_AS_MO); + case DK_CDROM: + return (DM_MT_CDROM); + case DK_CDR: + return (DM_MT_CDR); + case DK_CDRW: + return (DM_MT_CDRW); + case DK_DVDROM: + return (DM_MT_DVDROM); + case DK_DVDR: + return (DM_MT_DVDR); + case DK_DVDRAM: + return (DM_MT_DVDRAM); + case DK_FIXED_DISK: + return (DM_MT_FIXED); + case DK_FLOPPY: + return (DM_MT_FLOPPY); + case DK_ZIP: + return (DM_MT_ZIP); + case DK_JAZ: + return (DM_MT_JAZ); + default: + return (DM_MT_UNKNOWN); + } +} + +/* + * This function handles removable media not under volume management. + */ +static int +get_non_volm_name(disk_t *dp, char *mname, int size) +{ + int loaded; + int fd; + + loaded = 0; + + if ((fd = drive_open_disk(dp, NULL, 0)) >= 0) { + struct dk_minfo minfo; + + if ((loaded = media_read_info(fd, &minfo))) { + struct vtoc vtoc; + + if (read_vtoc(fd, &vtoc) >= 0) { + if (vtoc.v_volume[0] != NULL) { + if (LEN_DKL_VVOL < size) { + (void) strlcpy(mname, vtoc.v_volume, LEN_DKL_VVOL); + } else { + (void) strlcpy(mname, vtoc.v_volume, size); + } + } + } + } + + (void) close(fd); + } + + return (loaded); +} diff --git a/usr/src/lib/libdiskmgt/common/partition.c b/usr/src/lib/libdiskmgt/common/partition.c new file mode 100644 index 0000000000..92e3e9579a --- /dev/null +++ b/usr/src/lib/libdiskmgt/common/partition.c @@ -0,0 +1,751 @@ +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <fcntl.h> +#include <libdevinfo.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/sunddi.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <dirent.h> +#include <unistd.h> +#include <sys/dkio.h> + +#include "libdiskmgt.h" +#include "disks_private.h" +#include "partition.h" + +#ifdef sparc +#define les(val) ((((val)&0xFF)<<8)|(((val)>>8)&0xFF)) +#define lel(val) (((unsigned)(les((val)&0x0000FFFF))<<16) | \ + (les((unsigned)((val)&0xffff0000)>>16))) +#else +#define les(val) (val) +#define lel(val) (val) +#endif + +#define ISIZE FD_NUMPART * sizeof (struct ipart) + +static int desc_ok(descriptor_t *dp); +static int get_attrs(descriptor_t *dp, struct ipart *iparts, + nvlist_t *attrs); +static int get_parts(disk_t *disk, struct ipart *iparts, char *opath, + int opath_len); +static int open_disk(disk_t *diskp, char *opath, int len); +static int has_slices(descriptor_t *desc, int *errp); + +descriptor_t ** +partition_get_assoc_descriptors(descriptor_t *desc, dm_desc_type_t type, + int *errp) +{ + if (!desc_ok(desc)) { + *errp = ENODEV; + return (NULL); + } + + switch (type) { + case DM_MEDIA: + return (media_get_assocs(desc, errp)); + case DM_SLICE: + if (!has_slices(desc, errp)) { + if (*errp != 0) { + return (NULL); + } + return (libdiskmgt_empty_desc_array(errp)); + } + return (slice_get_assocs(desc, errp)); + } + + *errp = EINVAL; + return (NULL); +} + +/* + * This is called by media/slice to get the associated partitions. + * For a media desc. we just get all the partitions, but for a slice desc. + * we just get the active solaris partition. + */ +descriptor_t ** +partition_get_assocs(descriptor_t *desc, int *errp) +{ + descriptor_t **partitions; + int pos; + int i; + struct ipart iparts[FD_NUMPART]; + char pname[MAXPATHLEN]; + int conv_flag = 0; + + if (get_parts(desc->p.disk, iparts, pname, sizeof (pname)) != 0) { + return (libdiskmgt_empty_desc_array(errp)); + } + + /* allocate the array for the descriptors */ + partitions = (descriptor_t **)calloc(FD_NUMPART + 1, + sizeof (descriptor_t *)); + if (partitions == NULL) { + *errp = ENOMEM; + return (NULL); + } + +#ifdef i386 + { + /* convert part. name (e.g. c0d0p0) */ + int len; + + len = strlen(pname); + if (len > 1 && *(pname + (len - 2)) == 'p') { + conv_flag = 1; + *(pname + (len - 1)) = 0; + } + } +#endif + + /* + * If this is a slice desc. we need the first active solaris partition + * and if there isn't one then we need the first solaris partition. + */ + if (desc->type == DM_SLICE) { + for (i = 0; i < FD_NUMPART; i++) { + if (iparts[i].bootid == ACTIVE && + (iparts[i].systid == SUNIXOS || + iparts[i].systid == SUNIXOS2)) { + break; + } + } + + /* no active solaris part., try to get the first solaris part. */ + if (i >= FD_NUMPART) { + for (i = 0; i < FD_NUMPART; i++) { + if (iparts[i].systid == SUNIXOS || + iparts[i].systid == SUNIXOS2) { + break; + } + } + } + + if (i < FD_NUMPART) { + /* we found a solaris partition to use */ + char part_name[MAXPATHLEN]; + + if (conv_flag) { + /* convert part. name (e.g. c0d0p0) */ + (void) snprintf(part_name, sizeof (part_name), "%s%d", + pname, i); + } else { + (void) snprintf(part_name, sizeof (part_name), "%d", i); + } + + /* the media name comes from the slice desc. */ + partitions[0] = cache_get_desc(DM_PARTITION, desc->p.disk, + part_name, desc->secondary_name, errp); + if (*errp != 0) { + cache_free_descriptors(partitions); + return (NULL); + } + partitions[1] = NULL; + + return (partitions); + + } + + return (libdiskmgt_empty_desc_array(errp)); + } + + /* Must be for media, so get all the parts. */ + + pos = 0; + for (i = 0; i < FD_NUMPART; i++) { + if (iparts[i].systid != 0) { + char part_name[MAXPATHLEN]; + + if (conv_flag) { + /* convert part. name (e.g. c0d0p0) */ + (void) snprintf(part_name, sizeof (part_name), "%s%d", + pname, i); + } else { + (void) snprintf(part_name, sizeof (part_name), "%d", i); + } + + /* the media name comes from the media desc. */ + partitions[pos] = cache_get_desc(DM_PARTITION, desc->p.disk, + part_name, desc->name, errp); + if (*errp != 0) { + cache_free_descriptors(partitions); + return (NULL); + } + + pos++; + } + } + partitions[pos] = NULL; + + *errp = 0; + return (partitions); +} + +nvlist_t * +partition_get_attributes(descriptor_t *dp, int *errp) +{ + nvlist_t *attrs = NULL; + struct ipart iparts[FD_NUMPART]; + + if (!desc_ok(dp)) { + *errp = ENODEV; + return (NULL); + } + + if ((*errp = get_parts(dp->p.disk, iparts, NULL, 0)) != 0) { + return (NULL); + } + + if (nvlist_alloc(&attrs, NVATTRS, 0) != 0) { + *errp = ENOMEM; + return (NULL); + } + + if ((*errp = get_attrs(dp, iparts, attrs)) != 0) { + nvlist_free(attrs); + attrs = NULL; + } + + return (attrs); +} + +/* + * Look for the partition by the partition number (which is not too useful). + */ +descriptor_t * +partition_get_descriptor_by_name(char *name, int *errp) +{ + descriptor_t **partitions; + int i; + descriptor_t *partition = NULL; + + partitions = cache_get_descriptors(DM_PARTITION, errp); + if (*errp != 0) { + return (NULL); + } + + for (i = 0; partitions[i]; i++) { + if (libdiskmgt_str_eq(name, partitions[i]->name)) { + partition = partitions[i]; + } else { + /* clean up the unused descriptors */ + cache_free_descriptor(partitions[i]); + } + } + free(partitions); + + if (partition == NULL) { + *errp = ENODEV; + } + + return (partition); +} + +/* ARGSUSED */ +descriptor_t ** +partition_get_descriptors(int filter[], int *errp) +{ + return (cache_get_descriptors(DM_PARTITION, errp)); +} + +char * +partition_get_name(descriptor_t *desc) +{ + return (desc->name); +} + +/* ARGSUSED */ +nvlist_t * +partition_get_stats(descriptor_t *dp, int stat_type, int *errp) +{ + /* There are no stat types defined for partitions */ + *errp = EINVAL; + return (NULL); +} + +/* ARGSUSED */ +int +partition_has_fdisk(disk_t *dp, int fd) +{ + char bootsect[512 * 3]; /* 3 sectors to be safe */ + +#ifdef sparc + if (dp->drv_type == DM_DT_FIXED) { + /* on sparc, only removable media can have fdisk parts. */ + return (0); + } +#endif + + /* + * We assume the caller already made sure media was inserted and + * spun up. + */ + + if ((ioctl(fd, DKIOCGMBOOT, bootsect) < 0) && (errno != ENOTTY)) { + return (0); + } + + return (1); +} + +/* + * A partition descriptor points to a disk, the name is the partition number + * and the secondary name is the media name. + */ +int +partition_make_descriptors() +{ + int error; + disk_t *dp; + + dp = cache_get_disklist(); + while (dp != NULL) { + struct ipart iparts[FD_NUMPART]; + char pname[MAXPATHLEN]; + + if (get_parts(dp, iparts, pname, sizeof (pname)) == 0) { + int i; + char mname[MAXPATHLEN]; + int conv_flag = 0; + +#ifdef i386 + /* convert part. name (e.g. c0d0p0) */ + int len; + + len = strlen(pname); + if (len > 1 && *(pname + (len - 2)) == 'p') { + conv_flag = 1; + *(pname + (len - 1)) = 0; + } +#endif + + mname[0] = 0; + (void) media_read_name(dp, mname, sizeof (mname)); + + for (i = 0; i < FD_NUMPART; i++) { + if (iparts[i].systid != 0) { + char part_name[MAXPATHLEN]; + + if (conv_flag) { + /* convert part. name (e.g. c0d0p0) */ + (void) snprintf(part_name, sizeof (part_name), + "%s%d", pname, i); + } else { + (void) snprintf(part_name, sizeof (part_name), + "%d", i); + } + + cache_load_desc(DM_PARTITION, dp, part_name, mname, + &error); + if (error != 0) { + return (error); + } + } + } + } + dp = dp->next; + } + + return (0); +} + +static int +get_attrs(descriptor_t *dp, struct ipart *iparts, nvlist_t *attrs) +{ + char *p; + int part_num; + + /* + * We already made sure the media was loaded and ready in the + * get_parts call within partition_get_attributes. + */ + + p = strrchr(dp->name, 'p'); + if (p == NULL) { + p = dp->name; + } else { + p++; + } + part_num = atoi(p); + if (part_num >= FD_NUMPART || iparts[part_num].systid == 0) { + return (ENODEV); + } + + /* we found the partition */ + + if (nvlist_add_uint32(attrs, DM_BOOTID, + (unsigned int)iparts[part_num].bootid) != 0) { + return (ENOMEM); + } + + if (nvlist_add_uint32(attrs, DM_PTYPE, + (unsigned int)iparts[part_num].systid) != 0) { + return (ENOMEM); + } + + if (nvlist_add_uint32(attrs, DM_BHEAD, + (unsigned int)iparts[part_num].beghead) != 0) { + return (ENOMEM); + } + + if (nvlist_add_uint32(attrs, DM_BSECT, + (unsigned int)((iparts[part_num].begsect) & 0x3f)) != 0) { + return (ENOMEM); + } + + if (nvlist_add_uint32(attrs, DM_BCYL, (unsigned int) + ((iparts[part_num].begcyl & 0xff) | + ((iparts[part_num].begsect & 0xc0) << 2))) != 0) { + return (ENOMEM); + } + + if (nvlist_add_uint32(attrs, DM_EHEAD, + (unsigned int)iparts[part_num].endhead) != 0) { + return (ENOMEM); + } + + if (nvlist_add_uint32(attrs, DM_ESECT, + (unsigned int)((iparts[part_num].endsect) & 0x3f)) != 0) { + return (ENOMEM); + } + + if (nvlist_add_uint32(attrs, DM_ECYL, (unsigned int) + ((iparts[part_num].endcyl & 0xff) | + ((iparts[part_num].endsect & 0xc0) << 2))) != 0) { + return (ENOMEM); + } + + if (nvlist_add_uint32(attrs, DM_RELSECT, + (unsigned int)iparts[part_num].relsect) != 0) { + return (ENOMEM); + } + + if (nvlist_add_uint32(attrs, DM_NSECTORS, + (unsigned int)iparts[part_num].numsect) != 0) { + return (ENOMEM); + } + + return (0); +} + +static int +get_parts(disk_t *disk, struct ipart *iparts, char *opath, int opath_len) +{ + int fd; + struct dk_minfo minfo; + struct mboot bootblk; + char bootsect[512]; + int i; + + /* Can't use drive_open_disk since we need the partition dev name. */ + if ((fd = open_disk(disk, opath, opath_len)) < 0) { + return (ENODEV); + } + + /* First make sure media is inserted and spun up. */ + if (!media_read_info(fd, &minfo)) { +#ifdef i386 + /* XXX Work around bug 4725434 */ + if (disk->removable) { +#endif + (void) close(fd); + return (ENODEV); +#ifdef i386 + } +#endif + } + + if (!partition_has_fdisk(disk, fd)) { + (void) close(fd); + return (ENOTTY); + } + + if (lseek(fd, 0, 0) == -1) { + (void) close(fd); + return (ENODEV); + } + + if (read(fd, bootsect, 512) != 512) { + (void) close(fd); + return (ENODEV); + } + (void) close(fd); + + (void) memcpy(&bootblk, bootsect, sizeof (bootblk)); + + if (les(bootblk.signature) != MBB_MAGIC) { + return (ENOTTY); + } + + (void) memcpy(iparts, bootblk.parts, ISIZE); + + for (i = 0; i < FD_NUMPART; i++) { + if (iparts[i].systid != 0) { + iparts[i].relsect = lel(iparts[i].relsect); + iparts[i].numsect = lel(iparts[i].numsect); + } + } + + return (0); +} +/* return 1 if the partition descriptor is still valid, 0 if not. */ +static int +desc_ok(descriptor_t *dp) +{ + /* First verify the media name for removable media */ + if (dp->p.disk->removable) { + char mname[MAXPATHLEN]; + + if (!media_read_name(dp->p.disk, mname, sizeof (mname))) { + return (0); + } + + if (mname[0] == 0) { + return (libdiskmgt_str_eq(dp->secondary_name, NULL)); + } else { + return (libdiskmgt_str_eq(dp->secondary_name, mname)); + } + } + + /* + * We could verify the partition is still there but this is kind of + * expensive and other code down the line will do that (e.g. see + * get_attrs). + */ + + return (1); +} + +/* + * Return 1 if partition has slices, 0 if not. + */ +static int +has_slices(descriptor_t *desc, int *errp) +{ + int pnum; + int i; + char *p; + struct ipart iparts[FD_NUMPART]; + + if (get_parts(desc->p.disk, iparts, NULL, 0) != 0) { + *errp = ENODEV; + return (0); + } + + p = strrchr(desc->name, 'p'); + if (p == NULL) { + p = desc->name; + } else { + p++; + } + pnum = atoi(p); + + /* + * Slices are associated with the active solaris partition or if there + * is no active solaris partition, then the first solaris partition. + */ + + *errp = 0; + if (iparts[pnum].bootid == ACTIVE && + (iparts[pnum].systid == SUNIXOS || + iparts[pnum].systid == SUNIXOS2)) { + return (1); + } else { + int active = 0; + + /* Check if there are no active solaris partitions. */ + for (i = 0; i < FD_NUMPART; i++) { + if (iparts[i].bootid == ACTIVE && + (iparts[i].systid == SUNIXOS || + iparts[i].systid == SUNIXOS2)) { + active = 1; + break; + } + } + + if (!active) { + /* Check if this is the first solaris partition. */ + for (i = 0; i < FD_NUMPART; i++) { + if (iparts[i].systid == SUNIXOS || + iparts[i].systid == SUNIXOS2) { + break; + } + } + + if (i < FD_NUMPART && i == pnum) { + return (1); + } + } + } + + return (0); +} + +static int +open_disk(disk_t *diskp, char *opath, int len) +{ + char rmmedia_devpath[MAXPATHLEN]; + + if (diskp->removable && media_get_volm_path(diskp, rmmedia_devpath, + sizeof (rmmedia_devpath))) { + + int fd; + struct stat buf; + + if (rmmedia_devpath[0] == 0) { + /* removable but no media */ + return (-1); + } + + if ((fd = open(rmmedia_devpath, O_RDONLY|O_NDELAY)) < 0) { + return (-1); + } + + if (fstat(fd, &buf) != 0) { + (void) close(fd); + return (-1); + } + + if (buf.st_mode & S_IFCHR) { + /* opened, is device, so done */ + if (opath != NULL) { + (void) strlcpy(opath, rmmedia_devpath, len); + } + return (fd); + + } else if (buf.st_mode & S_IFDIR) { + /* disk w/ slices so handle the directory */ + DIR *dirp; + struct dirent *dentp; + int dfd; +#ifdef _LP64 + struct dirent *result; +#endif + + /* each device file in the dir represents a slice */ + + if ((dirp = fdopendir(fd)) == NULL) { + (void) close(fd); + return (-1); + } + + if ((dentp = (struct dirent *)malloc(sizeof (struct dirent) + + _PC_NAME_MAX + 1)) == NULL) { + /* out of memory */ + (void) close(fd); + return (-1); + } +#ifdef _LP64 + while (readdir_r(dirp, dentp, &result) != NULL) { +#else + while (readdir_r(dirp, dentp) != NULL) { +#endif + char slice_path[MAXPATHLEN]; + + if (libdiskmgt_str_eq(".", dentp->d_name) || + libdiskmgt_str_eq("..", dentp->d_name)) { + continue; + } + + (void) snprintf(slice_path, sizeof (slice_path), "%s/%s", + rmmedia_devpath, dentp->d_name); + + if ((dfd = open(slice_path, O_RDONLY|O_NDELAY)) < 0) { + continue; + } + + if (fstat(dfd, &buf) == 0 && (buf.st_mode & S_IFCHR)) { + /* opened, is device, so done */ + free(dentp); + (void) close(fd); + if (opath != NULL) { + (void) strlcpy(opath, slice_path, len); + } + return (dfd); + } + + /* not a device, keep looking */ + (void) close(dfd); + } + + /* did not find a device under the rmmedia_path */ + free(dentp); + (void) close(fd); + } + + /* didn't find a device under volume management control */ + return (-1); + } + + /* + * Not removable media under volume management control so just open the + * first devpath. + */ + if (diskp->aliases != NULL && diskp->aliases->devpaths != NULL) { +#ifdef sparc + if (opath != NULL) { + (void) strlcpy(opath, diskp->aliases->devpaths->devpath, len); + } + return (open(diskp->aliases->devpaths->devpath, O_RDONLY|O_NDELAY)); +#else + /* On intel we need to open partition device (e.g. c0d0p0). */ + char part_dev[MAXPATHLEN]; + char *p; + + (void) strlcpy(part_dev, diskp->aliases->devpaths->devpath, + sizeof (part_dev)); + p = strrchr(part_dev, '/'); + if (p == NULL) { + p = strrchr(part_dev, 's'); + if (p != NULL) { + *p = 'p'; + } + } else { + char *ps; + + *p = 0; + ps = strrchr((p + 1), 's'); + if (ps != NULL) { + *ps = 'p'; + } + *p = '/'; + } + + if (opath != NULL) { + (void) strlcpy(opath, part_dev, len); + } + return (open(part_dev, O_RDONLY|O_NDELAY)); +#endif + } + + return (-1); +} diff --git a/usr/src/lib/libdiskmgt/common/partition.h b/usr/src/lib/libdiskmgt/common/partition.h new file mode 100644 index 0000000000..608f4c7ffe --- /dev/null +++ b/usr/src/lib/libdiskmgt/common/partition.h @@ -0,0 +1,57 @@ +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _PARTITION_H +#define _PARTITION_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include <libdevinfo.h> +#include <sys/dkio.h> +#include <sys/dktp/fdisk.h> +#include <devid.h> + +descriptor_t **partition_get_descriptors(int filter[], int *errp); +descriptor_t **partition_get_assoc_descriptors(descriptor_t *desc, + dm_desc_type_t type, int *errp); +descriptor_t **partition_get_assocs(descriptor_t *desc, int *errp); +descriptor_t *partition_get_descriptor_by_name(char *name, int *errp); +char *partition_get_name(descriptor_t *desc); +nvlist_t *partition_get_attributes(descriptor_t *desc, int *errp); +nvlist_t *partition_get_stats(descriptor_t *desc, int stat_type, + int *errp); +int partition_has_fdisk(disk_t *dp, int fd); +int partition_make_descriptors(); + +#ifdef __cplusplus +} +#endif + +#endif /* _PARTITION_H */ diff --git a/usr/src/lib/libdiskmgt/common/path.c b/usr/src/lib/libdiskmgt/common/path.c new file mode 100644 index 0000000000..b5080dc93b --- /dev/null +++ b/usr/src/lib/libdiskmgt/common/path.c @@ -0,0 +1,330 @@ +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <fcntl.h> +#include <libdevinfo.h> +#include <stdlib.h> +#include <sys/sunddi.h> +#include <sys/types.h> + +#include "libdiskmgt.h" +#include "disks_private.h" + +static int add_path_state(descriptor_t *dp, nvlist_t *attrs); +static int add_wwn(descriptor_t *dp, nvlist_t *attrs); +static descriptor_t **get_assoc_drives(descriptor_t *desc, int *errp); +static descriptor_t **get_assoc_controllers(descriptor_t *desc, int *errp); +static char *path_state_name(di_path_state_t st); + +descriptor_t ** +path_get_assoc_descriptors(descriptor_t *desc, dm_desc_type_t type, int *errp) +{ + switch (type) { + case DM_DRIVE: + return (get_assoc_drives(desc, errp)); + case DM_CONTROLLER: + return (get_assoc_controllers(desc, errp)); + } + + *errp = EINVAL; + return (NULL); +} + +nvlist_t * +path_get_attributes(descriptor_t *dp, int *errp) +{ + path_t *pp; + nvlist_t *attrs = NULL; + + pp = dp->p.path; + + if (nvlist_alloc(&attrs, NVATTRS, 0) != 0) { + *errp = ENOMEM; + return (NULL); + } + + if (nvlist_add_string(attrs, DM_CTYPE, pp->ctype) != 0) { + nvlist_free(attrs); + *errp = ENOMEM; + return (NULL); + } + + /* + * We add the path state and wwn attributes only for descriptors that + * we got via their association to a specific drive (since these + * attributes are drive specific). + */ + if (dp->name != NULL) { + if (add_path_state(dp, attrs) != 0) { + nvlist_free(attrs); + *errp = ENOMEM; + return (NULL); + } + if (add_wwn(dp, attrs) != 0) { + nvlist_free(attrs); + *errp = ENOMEM; + return (NULL); + } + } + + *errp = 0; + return (attrs); +} + +descriptor_t * +path_get_descriptor_by_name(char *name, int *errp) +{ + descriptor_t **paths; + int i; + descriptor_t *path = NULL; + + paths = cache_get_descriptors(DM_PATH, errp); + if (*errp != 0) { + return (NULL); + } + + for (i = 0; paths[i]; i++) { + if (libdiskmgt_str_eq(name, paths[i]->p.path->name)) { + path = paths[i]; + } else { + /* clean up the unused descriptors */ + cache_free_descriptor(paths[i]); + } + } + free(paths); + + if (path == NULL) { + *errp = ENODEV; + } + + return (NULL); +} + +/* ARGSUSED */ +descriptor_t ** +path_get_descriptors(int filter[], int *errp) +{ + return (cache_get_descriptors(DM_PATH, errp)); +} + +char * +path_get_name(descriptor_t *desc) +{ + return (desc->p.path->name); +} + +/* ARGSUSED */ +nvlist_t * +path_get_stats(descriptor_t *dp, int stat_type, int *errp) +{ + /* There are no stat types defined for paths */ + *errp = EINVAL; + return (NULL); +} + +int +path_make_descriptors() +{ + int error; + controller_t *cp; + + cp = cache_get_controllerlist(); + while (cp != NULL) { + if (cp->paths != NULL) { + int i; + + for (i = 0; cp->paths[i]; i++) { + cache_load_desc(DM_PATH, cp->paths[i], NULL, NULL, &error); + if (error != 0) { + return (error); + } + } + } + cp = cp->next; + } + + return (0); +} + +/* + * This is called when we have a name in the descriptor name field. That + * only will be the case when the descriptor was created by getting the + * association from a drive to the path. Since we filled in the name with + * the drive device id in that case, we can use the device id to look up the + * drive-path state. + */ +static int +add_path_state(descriptor_t *dp, nvlist_t *attrs) +{ + ddi_devid_t devid; + path_t *pp; + int i; + int status = 0; + + if (devid_str_decode(dp->name, &devid, NULL) != 0) { + return (0); + } + + /* find the index of the correct drive assoc. */ + pp = dp->p.path; + for (i = 0; pp->disks[i] && pp->states[i] != -1; i++) { + if (pp->disks[i]->devid != NULL && + devid_compare(pp->disks[i]->devid, devid) == 0) { + + /* add the corresponding state */ + if (nvlist_add_string(attrs, DM_PATH_STATE, + path_state_name(pp->states[i])) != 0) { + status = ENOMEM; + } + break; + } + } + devid_free(devid); + + return (status); +} + +/* + * This is called when we have a name in the descriptor name field. That + * only will be the case when the descriptor was created by getting the + * association from a drive to the path. Since we filled in the name with + * the drive device id in that case, we can use the device id to look up the + * drive wwn. + */ +static int +add_wwn(descriptor_t *dp, nvlist_t *attrs) +{ + ddi_devid_t devid; + path_t *pp; + int i; + int status = 0; + + if (devid_str_decode(dp->name, &devid, NULL) != 0) { + return (0); + } + + /* find the index of the correct drive assoc. */ + pp = dp->p.path; + for (i = 0; pp->disks[i] && pp->states[i] != -1; i++) { + if (pp->disks[i]->devid != NULL && + devid_compare(pp->disks[i]->devid, devid) == 0) { + + /* add the corresponding state */ + if (nvlist_add_string(attrs, DM_WWN, pp->wwns[i]) != 0) { + status = ENOMEM; + } + break; + } + } + devid_free(devid); + + return (status); +} + +static descriptor_t ** +get_assoc_controllers(descriptor_t *desc, int *errp) +{ + path_t *pp; + descriptor_t **controllers; + int i; + + pp = desc->p.path; + + /* a path can have just one controller */ + + controllers = (descriptor_t **)calloc(2, sizeof (descriptor_t *)); + if (controllers == NULL) { + *errp = ENOMEM; + return (NULL); + } + + i = 0; + if (pp->controller != NULL) { + controllers[i++] = cache_get_desc(DM_CONTROLLER, + pp->controller, NULL, NULL, errp); + if (*errp != 0) { + cache_free_descriptors(controllers); + return (NULL); + } + } + + controllers[i] = NULL; + + *errp = 0; + return (controllers); +} + +static descriptor_t ** +get_assoc_drives(descriptor_t *desc, int *errp) +{ + path_t *pp; + descriptor_t **drives; + int cnt; + int i; + + pp = desc->p.path; + + /* Count how many we have. */ + for (cnt = 0; pp->disks[cnt]; cnt++); + + /* make the snapshot */ + drives = (descriptor_t **)calloc(cnt + 1, sizeof (descriptor_t *)); + if (drives == NULL) { + *errp = ENOMEM; + return (NULL); + } + + for (i = 0; pp->disks[i]; i++) { + drives[i] = cache_get_desc(DM_DRIVE, pp->disks[i], NULL, NULL, + errp); + if (*errp != 0) { + cache_free_descriptors(drives); + return (NULL); + } + } + drives[i] = NULL; + + *errp = 0; + return (drives); +} + +static char * +path_state_name(di_path_state_t st) +{ + switch (st) { + case DI_PATH_STATE_ONLINE: + return ("online"); + case DI_PATH_STATE_STANDBY: + return ("standby"); + case DI_PATH_STATE_OFFLINE: + return ("offline"); + case DI_PATH_STATE_FAULT: + return ("faulted"); + } + return ("unknown"); +} diff --git a/usr/src/lib/libdiskmgt/common/slice.c b/usr/src/lib/libdiskmgt/common/slice.c new file mode 100644 index 0000000000..607efeb120 --- /dev/null +++ b/usr/src/lib/libdiskmgt/common/slice.c @@ -0,0 +1,1399 @@ +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <fcntl.h> +#include <libdevinfo.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <dirent.h> +#include <sys/dkio.h> +#include <sys/stat.h> +#include <sys/sunddi.h> +#include <sys/types.h> +#include <sys/vtoc.h> +#include <unistd.h> +#include <devid.h> +#include <dirent.h> +#include <sys/dktp/fdisk.h> +#include <sys/efi_partition.h> + +#include "libdiskmgt.h" +#include "disks_private.h" +#include "partition.h" +#ifndef VT_ENOTSUP +#define VT_ENOTSUP (-5) +#endif + +#define FMT_UNKNOWN 0 +#define FMT_VTOC 1 +#define FMT_EFI 2 + +static struct inuse_detectors { + int (*detectorp)(char *slice, nvlist_t *attrs, int *errp); + char *used_by; +} detectors[] = { + {inuse_mnt, DM_USE_MOUNT}, + {inuse_svm, DM_USE_SVM}, + {inuse_lu, DM_USE_LU}, + {inuse_dump, DM_USE_DUMP}, + {inuse_vxvm, DM_USE_VXVM}, + {inuse_fs, DM_USE_FS}, /* fs should always be last */ + {NULL, NULL} +}; + +static int add_inuse(char *name, nvlist_t *attrs); +static int desc_ok(descriptor_t *dp); +static void dsk2rdsk(char *dsk, char *rdsk, int size); +static int get_attrs(descriptor_t *dp, int fd, nvlist_t *attrs); +static descriptor_t **get_fixed_assocs(descriptor_t *desc, int *errp); +static descriptor_t **get_removable_assocs(descriptor_t *desc, char *volm_path, + int *errp); +static int get_slice_num(slice_t *devp); +static int match_fixed_name(disk_t *dp, char *name, int *errp); +static int match_removable_name(disk_t *dp, char *name, int *errp); +static int make_fixed_descriptors(disk_t *dp); +static int make_removable_descriptors(disk_t *dp); +static int make_volm_dir_descriptors(disk_t *dp, int fd, + char *volm_path); +static int num_removable_slices(int fd, struct stat *bufp, + char *volm_path); + +descriptor_t ** +slice_get_assoc_descriptors(descriptor_t *desc, dm_desc_type_t type, + int *errp) +{ + if (!desc_ok(desc)) { + *errp = ENODEV; + return (NULL); + } + + switch (type) { + case DM_MEDIA: + return (media_get_assocs(desc, errp)); + case DM_PARTITION: + return (partition_get_assocs(desc, errp)); + } + + *errp = EINVAL; + return (NULL); +} + +/* + * This is called by media/partition to get the slice descriptors for the given + * media/partition descriptor. + * For media, just get the slices, but for a partition, it must be a solaris + * partition and if there are active partitions, it must be the active one. + */ +descriptor_t ** +slice_get_assocs(descriptor_t *desc, int *errp) +{ + int under_volm = 0; + char volm_path[MAXPATHLEN]; + + /* Just check the first drive name. */ + if (desc->p.disk->aliases == NULL) { + *errp = 0; + return (libdiskmgt_empty_desc_array(errp)); + } + + if (desc->p.disk->removable) { + if ((under_volm = media_get_volm_path(desc->p.disk, volm_path, + sizeof (volm_path)))) { + if (volm_path[0] == 0) { + /* no media */ + *errp = 0; + return (libdiskmgt_empty_desc_array(errp)); + } + } + } + + if (desc->p.disk->removable) { + if (under_volm) { + return (get_removable_assocs(desc, volm_path, errp)); + } else { + return (get_fixed_assocs(desc, errp)); + } + } else { + return (get_fixed_assocs(desc, errp)); + } +} + +nvlist_t * +slice_get_attributes(descriptor_t *dp, int *errp) +{ + nvlist_t *attrs = NULL; + int fd; + char devpath[MAXPATHLEN]; + + if (!desc_ok(dp)) { + *errp = ENODEV; + return (NULL); + } + + if (nvlist_alloc(&attrs, NVATTRS, 0) != 0) { + *errp = ENOMEM; + return (NULL); + } + + /* dp->name is /dev/dsk, need to convert back to /dev/rdsk */ + dsk2rdsk(dp->name, devpath, sizeof (devpath)); + fd = open(devpath, O_RDONLY|O_NDELAY); + + if ((*errp = get_attrs(dp, fd, attrs)) != 0) { + nvlist_free(attrs); + attrs = NULL; + } + + if (fd >= 0) { + (void) close(fd); + } + + return (attrs); +} + +/* + * Look for the slice by the slice devpath. + */ +descriptor_t * +slice_get_descriptor_by_name(char *name, int *errp) +{ + int found = 0; + disk_t *dp; + + for (dp = cache_get_disklist(); dp != NULL; dp = dp->next) { + if (dp->removable) { + found = match_removable_name(dp, name, errp); + } else { + found = match_fixed_name(dp, name, errp); + } + + if (found) { + char mname[MAXPATHLEN]; + + if (*errp != 0) { + return (NULL); + } + + mname[0] = 0; + (void) media_read_name(dp, mname, sizeof (mname)); + + return (cache_get_desc(DM_SLICE, dp, name, mname, errp)); + } + } + + *errp = ENODEV; + return (NULL); +} + +/* ARGSUSED */ +descriptor_t ** +slice_get_descriptors(int filter[], int *errp) +{ + return (cache_get_descriptors(DM_SLICE, errp)); +} + +char * +slice_get_name(descriptor_t *desc) +{ + return (desc->name); +} + +nvlist_t * +slice_get_stats(descriptor_t *dp, int stat_type, int *errp) +{ + nvlist_t *stats; + char *str; + + if (stat_type != DM_SLICE_STAT_USE) { + *errp = EINVAL; + return (NULL); + } + + *errp = 0; + + if (nvlist_alloc(&stats, NVATTRS_STAT, 0) != 0) { + *errp = ENOMEM; + return (NULL); + } + + if ((*errp = add_inuse(dp->name, stats)) != 0) { + return (NULL); + } + + /* if no cluster use, check for a use of the local name */ + if (nvlist_lookup_string(stats, DM_USED_BY, &str) != 0) { + disk_t *diskp; + + diskp = dp->p.disk; + if (diskp->aliases != NULL && diskp->aliases->cluster) { + slice_t *sp; + int snum = -1; + struct dk_minfo minfo; + struct dk_cinfo dkinfo; + char devpath[MAXPATHLEN]; + int fd; + + /* dp->name is /dev/dsk, need to convert back to /dev/rdsk */ + dsk2rdsk(dp->name, devpath, sizeof (devpath)); + fd = open(devpath, O_RDONLY|O_NDELAY); + + if (fd >= 0 && media_read_info(fd, &minfo) && + ioctl(fd, DKIOCINFO, &dkinfo) >= 0) { + snum = dkinfo.dki_partition; + } + + if (fd >= 0) { + (void) close(fd); + } + + if (snum >= 0) { + for (sp = diskp->aliases->orig_paths; sp != NULL; + sp = sp->next) { + + if (sp->slice_num == snum) { + char localpath[MAXPATHLEN]; + + slice_rdsk2dsk(sp->devpath, localpath, + sizeof (localpath)); + + if ((*errp = add_inuse(localpath, stats)) != 0) { + return (NULL); + } + + break; + } + } + } + } + } + + return (stats); +} + +/* + * A slice descriptor points to a disk, the name is the devpath and the + * secondary name is the media name. + */ +int +slice_make_descriptors() +{ + disk_t *dp; + + dp = cache_get_disklist(); + while (dp != NULL) { + int error; + + if (dp->removable) { + error = make_removable_descriptors(dp); + } else { + error = make_fixed_descriptors(dp); + } + if (error != 0) { + return (error); + } + + dp = dp->next; + } + + return (0); +} + +/* convert rdsk paths to dsk paths */ +void +slice_rdsk2dsk(char *rdsk, char *dsk, int size) +{ + char *strp; + + (void) strlcpy(dsk, rdsk, size); + + if ((strp = strstr(dsk, "/rdsk/")) == NULL) { + /* not rdsk, check for floppy */ + strp = strstr(dsk, "/rdiskette"); + } + + if (strp != NULL) { + strp++; /* move ptr to the r in rdsk or rdiskette */ + + /* move the succeeding chars over by one */ + do { + *strp = *(strp + 1); + strp++; + } while (*strp); + } +} + +/* + * Check if/how the slice is used. + */ +static int +add_inuse(char *name, nvlist_t *attrs) +{ + int i; + int error; + + for (i = 0; detectors[i].detectorp != NULL; i ++) { + if ((detectors[i].detectorp)(name, attrs, &error) || error != 0) { + if (error != 0) { + return (error); + } + break; + } + } + + return (0); +} + +/* return 1 if the slice descriptor is still valid, 0 if not. */ +static int +desc_ok(descriptor_t *dp) +{ + /* First verify the media name for removable media */ + if (dp->p.disk->removable) { + char mname[MAXPATHLEN]; + + if (!media_read_name(dp->p.disk, mname, sizeof (mname))) { + return (0); + } + + if (mname[0] == 0) { + return (libdiskmgt_str_eq(dp->secondary_name, NULL)); + } else { + return (libdiskmgt_str_eq(dp->secondary_name, mname)); + } + } + + /* + * We could verify the slice is still there, but other code down the + * line already does these checks (e.g. see get_attrs). + */ + + return (1); +} + +/* convert dsk paths to rdsk paths */ +static void +dsk2rdsk(char *dsk, char *rdsk, int size) +{ + char *slashp; + size_t len; + + (void) strlcpy(rdsk, dsk, size); + + /* make sure there is enough room to add the r to dsk */ + len = strlen(dsk); + if (len + 2 > size) { + return; + } + + if ((slashp = strstr(rdsk, "/dsk/")) == NULL) { + /* not dsk, check for floppy */ + slashp = strstr(rdsk, "/diskette"); + } + + if (slashp != NULL) { + char *endp; + + endp = rdsk + len; /* point to terminating 0 */ + /* move the succeeding chars over by one */ + do { + *(endp + 1) = *endp; + endp--; + } while (endp != slashp); + + *(endp + 1) = 'r'; + } +} + +static int +get_attrs(descriptor_t *dp, int fd, nvlist_t *attrs) +{ + struct dk_minfo minfo; + int status; + int data_format = FMT_UNKNOWN; + int snum = -1; + int error; + struct vtoc vtoc; + struct dk_gpt *efip; + struct dk_cinfo dkinfo; + disk_t *diskp; + char localpath[MAXPATHLEN]; + int cooked_fd; + struct stat buf; + int mntpnt = 0; + + if (fd < 0) { + return (ENODEV); + } + + /* First make sure media is inserted and spun up. */ + if (!media_read_info(fd, &minfo)) { +#ifdef i386 + /* XXX Work around bug 4725434 */ + if (dp->p.disk->removable) +#endif + return (ENODEV); + } + + if ((status = read_vtoc(fd, &vtoc)) >= 0) { + data_format = FMT_VTOC; + } else if (status == VT_ENOTSUP && efi_alloc_and_read(fd, &efip) >= 0) { + data_format = FMT_EFI; + if (nvlist_add_boolean(attrs, DM_EFI) != 0) { + efi_free(efip); + return (ENOMEM); + } + } + + if (data_format == FMT_UNKNOWN) { + return (ENODEV); + } + + if (ioctl(fd, DKIOCINFO, &dkinfo) >= 0) { + snum = dkinfo.dki_partition; + } + + /* check the slice */ + if (data_format == FMT_VTOC) { + if (snum < 0 || snum >= vtoc.v_nparts || + vtoc.v_part[snum].p_size == 0) { + return (ENODEV); + } + } else { /* data_format == FMT_EFI */ + if (snum < 0 || snum >= efip->efi_nparts || + efip->efi_parts[snum].p_size == 0) { + efi_free(efip); + return (ENODEV); + } + } + + /* the slice exists */ + + if (nvlist_add_uint32(attrs, DM_INDEX, snum) != 0) { + if (data_format == FMT_EFI) { + efi_free(efip); + } + return (ENOMEM); + } + + if (data_format == FMT_VTOC) { + if (nvlist_add_uint64(attrs, DM_START, vtoc.v_part[snum].p_start) + != 0) { + return (ENOMEM); + } + + if (nvlist_add_uint64(attrs, DM_SIZE, vtoc.v_part[snum].p_size) + != 0) { + return (ENOMEM); + } + + if (nvlist_add_uint32(attrs, DM_TAG, vtoc.v_part[snum].p_tag) + != 0) { + return (ENOMEM); + } + + if (nvlist_add_uint32(attrs, DM_FLAG, vtoc.v_part[snum].p_flag) + != 0) { + return (ENOMEM); + } + + } else { /* data_format == FMT_EFI */ + if (nvlist_add_uint64(attrs, DM_START, + efip->efi_parts[snum].p_start) != 0) { + efi_free(efip); + return (ENOMEM); + } + + if (nvlist_add_uint64(attrs, DM_SIZE, efip->efi_parts[snum].p_size) + != 0) { + efi_free(efip); + return (ENOMEM); + } + + if (efip->efi_parts[snum].p_name[0] != 0) { + char label[EFI_PART_NAME_LEN + 1]; + + (void) snprintf(label, sizeof (label), "%.*s", + EFI_PART_NAME_LEN, efip->efi_parts[snum].p_name); + if (nvlist_add_string(attrs, DM_EFI_NAME, label) != 0) { + efi_free(efip); + return (ENOMEM); + } + } + } + + if (data_format == FMT_EFI) { + efi_free(efip); + } + + if (inuse_mnt(dp->name, attrs, &error)) { + if (error != 0) { + return (error); + } + mntpnt = 1; + } + + /* + * Some extra attrs for cluster slices. + * + * get localname and possible mnt point for localpath + */ + localpath[0] = 0; + diskp = dp->p.disk; + if (diskp->aliases != NULL && diskp->aliases->cluster) { + slice_t *sp; + + for (sp = diskp->aliases->orig_paths; sp != NULL; sp = sp->next) { + if (sp->slice_num == -1) { + /* determine the slice number for this path */ + int sfd; + struct dk_cinfo dkinfo; + + if ((sfd = open(sp->devpath, O_RDONLY|O_NDELAY)) >= 0) { + if (ioctl(sfd, DKIOCINFO, &dkinfo) >= 0) { + sp->slice_num = dkinfo.dki_partition; + } + (void) close(sfd); + } + } + + if (sp->slice_num == snum) { + slice_rdsk2dsk(sp->devpath, localpath, sizeof (localpath)); + + if (nvlist_add_string(attrs, DM_LOCALNAME, localpath) + != 0) { + return (ENOMEM); + } + + if (mntpnt == 0) { + if (inuse_mnt(localpath, attrs, &error)) { + if (error != 0) { + return (error); + } + } + } + + break; + } + } + } + + if (fstat(fd, &buf) != -1) { + if (nvlist_add_uint64(attrs, DM_DEVT, buf.st_rdev) != 0) { + return (ENOMEM); + } + } + + /* + * We need to open the cooked slice (not the raw one) to get the + * correct devid. Also see if we need to read the localpath for the + * cluster disk, since the minor name is unavailable for the did pseudo + * device. + */ + if (localpath[0] != 0) { + cooked_fd = open(localpath, O_RDONLY|O_NDELAY); + } else { + cooked_fd = open(dp->name, O_RDONLY|O_NDELAY); + } + + if (cooked_fd >= 0) { + int no_mem = 0; + ddi_devid_t devid; + + if (devid_get(cooked_fd, &devid) == 0) { + char *minor; + + if (devid_get_minor_name(cooked_fd, &minor) == 0) { + char *devidstr; + + if ((devidstr = devid_str_encode(devid, minor)) != 0) { + + if (nvlist_add_string(attrs, DM_DEVICEID, devidstr) + != 0) { + no_mem = 1; + } + + devid_str_free(devidstr); + } + devid_str_free(minor); + } + devid_free(devid); + } + (void) close(cooked_fd); + + if (no_mem) { + return (ENOMEM); + } + } + + return (0); +} + +static descriptor_t ** +get_fixed_assocs(descriptor_t *desc, int *errp) +{ + int fd; + int status; + int data_format = FMT_UNKNOWN; + int cnt; + struct vtoc vtoc; + struct dk_gpt *efip; + int pos; + char *media_name = NULL; + slice_t *devp; + descriptor_t **slices; + + if ((fd = drive_open_disk(desc->p.disk, NULL, 0)) < 0) { + *errp = ENODEV; + return (NULL); + } + + if ((status = read_vtoc(fd, &vtoc)) >= 0) { + data_format = FMT_VTOC; + } else if (status == VT_ENOTSUP && efi_alloc_and_read(fd, &efip) >= 0) { + data_format = FMT_EFI; + } else { + (void) close(fd); + *errp = 0; + return (libdiskmgt_empty_desc_array(errp)); + } + (void) close(fd); + + /* count the number of slices */ + for (cnt = 0, devp = desc->p.disk->aliases->devpaths; devp != NULL; + devp = devp->next, cnt++); + + /* allocate the array for the descriptors */ + slices = (descriptor_t **)calloc(cnt + 1, sizeof (descriptor_t *)); + if (slices == NULL) { + if (data_format == FMT_EFI) { + efi_free(efip); + } + *errp = ENOMEM; + return (NULL); + } + + /* get the media name from the descriptor */ + if (desc->type == DM_MEDIA) { + media_name = desc->name; + } else { + /* must be a DM_PARTITION */ + media_name = desc->secondary_name; + } + + pos = 0; + for (devp = desc->p.disk->aliases->devpaths; devp != NULL; + devp = devp->next) { + + int slice_num; + char devpath[MAXPATHLEN]; + + slice_num = get_slice_num(devp); + /* can't get slicenum, so no need to keep trying the drive */ + if (slice_num == -1) { + break; + } + + if (data_format == FMT_VTOC) { + if (slice_num >= vtoc.v_nparts || + vtoc.v_part[slice_num].p_size == 0) { + continue; + } + } else { /* data_format == FMT_EFI */ + if (slice_num >= efip->efi_nparts || + efip->efi_parts[slice_num].p_size == 0) { + continue; + } + } + + slice_rdsk2dsk(devp->devpath, devpath, sizeof (devpath)); + slices[pos] = cache_get_desc(DM_SLICE, desc->p.disk, devpath, + media_name, errp); + if (*errp != 0) { + cache_free_descriptors(slices); + if (data_format == FMT_EFI) { + efi_free(efip); + } + return (NULL); + } + pos++; + } + slices[pos] = NULL; + + if (data_format == FMT_EFI) { + efi_free(efip); + } + + *errp = 0; + return (slices); +} + +/* + * Called for loaded removable media under volume management control. + */ +static descriptor_t ** +get_removable_assocs(descriptor_t *desc, char *volm_path, int *errp) +{ + int pos; + int fd; + int cnt; + struct stat buf; + descriptor_t **slices; + char *media_name = NULL; + char devpath[MAXPATHLEN]; + + /* get the media name from the descriptor */ + if (desc->type == DM_MEDIA) { + media_name = desc->name; + } else { + /* must be a DM_PARTITION */ + media_name = desc->secondary_name; + } + + /* + * For removable media under volm control the volm_path will + * either be a device (if the media is made up of a single slice) or + * a directory (if the media has multiple slices) with the slices + * as devices contained in the directory. + */ + + if ((fd = open(volm_path, O_RDONLY|O_NDELAY)) < 0 || + fstat(fd, &buf) != 0) { + *errp = ENODEV; + return (NULL); + } + + cnt = num_removable_slices(fd, &buf, volm_path); + + /* allocate the array for the descriptors */ + slices = (descriptor_t **)calloc(cnt + 1, sizeof (descriptor_t *)); + if (slices == NULL) { + *errp = ENOMEM; + return (NULL); + } + + slice_rdsk2dsk(volm_path, devpath, sizeof (devpath)); + + pos = 0; + *errp = 0; + if (buf.st_mode & S_IFCHR) { + struct dk_minfo minfo; + + /* Make sure media has readable label */ + if (media_read_info(fd, &minfo)) { + int status; + int data_format = FMT_UNKNOWN; + struct vtoc vtoc; + struct dk_gpt *efip; + + if ((status = read_vtoc(fd, &vtoc)) >= 0) { + data_format = FMT_VTOC; + } else if (status == VT_ENOTSUP && + efi_alloc_and_read(fd, &efip) >= 0) { + data_format = FMT_EFI; + } + + if (data_format != FMT_UNKNOWN) { + /* has a readable label */ + slices[pos++] = cache_get_desc(DM_SLICE, desc->p.disk, + devpath, media_name, errp); + } + } + + } else if (buf.st_mode & S_IFDIR) { + DIR *dirp; +#ifdef _LP64 + struct dirent *result; +#endif + + /* rewind, num_removable_slices already traversed */ + (void) lseek(fd, 0, SEEK_SET); + + if ((dirp = fdopendir(fd)) != NULL) { + struct dirent *dentp; + + dentp = (struct dirent *)malloc(sizeof (struct dirent) + + _PC_NAME_MAX + 1); + if (dentp != NULL) { +#ifdef _LP64 + while (readdir_r(dirp, dentp, &result) != NULL) { +#else + while (readdir_r(dirp, dentp) != NULL) { +#endif + int dfd; + int is_dev = 0; + char slice_path[MAXPATHLEN]; + + if (libdiskmgt_str_eq(".", dentp->d_name) || + libdiskmgt_str_eq("..", dentp->d_name)) { + continue; + } + + (void) snprintf(slice_path, sizeof (slice_path), + "%s/%s", devpath, dentp->d_name); + + if ((dfd = open(slice_path, O_RDONLY|O_NDELAY)) >= 0) { + struct stat buf; + + if (fstat(dfd, &buf) == 0 && + buf.st_mode & S_IFCHR) { + is_dev = 1; + } + (void) close(dfd); + } + + if (!is_dev) { + continue; + } + + slices[pos++] = cache_get_desc(DM_SLICE, desc->p.disk, + slice_path, media_name, errp); + if (*errp != 0) { + break; + } + + } + free(dentp); + } + /* don't call closedir since it closes the fd */ + } + } + + (void) close(fd); + + slices[pos] = NULL; + + if (*errp != 0) { + cache_free_descriptors(slices); + return (NULL); + } + + return (slices); +} + +static int +get_slice_num(slice_t *devp) +{ + /* check if we already determined the devpath slice number */ + if (devp->slice_num == -1) { + int fd; + + if ((fd = open(devp->devpath, O_RDONLY|O_NDELAY)) >= 0) { + struct dk_cinfo dkinfo; + if (ioctl(fd, DKIOCINFO, &dkinfo) >= 0) { + devp->slice_num = dkinfo.dki_partition; + } + (void) close(fd); + } + } + + return (devp->slice_num); +} + +static int +make_fixed_descriptors(disk_t *dp) +{ + int error = 0; + alias_t *ap; + slice_t *devp; + char mname[MAXPATHLEN]; + int data_format = FMT_UNKNOWN; + struct vtoc vtoc; + struct dk_gpt *efip; + + /* Just check the first drive name. */ + if ((ap = dp->aliases) == NULL) { + return (0); + } + + mname[0] = 0; + (void) media_read_name(dp, mname, sizeof (mname)); + + for (devp = ap->devpaths; devp != NULL; devp = devp->next) { + int slice_num; + char devpath[MAXPATHLEN]; + + slice_num = get_slice_num(devp); + /* can't get slicenum, so no need to keep trying the drive */ + if (slice_num == -1) { + break; + } + + if (data_format == FMT_UNKNOWN) { + int fd; + int status; + + if ((fd = drive_open_disk(dp, NULL, 0)) >= 0) { + if ((status = read_vtoc(fd, &vtoc)) >= 0) { + data_format = FMT_VTOC; + } else if (status == VT_ENOTSUP && + efi_alloc_and_read(fd, &efip) >= 0) { + data_format = FMT_EFI; + } + (void) close(fd); + } + } + + /* can't get slice data, so no need to keep trying the drive */ + if (data_format == FMT_UNKNOWN) { + break; + } + + if (data_format == FMT_VTOC) { + if (slice_num >= vtoc.v_nparts || + vtoc.v_part[slice_num].p_size == 0) { + continue; + } + } else { /* data_format == FMT_EFI */ + if (slice_num >= efip->efi_nparts || + efip->efi_parts[slice_num].p_size == 0) { + continue; + } + } + + slice_rdsk2dsk(devp->devpath, devpath, sizeof (devpath)); + cache_load_desc(DM_SLICE, dp, devpath, mname, &error); + if (error != 0) { + break; + } + } + + if (data_format == FMT_EFI) { + efi_free(efip); + } + + return (error); +} + +/* + * For removable media under volm control we have to do some special handling. + * We don't use the vtoc and /dev/dsk devpaths, since the slices are named + * under the /vol fs. + */ +static int +make_removable_descriptors(disk_t *dp) +{ + char volm_path[MAXPATHLEN]; + int error; + int fd; + + /* + * If this removable drive is not under volm control, just use + * normal handling. + */ + if (!media_get_volm_path(dp, volm_path, sizeof (volm_path))) { + return (make_fixed_descriptors(dp)); + } + + if (volm_path[0] == 0) { + /* no media */ + return (0); + } + + /* + * For removable media under volm control the rmmedia_devapth will + * either be a device (if the media is made up of a single slice) or + * a directory (if the media has multiple slices) with the slices + * as devices contained in the directory. + */ + error = 0; + if ((fd = open(volm_path, O_RDONLY|O_NDELAY)) >= 0) { + struct stat buf; + + if (fstat(fd, &buf) == 0) { + if (buf.st_mode & S_IFCHR) { + int status; + int data_format = FMT_UNKNOWN; + struct dk_minfo minfo; + int error; + struct vtoc vtoc; + struct dk_gpt *efip; + char devpath[MAXPATHLEN]; + + /* Make sure media has readable label */ + if (!media_read_info(fd, &minfo)) { + /* no media */ + return (0); + } + + if ((status = read_vtoc(fd, &vtoc)) >= 0) { + data_format = FMT_VTOC; + } else if (status == VT_ENOTSUP && + efi_alloc_and_read(fd, &efip) >= 0) { + data_format = FMT_EFI; + } + + if (data_format == FMT_UNKNOWN) { + /* no readable label */ + return (0); + } + + slice_rdsk2dsk(volm_path, devpath, sizeof (devpath)); + /* The media name is the volm_path in this case. */ + cache_load_desc(DM_SLICE, dp, devpath, volm_path, &error); + + } else if (buf.st_mode & S_IFDIR) { + /* each device file in the dir represents a slice */ + error = make_volm_dir_descriptors(dp, fd, volm_path); + } + } + (void) close(fd); + } + + return (error); +} + +/* + * This handles removable media with slices under volume management control. + * In this case we have a dir which is the media name and each slice on the + * media is a device file in this dir. + */ +static int +make_volm_dir_descriptors(disk_t *dp, int dirfd, char *volm_path) +{ + int error; + DIR *dirp; + struct dirent *dentp; +#ifdef _LP64 + struct dirent *result; +#endif + char devpath[MAXPATHLEN]; + + if ((dirp = fdopendir(dirfd)) == NULL) { + return (0); + } + + slice_rdsk2dsk(volm_path, devpath, sizeof (devpath)); + + error = 0; + dentp = (struct dirent *)malloc(sizeof (struct dirent) + + _PC_NAME_MAX + 1); + if (dentp != NULL) { +#ifdef _LP64 + while (readdir_r(dirp, dentp, &result) != NULL) { +#else + while (readdir_r(dirp, dentp) != NULL) { +#endif + int fd; + char slice_path[MAXPATHLEN]; + + if (libdiskmgt_str_eq(".", dentp->d_name) || + libdiskmgt_str_eq("..", dentp->d_name)) { + continue; + } + + (void) snprintf(slice_path, sizeof (slice_path), "%s/%s", + devpath, dentp->d_name); + + if ((fd = open(slice_path, O_RDONLY|O_NDELAY)) >= 0) { + struct stat buf; + + if (fstat(fd, &buf) == 0 && buf.st_mode & S_IFCHR) { + /* The media name is the volm_path in this case. */ + cache_load_desc(DM_SLICE, dp, slice_path, volm_path, + &error); + if (error != 0) { + (void) close(fd); + break; + } + } + + (void) close(fd); + } + } + free(dentp); + } + /* don't call closedir since it closes the fd */ + + return (error); +} + +/* + * Just look for the name on the devpaths we have cached. Return 1 if we + * find the name and the size of that slice is non-zero. + */ +static int +match_fixed_name(disk_t *diskp, char *name, int *errp) +{ + slice_t *dp = NULL; + alias_t *ap; + int slice_num; + int fd; + int status; + int data_format = FMT_UNKNOWN; + struct vtoc vtoc; + struct dk_gpt *efip; + + ap = diskp->aliases; + while (ap != NULL) { + slice_t *devp; + + devp = ap->devpaths; + while (devp != NULL) { + char path[MAXPATHLEN]; + + slice_rdsk2dsk(devp->devpath, path, sizeof (path)); + if (libdiskmgt_str_eq(path, name)) { + /* found it */ + dp = devp; + break; + } + + devp = devp->next; + } + + if (dp != NULL) { + break; + } + + ap = ap->next; + } + + if (dp == NULL) { + *errp = 0; + return (0); + } + + /* + * If we found a match on the name we now have to check that this + * slice really exists (non-0 size). + */ + + slice_num = get_slice_num(dp); + /* can't get slicenum, so no slice */ + if (slice_num == -1) { + *errp = ENODEV; + return (1); + } + + if ((fd = drive_open_disk(diskp, NULL, 0)) < 0) { + *errp = ENODEV; + return (1); + } + + if ((status = read_vtoc(fd, &vtoc)) >= 0) { + data_format = FMT_VTOC; + } else if (status == VT_ENOTSUP && efi_alloc_and_read(fd, &efip) >= 0) { + data_format = FMT_EFI; + } else { + (void) close(fd); + *errp = ENODEV; + return (1); + } + (void) close(fd); + + if (data_format == FMT_VTOC) { + if (slice_num < vtoc.v_nparts && + vtoc.v_part[slice_num].p_size > 0) { + *errp = 0; + return (1); + } + } else { /* data_format == FMT_EFI */ + if (slice_num < efip->efi_nparts && + efip->efi_parts[slice_num].p_size > 0) { + efi_free(efip); + *errp = 0; + return (1); + } + efi_free(efip); + } + + *errp = ENODEV; + return (1); +} + +static int +match_removable_name(disk_t *diskp, char *name, int *errp) +{ + char volm_path[MAXPATHLEN]; + int found; + int fd; + struct stat buf; + + /* + * If this removable drive is not under volm control, just use + * normal handling. + */ + if (!media_get_volm_path(diskp, volm_path, sizeof (volm_path))) { + return (match_fixed_name(diskp, name, errp)); + } + + if (volm_path[0] == 0) { + /* no media */ + *errp = 0; + return (0); + } + + /* + * For removable media under volm control the rmmedia_devapth will + * either be a device (if the media is made up of a single slice) or + * a directory (if the media has multiple slices) with the slices + * as devices contained in the directory. + */ + + *errp = 0; + + found = 0; + if ((fd = open(volm_path, O_RDONLY|O_NDELAY)) >= 0 && + fstat(fd, &buf) == 0) { + + if (buf.st_mode & S_IFCHR) { + char devpath[MAXPATHLEN]; + + slice_rdsk2dsk(volm_path, devpath, sizeof (devpath)); + if (libdiskmgt_str_eq(name, devpath)) { + found = 1; + } + + } else if (buf.st_mode & S_IFDIR) { + /* each device file in the dir represents a slice */ + DIR *dirp; + + if ((dirp = fdopendir(fd)) != NULL) { + struct dirent *dentp; +#ifdef _LP64 + struct dirent *result; +#endif + char devpath[MAXPATHLEN]; + + slice_rdsk2dsk(volm_path, devpath, sizeof (devpath)); + + dentp = (struct dirent *)malloc(sizeof (struct dirent) + + _PC_NAME_MAX + 1); + if (dentp != NULL) { +#ifdef _LP64 + while (readdir_r(dirp, dentp, &result) != NULL) { +#else + while (readdir_r(dirp, dentp) != NULL) { +#endif + char slice_path[MAXPATHLEN]; + + if (libdiskmgt_str_eq(".", dentp->d_name) || + libdiskmgt_str_eq("..", dentp->d_name)) { + continue; + } + + (void) snprintf(slice_path, sizeof (slice_path), + "%s/%s", devpath, dentp->d_name); + + if (libdiskmgt_str_eq(name, slice_path)) { + /* found name, check device */ + int dfd; + int is_dev = 0; + + if ((dfd = open(slice_path, O_RDONLY|O_NDELAY)) + >= 0) { + struct stat buf; + + if (fstat(dfd, &buf) == 0 && + buf.st_mode & S_IFCHR) { + is_dev = 1; + } + (void) close(dfd); + } + + /* we found the name */ + found = 1; + + if (!is_dev) { + *errp = ENODEV; + } + + break; + } + } + free(dentp); + } + /* don't call closedir since it closes the fd */ + } + } /* end of dir handling */ + + (void) close(fd); + } + + return (found); +} + +static int +num_removable_slices(int fd, struct stat *bufp, char *volm_path) +{ + int cnt = 0; + + if (bufp->st_mode & S_IFCHR) { + cnt = 1; + + } else if (bufp->st_mode & S_IFDIR) { + /* each device file in the dir represents a slice */ + DIR *dirp; + + if ((dirp = fdopendir(fd)) != NULL) { + struct dirent *dentp; +#ifdef _LP64 + struct dirent *result; +#endif + char devpath[MAXPATHLEN]; + + slice_rdsk2dsk(volm_path, devpath, sizeof (devpath)); + + dentp = (struct dirent *)malloc(sizeof (struct dirent) + + _PC_NAME_MAX + 1); + if (dentp != NULL) { +#ifdef _LP64 + while (readdir_r(dirp, dentp, &result) != NULL) { +#else + while (readdir_r(dirp, dentp) != NULL) { +#endif + int dfd; + char slice_path[MAXPATHLEN]; + + if (libdiskmgt_str_eq(".", dentp->d_name) || + libdiskmgt_str_eq("..", dentp->d_name)) { + continue; + } + + (void) snprintf(slice_path, sizeof (slice_path), + "%s/%s", devpath, dentp->d_name); + + if ((dfd = open(slice_path, O_RDONLY|O_NDELAY)) >= 0) { + struct stat buf; + + if (fstat(dfd, &buf) == 0 && + buf.st_mode & S_IFCHR) { + cnt++; + } + (void) close(dfd); + } + } + free(dentp); + } + /* don't call closedir since it closes the fd */ + } + } /* end of dir handling */ + + return (cnt); +} diff --git a/usr/src/lib/libdiskmgt/i386/Makefile b/usr/src/lib/libdiskmgt/i386/Makefile new file mode 100644 index 0000000000..ee785147da --- /dev/null +++ b/usr/src/lib/libdiskmgt/i386/Makefile @@ -0,0 +1,32 @@ +# +# 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 2002 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# lib/libdiskmgt/i386/Makefile + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/libdiskmgt/sparc/Makefile b/usr/src/lib/libdiskmgt/sparc/Makefile new file mode 100644 index 0000000000..7cda8e1b48 --- /dev/null +++ b/usr/src/lib/libdiskmgt/sparc/Makefile @@ -0,0 +1,32 @@ +# +# 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 2002 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# lib/libdiskmgt/sparc/Makefile + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/libdiskmgt/sparcv9/Makefile b/usr/src/lib/libdiskmgt/sparcv9/Makefile new file mode 100644 index 0000000000..f2593718fe --- /dev/null +++ b/usr/src/lib/libdiskmgt/sparcv9/Makefile @@ -0,0 +1,38 @@ +# +# 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 2003 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#pragma ident "%Z%%M% %I% %E% SMI" +# +# lib/libdiskmgt/sparcv9/Makefile + +MAPDIR= ../spec/sparcv9 +include ../Makefile.com +include ../../Makefile.lib.64 + +.KEEP_STATE: + +all: $(LIBS) + +install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64) diff --git a/usr/src/lib/libdiskmgt/spec/Makefile b/usr/src/lib/libdiskmgt/spec/Makefile new file mode 100644 index 0000000000..7ec77b7139 --- /dev/null +++ b/usr/src/lib/libdiskmgt/spec/Makefile @@ -0,0 +1,30 @@ +# +# 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 2002 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# lib/libdiskmgt/spec/Makefile + +include $(SRC)/lib/Makefile.spec.arch diff --git a/usr/src/lib/libdiskmgt/spec/Makefile.targ b/usr/src/lib/libdiskmgt/spec/Makefile.targ new file mode 100644 index 0000000000..3b1f0e9cc9 --- /dev/null +++ b/usr/src/lib/libdiskmgt/spec/Makefile.targ @@ -0,0 +1,33 @@ +# +# 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 2002 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# lib/libdiskmgt/spec/Makefile.targ + +LIBRARY = libdiskmgt.a +VERS = .1 +OBJECTS = diskmgt.o +SPECCPP = -I../../common diff --git a/usr/src/lib/libdiskmgt/spec/amd64/Makefile b/usr/src/lib/libdiskmgt/spec/amd64/Makefile new file mode 100644 index 0000000000..5dd1b7240f --- /dev/null +++ b/usr/src/lib/libdiskmgt/spec/amd64/Makefile @@ -0,0 +1,42 @@ +# +# 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 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.targ + +# Add arch specific objects here +OBJECTS += + +include $(SRC)/lib/Makefile.lib +include $(SRC)/lib/Makefile.lib.64 + +# Uncomment the following if the linker complains +#amd64_C_PICFLAGS = $(amd64_C_BIGPICFLAGS) + +include $(SRC)/lib/Makefile.spec + +install: $(ROOTABILIB64) diff --git a/usr/src/lib/libdiskmgt/spec/diskmgt.spec b/usr/src/lib/libdiskmgt/spec/diskmgt.spec new file mode 100644 index 0000000000..2d35464181 --- /dev/null +++ b/usr/src/lib/libdiskmgt/spec/diskmgt.spec @@ -0,0 +1,111 @@ +# +# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# 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 +# +# ident "%Z%%M% %I% %E% SMI" +# +# lib/libdiskmgt/spec/diskmgt.spec + +function dm_free_descriptors +include <libdiskmgt.h> +declaration void dm_free_descriptors(dm_descriptor_t *desc_list) +version SUNWprivate_1.1 +end + +function dm_free_descriptor +include <libdiskmgt.h> +declaration void dm_free_descriptor(dm_descriptor_t desc) +version SUNWprivate_1.1 +end + +function dm_free_name +include <libdiskmgt.h> +declaration void dm_free_name(char *name) +version SUNWprivate_1.1 +end + +function dm_get_descriptors +include <libdiskmgt.h> +declaration dm_descriptor_t *dm_get_descriptors(dm_desc_type_t type, \ + int filter[], int *errp) +version SUNWprivate_1.1 +end + +function dm_get_associated_descriptors +include <libdiskmgt.h> +declaration dm_descriptor_t * \ + dm_get_associated_descriptors(dm_descriptor_t desc, \ + dm_desc_type_t type, int *errp) +version SUNWprivate_1.1 +end + +function dm_get_associated_types +include <libdiskmgt.h> +declaration dm_desc_type_t *dm_get_associated_types(dm_desc_type_t type) +version SUNWprivate_1.1 +end + +function dm_get_descriptor_by_name +include <libdiskmgt.h> +declaration dm_descriptor_t dm_get_descriptor_by_name(dm_desc_type_t \ + desc_type, char *name, int *errp) +version SUNWprivate_1.1 +end + +function dm_get_name +include <libdiskmgt.h> +declaration char *dm_get_name(dm_descriptor_t desc, int *errp) +version SUNWprivate_1.1 +end + +function dm_get_type +include <libdiskmgt.h> +declaration dm_desc_type_t dm_get_type(dm_descriptor_t desc) +version SUNWprivate_1.1 +end + +function dm_get_attributes +include <libdiskmgt.h> +declaration nvlist_t *dm_get_attributes(dm_descriptor_t desc, int *errp) +version SUNWprivate_1.1 +end + +function dm_get_stats +include <libdiskmgt.h> +declaration nvlist_t *dm_get_stats(dm_descriptor_t desc, int stat_type, \ + int *errp) +version SUNWprivate_1.1 +end + +function dm_init_event_queue +include <libdiskmgt.h> +declaration void dm_init_event_queue(void(*callback)(nvlist_t *, int), \ + int *errp) +version SUNWprivate_1.1 +end + +function dm_get_event +include <libdiskmgt.h> +declaration nvlist_t *dm_get_event(int *errp) +version SUNWprivate_1.1 +end diff --git a/usr/src/lib/libdiskmgt/spec/i386/Makefile b/usr/src/lib/libdiskmgt/spec/i386/Makefile new file mode 100644 index 0000000000..2548b2bdb4 --- /dev/null +++ b/usr/src/lib/libdiskmgt/spec/i386/Makefile @@ -0,0 +1,36 @@ +# +# 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 2002 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# lib/libdiskmgt/spec/i386/Makefile + +.KEEP_STATE: + +include ../Makefile.targ +include $(SRC)/lib/Makefile.lib +include $(SRC)/lib/Makefile.spec + +install: $(ROOTABILIB) diff --git a/usr/src/lib/libdiskmgt/spec/sparc/Makefile b/usr/src/lib/libdiskmgt/spec/sparc/Makefile new file mode 100644 index 0000000000..b1aa1cc40c --- /dev/null +++ b/usr/src/lib/libdiskmgt/spec/sparc/Makefile @@ -0,0 +1,36 @@ +# +# 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 2002 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# lib/libdiskmgt/spec/sparc/Makefile + +.KEEP_STATE: + +include ../Makefile.targ +include $(SRC)/lib/Makefile.lib +include $(SRC)/lib/Makefile.spec + +install: $(ROOTABILIB) diff --git a/usr/src/lib/libdiskmgt/spec/sparcv9/Makefile b/usr/src/lib/libdiskmgt/spec/sparcv9/Makefile new file mode 100644 index 0000000000..1d4ec142d6 --- /dev/null +++ b/usr/src/lib/libdiskmgt/spec/sparcv9/Makefile @@ -0,0 +1,43 @@ +# +# 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 2002-2003 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# lib/libdiskmgt/spec/sparcv9/Makefile + +include ../Makefile.targ + +# Add arch specific objects here +OBJECTS += + +include $(SRC)/lib/Makefile.lib +include $(SRC)/lib/Makefile.lib.64 + +# Uncomment the following if the linker complains +#sparcv9_C_PICFLAGS = -K PIC + +include $(SRC)/lib/Makefile.spec + +install: $(ROOTABILIB64) diff --git a/usr/src/lib/libdiskmgt/spec/versions b/usr/src/lib/libdiskmgt/spec/versions new file mode 100644 index 0000000000..22e0dd1104 --- /dev/null +++ b/usr/src/lib/libdiskmgt/spec/versions @@ -0,0 +1,39 @@ +# +# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# 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 +# +# ident "%Z%%M% %I% %E% SMI" +# + +sparc { + SUNWprivate_1.1; +} +sparcv9 { + SUNWprivate_1.1; +} +i386 { + SUNWprivate_1.1; +} +amd64 { + SUNWprivate_1.1; +} |