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 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 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 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 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 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 * 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