xref: /onnv-gate/usr/src/cmd/rcm_daemon/common/filesys_rcm.c (revision 7202:41866725ef2f)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
54845Svikram  * Common Development and Distribution License (the "License").
64845Svikram  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*7202Svikram  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
270Sstevel@tonic-gate 
280Sstevel@tonic-gate /*
290Sstevel@tonic-gate  * This module adds support to the RCM framework for mounted filesystems.
300Sstevel@tonic-gate  *
310Sstevel@tonic-gate  * The module provides this functionality:
320Sstevel@tonic-gate  * 	1) reports device usage for mounted filesystems
330Sstevel@tonic-gate  *	2) prevents offline operations for mounted resources
340Sstevel@tonic-gate  *	3) prevents suspend operations (unless forced) of those filesystems
350Sstevel@tonic-gate  *	   deemed critical for the continued operation of the OS
360Sstevel@tonic-gate  *	4) propagates RCM operations from mounted resources to the consumers
370Sstevel@tonic-gate  *	   of files within the mounted filesystems
380Sstevel@tonic-gate  */
390Sstevel@tonic-gate 
400Sstevel@tonic-gate #include <stdio.h>
410Sstevel@tonic-gate #include <assert.h>
420Sstevel@tonic-gate #include <string.h>
430Sstevel@tonic-gate #include <synch.h>
440Sstevel@tonic-gate #include <libintl.h>
450Sstevel@tonic-gate #include <errno.h>
460Sstevel@tonic-gate #include <sys/mnttab.h>
470Sstevel@tonic-gate #include <sys/param.h>
480Sstevel@tonic-gate #include <sys/stat.h>
490Sstevel@tonic-gate #include <sys/utssys.h>
504845Svikram #include <unistd.h>
514845Svikram #include <limits.h>
520Sstevel@tonic-gate 
530Sstevel@tonic-gate #include "rcm_module.h"
540Sstevel@tonic-gate 
550Sstevel@tonic-gate /* Definitions */
560Sstevel@tonic-gate 
570Sstevel@tonic-gate #define	HASH_DEFAULT		4
580Sstevel@tonic-gate #define	HASH_THRESHOLD		256
590Sstevel@tonic-gate 
600Sstevel@tonic-gate #define	OPT_IGNORE		"ignore"
610Sstevel@tonic-gate 
620Sstevel@tonic-gate #define	MSG_HDR_STD		gettext("mounted filesystem")
630Sstevel@tonic-gate #define	MSG_HDR_STD_MULTI	gettext("mounted filesystems")
640Sstevel@tonic-gate #define	MSG_HDR_CRIT		gettext("cannot suspend filesystem")
650Sstevel@tonic-gate #define	MSG_HDR_CRIT_MULTI	gettext("cannot suspend filesystems")
660Sstevel@tonic-gate #define	MSG_SEPARATOR		gettext(", ")
670Sstevel@tonic-gate #define	MSG_FAIL_USAGE		gettext("failed to construct usage string.")
680Sstevel@tonic-gate #define	MSG_FAIL_DEPENDENTS	gettext("failed while calling dependents.")
690Sstevel@tonic-gate #define	MSG_FAIL_REMOVE		gettext("filesystems cannot be removed.")
700Sstevel@tonic-gate #define	MSG_FAIL_INTERNAL	gettext("internal processing failure.")
710Sstevel@tonic-gate 
720Sstevel@tonic-gate typedef struct hashentry {
730Sstevel@tonic-gate 	int n_mounts;
740Sstevel@tonic-gate 	char *special;
754845Svikram 	char *fstype;
760Sstevel@tonic-gate 	char **mountps;
770Sstevel@tonic-gate 	struct hashentry *next;
780Sstevel@tonic-gate } hashentry_t;
790Sstevel@tonic-gate 
800Sstevel@tonic-gate typedef struct {
810Sstevel@tonic-gate 	time_t timestamp;
820Sstevel@tonic-gate 	uint32_t hash_size;
830Sstevel@tonic-gate 	hashentry_t **mounts;
840Sstevel@tonic-gate } cache_t;
850Sstevel@tonic-gate 
860Sstevel@tonic-gate /* Forward Declarations */
870Sstevel@tonic-gate 
880Sstevel@tonic-gate /* module interface routines */
890Sstevel@tonic-gate static int mnt_register(rcm_handle_t *);
900Sstevel@tonic-gate static int mnt_unregister(rcm_handle_t *);
910Sstevel@tonic-gate static int mnt_getinfo(rcm_handle_t *, char *, id_t, uint_t, char **, char **,
920Sstevel@tonic-gate     nvlist_t *, rcm_info_t **);
930Sstevel@tonic-gate static int mnt_suspend(rcm_handle_t *, char *, id_t, timespec_t *,
940Sstevel@tonic-gate     uint_t, char **, rcm_info_t **);
950Sstevel@tonic-gate static int mnt_resume(rcm_handle_t *, char *, id_t, uint_t, char **,
960Sstevel@tonic-gate     rcm_info_t **);
970Sstevel@tonic-gate static int mnt_offline(rcm_handle_t *, char *, id_t, uint_t, char **,
980Sstevel@tonic-gate     rcm_info_t **);
990Sstevel@tonic-gate static int mnt_online(rcm_handle_t *, char *, id_t, uint_t, char **,
1000Sstevel@tonic-gate     rcm_info_t **);
1010Sstevel@tonic-gate static int mnt_remove(rcm_handle_t *, char *, id_t, uint_t, char **,
1020Sstevel@tonic-gate     rcm_info_t **);
1030Sstevel@tonic-gate 
1040Sstevel@tonic-gate /* cache functions */
1050Sstevel@tonic-gate static cache_t *cache_create();
1060Sstevel@tonic-gate static int cache_insert(cache_t *, struct mnttab *);
1070Sstevel@tonic-gate static int cache_sync(rcm_handle_t *, cache_t **);
1080Sstevel@tonic-gate static hashentry_t *cache_lookup(cache_t *, char *);
1090Sstevel@tonic-gate static void free_cache(cache_t **);
1100Sstevel@tonic-gate static void free_entry(hashentry_t **);
1110Sstevel@tonic-gate static void free_list(char **);
1120Sstevel@tonic-gate 
1130Sstevel@tonic-gate /* miscellaneous functions */
1140Sstevel@tonic-gate static uint32_t hash(uint32_t, char *);
1150Sstevel@tonic-gate static void register_rsrc(rcm_handle_t *, char *);
1160Sstevel@tonic-gate static void unregister_rsrc(rcm_handle_t *, char *);
1170Sstevel@tonic-gate static char *create_message(char *, char *, char **);
1180Sstevel@tonic-gate static int detect_critical_failure(char **, uint_t, char **);
1190Sstevel@tonic-gate static int is_critical(char *);
1200Sstevel@tonic-gate static int use_cache(char *, char **, char ***);
1210Sstevel@tonic-gate static void prune_dependents(char **, char *);
1220Sstevel@tonic-gate static char **create_dependents(hashentry_t *);
1230Sstevel@tonic-gate 
1240Sstevel@tonic-gate /* Module-Private data */
1250Sstevel@tonic-gate 
1260Sstevel@tonic-gate static struct rcm_mod_ops mnt_ops =
1270Sstevel@tonic-gate {
1280Sstevel@tonic-gate 	RCM_MOD_OPS_VERSION,
1290Sstevel@tonic-gate 	mnt_register,
1300Sstevel@tonic-gate 	mnt_unregister,
1310Sstevel@tonic-gate 	mnt_getinfo,
1320Sstevel@tonic-gate 	mnt_suspend,
1330Sstevel@tonic-gate 	mnt_resume,
1340Sstevel@tonic-gate 	mnt_offline,
1350Sstevel@tonic-gate 	mnt_online,
1360Sstevel@tonic-gate 	mnt_remove
1370Sstevel@tonic-gate };
1380Sstevel@tonic-gate 
1390Sstevel@tonic-gate static cache_t *mnt_cache;
1400Sstevel@tonic-gate static mutex_t cache_lock;
1410Sstevel@tonic-gate 
1420Sstevel@tonic-gate /* Module Interface Routines */
1430Sstevel@tonic-gate 
1440Sstevel@tonic-gate /*
1450Sstevel@tonic-gate  * rcm_mod_init()
1460Sstevel@tonic-gate  *
1470Sstevel@tonic-gate  *	Called when module is loaded.  Returns the ops vector.
1480Sstevel@tonic-gate  */
1490Sstevel@tonic-gate struct rcm_mod_ops *
rcm_mod_init()1500Sstevel@tonic-gate rcm_mod_init()
1510Sstevel@tonic-gate {
1520Sstevel@tonic-gate 	return (&mnt_ops);
1530Sstevel@tonic-gate }
1540Sstevel@tonic-gate 
1550Sstevel@tonic-gate /*
1560Sstevel@tonic-gate  * rcm_mod_info()
1570Sstevel@tonic-gate  *
1580Sstevel@tonic-gate  *	Returns a string identifying this module.
1590Sstevel@tonic-gate  */
1600Sstevel@tonic-gate const char *
rcm_mod_info()1610Sstevel@tonic-gate rcm_mod_info()
1620Sstevel@tonic-gate {
163*7202Svikram 	return ("File system module 1.9");
1640Sstevel@tonic-gate }
1650Sstevel@tonic-gate 
1660Sstevel@tonic-gate /*
1670Sstevel@tonic-gate  * rcm_mod_fini()
1680Sstevel@tonic-gate  *
1690Sstevel@tonic-gate  *	Called when module is unloaded.  Frees up all used memory.
1700Sstevel@tonic-gate  *
1710Sstevel@tonic-gate  *	Locking: the cache is locked for the duration of this function.
1720Sstevel@tonic-gate  */
1730Sstevel@tonic-gate int
rcm_mod_fini()1740Sstevel@tonic-gate rcm_mod_fini()
1750Sstevel@tonic-gate {
1760Sstevel@tonic-gate 	(void) mutex_lock(&cache_lock);
1770Sstevel@tonic-gate 	free_cache(&mnt_cache);
1780Sstevel@tonic-gate 	(void) mutex_unlock(&cache_lock);
1790Sstevel@tonic-gate 
1800Sstevel@tonic-gate 	return (RCM_SUCCESS);
1810Sstevel@tonic-gate }
1820Sstevel@tonic-gate 
1830Sstevel@tonic-gate /*
1840Sstevel@tonic-gate  * mnt_register()
1850Sstevel@tonic-gate  *
1860Sstevel@tonic-gate  *	Called to synchronize the module's registrations.  Results in the
1870Sstevel@tonic-gate  *	construction of a new cache, destruction of any old cache data,
1880Sstevel@tonic-gate  *	and a full synchronization of the module's registrations.
1890Sstevel@tonic-gate  *
1900Sstevel@tonic-gate  *	Locking: the cache is locked for the duration of this function.
1910Sstevel@tonic-gate  */
1920Sstevel@tonic-gate int
mnt_register(rcm_handle_t * hd)1930Sstevel@tonic-gate mnt_register(rcm_handle_t *hd)
1940Sstevel@tonic-gate {
1950Sstevel@tonic-gate 	assert(hd != NULL);
1960Sstevel@tonic-gate 
1970Sstevel@tonic-gate 	rcm_log_message(RCM_TRACE1, "FILESYS: register()\n");
1980Sstevel@tonic-gate 
1990Sstevel@tonic-gate 	(void) mutex_lock(&cache_lock);
2000Sstevel@tonic-gate 
2010Sstevel@tonic-gate 	/* cache_sync() does all of the necessary work */
2020Sstevel@tonic-gate 	if (cache_sync(hd, &mnt_cache) < 0) {
2030Sstevel@tonic-gate 		rcm_log_message(RCM_ERROR,
2040Sstevel@tonic-gate 		    "FILESYS: failed to synchronize cache (%s).\n",
2050Sstevel@tonic-gate 		    strerror(errno));
2060Sstevel@tonic-gate 		(void) mutex_unlock(&cache_lock);
2070Sstevel@tonic-gate 		return (RCM_FAILURE);
2080Sstevel@tonic-gate 	}
2090Sstevel@tonic-gate 
2100Sstevel@tonic-gate 	(void) mutex_unlock(&cache_lock);
2110Sstevel@tonic-gate 
2120Sstevel@tonic-gate 	return (RCM_SUCCESS);
2130Sstevel@tonic-gate }
2140Sstevel@tonic-gate 
2150Sstevel@tonic-gate /*
2160Sstevel@tonic-gate  * mnt_unregister()
2170Sstevel@tonic-gate  *
2180Sstevel@tonic-gate  *	Manually walk through the cache, unregistering all the special
2190Sstevel@tonic-gate  *	files and mount points.
2200Sstevel@tonic-gate  *
2210Sstevel@tonic-gate  *	Locking: the cache is locked throughout the execution of this
2220Sstevel@tonic-gate  *	routine because it reads and modifies cache links continuously.
2230Sstevel@tonic-gate  */
2240Sstevel@tonic-gate int
mnt_unregister(rcm_handle_t * hd)2250Sstevel@tonic-gate mnt_unregister(rcm_handle_t *hd)
2260Sstevel@tonic-gate {
2270Sstevel@tonic-gate 	uint32_t index;
2280Sstevel@tonic-gate 	hashentry_t *entry;
2290Sstevel@tonic-gate 
2300Sstevel@tonic-gate 	assert(hd != NULL);
2310Sstevel@tonic-gate 
2320Sstevel@tonic-gate 	rcm_log_message(RCM_TRACE1, "FILESYS: unregister()\n");
2330Sstevel@tonic-gate 
2340Sstevel@tonic-gate 	(void) mutex_lock(&cache_lock);
2350Sstevel@tonic-gate 
2360Sstevel@tonic-gate 	/* Unregister everything in the cache */
2370Sstevel@tonic-gate 	if (mnt_cache) {
2380Sstevel@tonic-gate 		for (index = 0; index < mnt_cache->hash_size; index++) {
2390Sstevel@tonic-gate 			for (entry = mnt_cache->mounts[index]; entry != NULL;
2400Sstevel@tonic-gate 			    entry = entry->next) {
2410Sstevel@tonic-gate 				unregister_rsrc(hd, entry->special);
2420Sstevel@tonic-gate 			}
2430Sstevel@tonic-gate 		}
2440Sstevel@tonic-gate 	}
2450Sstevel@tonic-gate 
2460Sstevel@tonic-gate 	/* Destroy the cache */
2470Sstevel@tonic-gate 	free_cache(&mnt_cache);
2480Sstevel@tonic-gate 
2490Sstevel@tonic-gate 	(void) mutex_unlock(&cache_lock);
2500Sstevel@tonic-gate 
2510Sstevel@tonic-gate 	return (RCM_SUCCESS);
2520Sstevel@tonic-gate }
2530Sstevel@tonic-gate 
2540Sstevel@tonic-gate /*
2550Sstevel@tonic-gate  * mnt_offline()
2560Sstevel@tonic-gate  *
2574845Svikram  *	Filesystem resources cannot be offlined. They can however be retired
2584845Svikram  *	if they don't provide a critical service. The offline entry point
2594845Svikram  *	checks if this is a retire operation and if it is and the filesystem
2604845Svikram  *	doesn't provide a critical service, the entry point returns success
2614845Svikram  *	For all other cases, failure is returned.
2620Sstevel@tonic-gate  *	Since no real action is taken, QUERY or not doesn't matter.
2630Sstevel@tonic-gate  */
2640Sstevel@tonic-gate int
mnt_offline(rcm_handle_t * hd,char * rsrc,id_t id,uint_t flags,char ** errorp,rcm_info_t ** dependent_info)2650Sstevel@tonic-gate mnt_offline(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags,
2660Sstevel@tonic-gate     char **errorp, rcm_info_t **dependent_info)
2670Sstevel@tonic-gate {
2680Sstevel@tonic-gate 	char **dependents;
2694845Svikram 	hashentry_t *entry;
2704845Svikram 	int retval;
2714845Svikram 	int i;
2720Sstevel@tonic-gate 
2730Sstevel@tonic-gate 	assert(hd != NULL);
2740Sstevel@tonic-gate 	assert(rsrc != NULL);
2750Sstevel@tonic-gate 	assert(id == (id_t)0);
2760Sstevel@tonic-gate 	assert(errorp != NULL);
2770Sstevel@tonic-gate 
2784845Svikram 	*errorp = NULL;
2794845Svikram 
2800Sstevel@tonic-gate 	rcm_log_message(RCM_TRACE1, "FILESYS: offline(%s)\n", rsrc);
2810Sstevel@tonic-gate 
2820Sstevel@tonic-gate 	/* Retrieve necessary info from the cache */
2834845Svikram 	if (use_cache(rsrc, errorp, &dependents) < 0) {
2844845Svikram 		if (flags & RCM_RETIRE_REQUEST)
2854845Svikram 			return (RCM_NO_CONSTRAINT);
2864845Svikram 		else
2874845Svikram 			return (RCM_FAILURE);
2884845Svikram 	}
2894845Svikram 
2904845Svikram 	if (flags & RCM_RETIRE_REQUEST) {
2914845Svikram 		(void) mutex_lock(&cache_lock);
2924845Svikram 		if ((entry = cache_lookup(mnt_cache, rsrc)) == NULL) {
2934845Svikram 			rcm_log_message(RCM_ERROR, "FILESYS: "
2944845Svikram 			    "failed to look up \"%s\" in cache (%s).\n",
2954845Svikram 			    rsrc, strerror(errno));
2964845Svikram 			(void) mutex_unlock(&cache_lock);
2974845Svikram 			retval = RCM_NO_CONSTRAINT;
2984845Svikram 			goto out;
2994845Svikram 		}
3004845Svikram 
3014845Svikram 		if (strcmp(entry->fstype, "zfs") == 0) {
3024845Svikram 			retval = RCM_NO_CONSTRAINT;
3034845Svikram 			rcm_log_message(RCM_TRACE1,
3044845Svikram 			    "FILESYS: zfs: NO_CONSTRAINT: %s\n", rsrc);
3054845Svikram 		} else {
3064845Svikram 			retval = RCM_SUCCESS;
3074845Svikram 			for (i = 0; dependents[i] != NULL; i++) {
3084845Svikram 				if (is_critical(dependents[i])) {
3094845Svikram 					retval = RCM_FAILURE;
3104845Svikram 					rcm_log_message(RCM_TRACE1, "FILESYS: "
3114845Svikram 					    "CRITICAL %s\n", rsrc);
3124845Svikram 					break;
3134845Svikram 				}
3144845Svikram 			}
3154845Svikram 		}
3164845Svikram 		(void) mutex_unlock(&cache_lock);
3174845Svikram 		goto out;
3184845Svikram 	}
3194845Svikram 
3204845Svikram 	retval = RCM_FAILURE;
3210Sstevel@tonic-gate 
3220Sstevel@tonic-gate 	/* Convert the gathered dependents into an error message */
3230Sstevel@tonic-gate 	*errorp = create_message(MSG_HDR_STD, MSG_HDR_STD_MULTI, dependents);
3240Sstevel@tonic-gate 	if (*errorp == NULL) {
3250Sstevel@tonic-gate 		rcm_log_message(RCM_ERROR,
3260Sstevel@tonic-gate 		    "FILESYS: failed to construct offline message (%s).\n",
3270Sstevel@tonic-gate 		    strerror(errno));
3280Sstevel@tonic-gate 	}
3294845Svikram 
3304845Svikram out:
3310Sstevel@tonic-gate 	free_list(dependents);
3324845Svikram 	return (retval);
3330Sstevel@tonic-gate }
3340Sstevel@tonic-gate 
3350Sstevel@tonic-gate /*
3360Sstevel@tonic-gate  * mnt_online()
3370Sstevel@tonic-gate  *
3380Sstevel@tonic-gate  *	Filesystem resources aren't offlined, so there's really nothing to do
3390Sstevel@tonic-gate  *	here.
3400Sstevel@tonic-gate  */
3410Sstevel@tonic-gate int
mnt_online(rcm_handle_t * hd,char * rsrc,id_t id,uint_t flag,char ** errorp,rcm_info_t ** dependent_reason)3420Sstevel@tonic-gate mnt_online(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flag, char **errorp,
3430Sstevel@tonic-gate     rcm_info_t **dependent_reason)
3440Sstevel@tonic-gate {
3450Sstevel@tonic-gate 	assert(hd != NULL);
3460Sstevel@tonic-gate 	assert(rsrc != NULL);
3470Sstevel@tonic-gate 	assert(id == (id_t)0);
3480Sstevel@tonic-gate 	assert(errorp != NULL);
3490Sstevel@tonic-gate 
3500Sstevel@tonic-gate 	rcm_log_message(RCM_TRACE1, "FILESYS: online(%s)\n", rsrc);
3510Sstevel@tonic-gate 
3520Sstevel@tonic-gate 	return (RCM_SUCCESS);
3530Sstevel@tonic-gate }
3540Sstevel@tonic-gate 
3550Sstevel@tonic-gate /*
3560Sstevel@tonic-gate  * mnt_getinfo()
3570Sstevel@tonic-gate  *
3580Sstevel@tonic-gate  *	Report how a given resource is in use by this module.  And also
3590Sstevel@tonic-gate  *	possibly include dependent consumers of the mounted filesystems.
3600Sstevel@tonic-gate  */
3610Sstevel@tonic-gate int
mnt_getinfo(rcm_handle_t * hd,char * rsrc,id_t id,uint_t flag,char ** usagep,char ** errorp,nvlist_t * props,rcm_info_t ** depend_info)3620Sstevel@tonic-gate mnt_getinfo(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flag, char **usagep,
3630Sstevel@tonic-gate     char **errorp, nvlist_t *props, rcm_info_t **depend_info)
3640Sstevel@tonic-gate {
3650Sstevel@tonic-gate 	int rv = RCM_SUCCESS;
3660Sstevel@tonic-gate 	char **dependents;
3670Sstevel@tonic-gate 
3680Sstevel@tonic-gate 	assert(hd != NULL);
3690Sstevel@tonic-gate 	assert(rsrc != NULL);
3700Sstevel@tonic-gate 	assert(id == (id_t)0);
3710Sstevel@tonic-gate 	assert(usagep != NULL);
3720Sstevel@tonic-gate 	assert(errorp != NULL);
3730Sstevel@tonic-gate 	assert(props != NULL);
3740Sstevel@tonic-gate 
3750Sstevel@tonic-gate 	rcm_log_message(RCM_TRACE1, "FILESYS: getinfo(%s)\n", rsrc);
3760Sstevel@tonic-gate 
3770Sstevel@tonic-gate 	/* Retrieve necessary info from the cache */
3780Sstevel@tonic-gate 	if (use_cache(rsrc, errorp, &dependents) < 0)
3790Sstevel@tonic-gate 		return (RCM_FAILURE);
3800Sstevel@tonic-gate 
3810Sstevel@tonic-gate 	/* Convert the gathered dependents into a usage message */
3820Sstevel@tonic-gate 	*usagep = create_message(MSG_HDR_STD, MSG_HDR_STD_MULTI, dependents);
3830Sstevel@tonic-gate 	if (*usagep == NULL) {
3840Sstevel@tonic-gate 		rcm_log_message(RCM_ERROR,
3850Sstevel@tonic-gate 		    "FILESYS: failed to construct usage message (%s).\n",
3860Sstevel@tonic-gate 		    strerror(errno));
3870Sstevel@tonic-gate 		*errorp = strdup(MSG_FAIL_USAGE);
3880Sstevel@tonic-gate 		free_list(dependents);
3890Sstevel@tonic-gate 		return (RCM_FAILURE);
3900Sstevel@tonic-gate 	}
3910Sstevel@tonic-gate 
3920Sstevel@tonic-gate 	/* Recurse on dependents if necessary */
3930Sstevel@tonic-gate 	if ((flag & RCM_INCLUDE_DEPENDENT) && (dependents != NULL)) {
3940Sstevel@tonic-gate 		prune_dependents(dependents, rsrc);
3950Sstevel@tonic-gate 		if (dependents[0] != NULL) {
3960Sstevel@tonic-gate 			if ((rv = rcm_get_info_list(hd, dependents, flag,
3970Sstevel@tonic-gate 			    depend_info)) != RCM_SUCCESS) {
3980Sstevel@tonic-gate 				*errorp = strdup(MSG_FAIL_DEPENDENTS);
3990Sstevel@tonic-gate 			}
4000Sstevel@tonic-gate 		}
4010Sstevel@tonic-gate 	}
4020Sstevel@tonic-gate 
4030Sstevel@tonic-gate 	/* Free up info retrieved from the cache */
4040Sstevel@tonic-gate 	free_list(dependents);
4050Sstevel@tonic-gate 
4060Sstevel@tonic-gate 	return (rv);
4070Sstevel@tonic-gate }
4080Sstevel@tonic-gate 
4090Sstevel@tonic-gate /*
4100Sstevel@tonic-gate  * mnt_suspend()
4110Sstevel@tonic-gate  *
4120Sstevel@tonic-gate  *	Notify all dependents that the resource is being suspended.
4130Sstevel@tonic-gate  *	Since no real action is taken, QUERY or not doesn't matter.
4140Sstevel@tonic-gate  */
4150Sstevel@tonic-gate int
mnt_suspend(rcm_handle_t * hd,char * rsrc,id_t id,timespec_t * interval,uint_t flag,char ** errorp,rcm_info_t ** depend_info)4160Sstevel@tonic-gate mnt_suspend(rcm_handle_t *hd, char *rsrc, id_t id, timespec_t *interval,
4170Sstevel@tonic-gate     uint_t flag, char **errorp, rcm_info_t **depend_info)
4180Sstevel@tonic-gate {
4190Sstevel@tonic-gate 	int rv = RCM_SUCCESS;
4200Sstevel@tonic-gate 	char **dependents;
4210Sstevel@tonic-gate 
4220Sstevel@tonic-gate 	assert(hd != NULL);
4230Sstevel@tonic-gate 	assert(rsrc != NULL);
4240Sstevel@tonic-gate 	assert(id == (id_t)0);
4250Sstevel@tonic-gate 	assert(interval != NULL);
4260Sstevel@tonic-gate 	assert(errorp != NULL);
4270Sstevel@tonic-gate 
4280Sstevel@tonic-gate 	rcm_log_message(RCM_TRACE1, "FILESYS: suspend(%s)\n", rsrc);
4290Sstevel@tonic-gate 
4300Sstevel@tonic-gate 	/* Retrieve necessary info from the cache */
4310Sstevel@tonic-gate 	if (use_cache(rsrc, errorp, &dependents) < 0)
4320Sstevel@tonic-gate 		return (RCM_FAILURE);
4330Sstevel@tonic-gate 
4340Sstevel@tonic-gate 	/* Unforced suspensions fail if any of the dependents are critical */
4350Sstevel@tonic-gate 	if (detect_critical_failure(errorp, flag, dependents)) {
4360Sstevel@tonic-gate 		free_list(dependents);
4370Sstevel@tonic-gate 		return (RCM_FAILURE);
4380Sstevel@tonic-gate 	}
4390Sstevel@tonic-gate 
4400Sstevel@tonic-gate 	/* Recurse on dependents if necessary */
4410Sstevel@tonic-gate 	if ((flag & RCM_INCLUDE_DEPENDENT) && (dependents != NULL)) {
4420Sstevel@tonic-gate 		prune_dependents(dependents, rsrc);
4430Sstevel@tonic-gate 		if (dependents[0] != NULL)
4440Sstevel@tonic-gate 			if ((rv = rcm_request_suspend_list(hd, dependents, flag,
4450Sstevel@tonic-gate 			    interval, depend_info)) != RCM_SUCCESS) {
4460Sstevel@tonic-gate 				*errorp = strdup(MSG_FAIL_DEPENDENTS);
4470Sstevel@tonic-gate 			}
4480Sstevel@tonic-gate 	}
4490Sstevel@tonic-gate 	free_list(dependents);
4500Sstevel@tonic-gate 
4510Sstevel@tonic-gate 	return (rv);
4520Sstevel@tonic-gate }
4530Sstevel@tonic-gate 
4540Sstevel@tonic-gate /*
4550Sstevel@tonic-gate  * mnt_resume()
4560Sstevel@tonic-gate  *
4570Sstevel@tonic-gate  *	Resume all the dependents of a suspended filesystem.
4580Sstevel@tonic-gate  */
4590Sstevel@tonic-gate int
mnt_resume(rcm_handle_t * hd,char * rsrc,id_t id,uint_t flag,char ** errorp,rcm_info_t ** depend_info)4600Sstevel@tonic-gate mnt_resume(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flag, char **errorp,
4610Sstevel@tonic-gate     rcm_info_t **depend_info)
4620Sstevel@tonic-gate {
4630Sstevel@tonic-gate 	int rv = RCM_SUCCESS;
4640Sstevel@tonic-gate 	char **dependents;
4650Sstevel@tonic-gate 
4660Sstevel@tonic-gate 	assert(hd != NULL);
4670Sstevel@tonic-gate 	assert(rsrc != NULL);
4680Sstevel@tonic-gate 	assert(id == (id_t)0);
4690Sstevel@tonic-gate 	assert(errorp != NULL);
4700Sstevel@tonic-gate 
4710Sstevel@tonic-gate 	rcm_log_message(RCM_TRACE1, "FILESYS: resume(%s)\n", rsrc);
4720Sstevel@tonic-gate 
4730Sstevel@tonic-gate 	/* Retrieve necessary info from the cache */
4740Sstevel@tonic-gate 	if (use_cache(rsrc, errorp, &dependents) < 0)
4750Sstevel@tonic-gate 		return (RCM_FAILURE);
4760Sstevel@tonic-gate 
4770Sstevel@tonic-gate 	/* Recurse on dependents if necessary */
4780Sstevel@tonic-gate 	if ((flag & RCM_INCLUDE_DEPENDENT) && (dependents != NULL)) {
4790Sstevel@tonic-gate 		prune_dependents(dependents, rsrc);
4800Sstevel@tonic-gate 		if (dependents[0] != NULL) {
4810Sstevel@tonic-gate 			if ((rv = rcm_notify_resume_list(hd, dependents, flag,
4820Sstevel@tonic-gate 			    depend_info)) != RCM_SUCCESS) {
4830Sstevel@tonic-gate 				*errorp = strdup(MSG_FAIL_DEPENDENTS);
4840Sstevel@tonic-gate 			}
4850Sstevel@tonic-gate 		}
4860Sstevel@tonic-gate 	}
4870Sstevel@tonic-gate 	free_list(dependents);
4880Sstevel@tonic-gate 
4890Sstevel@tonic-gate 	return (rv);
4900Sstevel@tonic-gate }
4910Sstevel@tonic-gate 
4924845Svikram static int
get_spec(char * line,char * spec,size_t ssz)4934845Svikram get_spec(char *line, char *spec, size_t ssz)
4944845Svikram {
4954845Svikram 	char	*cp;
4964845Svikram 	char	*start;
4974845Svikram 
4984845Svikram 	if (strlcpy(spec, line, ssz) >= ssz) {
4994845Svikram 		rcm_log_message(RCM_ERROR, "FILESYS: get_spec() failed: "
5004845Svikram 		    "line: %s\n", line);
5014845Svikram 		return (-1);
5024845Svikram 	}
5034845Svikram 
5044845Svikram 	cp = spec;
5054845Svikram 	while (*cp == ' ' || *cp == '\t')
5064845Svikram 		cp++;
5074845Svikram 
5084845Svikram 	if (*cp == '#')
5094845Svikram 		return (-1);
5104845Svikram 
5114845Svikram 	start = cp;
5124845Svikram 
5134845Svikram 	while (*cp != ' ' && *cp != '\t' && *cp != '\0')
5144845Svikram 		cp++;
5154845Svikram 	*cp = '\0';
5164845Svikram 
5174845Svikram 	(void) memmove(spec, start, strlen(start) + 1);
5184845Svikram 
5194845Svikram 	return (0);
5204845Svikram }
5214845Svikram 
5224845Svikram static int
path_match(char * rsrc,char * spec)5234845Svikram path_match(char *rsrc, char *spec)
5244845Svikram {
5254845Svikram 	char r[PATH_MAX];
5264845Svikram 	char s[PATH_MAX];
5274845Svikram 	size_t len;
5284845Svikram 
5294845Svikram 	if (realpath(rsrc, r) == NULL)
5304845Svikram 		goto error;
5314845Svikram 
5324845Svikram 	if (realpath(spec, s) == NULL)
5334845Svikram 		goto error;
5344845Svikram 
5354845Svikram 	len = strlen("/devices/");
5364845Svikram 
5374845Svikram 	if (strncmp(r, "/devices/", len) != 0) {
5384845Svikram 		errno = ENXIO;
5394845Svikram 		goto error;
5404845Svikram 	}
5414845Svikram 
5424845Svikram 	if (strncmp(s, "/devices/", len) != 0) {
5434845Svikram 		errno = ENXIO;
5444845Svikram 		goto error;
5454845Svikram 	}
5464845Svikram 
5474845Svikram 	len = strlen(r);
5484845Svikram 	if (strncmp(r, s, len) == 0 && (s[len] == '\0' || s[len] == ':'))
5494845Svikram 		return (0);
5504845Svikram 	else
5514845Svikram 		return (1);
5524845Svikram 
5534845Svikram error:
5544845Svikram 	rcm_log_message(RCM_DEBUG, "FILESYS: path_match() failed "
5554845Svikram 	    "rsrc=%s spec=%s: %s\n", rsrc, spec, strerror(errno));
5564845Svikram 	return (-1);
5574845Svikram }
5584845Svikram 
5594845Svikram #define	VFSTAB		"/etc/vfstab"
5604845Svikram #define	RETIRED_PREFIX	"## RETIRED ##"
5614845Svikram 
5624845Svikram static int
disable_vfstab_entry(char * rsrc)5634845Svikram disable_vfstab_entry(char *rsrc)
5644845Svikram {
5654845Svikram 	FILE	*vfp;
5664845Svikram 	FILE	*tfp;
5674845Svikram 	int	retval;
5684845Svikram 	int	update;
5694845Svikram 	char	tmp[PATH_MAX];
5704845Svikram 	char	line[MNT_LINE_MAX + 1];
5714845Svikram 
5724845Svikram 	vfp = fopen(VFSTAB, "r");
5734845Svikram 	if (vfp == NULL) {
5744845Svikram 		rcm_log_message(RCM_ERROR, "FILESYS: failed to open /etc/vfstab"
5754845Svikram 		    " for reading: %s\n", strerror(errno));
5764845Svikram 		return (RCM_FAILURE);
5774845Svikram 	}
5784845Svikram 
5794845Svikram 	(void) snprintf(tmp, sizeof (tmp), "/etc/vfstab.retire.%lu", getpid());
5804845Svikram 
5814845Svikram 	tfp = fopen(tmp, "w");
5824845Svikram 	if (tfp == NULL) {
5834845Svikram 		rcm_log_message(RCM_ERROR, "FILESYS: failed to open "
5844845Svikram 		    "/etc/vfstab.retire for writing: %s\n", strerror(errno));
5854845Svikram 		(void) fclose(vfp);
5864845Svikram 		return (RCM_FAILURE);
5874845Svikram 	}
5884845Svikram 
5894845Svikram 	retval = RCM_SUCCESS;
5904845Svikram 	update = 0;
5914845Svikram 	while (fgets(line, sizeof (line), vfp)) {
5924845Svikram 
5934845Svikram 		char	spec[MNT_LINE_MAX + 1];
5944845Svikram 		char	newline[MNT_LINE_MAX + 1];
5954845Svikram 		char	*l;
5964845Svikram 
5974845Svikram 		if (get_spec(line, spec, sizeof (spec)) == -1) {
5984845Svikram 			l = line;
5994845Svikram 			goto foot;
6004845Svikram 		}
6014845Svikram 
6024845Svikram 		if (path_match(rsrc, spec) != 0) {
6034845Svikram 			l = line;
6044845Svikram 			goto foot;
6054845Svikram 		}
6064845Svikram 
6074845Svikram 		update = 1;
6084845Svikram 
6094845Svikram 		/* Paths match. Disable this entry */
6104845Svikram 		(void) snprintf(newline, sizeof (newline), "%s %s",
6114845Svikram 		    RETIRED_PREFIX, line);
6124845Svikram 
6134845Svikram 		rcm_log_message(RCM_TRACE1, "FILESYS: disabling line\n\t%s\n",
6144845Svikram 		    line);
6154845Svikram 
6164845Svikram 		l = newline;
6174845Svikram foot:
6184845Svikram 		if (fputs(l, tfp) == EOF) {
6194845Svikram 			rcm_log_message(RCM_ERROR, "FILESYS: failed to write "
6204845Svikram 			    "new vfstab: %s\n", strerror(errno));
6214845Svikram 			update = 0;
6224845Svikram 			retval = RCM_FAILURE;
6234845Svikram 			break;
6244845Svikram 		}
6254845Svikram 	}
6264845Svikram 
6274845Svikram 	if (vfp)
6284845Svikram 		(void) fclose(vfp);
6294845Svikram 	if (tfp)
6304845Svikram 		(void) fclose(tfp);
6314845Svikram 
6324845Svikram 	if (update) {
6334845Svikram 		if (rename(tmp, VFSTAB) != 0) {
6344845Svikram 			rcm_log_message(RCM_ERROR, "FILESYS: vfstab rename "
6354845Svikram 			    "failed: %s\n", strerror(errno));
6364845Svikram 			retval = RCM_FAILURE;
6374845Svikram 		}
6384845Svikram 	}
6394845Svikram 
6404845Svikram 	(void) unlink(tmp);
6414845Svikram 
6424845Svikram 	return (retval);
6434845Svikram }
6444845Svikram 
6450Sstevel@tonic-gate /*
6460Sstevel@tonic-gate  * mnt_remove()
6470Sstevel@tonic-gate  *
6484845Svikram  *	Remove will only be called in the retire case i.e. if RCM_RETIRE_NOTIFY
6494845Svikram  *	flag is set.
6500Sstevel@tonic-gate  *
6514845Svikram  *	If the flag is not set, then return failure and log the mistake if a
6524845Svikram  *	remove is ever received for a mounted filesystem resource.
6530Sstevel@tonic-gate  */
6540Sstevel@tonic-gate int
mnt_remove(rcm_handle_t * hd,char * rsrc,id_t id,uint_t flag,char ** errorp,rcm_info_t ** depend_info)6550Sstevel@tonic-gate mnt_remove(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flag, char **errorp,
6560Sstevel@tonic-gate     rcm_info_t **depend_info)
6570Sstevel@tonic-gate {
6580Sstevel@tonic-gate 	assert(hd != NULL);
6590Sstevel@tonic-gate 	assert(rsrc != NULL);
6600Sstevel@tonic-gate 	assert(id == (id_t)0);
6610Sstevel@tonic-gate 	assert(errorp != NULL);
6620Sstevel@tonic-gate 
6630Sstevel@tonic-gate 	rcm_log_message(RCM_TRACE1, "FILESYS: remove(%s)\n", rsrc);
6640Sstevel@tonic-gate 
6654845Svikram 	if (!(flag & RCM_RETIRE_NOTIFY)) {
6664845Svikram 		/* Log the mistake */
6674845Svikram 		rcm_log_message(RCM_ERROR, "FILESYS: invalid remove of "
6684845Svikram 		    "\"%s\"\n", rsrc);
6694845Svikram 		*errorp = strdup(MSG_FAIL_REMOVE);
6704845Svikram 		return (RCM_FAILURE);
6714845Svikram 	}
6720Sstevel@tonic-gate 
6734845Svikram 	return (disable_vfstab_entry(rsrc));
6740Sstevel@tonic-gate }
6750Sstevel@tonic-gate 
6760Sstevel@tonic-gate /*
6770Sstevel@tonic-gate  * Cache management routines
6780Sstevel@tonic-gate  */
6790Sstevel@tonic-gate 
6800Sstevel@tonic-gate /*
6810Sstevel@tonic-gate  * cache_create()
6820Sstevel@tonic-gate  *
6830Sstevel@tonic-gate  *	This routine constructs a new cache of the current mnttab file.
6840Sstevel@tonic-gate  *
6850Sstevel@tonic-gate  *	Locking: the cache must be locked prior to calling this function.
6860Sstevel@tonic-gate  *
6870Sstevel@tonic-gate  *	Return Values: NULL with errno set on failure, new cache point on
6880Sstevel@tonic-gate  *	success.
6890Sstevel@tonic-gate  */
6900Sstevel@tonic-gate static cache_t *
cache_create()6910Sstevel@tonic-gate cache_create()
6920Sstevel@tonic-gate {
6930Sstevel@tonic-gate 	FILE *fp;
6940Sstevel@tonic-gate 	cache_t *cache;
6950Sstevel@tonic-gate 	int i;
6960Sstevel@tonic-gate 	uint32_t size;
6970Sstevel@tonic-gate 	struct stat st;
6980Sstevel@tonic-gate 	struct mnttab mt;
6990Sstevel@tonic-gate 
7000Sstevel@tonic-gate 	/*
7010Sstevel@tonic-gate 	 * To keep the hash table relatively sparse, default values are
7020Sstevel@tonic-gate 	 * used for smaller mnttab files and these values are scaled up
7030Sstevel@tonic-gate 	 * as a fraction of the total mnttab file size for larger ones.
7040Sstevel@tonic-gate 	 */
7050Sstevel@tonic-gate 	if (stat(MNTTAB, &st) < 0) {
7060Sstevel@tonic-gate 		rcm_log_message(RCM_ERROR,
7070Sstevel@tonic-gate 		    "FILESYS: failed to stat \"%s\" (%s).\n", MNTTAB,
7080Sstevel@tonic-gate 		    strerror(errno));
7090Sstevel@tonic-gate 		errno = EBADF;
7100Sstevel@tonic-gate 		return (NULL);
7110Sstevel@tonic-gate 	}
7120Sstevel@tonic-gate 	if (st.st_size > HASH_THRESHOLD) {
7130Sstevel@tonic-gate 		size = st.st_size / HASH_THRESHOLD;
7140Sstevel@tonic-gate 		for (i = 0; size > 1; i++, size >>= 1);
7150Sstevel@tonic-gate 		for (; i > -1; i--, size <<= 1);
7160Sstevel@tonic-gate 	} else {
7170Sstevel@tonic-gate 		size = HASH_DEFAULT;
7180Sstevel@tonic-gate 	}
7190Sstevel@tonic-gate 
7200Sstevel@tonic-gate 	/* Allocate a new empty cache */
7210Sstevel@tonic-gate 	if ((cache = (cache_t *)calloc(1, sizeof (cache_t))) == NULL) {
7220Sstevel@tonic-gate 		rcm_log_message(RCM_ERROR,
7230Sstevel@tonic-gate 		    "FILESYS: failed to allocate cache (%s).\n",
7240Sstevel@tonic-gate 		    strerror(errno));
7250Sstevel@tonic-gate 		errno = ENOMEM;
7260Sstevel@tonic-gate 		return (NULL);
7270Sstevel@tonic-gate 	}
7280Sstevel@tonic-gate 	cache->hash_size = size;
7290Sstevel@tonic-gate 	cache->timestamp = st.st_mtime;
7300Sstevel@tonic-gate 
7310Sstevel@tonic-gate 	/* Allocate an empty hash table for the registered special devices */
7320Sstevel@tonic-gate 	cache->mounts = (hashentry_t **)calloc(size, sizeof (hashentry_t *));
7330Sstevel@tonic-gate 	if (cache->mounts == NULL) {
7340Sstevel@tonic-gate 		rcm_log_message(RCM_ERROR,
7350Sstevel@tonic-gate 		    "FILESYS: failed to allocate mount table (%s).\n",
7360Sstevel@tonic-gate 		    strerror(errno));
7370Sstevel@tonic-gate 		free_cache(&cache);
7380Sstevel@tonic-gate 		errno = ENOMEM;
7390Sstevel@tonic-gate 		return (NULL);
7400Sstevel@tonic-gate 	}
7410Sstevel@tonic-gate 
7420Sstevel@tonic-gate 	/* Open the mnttab file */
7430Sstevel@tonic-gate 	if ((fp = fopen(MNTTAB, "r")) == NULL) {
7440Sstevel@tonic-gate 		rcm_log_message(RCM_ERROR,
7450Sstevel@tonic-gate 		    "FILESYS: failed to open \"%s\" (%s).\n", MNTTAB,
7460Sstevel@tonic-gate 		    strerror(errno));
7470Sstevel@tonic-gate 		free_cache(&cache);
7480Sstevel@tonic-gate 		errno = EIO;
7490Sstevel@tonic-gate 		return (NULL);
7500Sstevel@tonic-gate 	}
7510Sstevel@tonic-gate 
7520Sstevel@tonic-gate 	/* Insert each mnttab entry into the cache */
7530Sstevel@tonic-gate 	while (getmntent(fp, &mt) == 0) {
7540Sstevel@tonic-gate 
7550Sstevel@tonic-gate 		/* Well, not each entry... some are meant to be ignored */
7560Sstevel@tonic-gate 		if ((mt.mnt_mntopts != NULL) &&
7570Sstevel@tonic-gate 		    (hasmntopt(&mt, OPT_IGNORE) != NULL))
7580Sstevel@tonic-gate 			continue;
7590Sstevel@tonic-gate 
7600Sstevel@tonic-gate 		if (cache_insert(cache, &mt) < 0) {
7610Sstevel@tonic-gate 			rcm_log_message(RCM_ERROR,
7620Sstevel@tonic-gate 			    "FILESYS: cache insertion failure (%s).\n",
7630Sstevel@tonic-gate 			    strerror(errno));
7640Sstevel@tonic-gate 			free_cache(&cache);
7650Sstevel@tonic-gate 			(void) fclose(fp);
7660Sstevel@tonic-gate 			errno = EFAULT;
7670Sstevel@tonic-gate 			return (NULL);
7680Sstevel@tonic-gate 		}
7690Sstevel@tonic-gate 	}
7700Sstevel@tonic-gate 
7710Sstevel@tonic-gate 	/* Close the mnttab file */
7720Sstevel@tonic-gate 	(void) fclose(fp);
7730Sstevel@tonic-gate 
7740Sstevel@tonic-gate 	return (cache);
7750Sstevel@tonic-gate }
7760Sstevel@tonic-gate 
7770Sstevel@tonic-gate /*
7780Sstevel@tonic-gate  * free_cache()
7790Sstevel@tonic-gate  *
7800Sstevel@tonic-gate  *	Free up all the memory associated with a cache.
7810Sstevel@tonic-gate  *
7820Sstevel@tonic-gate  *	Locking: the cache must be locked before calling this function.
7830Sstevel@tonic-gate  */
7840Sstevel@tonic-gate static void
free_cache(cache_t ** cachep)7850Sstevel@tonic-gate free_cache(cache_t **cachep)
7860Sstevel@tonic-gate {
7870Sstevel@tonic-gate 	uint32_t index;
7880Sstevel@tonic-gate 	hashentry_t *entry;
7890Sstevel@tonic-gate 	hashentry_t *entry_tmp;
7900Sstevel@tonic-gate 
7910Sstevel@tonic-gate 	/* Do nothing with empty caches */
7920Sstevel@tonic-gate 	if ((cachep == NULL) || (*cachep == NULL))
7930Sstevel@tonic-gate 		return;
7940Sstevel@tonic-gate 
7950Sstevel@tonic-gate 	if ((*cachep)->mounts) {
7960Sstevel@tonic-gate 		/* Walk through the hashtable, emptying it */
7970Sstevel@tonic-gate 		for (index = 0; index < (*cachep)->hash_size; index++) {
7980Sstevel@tonic-gate 			entry = (*cachep)->mounts[index];
7990Sstevel@tonic-gate 			while (entry) {
8000Sstevel@tonic-gate 				entry_tmp = entry->next;
8010Sstevel@tonic-gate 				free_entry(&entry);
8020Sstevel@tonic-gate 				entry = entry_tmp;
8030Sstevel@tonic-gate 			}
8040Sstevel@tonic-gate 		}
8050Sstevel@tonic-gate 		free((*cachep)->mounts);
8060Sstevel@tonic-gate 	}
8070Sstevel@tonic-gate 
8080Sstevel@tonic-gate 	free(*cachep);
8090Sstevel@tonic-gate 	*cachep = NULL;
8100Sstevel@tonic-gate }
8110Sstevel@tonic-gate 
8120Sstevel@tonic-gate /*
8130Sstevel@tonic-gate  * free_entry()
8140Sstevel@tonic-gate  *
8150Sstevel@tonic-gate  *	Free up memory associated with a hashtable entry.
8160Sstevel@tonic-gate  *
8170Sstevel@tonic-gate  *	Locking: the cache must be locked before calling this function.
8180Sstevel@tonic-gate  */
8190Sstevel@tonic-gate static void
free_entry(hashentry_t ** entryp)8200Sstevel@tonic-gate free_entry(hashentry_t **entryp)
8210Sstevel@tonic-gate {
8220Sstevel@tonic-gate 	if (entryp) {
8230Sstevel@tonic-gate 		if (*entryp) {
8240Sstevel@tonic-gate 			if ((*entryp)->special)
8250Sstevel@tonic-gate 				free((*entryp)->special);
8264845Svikram 			if ((*entryp)->fstype)
8274845Svikram 				free((*entryp)->fstype);
8280Sstevel@tonic-gate 			free_list((*entryp)->mountps);
8290Sstevel@tonic-gate 			free(*entryp);
8300Sstevel@tonic-gate 		}
8310Sstevel@tonic-gate 		*entryp = NULL;
8320Sstevel@tonic-gate 	}
8330Sstevel@tonic-gate }
8340Sstevel@tonic-gate 
8350Sstevel@tonic-gate /*
8360Sstevel@tonic-gate  * free_list()
8370Sstevel@tonic-gate  *
8380Sstevel@tonic-gate  *	Free up memory associated with a null terminated list of names.
8390Sstevel@tonic-gate  */
8400Sstevel@tonic-gate static void
free_list(char ** list)8410Sstevel@tonic-gate free_list(char **list)
8420Sstevel@tonic-gate {
8430Sstevel@tonic-gate 	int i;
8440Sstevel@tonic-gate 
8450Sstevel@tonic-gate 	if (list) {
8460Sstevel@tonic-gate 		for (i = 0; list[i] != NULL; i++)
8470Sstevel@tonic-gate 			free(list[i]);
8480Sstevel@tonic-gate 		free(list);
8490Sstevel@tonic-gate 	}
8500Sstevel@tonic-gate }
8510Sstevel@tonic-gate 
8520Sstevel@tonic-gate /*
8530Sstevel@tonic-gate  * cache_sync()
8540Sstevel@tonic-gate  *
8550Sstevel@tonic-gate  *	Resynchronize the mnttab cache with the mnttab file.
8560Sstevel@tonic-gate  *
8570Sstevel@tonic-gate  *	Locking: the cache must be locked before calling this function.
8580Sstevel@tonic-gate  *
8590Sstevel@tonic-gate  *	Return Values: -1 with errno set on failure, 0 on success.
8600Sstevel@tonic-gate  */
8610Sstevel@tonic-gate static int
cache_sync(rcm_handle_t * hd,cache_t ** cachep)8620Sstevel@tonic-gate cache_sync(rcm_handle_t *hd, cache_t **cachep)
8630Sstevel@tonic-gate {
8640Sstevel@tonic-gate 	uint32_t index;
8650Sstevel@tonic-gate 	cache_t *new_cache;
8660Sstevel@tonic-gate 	cache_t *old_cache;
8670Sstevel@tonic-gate 	hashentry_t *entry;
8680Sstevel@tonic-gate 	struct stat st;
8690Sstevel@tonic-gate 
8700Sstevel@tonic-gate 	/* Only accept valid arguments */
8710Sstevel@tonic-gate 	if ((hd == NULL) || (cachep == NULL)) {
8720Sstevel@tonic-gate 		rcm_log_message(RCM_ERROR,
8730Sstevel@tonic-gate 		    "FILESYS: invalid arguments to cache_sync().\n");
8740Sstevel@tonic-gate 		errno = EINVAL;
8750Sstevel@tonic-gate 		return (-1);
8760Sstevel@tonic-gate 	}
8770Sstevel@tonic-gate 
8780Sstevel@tonic-gate 	/* Do nothing if there's already an up-to-date cache */
8790Sstevel@tonic-gate 	old_cache = *cachep;
8800Sstevel@tonic-gate 	if (old_cache) {
8810Sstevel@tonic-gate 		if (stat(MNTTAB, &st) == 0) {
8820Sstevel@tonic-gate 			if (old_cache->timestamp >= st.st_mtime) {
8830Sstevel@tonic-gate 				return (0);
8840Sstevel@tonic-gate 			}
8850Sstevel@tonic-gate 		} else {
8860Sstevel@tonic-gate 			rcm_log_message(RCM_WARNING,
8870Sstevel@tonic-gate 			    "FILESYS: failed to stat \"%s\", cache is stale "
8880Sstevel@tonic-gate 			    "(%s).\n", MNTTAB, strerror(errno));
8890Sstevel@tonic-gate 			errno = EIO;
8900Sstevel@tonic-gate 			return (-1);
8910Sstevel@tonic-gate 		}
8920Sstevel@tonic-gate 	}
8930Sstevel@tonic-gate 
8940Sstevel@tonic-gate 	/* Create a new cache based on the new mnttab file.  */
8950Sstevel@tonic-gate 	if ((new_cache = cache_create()) == NULL) {
8960Sstevel@tonic-gate 		rcm_log_message(RCM_WARNING,
8970Sstevel@tonic-gate 		    "FILESYS: failed creating cache, cache is stale (%s).\n",
8980Sstevel@tonic-gate 		    strerror(errno));
8990Sstevel@tonic-gate 		errno = EIO;
9000Sstevel@tonic-gate 		return (-1);
9010Sstevel@tonic-gate 	}
9020Sstevel@tonic-gate 
9030Sstevel@tonic-gate 	/* Register any specials found in the new cache but not the old one */
9040Sstevel@tonic-gate 	for (index = 0; index < new_cache->hash_size; index++) {
9050Sstevel@tonic-gate 		for (entry = new_cache->mounts[index]; entry != NULL;
9060Sstevel@tonic-gate 		    entry = entry->next) {
9070Sstevel@tonic-gate 			if (cache_lookup(old_cache, entry->special) == NULL) {
9080Sstevel@tonic-gate 				register_rsrc(hd, entry->special);
9090Sstevel@tonic-gate 			}
9100Sstevel@tonic-gate 		}
9110Sstevel@tonic-gate 	}
9120Sstevel@tonic-gate 
9130Sstevel@tonic-gate 	/* Pass the new cache pointer to the calling function */
9140Sstevel@tonic-gate 	*cachep = new_cache;
9150Sstevel@tonic-gate 
9160Sstevel@tonic-gate 	/* If there wasn't an old cache, return successfully now */
9170Sstevel@tonic-gate 	if (old_cache == NULL)
9180Sstevel@tonic-gate 		return (0);
9190Sstevel@tonic-gate 
9200Sstevel@tonic-gate 	/*
9210Sstevel@tonic-gate 	 * If there was an old cache, then unregister whatever specials it
9220Sstevel@tonic-gate 	 * contains that aren't in the new cache.  And then destroy the old
9230Sstevel@tonic-gate 	 * cache.
9240Sstevel@tonic-gate 	 */
9250Sstevel@tonic-gate 	for (index = 0; index < old_cache->hash_size; index++) {
9260Sstevel@tonic-gate 		for (entry = old_cache->mounts[index]; entry != NULL;
9270Sstevel@tonic-gate 		    entry = entry->next) {
9280Sstevel@tonic-gate 			if (cache_lookup(new_cache, entry->special) == NULL) {
9290Sstevel@tonic-gate 				unregister_rsrc(hd, entry->special);
9300Sstevel@tonic-gate 			}
9310Sstevel@tonic-gate 		}
9320Sstevel@tonic-gate 	}
9330Sstevel@tonic-gate 	free_cache(&old_cache);
9340Sstevel@tonic-gate 
9350Sstevel@tonic-gate 	return (0);
9360Sstevel@tonic-gate }
9370Sstevel@tonic-gate 
9380Sstevel@tonic-gate /*
9390Sstevel@tonic-gate  * cache_insert()
9400Sstevel@tonic-gate  *
9410Sstevel@tonic-gate  *	Given a cache and a mnttab entry, this routine inserts that entry in
9424845Svikram  *	the cache.  The mnttab entry's special device and filesystem type
9434845Svikram  *	is added to the 'mounts' hashtable of the cache, and the entry's
9444845Svikram  *	mountp value is added to the list of associated mountpoints for the
9454845Svikram  *	corresponding hashtable entry.
9460Sstevel@tonic-gate  *
9470Sstevel@tonic-gate  *	Locking: the cache must be locked before calling this function.
9480Sstevel@tonic-gate  *
9490Sstevel@tonic-gate  *	Return Values: -1 with errno set on failure, 0 on success.
9500Sstevel@tonic-gate  */
9510Sstevel@tonic-gate static int
cache_insert(cache_t * cache,struct mnttab * mt)9520Sstevel@tonic-gate cache_insert(cache_t *cache, struct mnttab *mt)
9530Sstevel@tonic-gate {
9540Sstevel@tonic-gate 	uint32_t index;
9550Sstevel@tonic-gate 	hashentry_t *entry;
9560Sstevel@tonic-gate 	char **mountps;
9570Sstevel@tonic-gate 
9580Sstevel@tonic-gate 	/* Only accept valid arguments */
9590Sstevel@tonic-gate 	if ((cache == NULL) ||
9600Sstevel@tonic-gate 	    (cache->mounts == NULL) ||
9610Sstevel@tonic-gate 	    (mt == NULL) ||
9620Sstevel@tonic-gate 	    (mt->mnt_special == NULL) ||
9634845Svikram 	    (mt->mnt_mountp == NULL) ||
9644845Svikram 	    (mt->mnt_fstype == NULL)) {
9650Sstevel@tonic-gate 		errno = EINVAL;
9660Sstevel@tonic-gate 		return (-1);
9670Sstevel@tonic-gate 	}
9680Sstevel@tonic-gate 
9690Sstevel@tonic-gate 	/*
9700Sstevel@tonic-gate 	 * Disregard any non-loopback mounts whose special device names
9710Sstevel@tonic-gate 	 * don't begin with "/dev".
9720Sstevel@tonic-gate 	 */
9730Sstevel@tonic-gate 	if ((strncmp(mt->mnt_special, "/dev", strlen("/dev")) != 0) &&
9740Sstevel@tonic-gate 	    (strcmp(mt->mnt_fstype, "lofs") != 0))
9750Sstevel@tonic-gate 		return (0);
9760Sstevel@tonic-gate 
9770Sstevel@tonic-gate 	/*
9780Sstevel@tonic-gate 	 * Find the special device's entry in the mounts hashtable, allocating
9790Sstevel@tonic-gate 	 * a new entry if necessary.
9800Sstevel@tonic-gate 	 */
9810Sstevel@tonic-gate 	index = hash(cache->hash_size, mt->mnt_special);
9820Sstevel@tonic-gate 	for (entry = cache->mounts[index]; entry != NULL; entry = entry->next) {
9830Sstevel@tonic-gate 		if (strcmp(entry->special, mt->mnt_special) == 0)
9840Sstevel@tonic-gate 			break;
9850Sstevel@tonic-gate 	}
9860Sstevel@tonic-gate 	if (entry == NULL) {
9870Sstevel@tonic-gate 		entry = (hashentry_t *)calloc(1, sizeof (hashentry_t));
9880Sstevel@tonic-gate 		if ((entry == NULL) ||
9894845Svikram 		    ((entry->special = strdup(mt->mnt_special)) == NULL) ||
9904845Svikram 		    ((entry->fstype = strdup(mt->mnt_fstype)) == NULL)) {
9910Sstevel@tonic-gate 			rcm_log_message(RCM_ERROR,
9920Sstevel@tonic-gate 			    "FILESYS: failed to allocate special device name "
9934845Svikram 			    "or filesystem type: (%s).\n", strerror(errno));
9940Sstevel@tonic-gate 			free_entry(&entry);
9950Sstevel@tonic-gate 			errno = ENOMEM;
9960Sstevel@tonic-gate 			return (-1);
9970Sstevel@tonic-gate 		}
9980Sstevel@tonic-gate 		entry->next = cache->mounts[index];
9990Sstevel@tonic-gate 		cache->mounts[index] = entry;
10000Sstevel@tonic-gate 	}
10010Sstevel@tonic-gate 
10020Sstevel@tonic-gate 	/*
10030Sstevel@tonic-gate 	 * Keep entries in the list of mounts unique, so exit early if the
10040Sstevel@tonic-gate 	 * mount is already in the list.
10050Sstevel@tonic-gate 	 */
10060Sstevel@tonic-gate 	for (index = 0; index < entry->n_mounts; index++) {
10070Sstevel@tonic-gate 		if (strcmp(entry->mountps[index], mt->mnt_mountp) == 0)
10080Sstevel@tonic-gate 			return (0);
10090Sstevel@tonic-gate 	}
10100Sstevel@tonic-gate 
10110Sstevel@tonic-gate 	/*
10120Sstevel@tonic-gate 	 * Add this mountpoint to the list of mounts associated with the
10130Sstevel@tonic-gate 	 * special device.
10140Sstevel@tonic-gate 	 */
10150Sstevel@tonic-gate 	mountps = (char **)realloc(entry->mountps,
10160Sstevel@tonic-gate 	    (entry->n_mounts + 2) * sizeof (char *));
10170Sstevel@tonic-gate 	if ((mountps == NULL) ||
10180Sstevel@tonic-gate 	    ((mountps[entry->n_mounts] = strdup(mt->mnt_mountp)) == NULL)) {
10190Sstevel@tonic-gate 		rcm_log_message(RCM_ERROR,
10200Sstevel@tonic-gate 		    "FILESYS: failed to allocate mountpoint name (%s).\n",
10210Sstevel@tonic-gate 		    strerror(errno));
10220Sstevel@tonic-gate 		if (entry->n_mounts == 0) {
10230Sstevel@tonic-gate 			cache->mounts[index] = entry->next;
10240Sstevel@tonic-gate 			free_entry(&entry);
10250Sstevel@tonic-gate 		}
10260Sstevel@tonic-gate 		errno = ENOMEM;
10270Sstevel@tonic-gate 		return (-1);
10280Sstevel@tonic-gate 	}
10290Sstevel@tonic-gate 	mountps[entry->n_mounts + 1] = NULL;
10300Sstevel@tonic-gate 	entry->n_mounts++;
10310Sstevel@tonic-gate 	entry->mountps = mountps;
10320Sstevel@tonic-gate 
10330Sstevel@tonic-gate 	return (0);
10340Sstevel@tonic-gate }
10350Sstevel@tonic-gate 
10360Sstevel@tonic-gate /*
10370Sstevel@tonic-gate  * cache_lookup()
10380Sstevel@tonic-gate  *
10390Sstevel@tonic-gate  *	Searches the cached table of mounts for a special device entry.
10400Sstevel@tonic-gate  *
10410Sstevel@tonic-gate  *	Locking: the cache must be locked before calling this function.
10420Sstevel@tonic-gate  *
10430Sstevel@tonic-gate  *	Return Value: NULL with errno set if failure, pointer to existing
10440Sstevel@tonic-gate  *	cache entry when successful.
10450Sstevel@tonic-gate  */
10460Sstevel@tonic-gate static hashentry_t *
cache_lookup(cache_t * cache,char * rsrc)10470Sstevel@tonic-gate cache_lookup(cache_t *cache, char *rsrc)
10480Sstevel@tonic-gate {
10490Sstevel@tonic-gate 	uint32_t index;
10500Sstevel@tonic-gate 	hashentry_t *entry;
10510Sstevel@tonic-gate 
10520Sstevel@tonic-gate 	/* Only accept valid arguments */
10530Sstevel@tonic-gate 	if ((cache == NULL) || (cache->mounts == NULL) || (rsrc == NULL)) {
10540Sstevel@tonic-gate 		errno = EINVAL;
10550Sstevel@tonic-gate 		return (NULL);
10560Sstevel@tonic-gate 	}
10570Sstevel@tonic-gate 
10580Sstevel@tonic-gate 	/* Search the cached mounts table for the resource's entry */
10590Sstevel@tonic-gate 	index = hash(cache->hash_size, rsrc);
10600Sstevel@tonic-gate 	if (cache->mounts[index]) {
10610Sstevel@tonic-gate 		for (entry = cache->mounts[index]; entry != NULL;
10620Sstevel@tonic-gate 		    entry = entry->next) {
10630Sstevel@tonic-gate 			if (strcmp(entry->special, rsrc) == 0)
10640Sstevel@tonic-gate 				return (entry);
10650Sstevel@tonic-gate 		}
10660Sstevel@tonic-gate 	}
10670Sstevel@tonic-gate 
10680Sstevel@tonic-gate 	errno = ENOENT;
10690Sstevel@tonic-gate 	return (NULL);
10700Sstevel@tonic-gate }
10710Sstevel@tonic-gate 
10720Sstevel@tonic-gate /*
10730Sstevel@tonic-gate  * Miscellaneous Functions
10740Sstevel@tonic-gate  */
10750Sstevel@tonic-gate 
10760Sstevel@tonic-gate /*
10770Sstevel@tonic-gate  * hash()
10780Sstevel@tonic-gate  *
10790Sstevel@tonic-gate  *	A naive hashing function that converts a string 's' to an index in a
10800Sstevel@tonic-gate  * 	hash table of size 'h'.  It seems to spread entries around well enough.
10810Sstevel@tonic-gate  */
10820Sstevel@tonic-gate static uint32_t
hash(uint32_t h,char * s)10830Sstevel@tonic-gate hash(uint32_t h, char *s)
10840Sstevel@tonic-gate {
10850Sstevel@tonic-gate 	uint32_t sum = 0;
10860Sstevel@tonic-gate 	unsigned char *byte;
10870Sstevel@tonic-gate 
10880Sstevel@tonic-gate 	if ((byte = (unsigned char *)s) != NULL) {
10890Sstevel@tonic-gate 		while (*byte) {
10900Sstevel@tonic-gate 			sum += 0x3F & (uint32_t)*byte;
10910Sstevel@tonic-gate 			byte++;
10920Sstevel@tonic-gate 		}
10930Sstevel@tonic-gate 	}
10940Sstevel@tonic-gate 
10950Sstevel@tonic-gate 	return (sum % h);
10960Sstevel@tonic-gate }
10970Sstevel@tonic-gate 
10980Sstevel@tonic-gate /*
10990Sstevel@tonic-gate  * register_rsrc()
11000Sstevel@tonic-gate  *
11010Sstevel@tonic-gate  *	Registers for any given resource, unless it's "/".
11020Sstevel@tonic-gate  */
11030Sstevel@tonic-gate static void
register_rsrc(rcm_handle_t * hd,char * rsrc)11040Sstevel@tonic-gate register_rsrc(rcm_handle_t *hd, char *rsrc)
11050Sstevel@tonic-gate {
11060Sstevel@tonic-gate 	/* Only accept valid arguments */
11070Sstevel@tonic-gate 	if ((hd == NULL) || (rsrc == NULL))
11080Sstevel@tonic-gate 		return;
11090Sstevel@tonic-gate 
11100Sstevel@tonic-gate 	/*
11110Sstevel@tonic-gate 	 * Register any resource other than "/" or "/devices"
11120Sstevel@tonic-gate 	 */
11130Sstevel@tonic-gate 	if ((strcmp(rsrc, "/") != 0) && (strcmp(rsrc, "/devices") != 0)) {
11140Sstevel@tonic-gate 		rcm_log_message(RCM_DEBUG, "FILESYS: registering %s\n", rsrc);
11150Sstevel@tonic-gate 		if (rcm_register_interest(hd, rsrc, 0, NULL) != RCM_SUCCESS) {
11160Sstevel@tonic-gate 			rcm_log_message(RCM_WARNING,
11170Sstevel@tonic-gate 			    "FILESYS: failed to register %s\n", rsrc);
11180Sstevel@tonic-gate 		}
11190Sstevel@tonic-gate 	}
11200Sstevel@tonic-gate 
11210Sstevel@tonic-gate }
11220Sstevel@tonic-gate 
11230Sstevel@tonic-gate /*
11240Sstevel@tonic-gate  * unregister_rsrc()
11250Sstevel@tonic-gate  *
11260Sstevel@tonic-gate  *	Unregister a resource.  This does a little filtering since we know
11270Sstevel@tonic-gate  *	"/" can't be registered, so we never bother unregistering for it.
11280Sstevel@tonic-gate  */
11290Sstevel@tonic-gate static void
unregister_rsrc(rcm_handle_t * hd,char * rsrc)11300Sstevel@tonic-gate unregister_rsrc(rcm_handle_t *hd, char *rsrc)
11310Sstevel@tonic-gate {
11320Sstevel@tonic-gate 	assert(hd != NULL);
11330Sstevel@tonic-gate 	assert(rsrc != NULL);
11340Sstevel@tonic-gate 
11350Sstevel@tonic-gate 	/* Unregister any resource other than "/" */
11360Sstevel@tonic-gate 	if (strcmp(rsrc, "/") != 0) {
11370Sstevel@tonic-gate 		rcm_log_message(RCM_DEBUG, "FILESYS: unregistering %s\n", rsrc);
11380Sstevel@tonic-gate 		(void) rcm_unregister_interest(hd, rsrc, 0);
11390Sstevel@tonic-gate 	}
11400Sstevel@tonic-gate }
11410Sstevel@tonic-gate 
11420Sstevel@tonic-gate /*
11430Sstevel@tonic-gate  * create_message()
11440Sstevel@tonic-gate  *
11450Sstevel@tonic-gate  *	Given some header strings and a list of dependent names, this
11460Sstevel@tonic-gate  *	constructs a single string.  If there's only one dependent, the
11470Sstevel@tonic-gate  *	string consists of the first header and the only dependent appended
11480Sstevel@tonic-gate  *	to the end of the string enclosed in quotemarks.  If there are
11490Sstevel@tonic-gate  *	multiple dependents, then the string uses the second header and the
11500Sstevel@tonic-gate  *	full list of dependents is appended at the end as a comma separated
11510Sstevel@tonic-gate  *	list of names enclosed in quotemarks.
11520Sstevel@tonic-gate  */
11530Sstevel@tonic-gate static char *
create_message(char * header,char * header_multi,char ** dependents)11540Sstevel@tonic-gate create_message(char *header, char *header_multi, char **dependents)
11550Sstevel@tonic-gate {
11560Sstevel@tonic-gate 	int i;
11570Sstevel@tonic-gate 	size_t len;
11580Sstevel@tonic-gate 	int ndependents;
11590Sstevel@tonic-gate 	char *msg_buf;
11600Sstevel@tonic-gate 	char *msg_header;
11610Sstevel@tonic-gate 	char *separator = MSG_SEPARATOR;
11620Sstevel@tonic-gate 
11630Sstevel@tonic-gate 	assert(header != NULL);
11640Sstevel@tonic-gate 	assert(header_multi != NULL);
11650Sstevel@tonic-gate 	assert(dependents != NULL);
11660Sstevel@tonic-gate 
11670Sstevel@tonic-gate 	/* Count the number of dependents */
11680Sstevel@tonic-gate 	for (ndependents = 0; dependents[ndependents] != NULL; ndependents++);
11690Sstevel@tonic-gate 
11700Sstevel@tonic-gate 	/* If there are no dependents, fail */
11710Sstevel@tonic-gate 	if (ndependents == 0) {
11720Sstevel@tonic-gate 		errno = ENOENT;
11730Sstevel@tonic-gate 		return (NULL);
11740Sstevel@tonic-gate 	}
11750Sstevel@tonic-gate 
11760Sstevel@tonic-gate 	/* Pick the appropriate header to use based on amount of dependents */
11770Sstevel@tonic-gate 	if (ndependents == 1) {
11780Sstevel@tonic-gate 		msg_header = header;
11790Sstevel@tonic-gate 	} else {
11800Sstevel@tonic-gate 		msg_header = header_multi;
11810Sstevel@tonic-gate 	}
11820Sstevel@tonic-gate 
11830Sstevel@tonic-gate 	/* Compute the size required for the message buffer */
11840Sstevel@tonic-gate 	len = strlen(msg_header) + 2;	/* +2 for the space and a NULL */
11850Sstevel@tonic-gate 	for (i = 0; dependents[i] != NULL; i++)
11860Sstevel@tonic-gate 		len += strlen(dependents[i]) + 2;	/* +2 for quotemarks */
11870Sstevel@tonic-gate 	len += strlen(separator) * (ndependents - 1);
11880Sstevel@tonic-gate 
11890Sstevel@tonic-gate 	/* Allocate the message buffer */
11900Sstevel@tonic-gate 	if ((msg_buf = (char *)calloc(len, sizeof (char))) == NULL) {
11910Sstevel@tonic-gate 		rcm_log_message(RCM_ERROR,
11920Sstevel@tonic-gate 		    "FILESYS: failed to allocate message buffer (%s).\n",
11930Sstevel@tonic-gate 		    strerror(errno));
11940Sstevel@tonic-gate 		errno = ENOMEM;
11950Sstevel@tonic-gate 		return (NULL);
11960Sstevel@tonic-gate 	}
11970Sstevel@tonic-gate 
11980Sstevel@tonic-gate 	/* Fill in the message buffer */
11990Sstevel@tonic-gate 	(void) snprintf(msg_buf, len, "%s ", msg_header);
12000Sstevel@tonic-gate 	for (i = 0; dependents[i] != NULL; i++) {
12010Sstevel@tonic-gate 		(void) strlcat(msg_buf, "\"", len);
12020Sstevel@tonic-gate 		(void) strlcat(msg_buf, dependents[i], len);
12030Sstevel@tonic-gate 		(void) strlcat(msg_buf, "\"", len);
12040Sstevel@tonic-gate 		if ((i + 1) < ndependents)
12050Sstevel@tonic-gate 			(void) strlcat(msg_buf, separator, len);
12060Sstevel@tonic-gate 	}
12070Sstevel@tonic-gate 
12080Sstevel@tonic-gate 	return (msg_buf);
12090Sstevel@tonic-gate }
12100Sstevel@tonic-gate 
12110Sstevel@tonic-gate /*
12120Sstevel@tonic-gate  * create_dependents()
12130Sstevel@tonic-gate  *
12140Sstevel@tonic-gate  *	Creates a copy of the list of dependent mounts associated with a
12150Sstevel@tonic-gate  *	given hashtable entry from the cache.
12160Sstevel@tonic-gate  *
12170Sstevel@tonic-gate  *	Return Values: NULL with errno set on failure, the resulting list of
12180Sstevel@tonic-gate  *	dependent resources when successful.
12190Sstevel@tonic-gate  */
12200Sstevel@tonic-gate static char **
create_dependents(hashentry_t * entry)12210Sstevel@tonic-gate create_dependents(hashentry_t *entry)
12220Sstevel@tonic-gate {
12230Sstevel@tonic-gate 	int i;
12240Sstevel@tonic-gate 	char **dependents;
12250Sstevel@tonic-gate 
12260Sstevel@tonic-gate 	if (entry == NULL) {
12270Sstevel@tonic-gate 		errno = EINVAL;
12280Sstevel@tonic-gate 		return (NULL);
12290Sstevel@tonic-gate 	}
12300Sstevel@tonic-gate 
12310Sstevel@tonic-gate 	if (entry->n_mounts == 0) {
12320Sstevel@tonic-gate 		errno = ENOENT;
12330Sstevel@tonic-gate 		return (NULL);
12340Sstevel@tonic-gate 	}
12350Sstevel@tonic-gate 
12360Sstevel@tonic-gate 	/* Allocate space for the full dependency list */
12370Sstevel@tonic-gate 	dependents = (char **)calloc(entry->n_mounts + 1, sizeof (char *));
12380Sstevel@tonic-gate 	if (dependents == NULL) {
12390Sstevel@tonic-gate 		rcm_log_message(RCM_ERROR,
12400Sstevel@tonic-gate 		    "FILESYS: failed to allocate dependents (%s).\n",
12410Sstevel@tonic-gate 		    strerror(errno));
12420Sstevel@tonic-gate 		errno = ENOMEM;
12430Sstevel@tonic-gate 		return (NULL);
12440Sstevel@tonic-gate 	}
12450Sstevel@tonic-gate 
12460Sstevel@tonic-gate 	/* Copy all the dependent names into the new list of dependents */
12470Sstevel@tonic-gate 	for (i = 0; i < entry->n_mounts; i++) {
12480Sstevel@tonic-gate 		if ((dependents[i] = strdup(entry->mountps[i])) == NULL) {
12490Sstevel@tonic-gate 			rcm_log_message(RCM_ERROR,
12500Sstevel@tonic-gate 			    "FILESYS: failed to allocate dependent \"%s\" "
12510Sstevel@tonic-gate 			    "(%s).\n", entry->mountps[i], strerror(errno));
12520Sstevel@tonic-gate 			free_list(dependents);
12530Sstevel@tonic-gate 			errno = ENOMEM;
12540Sstevel@tonic-gate 			return (NULL);
12550Sstevel@tonic-gate 		}
12560Sstevel@tonic-gate 	}
12570Sstevel@tonic-gate 
12580Sstevel@tonic-gate 	return (dependents);
12590Sstevel@tonic-gate }
12600Sstevel@tonic-gate 
12610Sstevel@tonic-gate /*
12620Sstevel@tonic-gate  * detect_critical_failure()
12630Sstevel@tonic-gate  *
12640Sstevel@tonic-gate  *	Given a list of dependents, a place to store an error message, and
12650Sstevel@tonic-gate  *	the flags associated with an operation, this function detects whether
12660Sstevel@tonic-gate  *	or not the operation should fail due to the presence of any critical
12670Sstevel@tonic-gate  *	filesystem resources.  When a failure is detected, an appropriate
12680Sstevel@tonic-gate  *	error message is constructed and passed back to the caller.  This is
12690Sstevel@tonic-gate  *	called during a suspend request operation.
12700Sstevel@tonic-gate  *
12710Sstevel@tonic-gate  *	Return Values: 0 when a critical resource failure shouldn't prevent
12720Sstevel@tonic-gate  *	the operation, and 1 when such a failure condition does exist.
12730Sstevel@tonic-gate  */
12740Sstevel@tonic-gate static int
detect_critical_failure(char ** errorp,uint_t flags,char ** dependents)12750Sstevel@tonic-gate detect_critical_failure(char **errorp, uint_t flags, char **dependents)
12760Sstevel@tonic-gate {
12770Sstevel@tonic-gate 	int i;
12780Sstevel@tonic-gate 	int n_critical;
12790Sstevel@tonic-gate 	char *tmp;
12800Sstevel@tonic-gate 
12810Sstevel@tonic-gate 	/* Do nothing if the operation is forced or there are no dependents */
12820Sstevel@tonic-gate 	if ((errorp == NULL) || (flags & RCM_FORCE) || (dependents == NULL))
12830Sstevel@tonic-gate 		return (0);
12840Sstevel@tonic-gate 
12850Sstevel@tonic-gate 	/*
12860Sstevel@tonic-gate 	 * Count how many of the dependents are critical, and shift the
12870Sstevel@tonic-gate 	 * critical resources to the head of the list.
12880Sstevel@tonic-gate 	 */
12890Sstevel@tonic-gate 	if (dependents) {
12900Sstevel@tonic-gate 		for (i = 0, n_critical = 0; dependents[i] != NULL; i++) {
12910Sstevel@tonic-gate 			if (is_critical(dependents[i])) {
12920Sstevel@tonic-gate 				if (n_critical != i) {
12930Sstevel@tonic-gate 					tmp = dependents[n_critical];
12940Sstevel@tonic-gate 					dependents[n_critical] = dependents[i];
12950Sstevel@tonic-gate 					dependents[i] = tmp;
12960Sstevel@tonic-gate 				}
12970Sstevel@tonic-gate 				n_critical++;
12980Sstevel@tonic-gate 			}
12990Sstevel@tonic-gate 		}
13000Sstevel@tonic-gate 	}
13010Sstevel@tonic-gate 
13020Sstevel@tonic-gate 	/* If no criticals were found, do nothing and return */
13030Sstevel@tonic-gate 	if (n_critical == 0)
13040Sstevel@tonic-gate 		return (0);
13050Sstevel@tonic-gate 
13060Sstevel@tonic-gate 	/*
13070Sstevel@tonic-gate 	 * Criticals were found.  Prune the list appropriately and construct
13080Sstevel@tonic-gate 	 * an error message.
13090Sstevel@tonic-gate 	 */
13100Sstevel@tonic-gate 
13110Sstevel@tonic-gate 	/* Prune non-criticals out of the list */
13120Sstevel@tonic-gate 	for (i = n_critical; dependents[i] != NULL; i++) {
13130Sstevel@tonic-gate 		free(dependents[i]);
13140Sstevel@tonic-gate 		dependents[i] = NULL;
13150Sstevel@tonic-gate 	}
13160Sstevel@tonic-gate 
13170Sstevel@tonic-gate 	/* Construct the critical resource error message */
13180Sstevel@tonic-gate 	*errorp = create_message(MSG_HDR_CRIT, MSG_HDR_CRIT_MULTI, dependents);
13190Sstevel@tonic-gate 
13200Sstevel@tonic-gate 	return (1);
13210Sstevel@tonic-gate }
13220Sstevel@tonic-gate 
13230Sstevel@tonic-gate /*
13240Sstevel@tonic-gate  * is_critical()
13250Sstevel@tonic-gate  *
13260Sstevel@tonic-gate  *	Test a resource to determine if it's critical to the system and thus
13270Sstevel@tonic-gate  *	cannot be suspended.
13280Sstevel@tonic-gate  *
13290Sstevel@tonic-gate  *	Return Values: 1 if the named resource is critical, 0 if not.
13300Sstevel@tonic-gate  */
13310Sstevel@tonic-gate static int
is_critical(char * rsrc)13320Sstevel@tonic-gate is_critical(char *rsrc)
13330Sstevel@tonic-gate {
13340Sstevel@tonic-gate 	assert(rsrc != NULL);
13350Sstevel@tonic-gate 
13360Sstevel@tonic-gate 	if ((strcmp(rsrc, "/") == 0) ||
13370Sstevel@tonic-gate 	    (strcmp(rsrc, "/usr") == 0) ||
13384845Svikram 	    (strcmp(rsrc, "/lib") == 0) ||
13390Sstevel@tonic-gate 	    (strcmp(rsrc, "/usr/lib") == 0) ||
13404845Svikram 	    (strcmp(rsrc, "/bin") == 0) ||
13410Sstevel@tonic-gate 	    (strcmp(rsrc, "/usr/bin") == 0) ||
13420Sstevel@tonic-gate 	    (strcmp(rsrc, "/tmp") == 0) ||
13430Sstevel@tonic-gate 	    (strcmp(rsrc, "/var") == 0) ||
13440Sstevel@tonic-gate 	    (strcmp(rsrc, "/var/run") == 0) ||
13450Sstevel@tonic-gate 	    (strcmp(rsrc, "/etc") == 0) ||
13460Sstevel@tonic-gate 	    (strcmp(rsrc, "/etc/mnttab") == 0) ||
13474845Svikram 	    (strcmp(rsrc, "/platform") == 0) ||
13484845Svikram 	    (strcmp(rsrc, "/usr/platform") == 0) ||
13494845Svikram 	    (strcmp(rsrc, "/sbin") == 0) ||
13504845Svikram 	    (strcmp(rsrc, "/usr/sbin") == 0))
13510Sstevel@tonic-gate 		return (1);
13520Sstevel@tonic-gate 
13530Sstevel@tonic-gate 	return (0);
13540Sstevel@tonic-gate }
13550Sstevel@tonic-gate 
13564845Svikram 
13570Sstevel@tonic-gate /*
13580Sstevel@tonic-gate  * use_cache()
13590Sstevel@tonic-gate  *
13600Sstevel@tonic-gate  *	This routine handles all the tasks necessary to lookup a resource
13610Sstevel@tonic-gate  *	in the cache and extract a separate list of dependents for that
13620Sstevel@tonic-gate  *	entry.  If an error occurs while doing this, an appropriate error
13630Sstevel@tonic-gate  *	message is passed back to the caller.
13640Sstevel@tonic-gate  *
13650Sstevel@tonic-gate  *	Locking: the cache is locked for the whole duration of this function.
13660Sstevel@tonic-gate  */
13670Sstevel@tonic-gate static int
use_cache(char * rsrc,char ** errorp,char *** dependentsp)13680Sstevel@tonic-gate use_cache(char *rsrc, char **errorp, char ***dependentsp)
13690Sstevel@tonic-gate {
13700Sstevel@tonic-gate 	hashentry_t *entry;
13710Sstevel@tonic-gate 
13720Sstevel@tonic-gate 	(void) mutex_lock(&cache_lock);
13730Sstevel@tonic-gate 	if ((entry = cache_lookup(mnt_cache, rsrc)) == NULL) {
13740Sstevel@tonic-gate 		rcm_log_message(RCM_ERROR,
13750Sstevel@tonic-gate 		    "FILESYS: failed looking up \"%s\" in cache (%s).\n",
13760Sstevel@tonic-gate 		    rsrc, strerror(errno));
13770Sstevel@tonic-gate 		*errorp = strdup(MSG_FAIL_INTERNAL);
13780Sstevel@tonic-gate 		(void) mutex_unlock(&cache_lock);
13790Sstevel@tonic-gate 		return (-1);
13800Sstevel@tonic-gate 	}
13810Sstevel@tonic-gate 	*dependentsp = create_dependents(entry);
13820Sstevel@tonic-gate 	(void) mutex_unlock(&cache_lock);
13830Sstevel@tonic-gate 
13840Sstevel@tonic-gate 	return (0);
13850Sstevel@tonic-gate }
13860Sstevel@tonic-gate 
13870Sstevel@tonic-gate /*
13880Sstevel@tonic-gate  * prune_dependents()
13890Sstevel@tonic-gate  *
13900Sstevel@tonic-gate  *	Before calling back into RCM with a list of dependents, the list
13910Sstevel@tonic-gate  *	must be cleaned up a little.  To avoid infinite recursion, "/" and
13920Sstevel@tonic-gate  *	the named resource must be pruned out of the list.
13930Sstevel@tonic-gate  */
13940Sstevel@tonic-gate static void
prune_dependents(char ** dependents,char * rsrc)13950Sstevel@tonic-gate prune_dependents(char **dependents, char *rsrc)
13960Sstevel@tonic-gate {
13970Sstevel@tonic-gate 	int i;
13980Sstevel@tonic-gate 	int n;
13990Sstevel@tonic-gate 
14000Sstevel@tonic-gate 	if (dependents) {
14010Sstevel@tonic-gate 
14020Sstevel@tonic-gate 		/* Set 'n' to the total length of the list */
14030Sstevel@tonic-gate 		for (n = 0; dependents[n] != NULL; n++);
14040Sstevel@tonic-gate 
14050Sstevel@tonic-gate 		/*
14060Sstevel@tonic-gate 		 * Move offending dependents to the tail of the list and
14070Sstevel@tonic-gate 		 * then truncate the list.
14080Sstevel@tonic-gate 		 */
14090Sstevel@tonic-gate 		for (i = 0; dependents[i] != NULL; i++) {
14100Sstevel@tonic-gate 			if ((strcmp(dependents[i], rsrc) == 0) ||
14110Sstevel@tonic-gate 			    (strcmp(dependents[i], "/") == 0)) {
14120Sstevel@tonic-gate 				free(dependents[i]);
14130Sstevel@tonic-gate 				dependents[i] = dependents[n - 1];
14140Sstevel@tonic-gate 				dependents[n] = NULL;
14150Sstevel@tonic-gate 				i--;
14160Sstevel@tonic-gate 				n--;
14170Sstevel@tonic-gate 			}
14180Sstevel@tonic-gate 		}
14190Sstevel@tonic-gate 	}
14200Sstevel@tonic-gate }
1421