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