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
inuse_mnt(char * slice,nvlist_t * attrs,int * errp)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
diff_mnttab(int send_event,struct mntpnt_list * firstp,struct mntpnt_list * secondp)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
free_mnttab(struct mntpnt_list * listp)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
in_list(struct mntpnt_list * elementp,struct mntpnt_list * listp)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
load_mnttab(int send_event)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
watch_mnttab()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