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/*
23 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27/*
28 * Attempt to dynamically link in the Veritas libvxvmsc.so so that we can
29 * see if there are any Veritas volumes on any of the slices.
30 */
31
32#include <stdlib.h>
33#include <stdio.h>
34#include <strings.h>
35#include <sys/param.h>
36#include <sys/errno.h>
37#include <thread.h>
38#include <synch.h>
39#include <dlfcn.h>
40#include <link.h>
41#include <ctype.h>
42
43#include "libdiskmgt.h"
44#include "disks_private.h"
45
46#define	VXVM_LIB_NAME	"libvxvmsc.so"
47
48#define	VXVM_NAME_SIZE	1
49#define	VXVM_PATH_SIZE	2
50
51typedef char	*vm_name_t;
52typedef char	*vm_path_t;
53
54/*
55 * Pointers to libvxvmsc.so functions that we dynamically resolve.
56 */
57static int (*vxdl_libvxvm_get_version)(int version);
58static int (*vxdl_libvxvm_get_conf)(int param);
59static int (*vxdl_libvxvm_get_dgs)(int len, vm_name_t namep[]);
60static int (*vxdl_libvxvm_get_disks)(vm_name_t dgname, int len,
61		vm_path_t pathp[]);
62
63#define	MAX_DISK_GROUPS 128
64#define	MAX_DISKS_DG 1024
65
66struct vxvm_list {
67	struct vxvm_list	*next;
68	char			*slice;
69};
70
71static struct vxvm_list	*vxvm_listp = NULL;
72static time_t		timestamp = 0;
73static mutex_t		vxvm_lock = DEFAULTMUTEX;
74
75static int	add_use_record(char *devname);
76static void	free_vxvm();
77static void	*init_vxvm();
78static int	is_ctds(char *name);
79static int	load_vxvm();
80
81int
82inuse_vxvm(char *slice, nvlist_t *attrs, int *errp)
83{
84	int		found = 0;
85	time_t		curr_time;
86	char		*sp = NULL;
87
88	*errp = 0;
89	if (slice == NULL) {
90		return (found);
91	}
92
93	/*
94	 * Since vxvm "encapsulates" the disk we need to match on any
95	 * slice passed in.  Strip the slice component from the devname.
96	 */
97	if (is_ctds(slice)) {
98		if ((sp = strrchr(slice, '/')) == NULL)
99			sp = slice;
100
101		while (*sp && *sp != 's')
102			sp++;
103
104		if (*sp)
105			*sp = 0;
106		else
107			sp = NULL;
108	}
109
110	(void) mutex_lock(&vxvm_lock);
111
112	curr_time = time(NULL);
113	if (timestamp < curr_time && (curr_time - timestamp) > 60) {
114		free_vxvm();		/* free old entries */
115		*errp = load_vxvm();	/* load the cache */
116
117		timestamp = curr_time;
118	}
119
120	if (*errp == 0) {
121		struct vxvm_list	*listp;
122
123		listp = vxvm_listp;
124		while (listp != NULL) {
125			if (strcmp(slice, listp->slice) == 0) {
126				libdiskmgt_add_str(attrs, DM_USED_BY,
127				    DM_USE_VXVM, errp);
128				libdiskmgt_add_str(attrs, DM_USED_NAME,
129				    "", errp);
130				found = 1;
131				break;
132			}
133			listp = listp->next;
134		}
135	}
136
137	(void) mutex_unlock(&vxvm_lock);
138
139	/* restore slice name to orignal value */
140	if (sp != NULL)
141		*sp = 's';
142
143	return (found);
144}
145
146static int
147add_use_record(char *devname)
148{
149	struct vxvm_list *sp;
150
151	sp = (struct vxvm_list *)malloc(sizeof (struct vxvm_list));
152	if (sp == NULL) {
153		return (ENOMEM);
154	}
155
156	if ((sp->slice = strdup(devname)) == NULL) {
157		free(sp);
158		return (ENOMEM);
159	}
160
161	sp->next = vxvm_listp;
162	vxvm_listp = sp;
163
164	/*
165	 * Since vxvm "encapsulates" the disk we need to match on any
166	 * slice passed in.  Strip the slice component from the devname.
167	 */
168	if (is_ctds(sp->slice)) {
169		char	*dp;
170
171		if ((dp = strrchr(sp->slice, '/')) == NULL)
172			dp = sp->slice;
173
174		while (*dp && *dp != 's')
175			dp++;
176		*dp = 0;
177	}
178
179	return (0);
180}
181
182/*
183 * If the input name is in c[t]ds format then return 1, otherwise return 0.
184 */
185static int
186is_ctds(char *name)
187{
188	char	*p;
189
190	if ((p = strrchr(name, '/')) == NULL)
191		p = name;
192	else
193		p++;
194
195	if (*p++ != 'c') {
196		return (0);
197	}
198	/* skip controller digits */
199	while (isdigit(*p)) {
200		p++;
201	}
202
203	/* handle optional target */
204	if (*p == 't') {
205		p++;
206		/* skip over target */
207		while (isdigit(*p) || isupper(*p)) {
208			p++;
209		}
210	}
211
212	if (*p++ != 'd') {
213		return (0);
214	}
215	while (isdigit(*p)) {
216		p++;
217	}
218
219	if (*p++ != 's') {
220		return (0);
221	}
222
223	/* check the slice number */
224	while (isdigit(*p)) {
225		p++;
226	}
227
228	if (*p != 0) {
229		return (0);
230	}
231
232	return (1);
233}
234
235/*
236 * Free the list of vxvm entries.
237 */
238static void
239free_vxvm()
240{
241	struct vxvm_list	*listp = vxvm_listp;
242	struct vxvm_list	*nextp;
243
244	while (listp != NULL) {
245		nextp = listp->next;
246		free((void *)listp->slice);
247		free((void *)listp);
248		listp = nextp;
249	}
250
251	vxvm_listp = NULL;
252}
253
254/*
255 * Try to dynamically link the vxvm functions we need.
256 */
257static void *
258init_vxvm()
259{
260	void	*lh;
261
262	if ((lh = dlopen(VXVM_LIB_NAME, RTLD_NOW)) == NULL) {
263		return (NULL);
264	}
265
266	if ((vxdl_libvxvm_get_version = (int (*)(int))dlsym(lh,
267	    "libvxvm_get_version")) == NULL) {
268		(void) dlclose(lh);
269		return (NULL);
270	}
271
272	if ((vxdl_libvxvm_get_conf = (int (*)(int))dlsym(lh,
273	    "libvxvm_get_conf")) == NULL) {
274		(void) dlclose(lh);
275		return (NULL);
276	}
277
278	if ((vxdl_libvxvm_get_dgs = (int (*)(int, vm_name_t []))dlsym(lh,
279	    "libvxvm_get_dgs")) == NULL) {
280		(void) dlclose(lh);
281		return (NULL);
282	}
283
284	if ((vxdl_libvxvm_get_disks = (int (*)(vm_name_t, int, vm_path_t []))
285	    dlsym(lh, "libvxvm_get_disks")) == NULL) {
286		(void) dlclose(lh);
287		return (NULL);
288	}
289
290	return (lh);
291}
292
293static int
294load_vxvm()
295{
296	void		*lh;
297	int		vers;
298	int		nsize;
299	int		psize;
300	int		n_disk_groups;
301	vm_name_t	*namep;
302	char		*pnp;
303	vm_path_t	*pathp;
304	int		i;
305
306	if ((lh = init_vxvm()) == NULL) {
307		/* No library. */
308		return (0);
309	}
310
311	vers = (vxdl_libvxvm_get_version)(1 << 8);
312	if (vers == -1) {
313		/* unsupported version */
314		(void) dlclose(lh);
315		return (0);
316	}
317
318	nsize = (vxdl_libvxvm_get_conf)(VXVM_NAME_SIZE);
319	psize = (vxdl_libvxvm_get_conf)(VXVM_PATH_SIZE);
320
321	if (nsize == -1 || psize == -1) {
322		(void) dlclose(lh);
323		return (0);
324	}
325
326	namep = (vm_name_t *)calloc(MAX_DISK_GROUPS, nsize);
327	if (namep == NULL) {
328		(void) dlclose(lh);
329		return (ENOMEM);
330	}
331
332	pathp = (vm_path_t *)calloc(MAX_DISKS_DG, psize);
333	if (pathp == NULL) {
334		(void) dlclose(lh);
335		free(namep);
336		return (ENOMEM);
337	}
338
339	n_disk_groups = (vxdl_libvxvm_get_dgs)(MAX_DISK_GROUPS, namep);
340	if (n_disk_groups < 0) {
341		(void) dlclose(lh);
342		free(namep);
343		free(pathp);
344		return (0);
345	}
346
347	pnp = (char *)namep;
348	for (i = 0; i < n_disk_groups; i++) {
349		int n_disks;
350
351		n_disks = (vxdl_libvxvm_get_disks)(pnp, MAX_DISKS_DG, pathp);
352
353		if (n_disks >= 0) {
354			int	j;
355			char	*ppp;
356
357			ppp = (char *)pathp;
358			for (j = 0; j < n_disks; j++) {
359
360				if (strncmp(ppp, "/dev/vx/", 8) == 0) {
361					char	*pslash;
362					char	nm[MAXPATHLEN];
363
364					pslash = strrchr(ppp, '/');
365					pslash++;
366
367					(void) snprintf(nm, sizeof (nm),
368					    "/dev/dsk/%s", pslash);
369					if (add_use_record(nm)) {
370						(void) dlclose(lh);
371						free(pathp);
372						free(namep);
373						return (ENOMEM);
374					}
375				} else {
376					if (add_use_record(ppp)) {
377						(void) dlclose(lh);
378						free(pathp);
379						free(namep);
380						return (ENOMEM);
381					}
382				}
383
384				ppp += psize;
385			}
386		}
387
388		pnp += nsize;
389	}
390
391	(void) dlclose(lh);
392	free(pathp);
393	free(namep);
394
395	return (0);
396}
397