Path: blob/main/sys/contrib/openzfs/lib/libzfs/libzfs_config.c
108091 views
// SPDX-License-Identifier: CDDL-1.01/*2* CDDL HEADER START3*4* The contents of this file are subject to the terms of the5* Common Development and Distribution License (the "License").6* You may not use this file except in compliance with the License.7*8* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE9* or https://opensource.org/licenses/CDDL-1.0.10* See the License for the specific language governing permissions11* and limitations under the License.12*13* When distributing Covered Code, include this CDDL HEADER in each14* file and include the License file at usr/src/OPENSOLARIS.LICENSE.15* If applicable, add the following below this CDDL HEADER, with the16* fields enclosed by brackets "[]" replaced with your own identifying17* information: Portions Copyright [yyyy] [name of copyright owner]18*19* CDDL HEADER END20*/2122/*23* Copyright 2009 Sun Microsystems, Inc. All rights reserved.24* Use is subject to license terms.25*/2627/*28* Copyright (c) 2012 by Delphix. All rights reserved.29* Copyright (c) 2015 by Syneto S.R.L. All rights reserved.30* Copyright 2016 Nexenta Systems, Inc.31*/3233/*34* The pool configuration repository is stored in /etc/zfs/zpool.cache as a35* single packed nvlist. While it would be nice to just read in this36* file from userland, this wouldn't work from a local zone. So we have to have37* a zpool ioctl to return the complete configuration for all pools. In the38* global zone, this will be identical to reading the file and unpacking it in39* userland.40*/4142#include <errno.h>43#include <sys/stat.h>44#include <fcntl.h>45#include <stddef.h>46#include <string.h>47#include <unistd.h>48#include <libintl.h>4950#include "libzfs_impl.h"5152typedef struct config_node {53char *cn_name;54nvlist_t *cn_config;55avl_node_t cn_avl;56} config_node_t;5758static int59config_node_compare(const void *a, const void *b)60{61const config_node_t *ca = (config_node_t *)a;62const config_node_t *cb = (config_node_t *)b;6364return (TREE_ISIGN(strcmp(ca->cn_name, cb->cn_name)));65}6667void68namespace_clear(libzfs_handle_t *hdl)69{70config_node_t *cn;71void *cookie = NULL;7273while ((cn = avl_destroy_nodes(&hdl->libzfs_ns_avl, &cookie)) != NULL) {74nvlist_free(cn->cn_config);75free(cn->cn_name);76free(cn);77}7879avl_destroy(&hdl->libzfs_ns_avl);80}8182/*83* Loads the pool namespace, or re-loads it if the cache has changed.84*/85static int86namespace_reload(libzfs_handle_t *hdl)87{88nvlist_t *config;89config_node_t *cn;90nvpair_t *elem;91zfs_cmd_t zc = {"\0"};92void *cookie;9394if (hdl->libzfs_ns_gen == 0) {95avl_create(&hdl->libzfs_ns_avl, config_node_compare,96sizeof (config_node_t), offsetof(config_node_t, cn_avl));97}9899zcmd_alloc_dst_nvlist(hdl, &zc, 0);100101for (;;) {102zc.zc_cookie = hdl->libzfs_ns_gen;103if (zfs_ioctl(hdl, ZFS_IOC_POOL_CONFIGS, &zc) != 0) {104switch (errno) {105case EEXIST:106/*107* The namespace hasn't changed.108*/109zcmd_free_nvlists(&zc);110return (0);111112case ENOMEM:113zcmd_expand_dst_nvlist(hdl, &zc);114break;115116default:117zcmd_free_nvlists(&zc);118return (zfs_standard_error(hdl, errno,119dgettext(TEXT_DOMAIN, "failed to read "120"pool configuration")));121}122} else {123hdl->libzfs_ns_gen = zc.zc_cookie;124break;125}126}127128if (zcmd_read_dst_nvlist(hdl, &zc, &config) != 0) {129zcmd_free_nvlists(&zc);130return (-1);131}132133zcmd_free_nvlists(&zc);134135/*136* Clear out any existing configuration information.137*/138cookie = NULL;139while ((cn = avl_destroy_nodes(&hdl->libzfs_ns_avl, &cookie)) != NULL) {140nvlist_free(cn->cn_config);141free(cn->cn_name);142free(cn);143}144145elem = NULL;146while ((elem = nvlist_next_nvpair(config, elem)) != NULL) {147nvlist_t *child;148avl_index_t where;149150cn = zfs_alloc(hdl, sizeof (config_node_t));151cn->cn_name = zfs_strdup(hdl, nvpair_name(elem));152child = fnvpair_value_nvlist(elem);153if (nvlist_dup(child, &cn->cn_config, 0) != 0) {154free(cn->cn_name);155free(cn);156nvlist_free(config);157return (no_memory(hdl));158}159verify(avl_find(&hdl->libzfs_ns_avl, cn, &where) == NULL);160161avl_insert(&hdl->libzfs_ns_avl, cn, where);162}163164nvlist_free(config);165return (0);166}167168/*169* Retrieve the configuration for the given pool. The configuration is an nvlist170* describing the vdevs, as well as the statistics associated with each one.171*/172nvlist_t *173zpool_get_config(zpool_handle_t *zhp, nvlist_t **oldconfig)174{175if (oldconfig)176*oldconfig = zhp->zpool_old_config;177return (zhp->zpool_config);178}179180/*181* Retrieves a list of enabled features and their refcounts and caches it in182* the pool handle.183*/184nvlist_t *185zpool_get_features(zpool_handle_t *zhp)186{187nvlist_t *config, *features;188189config = zpool_get_config(zhp, NULL);190191if (config == NULL || !nvlist_exists(config,192ZPOOL_CONFIG_FEATURE_STATS)) {193int error;194boolean_t missing = B_FALSE;195196error = zpool_refresh_stats(zhp, &missing);197198if (error != 0 || missing)199return (NULL);200201config = zpool_get_config(zhp, NULL);202}203204if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_FEATURE_STATS,205&features) != 0)206return (NULL);207208return (features);209}210211/*212* Refresh the vdev statistics associated with the given pool. This is used in213* iostat to show configuration changes and determine the delta from the last214* time the function was called. This function can fail, in case the pool has215* been destroyed.216*/217int218zpool_refresh_stats(zpool_handle_t *zhp, boolean_t *missing)219{220zfs_cmd_t zc = {"\0"};221int error;222nvlist_t *config;223libzfs_handle_t *hdl = zhp->zpool_hdl;224225*missing = B_FALSE;226(void) strcpy(zc.zc_name, zhp->zpool_name);227228if (zhp->zpool_config_size == 0)229zhp->zpool_config_size = 1 << 16;230231zcmd_alloc_dst_nvlist(hdl, &zc, zhp->zpool_config_size);232233for (;;) {234if (zfs_ioctl(zhp->zpool_hdl, ZFS_IOC_POOL_STATS,235&zc) == 0) {236/*237* The real error is returned in the zc_cookie field.238*/239error = zc.zc_cookie;240break;241}242243if (errno == ENOMEM)244zcmd_expand_dst_nvlist(hdl, &zc);245else {246zcmd_free_nvlists(&zc);247if (errno == ENOENT || errno == EINVAL)248*missing = B_TRUE;249zhp->zpool_state = POOL_STATE_UNAVAIL;250return (0);251}252}253254if (zcmd_read_dst_nvlist(hdl, &zc, &config) != 0) {255zcmd_free_nvlists(&zc);256return (-1);257}258259zcmd_free_nvlists(&zc);260261zhp->zpool_config_size = zc.zc_nvlist_dst_size;262263if (zhp->zpool_config != NULL) {264nvlist_free(zhp->zpool_old_config);265266zhp->zpool_old_config = zhp->zpool_config;267}268269zhp->zpool_config = config;270if (error)271zhp->zpool_state = POOL_STATE_UNAVAIL;272else273zhp->zpool_state = POOL_STATE_ACTIVE;274275return (0);276}277278/*279* Copies the pool config and state from szhp to dzhp. szhp and dzhp must280* represent the same pool. Used by pool_list_refresh() to avoid another281* round-trip into the kernel to get stats already collected earlier in the282* function.283*/284void285zpool_refresh_stats_from_handle(zpool_handle_t *dzhp, zpool_handle_t *szhp)286{287VERIFY0(strcmp(dzhp->zpool_name, szhp->zpool_name));288nvlist_free(dzhp->zpool_old_config);289dzhp->zpool_old_config = dzhp->zpool_config;290dzhp->zpool_config = fnvlist_dup(szhp->zpool_config);291dzhp->zpool_config_size = szhp->zpool_config_size;292dzhp->zpool_state = szhp->zpool_state;293}294295/*296* The following environment variables are undocumented297* and should be used for testing purposes only:298*299* __ZFS_POOL_EXCLUDE - don't iterate over the pools it lists300* __ZFS_POOL_RESTRICT - iterate only over the pools it lists301*302* This function returns B_TRUE if the pool should be skipped303* during iteration.304*/305boolean_t306zpool_skip_pool(const char *poolname)307{308static boolean_t initialized = B_FALSE;309static const char *exclude = NULL;310static const char *restricted = NULL;311312const char *cur, *end;313int len;314int namelen = strlen(poolname);315316if (!initialized) {317initialized = B_TRUE;318exclude = getenv("__ZFS_POOL_EXCLUDE");319restricted = getenv("__ZFS_POOL_RESTRICT");320}321322if (exclude != NULL) {323cur = exclude;324do {325end = strchr(cur, ' ');326len = (NULL == end) ? strlen(cur) : (end - cur);327if (len == namelen && 0 == strncmp(cur, poolname, len))328return (B_TRUE);329cur += (len + 1);330} while (NULL != end);331}332333if (NULL == restricted)334return (B_FALSE);335336cur = restricted;337do {338end = strchr(cur, ' ');339len = (NULL == end) ? strlen(cur) : (end - cur);340341if (len == namelen && 0 == strncmp(cur, poolname, len)) {342return (B_FALSE);343}344345cur += (len + 1);346} while (NULL != end);347348return (B_TRUE);349}350351/*352* Iterate over all pools in the system.353*/354int355zpool_iter(libzfs_handle_t *hdl, zpool_iter_f func, void *data)356{357config_node_t *cn;358zpool_handle_t *zhp;359int ret;360361/*362* If someone makes a recursive call to zpool_iter(), we want to avoid363* refreshing the namespace because that will invalidate the parent364* context. We allow recursive calls, but simply re-use the same365* namespace AVL tree.366*/367if (!hdl->libzfs_pool_iter && namespace_reload(hdl) != 0)368return (-1);369370hdl->libzfs_pool_iter++;371for (cn = avl_first(&hdl->libzfs_ns_avl); cn != NULL;372cn = AVL_NEXT(&hdl->libzfs_ns_avl, cn)) {373374if (zpool_skip_pool(cn->cn_name))375continue;376377if (zpool_open_silent(hdl, cn->cn_name, &zhp) != 0) {378hdl->libzfs_pool_iter--;379return (-1);380}381382if (zhp == NULL)383continue;384385if ((ret = func(zhp, data)) != 0) {386hdl->libzfs_pool_iter--;387return (ret);388}389}390hdl->libzfs_pool_iter--;391392return (0);393}394395/*396* Iterate over root datasets, calling the given function for each. The zfs397* handle passed each time must be explicitly closed by the callback.398*/399int400zfs_iter_root(libzfs_handle_t *hdl, zfs_iter_f func, void *data)401{402config_node_t *cn;403zfs_handle_t *zhp;404int ret;405406if (namespace_reload(hdl) != 0)407return (-1);408409for (cn = avl_first(&hdl->libzfs_ns_avl); cn != NULL;410cn = AVL_NEXT(&hdl->libzfs_ns_avl, cn)) {411412if (zpool_skip_pool(cn->cn_name))413continue;414415if ((zhp = make_dataset_handle(hdl, cn->cn_name)) == NULL)416continue;417418if ((ret = func(zhp, data)) != 0)419return (ret);420}421422return (0);423}424425426