1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * 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.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24/*
25 * Copyright 2010 Nexenta Systems, Inc.  All rights reserved.
26 */
27
28#include <fm/fmd_fmri.h>
29#include <strings.h>
30#include <libzfs.h>
31
32typedef struct cbdata {
33	uint64_t	cb_guid;
34	zpool_handle_t	*cb_pool;
35} cbdata_t;
36
37libzfs_handle_t *g_zfs;
38
39static int
40find_pool(zpool_handle_t *zhp, void *data)
41{
42	cbdata_t *cbp = data;
43
44	if (zpool_get_prop_int(zhp, ZPOOL_PROP_GUID, NULL) == cbp->cb_guid) {
45		cbp->cb_pool = zhp;
46		return (1);
47	}
48
49	zpool_close(zhp);
50
51	return (0);
52}
53
54ssize_t
55fmd_fmri_nvl2str(nvlist_t *nvl, char *buf, size_t buflen)
56{
57	uint64_t pool_guid, vdev_guid;
58	cbdata_t cb;
59	ssize_t len;
60	const char *name;
61	char guidbuf[64];
62
63	(void) nvlist_lookup_uint64(nvl, FM_FMRI_ZFS_POOL, &pool_guid);
64
65	/*
66	 * Attempt to convert the pool guid to a name.
67	 */
68	cb.cb_guid = pool_guid;
69	cb.cb_pool = NULL;
70
71	if (zpool_iter(g_zfs, find_pool, &cb) == 1) {
72		name = zpool_get_name(cb.cb_pool);
73	} else {
74		(void) snprintf(guidbuf, sizeof (guidbuf), "%llx", pool_guid);
75		name = guidbuf;
76	}
77
78	if (nvlist_lookup_uint64(nvl, FM_FMRI_ZFS_VDEV, &vdev_guid) == 0)
79		len = snprintf(buf, buflen, "%s://pool=%s/vdev=%llx",
80		    FM_FMRI_SCHEME_ZFS, name, vdev_guid);
81	else
82		len = snprintf(buf, buflen, "%s://pool=%s",
83		    FM_FMRI_SCHEME_ZFS, name);
84
85	if (cb.cb_pool)
86		zpool_close(cb.cb_pool);
87
88	return (len);
89}
90
91static nvlist_t *
92find_vdev_iter(nvlist_t *nv, uint64_t search)
93{
94	uint_t c, children;
95	nvlist_t **child;
96	uint64_t guid;
97	nvlist_t *ret;
98
99	(void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, &guid);
100
101	if (search == guid)
102		return (nv);
103
104	if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
105	    &child, &children) == 0) {
106
107		for (c = 0; c < children; c++)
108			if ((ret = find_vdev_iter(child[c], search)) != 0)
109				return (ret);
110	}
111
112	if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_L2CACHE,
113	    &child, &children) == 0) {
114
115		for (c = 0; c < children; c++)
116			if ((ret = find_vdev_iter(child[c], search)) != 0)
117				return (ret);
118	}
119
120	if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_SPARES,
121	    &child, &children) == 0) {
122
123		for (c = 0; c < children; c++)
124			if ((ret = find_vdev_iter(child[c], search)) != 0)
125				return (ret);
126	}
127
128	return (NULL);
129}
130
131static nvlist_t *
132find_vdev(zpool_handle_t *zhp, uint64_t guid)
133{
134	nvlist_t *config, *nvroot;
135
136	config = zpool_get_config(zhp, NULL);
137
138	(void) nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &nvroot);
139
140	return (find_vdev_iter(nvroot, guid));
141}
142
143int
144fmd_fmri_present(nvlist_t *nvl)
145{
146	uint64_t pool_guid, vdev_guid;
147	cbdata_t cb;
148	int ret;
149
150	(void) nvlist_lookup_uint64(nvl, FM_FMRI_ZFS_POOL, &pool_guid);
151
152	cb.cb_guid = pool_guid;
153	cb.cb_pool = NULL;
154
155	if (zpool_iter(g_zfs, find_pool, &cb) != 1)
156		return (0);
157
158	if (nvlist_lookup_uint64(nvl, FM_FMRI_ZFS_VDEV, &vdev_guid) != 0) {
159		zpool_close(cb.cb_pool);
160		return (1);
161	}
162
163	ret = (find_vdev(cb.cb_pool, vdev_guid) != NULL);
164
165	zpool_close(cb.cb_pool);
166
167	return (ret);
168}
169
170int
171fmd_fmri_replaced(nvlist_t *nvl)
172{
173	uint64_t pool_guid, vdev_guid;
174	cbdata_t cb;
175	int ret;
176
177	(void) nvlist_lookup_uint64(nvl, FM_FMRI_ZFS_POOL, &pool_guid);
178
179	cb.cb_guid = pool_guid;
180	cb.cb_pool = NULL;
181
182	if (zpool_iter(g_zfs, find_pool, &cb) != 1)
183		return (FMD_OBJ_STATE_NOT_PRESENT);
184
185	if (nvlist_lookup_uint64(nvl, FM_FMRI_ZFS_VDEV, &vdev_guid) != 0) {
186		zpool_close(cb.cb_pool);
187		return (FMD_OBJ_STATE_STILL_PRESENT);
188	}
189
190	ret = (find_vdev(cb.cb_pool, vdev_guid) != NULL) ?
191	    FMD_OBJ_STATE_STILL_PRESENT : FMD_OBJ_STATE_NOT_PRESENT;
192
193	zpool_close(cb.cb_pool);
194
195	return (ret);
196}
197
198int
199fmd_fmri_unusable(nvlist_t *nvl)
200{
201	uint64_t pool_guid, vdev_guid;
202	cbdata_t cb;
203	nvlist_t *vd;
204	int ret;
205
206	(void) nvlist_lookup_uint64(nvl, FM_FMRI_ZFS_POOL, &pool_guid);
207
208	cb.cb_guid = pool_guid;
209	cb.cb_pool = NULL;
210
211	if (zpool_iter(g_zfs, find_pool, &cb) != 1)
212		return (1);
213
214	if (nvlist_lookup_uint64(nvl, FM_FMRI_ZFS_VDEV, &vdev_guid) != 0) {
215		ret = (zpool_get_state(cb.cb_pool) == POOL_STATE_UNAVAIL);
216		zpool_close(cb.cb_pool);
217		return (ret);
218	}
219
220	vd = find_vdev(cb.cb_pool, vdev_guid);
221	if (vd == NULL) {
222		ret = 1;
223	} else {
224		vdev_stat_t *vs;
225		uint_t c;
226
227		(void) nvlist_lookup_uint64_array(vd, ZPOOL_CONFIG_VDEV_STATS,
228		    (uint64_t **)&vs, &c);
229
230		ret = (vs->vs_state < VDEV_STATE_DEGRADED);
231	}
232
233	zpool_close(cb.cb_pool);
234
235	return (ret);
236}
237
238int
239fmd_fmri_init(void)
240{
241	g_zfs = libzfs_init();
242
243	if (g_zfs == NULL)
244		return (-1);
245	else
246		return (0);
247}
248
249void
250fmd_fmri_fini(void)
251{
252	if (g_zfs)
253		libzfs_fini(g_zfs);
254}
255