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 
49 struct mntpnt_list {
50 	struct mntpnt_list	*next;
51 	char			*special;
52 	char			*mountp;
53 };
54 
55 static struct mntpnt_list	*mntpoint_listp = NULL;
56 static rwlock_t			mntpoint_lock = DEFAULTRWLOCK;
57 static int			initialized = 0;
58 static mutex_t			init_lock = DEFAULTMUTEX;
59 
60 static boolean_t	diff_mnttab(int send_event, struct mntpnt_list *firstp,
61 			    struct mntpnt_list *secondp);
62 static void		free_mnttab(struct mntpnt_list *listp);
63 static boolean_t	in_list(struct mntpnt_list *elementp,
64 			    struct mntpnt_list *listp);
65 static int		load_mnttab(int send_event);
66 static 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  */
72 int
inuse_mnt(char * slice,nvlist_t * attrs,int * errp)73 inuse_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  */
122 static boolean_t
diff_mnttab(int send_event,struct mntpnt_list * firstp,struct mntpnt_list * secondp)123 diff_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  */
161 static void
free_mnttab(struct mntpnt_list * listp)162 free_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  */
178 static boolean_t
in_list(struct mntpnt_list * elementp,struct mntpnt_list * listp)179 in_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  */
198 static int
load_mnttab(int send_event)199 load_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  */
360 static void *
watch_mnttab(void * arg __unused)361 watch_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