/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Creates and maintains a cache of mount points.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <synch.h>
#include <thread.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mnttab.h>
#include <sys/swap.h>
#include "libdiskmgt.h"
#include "disks_private.h"
/*
* The list of mount point entries in /etc/mnttab
*/
struct mntpnt_list {
struct mntpnt_list *next;
char *special;
char *mountp;
};
static struct mntpnt_list *mntpoint_listp = NULL;
static rwlock_t mntpoint_lock = DEFAULTRWLOCK;
static int initialized = 0;
static mutex_t init_lock = DEFAULTMUTEX;
static boolean_t diff_mnttab(int send_event, struct mntpnt_list *firstp,
struct mntpnt_list *secondp);
static void free_mnttab(struct mntpnt_list *listp);
static boolean_t in_list(struct mntpnt_list *elementp,
struct mntpnt_list *listp);
static int load_mnttab(int send_event);
static void watch_mnttab();
/*
* Search the list of devices from /etc/mnttab to find the mount point
* for the specified device.
*/
int
inuse_mnt(char *slice, nvlist_t *attrs, int *errp)
{
struct mntpnt_list *listp;
int found = 0;
*errp = 0;
if (slice == NULL) {
return (found);
}
(void) mutex_lock(&init_lock);
if (!initialized) {
thread_t mnttab_thread;
/* load the mntpnt cache */
*errp = load_mnttab(B_FALSE);
if (*errp == 0) {
/* start a thread to monitor the mnttab */
*errp = thr_create(NULL, NULL, (void *(*)(void *))watch_mnttab,
NULL, THR_NEW_LWP | THR_DAEMON, &mnttab_thread);
}
if (*errp == 0) {
initialized = 1;
}
}
(void) mutex_unlock(&init_lock);
(void) rw_rdlock(&mntpoint_lock);
listp = mntpoint_listp;
while (listp != NULL) {
if (libdiskmgt_str_eq(slice, listp->special)) {
libdiskmgt_add_str(attrs, DM_USED_BY, DM_USE_MOUNT, errp);
libdiskmgt_add_str(attrs, DM_USED_NAME, listp->mountp, errp);
found = 1;
break;
}
listp = listp->next;
}
(void) rw_unlock(&mntpoint_lock);
return (found);
}
/*
* Return true if the lists are different. Send an event for each different
* device.
*/
static boolean_t
diff_mnttab(int send_event, struct mntpnt_list *firstp,
struct mntpnt_list *secondp)
{
boolean_t different = B_FALSE;
struct mntpnt_list *listp;
listp = firstp;
while (listp != NULL) {
if (! in_list(listp, secondp)) {
/* not in new list, so was mounted and now unmounted */
if (send_event) {
events_new_slice_event(listp->special, DM_EV_TCHANGE);
}
different = B_TRUE;
}
listp = listp->next;
}
listp = secondp;
while (listp != NULL) {
if (! in_list(listp, firstp)) {
/* not in orig list, so this is a new mount */
if (send_event) {
events_new_slice_event(listp->special, DM_EV_TCHANGE);
}
different = B_TRUE;
}
listp = listp->next;
}
return (different);
}
/*
* free_mnttab()
*
* Free the list of metadevices from /etc/mnttab.
*/
static void
free_mnttab(struct mntpnt_list *listp) {
struct mntpnt_list *nextp;
while (listp != NULL) {
nextp = listp->next;
free((void *)listp->special);
free((void *)listp->mountp);
free((void *)listp);
listp = nextp;
}
}
/*
* Return true if the element is in the list.
*/
static boolean_t
in_list(struct mntpnt_list *elementp, struct mntpnt_list *listp)
{
while (listp != NULL) {
if (libdiskmgt_str_eq(elementp->special, listp->special) &&
libdiskmgt_str_eq(elementp->mountp, listp->mountp)) {
return (B_TRUE);
}
listp = listp->next;
}
return (B_FALSE);
}
/*
* load_mnttab()
*
* Create a list of devices from /etc/mnttab and swap.
* return 1 if the list has changed, 0 if the list is still the same
*/
static int
load_mnttab(int send_event)
{
struct mntpnt_list *currp;
FILE *fp;
struct mntpnt_list *headp;
int num;
struct mntpnt_list *prevp;
struct swaptable *st;
struct swapent *swapent;
int err;
int i;
headp = NULL;
prevp = NULL;
/* get the mnttab entries */
if ((fp = fopen("/etc/mnttab", "r")) != NULL) {
struct mnttab entry;
while (getmntent(fp, &entry) == 0) {
/*
* Ignore entries that are incomplete or that are not
* devices (skips network mounts, automounter entries,
* /proc, etc.).
*/
if (entry.mnt_special == NULL ||
entry.mnt_mountp == NULL ||
strncmp(entry.mnt_special, "/dev", 4) != 0) {
continue;
}
currp = (struct mntpnt_list *)calloc((size_t)1,
(size_t)sizeof (struct mntpnt_list));
if (currp == NULL) {
/*
* out of memory, free what we have and return
*/
free_mnttab(headp);
(void) fclose(fp);
return (ENOMEM);
}
if (headp == NULL) {
headp = currp;
} else {
prevp->next = currp;
}
currp->next = NULL;
currp->special = strdup(entry.mnt_special);
if (currp->special == NULL) {
/*
* out of memory, free what we have and return
*/
free_mnttab(headp);
(void) fclose(fp);
return (ENOMEM);
}
currp->mountp = strdup(entry.mnt_mountp);
if (currp->mountp == NULL) {
/*
* out of memory, free what we have and return
*/
free_mnttab(headp);
(void) fclose(fp);
return (ENOMEM);
}
prevp = currp;
}
(void) fclose(fp);
}
/* get the swap entries */
num = dm_get_swapentries(&st, &err);
if (num < 0) {
free_mnttab(headp);
return (ENOMEM);
}
for (i = 0, swapent = st->swt_ent; i < num; i++, swapent++) {
char fullpath[MAXPATHLEN+1];
currp = (struct mntpnt_list *)
calloc((size_t)1, (size_t)sizeof (struct mntpnt_list));
if (currp == NULL) {
/* out of memory, free what we have and return */
dm_free_swapentries(st);
free_mnttab(headp);
return (ENOMEM);
}
if (headp == NULL) {
headp = currp;
} else {
prevp->next = currp;
}
currp->next = NULL;
if (*swapent->ste_path != '/') {
(void) snprintf(fullpath, sizeof (fullpath), "/dev/%s",
swapent->ste_path);
} else {
(void) strlcpy(fullpath, swapent->ste_path,
sizeof (fullpath));
}
currp->special = strdup(fullpath);
if (currp->special == NULL) {
/* out of memory, free what we have and return */
dm_free_swapentries(st);
free_mnttab(headp);
return (ENOMEM);
}
currp->mountp = strdup("swap");
if (currp->mountp == NULL) {
/* out of memory, free what we have and return */
dm_free_swapentries(st);
free_mnttab(headp);
return (ENOMEM);
}
prevp = currp;
}
if (num)
dm_free_swapentries(st);
/* note that we unlock the mutex in both paths of this if statement */
(void) rw_wrlock(&mntpoint_lock);
if (diff_mnttab(send_event, mntpoint_listp, headp) == B_TRUE) {
struct mntpnt_list *tmpp;
tmpp = mntpoint_listp;
mntpoint_listp = headp;
(void) rw_unlock(&mntpoint_lock);
/* free the old list */
free_mnttab(tmpp);
} else {
(void) rw_unlock(&mntpoint_lock);
/* no change that we care about, so keep the current list */
free_mnttab(headp);
}
return (0);
}
/*
* This is a thread that runs forever, watching for changes in the mnttab
* that would cause us to flush and reload the cache of mnt entries. Only
* changes to /dev devices will cause the cache to be flushed and reloaded.
*/
static void
watch_mnttab()
{
struct pollfd fds[1];
int res;
if ((fds[0].fd = open("/etc/mnttab", O_RDONLY)) != -1) {
char buf[81];
/* do the initial read so we don't get the event right away */
(void) read(fds[0].fd, buf, (size_t)(sizeof (buf) - 1));
(void) lseek(fds[0].fd, 0, SEEK_SET);
fds[0].events = POLLRDBAND;
while (res = poll(fds, (nfds_t)1, -1)) {
if (res <= 0)
continue;
(void) load_mnttab(B_TRUE);
(void) read(fds[0].fd, buf, (size_t)(sizeof (buf) - 1));
(void) lseek(fds[0].fd, 0, SEEK_SET);
}
}
}