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