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