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 2007 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26/*
27 * Creates and maintains a cache of mount points.
28 */
29
30#include <stdlib.h>
31#include <stdio.h>
32#include <string.h>
33#include <synch.h>
34#include <thread.h>
35#include <unistd.h>
36#include <sys/types.h>
37#include <sys/stat.h>
38#include <fcntl.h>
39#include <sys/mnttab.h>
40#include <sys/swap.h>
41
42#include "libdiskmgt.h"
43#include "disks_private.h"
44
45/*
46 * The list of mount point entries in /etc/mnttab
47 */
48
49struct mntpnt_list {
50	struct mntpnt_list	*next;
51	char			*special;
52	char			*mountp;
53};
54
55static struct mntpnt_list	*mntpoint_listp = NULL;
56static rwlock_t			mntpoint_lock = DEFAULTRWLOCK;
57static int			initialized = 0;
58static mutex_t			init_lock = DEFAULTMUTEX;
59
60static boolean_t	diff_mnttab(int send_event, struct mntpnt_list *firstp,
61			    struct mntpnt_list *secondp);
62static void		free_mnttab(struct mntpnt_list *listp);
63static boolean_t	in_list(struct mntpnt_list *elementp,
64			    struct mntpnt_list *listp);
65static int		load_mnttab(int send_event);
66static void		*watch_mnttab(void *);
67
68/*
69 * Search the list of devices from /etc/mnttab to find the mount point
70 * for the specified device.
71 */
72int
73inuse_mnt(char *slice, nvlist_t *attrs, int *errp)
74{
75	struct mntpnt_list	*listp;
76	int			found = 0;
77
78	*errp = 0;
79	if (slice == NULL) {
80	    return (found);
81	}
82
83	(void) mutex_lock(&init_lock);
84	if (!initialized) {
85	    thread_t	mnttab_thread;
86
87	    /* load the mntpnt cache */
88	    *errp = load_mnttab(B_FALSE);
89
90	    if (*errp == 0) {
91		/* start a thread to monitor the mnttab */
92		*errp = thr_create(NULL, 0, watch_mnttab,
93		    NULL, THR_NEW_LWP | THR_DAEMON, &mnttab_thread);
94	    }
95
96	    if (*errp == 0) {
97		initialized = 1;
98	    }
99	}
100	(void) mutex_unlock(&init_lock);
101
102	(void) rw_rdlock(&mntpoint_lock);
103	listp = mntpoint_listp;
104	while (listp != NULL) {
105	    if (libdiskmgt_str_eq(slice, listp->special)) {
106		libdiskmgt_add_str(attrs, DM_USED_BY, DM_USE_MOUNT, errp);
107		libdiskmgt_add_str(attrs, DM_USED_NAME, listp->mountp, errp);
108		found = 1;
109		break;
110	    }
111	    listp = listp->next;
112	}
113	(void) rw_unlock(&mntpoint_lock);
114
115	return (found);
116}
117
118/*
119 * Return true if the lists are different.  Send an event for each different
120 * device.
121 */
122static boolean_t
123diff_mnttab(int send_event, struct mntpnt_list *firstp,
124    struct mntpnt_list *secondp)
125{
126	boolean_t		different = B_FALSE;
127	struct mntpnt_list	*listp;
128
129	listp = firstp;
130	while (listp != NULL) {
131	    if (! in_list(listp, secondp)) {
132		/* not in new list, so was mounted and now unmounted */
133		if (send_event) {
134		    events_new_slice_event(listp->special, DM_EV_TCHANGE);
135		}
136		different = B_TRUE;
137	    }
138	    listp = listp->next;
139	}
140
141	listp = secondp;
142	while (listp != NULL) {
143	    if (! in_list(listp, firstp)) {
144		/* not in orig list, so this is a new mount */
145		if (send_event) {
146		    events_new_slice_event(listp->special, DM_EV_TCHANGE);
147		}
148		different = B_TRUE;
149	    }
150	    listp = listp->next;
151	}
152
153	return (different);
154}
155
156/*
157 * free_mnttab()
158 *
159 * Free the list of metadevices from /etc/mnttab.
160 */
161static void
162free_mnttab(struct mntpnt_list	*listp) {
163
164	struct mntpnt_list	*nextp;
165
166	while (listp != NULL) {
167		nextp = listp->next;
168		free((void *)listp->special);
169		free((void *)listp->mountp);
170		free((void *)listp);
171		listp = nextp;
172	}
173}
174
175/*
176 * Return true if the element is in the list.
177 */
178static boolean_t
179in_list(struct mntpnt_list *elementp, struct mntpnt_list *listp)
180{
181	while (listp != NULL) {
182	    if (libdiskmgt_str_eq(elementp->special, listp->special) &&
183		libdiskmgt_str_eq(elementp->mountp, listp->mountp)) {
184		return (B_TRUE);
185	    }
186	    listp = listp->next;
187	}
188
189	return (B_FALSE);
190}
191
192/*
193 * load_mnttab()
194 *
195 * Create a list of devices from /etc/mnttab and swap.
196 * return 1 if the list has changed, 0 if the list is still the same
197 */
198static int
199load_mnttab(int send_event)
200{
201
202	struct mntpnt_list	*currp;
203	FILE			*fp;
204	struct mntpnt_list	*headp;
205	int			num;
206	struct mntpnt_list	*prevp;
207	struct swaptable	*st;
208	struct swapent		*swapent;
209	int			err;
210	int			i;
211
212	headp = NULL;
213	prevp = NULL;
214
215	/* get the mnttab entries */
216	if ((fp = fopen("/etc/mnttab", "r")) != NULL) {
217
218		struct mnttab	entry;
219
220		while (getmntent(fp, &entry) == 0) {
221
222			/*
223			 * Ignore entries that are incomplete or that are not
224			 * devices (skips network mounts, automounter entries,
225			 * /proc, etc.).
226			 */
227			if (entry.mnt_special == NULL ||
228				entry.mnt_mountp == NULL ||
229				strncmp(entry.mnt_special, "/dev", 4) != 0) {
230				continue;
231			}
232
233			currp = (struct mntpnt_list *)calloc((size_t)1,
234				(size_t)sizeof (struct mntpnt_list));
235
236			if (currp == NULL) {
237				/*
238				 * out of memory, free what we have and return
239				 */
240				free_mnttab(headp);
241				(void) fclose(fp);
242				return (ENOMEM);
243			}
244
245			if (headp == NULL) {
246				headp = currp;
247			} else {
248				prevp->next = currp;
249			}
250
251			currp->next = NULL;
252
253			currp->special = strdup(entry.mnt_special);
254			if (currp->special == NULL) {
255				/*
256				 * out of memory, free what we have and return
257				 */
258				free_mnttab(headp);
259				(void) fclose(fp);
260				return (ENOMEM);
261			}
262
263			currp->mountp = strdup(entry.mnt_mountp);
264			if (currp->mountp == NULL) {
265				/*
266				 * out of memory, free what we have and return
267				 */
268				free_mnttab(headp);
269				(void) fclose(fp);
270				return (ENOMEM);
271			}
272
273			prevp = currp;
274		}
275
276		(void) fclose(fp);
277	}
278
279	/* get the swap entries */
280	num = dm_get_swapentries(&st, &err);
281	if (num < 0) {
282		free_mnttab(headp);
283		return (ENOMEM);
284	}
285
286	for (i = 0, swapent = st->swt_ent; i < num; i++, swapent++) {
287		char		fullpath[MAXPATHLEN+1];
288
289		currp = (struct mntpnt_list *)
290		    calloc((size_t)1, (size_t)sizeof (struct mntpnt_list));
291
292		if (currp == NULL) {
293			/* out of memory, free what we have and return */
294			dm_free_swapentries(st);
295			free_mnttab(headp);
296			return (ENOMEM);
297		}
298
299		if (headp == NULL) {
300			headp = currp;
301		} else {
302			prevp->next = currp;
303		}
304
305		currp->next = NULL;
306
307		if (*swapent->ste_path != '/') {
308			(void) snprintf(fullpath, sizeof (fullpath), "/dev/%s",
309			    swapent->ste_path);
310		} else {
311			(void) strlcpy(fullpath, swapent->ste_path,
312			    sizeof (fullpath));
313		}
314
315		currp->special = strdup(fullpath);
316		if (currp->special == NULL) {
317			/* out of memory, free what we have and return */
318			dm_free_swapentries(st);
319			free_mnttab(headp);
320			return (ENOMEM);
321		}
322
323		currp->mountp = strdup("swap");
324		if (currp->mountp == NULL) {
325			/* out of memory, free what we have and return */
326			dm_free_swapentries(st);
327			free_mnttab(headp);
328			return (ENOMEM);
329		}
330
331		prevp = currp;
332	}
333	if (num)
334		dm_free_swapentries(st);
335
336	/* note that we unlock the mutex in both paths of this if statement */
337	(void) rw_wrlock(&mntpoint_lock);
338	if (diff_mnttab(send_event, mntpoint_listp, headp) == B_TRUE) {
339		struct mntpnt_list	*tmpp;
340
341		tmpp = mntpoint_listp;
342		mntpoint_listp = headp;
343		(void) rw_unlock(&mntpoint_lock);
344
345		/* free the old list */
346		free_mnttab(tmpp);
347	} else {
348		(void) rw_unlock(&mntpoint_lock);
349		/* no change that we care about, so keep the current list */
350		free_mnttab(headp);
351	}
352	return (0);
353}
354
355/*
356 * This is a thread that runs forever, watching for changes in the mnttab
357 * that would cause us to flush and reload the cache of mnt entries.  Only
358 * changes to /dev devices will cause the cache to be flushed and reloaded.
359 */
360static void *
361watch_mnttab(void *arg __unused)
362{
363	struct pollfd fds[1];
364	int res;
365
366	if ((fds[0].fd = open("/etc/mnttab", O_RDONLY)) != -1) {
367
368	    char buf[81];
369
370	    /* do the initial read so we don't get the event right away */
371	    (void) read(fds[0].fd, buf, (size_t)(sizeof (buf) - 1));
372	    (void) lseek(fds[0].fd, 0, SEEK_SET);
373
374	    fds[0].events = POLLRDBAND;
375	    while (res = poll(fds, (nfds_t)1, -1)) {
376		if (res <= 0)
377		    continue;
378
379		(void) load_mnttab(B_TRUE);
380
381		(void) read(fds[0].fd, buf, (size_t)(sizeof (buf) - 1));
382		(void) lseek(fds[0].fd, 0, SEEK_SET);
383	    }
384	}
385	return (NULL);
386}
387