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