1 /* 2 * 3 * CDDL HEADER START 4 * 5 * The contents of this file are subject to the terms of the 6 * Common Development and Distribution License (the "License"). 7 * You may not use this file except in compliance with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 23 /* 24 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. 25 * Copyright 2013 Nexenta Systems, Inc. All rights reserved. 26 */ 27 28 #include <stdio.h> 29 #include <stdlib.h> 30 #include <string.h> 31 #include <errno.h> 32 #include <ctype.h> 33 #include <alloca.h> 34 #include <limits.h> 35 #include <fm/topo_mod.h> 36 #include <sys/param.h> 37 #include <sys/systeminfo.h> 38 #include <sys/fm/protocol.h> 39 #include <sys/stat.h> 40 41 #include <topo_method.h> 42 #include <topo_subr.h> 43 #include <libzfs.h> 44 #include <zfs.h> 45 #include <pthread.h> 46 47 static int zfs_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t, 48 topo_instance_t, void *, void *); 49 static void zfs_rele(topo_mod_t *, tnode_t *); 50 static int zfs_fmri_nvl2str(topo_mod_t *, tnode_t *, topo_version_t, 51 nvlist_t *, nvlist_t **); 52 53 const topo_method_t zfs_methods[] = { 54 { TOPO_METH_NVL2STR, TOPO_METH_NVL2STR_DESC, TOPO_METH_NVL2STR_VERSION, 55 TOPO_STABILITY_INTERNAL, zfs_fmri_nvl2str }, 56 { NULL } 57 }; 58 59 static const topo_modops_t zfs_ops = 60 { zfs_enum, zfs_rele }; 61 static const topo_modinfo_t zfs_info = 62 { ZFS, FM_FMRI_SCHEME_ZFS, ZFS_VERSION, &zfs_ops }; 63 64 static libzfs_handle_t *g_zfs = NULL; 65 static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER; 66 static int g_refcount = 0; 67 68 int 69 zfs_init(topo_mod_t *mod, topo_version_t version) 70 { 71 /* 72 * Turn on module debugging output 73 */ 74 if (getenv("TOPOZFSDEBUG")) 75 topo_mod_setdebug(mod); 76 77 topo_mod_dprintf(mod, "initializing zfs builtin\n"); 78 79 if (version != ZFS_VERSION) 80 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 81 82 if (topo_mod_register(mod, &zfs_info, TOPO_VERSION) != 0) { 83 topo_mod_dprintf(mod, "failed to register zfs: " 84 "%s\n", topo_mod_errmsg(mod)); 85 return (-1); /* mod errno already set */ 86 } 87 88 (void) pthread_mutex_lock(&g_lock); 89 if (g_refcount == 0) { 90 if ((g_zfs = libzfs_init()) == NULL) { 91 (void) pthread_mutex_unlock(&g_lock); 92 topo_mod_dprintf(mod, "libzfs_init() failed"); 93 topo_mod_unregister(mod); 94 return (topo_mod_seterrno(mod, EMOD_UNKNOWN)); 95 } 96 } 97 g_refcount++; 98 (void) pthread_mutex_unlock(&g_lock); 99 100 return (0); 101 } 102 103 void 104 zfs_fini(topo_mod_t *mod) 105 { 106 (void) pthread_mutex_lock(&g_lock); 107 g_refcount--; 108 if (g_refcount == 0) { 109 libzfs_fini(g_zfs); 110 g_zfs = NULL; 111 } 112 (void) pthread_mutex_unlock(&g_lock); 113 topo_mod_unregister(mod); 114 } 115 116 117 /*ARGSUSED*/ 118 int 119 zfs_enum(topo_mod_t *mod, tnode_t *pnode, const char *name, topo_instance_t min, 120 topo_instance_t max, void *notused1, void *notused2) 121 { 122 /* 123 * Methods are registered, but there is no enumeration. Should 124 * enumeration be added be sure to cater for global vs non-global 125 * zones. 126 */ 127 (void) topo_method_register(mod, pnode, zfs_methods); 128 return (0); 129 } 130 131 /*ARGSUSED*/ 132 static void 133 zfs_rele(topo_mod_t *mp, tnode_t *node) 134 { 135 topo_method_unregister_all(mp, node); 136 } 137 138 typedef struct cbdata { 139 uint64_t cb_guid; 140 zpool_handle_t *cb_pool; 141 } cbdata_t; 142 143 static int 144 find_pool(zpool_handle_t *zhp, void *data) 145 { 146 cbdata_t *cbp = data; 147 148 if (zpool_get_prop_int(zhp, ZPOOL_PROP_GUID, NULL) == cbp->cb_guid) { 149 cbp->cb_pool = zhp; 150 return (1); 151 } 152 153 zpool_close(zhp); 154 155 return (0); 156 } 157 158 static ssize_t 159 fmri_nvl2str(nvlist_t *nvl, char *buf, size_t buflen) 160 { 161 uint64_t pool_guid, vdev_guid; 162 cbdata_t cb; 163 ssize_t len; 164 const char *name; 165 char guidbuf[64]; 166 167 (void) nvlist_lookup_uint64(nvl, FM_FMRI_ZFS_POOL, &pool_guid); 168 169 /* 170 * Attempt to convert the pool guid to a name. 171 */ 172 cb.cb_guid = pool_guid; 173 cb.cb_pool = NULL; 174 175 if (zpool_iter(g_zfs, find_pool, &cb) == 1) { 176 name = zpool_get_name(cb.cb_pool); 177 } else { 178 (void) snprintf(guidbuf, sizeof (guidbuf), "%llx", pool_guid); 179 name = guidbuf; 180 } 181 182 if (nvlist_lookup_uint64(nvl, FM_FMRI_ZFS_VDEV, &vdev_guid) == 0) 183 len = snprintf(buf, buflen, "%s://pool=%s/vdev=%llx", 184 FM_FMRI_SCHEME_ZFS, name, vdev_guid); 185 else 186 len = snprintf(buf, buflen, "%s://pool=%s", 187 FM_FMRI_SCHEME_ZFS, name); 188 189 if (cb.cb_pool) 190 zpool_close(cb.cb_pool); 191 192 return (len); 193 } 194 195 /*ARGSUSED*/ 196 static int 197 zfs_fmri_nvl2str(topo_mod_t *mod, tnode_t *node, topo_version_t version, 198 nvlist_t *nvl, nvlist_t **out) 199 { 200 ssize_t len; 201 char *name = NULL; 202 nvlist_t *fmristr; 203 204 if (version > TOPO_METH_NVL2STR_VERSION) 205 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 206 207 if ((len = fmri_nvl2str(nvl, NULL, 0)) == 0 || 208 (name = topo_mod_alloc(mod, len + 1)) == NULL || 209 fmri_nvl2str(nvl, name, len + 1) == 0) { 210 if (name != NULL) 211 topo_mod_free(mod, name, len + 1); 212 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 213 } 214 215 if (topo_mod_nvalloc(mod, &fmristr, NV_UNIQUE_NAME) != 0) { 216 topo_mod_free(mod, name, len + 1); 217 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 218 } 219 if (nvlist_add_string(fmristr, "fmri-string", name) != 0) { 220 topo_mod_free(mod, name, len + 1); 221 nvlist_free(fmristr); 222 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 223 } 224 topo_mod_free(mod, name, len + 1); 225 *out = fmristr; 226 227 return (0); 228 } 229