4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 * This module adds support to the RCM framework for mounted filesystems.
29 * The module provides this functionality:
30 * 1) reports device usage for mounted filesystems
31 * 2) prevents offline operations for mounted resources
32 * 3) prevents suspend operations (unless forced) of those filesystems
33 * deemed critical for the continued operation of the OS
34 * 4) propagates RCM operations from mounted resources to the consumers
35 * of files within the mounted filesystems
44 #include <sys/mnttab.h>
45 #include <sys/param.h>
47 #include <sys/utssys.h>
51 #include "rcm_module.h"
55 #define HASH_DEFAULT 4
56 #define HASH_THRESHOLD 256
58 #define OPT_IGNORE "ignore"
60 #define MSG_HDR_STD gettext("mounted filesystem")
61 #define MSG_HDR_STD_MULTI gettext("mounted filesystems")
62 #define MSG_HDR_CRIT gettext("cannot suspend filesystem")
63 #define MSG_HDR_CRIT_MULTI gettext("cannot suspend filesystems")
64 #define MSG_SEPARATOR gettext(", ")
65 #define MSG_FAIL_USAGE gettext("failed to construct usage string.")
66 #define MSG_FAIL_DEPENDENTS gettext("failed while calling dependents.")
67 #define MSG_FAIL_REMOVE gettext("filesystems cannot be removed.")
68 #define MSG_FAIL_INTERNAL gettext("internal processing failure.")
70 typedef struct hashentry
{
75 struct hashentry
*next
;
84 /* Forward Declarations */
86 /* module interface routines */
87 static int mnt_register(rcm_handle_t
*);
88 static int mnt_unregister(rcm_handle_t
*);
89 static int mnt_getinfo(rcm_handle_t
*, char *, id_t
, uint_t
, char **, char **,
90 nvlist_t
*, rcm_info_t
**);
91 static int mnt_suspend(rcm_handle_t
*, char *, id_t
, timespec_t
*,
92 uint_t
, char **, rcm_info_t
**);
93 static int mnt_resume(rcm_handle_t
*, char *, id_t
, uint_t
, char **,
95 static int mnt_offline(rcm_handle_t
*, char *, id_t
, uint_t
, char **,
97 static int mnt_online(rcm_handle_t
*, char *, id_t
, uint_t
, char **,
99 static int mnt_remove(rcm_handle_t
*, char *, id_t
, uint_t
, char **,
102 /* cache functions */
103 static cache_t
*cache_create();
104 static int cache_insert(cache_t
*, struct mnttab
*);
105 static int cache_sync(rcm_handle_t
*, cache_t
**);
106 static hashentry_t
*cache_lookup(cache_t
*, char *);
107 static void free_cache(cache_t
**);
108 static void free_entry(hashentry_t
**);
109 static void free_list(char **);
111 /* miscellaneous functions */
112 static uint32_t hash(uint32_t, char *);
113 static void register_rsrc(rcm_handle_t
*, char *);
114 static void unregister_rsrc(rcm_handle_t
*, char *);
115 static char *create_message(char *, char *, char **);
116 static int detect_critical_failure(char **, uint_t
, char **);
117 static int is_critical(char *);
118 static int use_cache(char *, char **, char ***);
119 static void prune_dependents(char **, char *);
120 static char **create_dependents(hashentry_t
*);
122 /* Module-Private data */
124 static struct rcm_mod_ops mnt_ops
=
137 static cache_t
*mnt_cache
;
138 static mutex_t cache_lock
;
140 /* Module Interface Routines */
145 * Called when module is loaded. Returns the ops vector.
156 * Returns a string identifying this module.
161 return ("File system module 1.9");
167 * Called when module is unloaded. Frees up all used memory.
169 * Locking: the cache is locked for the duration of this function.
174 (void) mutex_lock(&cache_lock
);
175 free_cache(&mnt_cache
);
176 (void) mutex_unlock(&cache_lock
);
178 return (RCM_SUCCESS
);
184 * Called to synchronize the module's registrations. Results in the
185 * construction of a new cache, destruction of any old cache data,
186 * and a full synchronization of the module's registrations.
188 * Locking: the cache is locked for the duration of this function.
191 mnt_register(rcm_handle_t
*hd
)
195 rcm_log_message(RCM_TRACE1
, "FILESYS: register()\n");
197 (void) mutex_lock(&cache_lock
);
199 /* cache_sync() does all of the necessary work */
200 if (cache_sync(hd
, &mnt_cache
) < 0) {
201 rcm_log_message(RCM_ERROR
,
202 "FILESYS: failed to synchronize cache (%s).\n",
204 (void) mutex_unlock(&cache_lock
);
205 return (RCM_FAILURE
);
208 (void) mutex_unlock(&cache_lock
);
210 return (RCM_SUCCESS
);
216 * Manually walk through the cache, unregistering all the special
217 * files and mount points.
219 * Locking: the cache is locked throughout the execution of this
220 * routine because it reads and modifies cache links continuously.
223 mnt_unregister(rcm_handle_t
*hd
)
230 rcm_log_message(RCM_TRACE1
, "FILESYS: unregister()\n");
232 (void) mutex_lock(&cache_lock
);
234 /* Unregister everything in the cache */
236 for (index
= 0; index
< mnt_cache
->hash_size
; index
++) {
237 for (entry
= mnt_cache
->mounts
[index
]; entry
!= NULL
;
238 entry
= entry
->next
) {
239 unregister_rsrc(hd
, entry
->special
);
244 /* Destroy the cache */
245 free_cache(&mnt_cache
);
247 (void) mutex_unlock(&cache_lock
);
249 return (RCM_SUCCESS
);
255 * Filesystem resources cannot be offlined. They can however be retired
256 * if they don't provide a critical service. The offline entry point
257 * checks if this is a retire operation and if it is and the filesystem
258 * doesn't provide a critical service, the entry point returns success
259 * For all other cases, failure is returned.
260 * Since no real action is taken, QUERY or not doesn't matter.
263 mnt_offline(rcm_handle_t
*hd
, char *rsrc
, id_t id
, uint_t flags
,
264 char **errorp
, rcm_info_t
**dependent_info
)
272 assert(rsrc
!= NULL
);
273 assert(id
== (id_t
)0);
274 assert(errorp
!= NULL
);
278 rcm_log_message(RCM_TRACE1
, "FILESYS: offline(%s)\n", rsrc
);
280 /* Retrieve necessary info from the cache */
281 if (use_cache(rsrc
, errorp
, &dependents
) < 0) {
282 if (flags
& RCM_RETIRE_REQUEST
)
283 return (RCM_NO_CONSTRAINT
);
285 return (RCM_FAILURE
);
288 if (flags
& RCM_RETIRE_REQUEST
) {
289 (void) mutex_lock(&cache_lock
);
290 if ((entry
= cache_lookup(mnt_cache
, rsrc
)) == NULL
) {
291 rcm_log_message(RCM_ERROR
, "FILESYS: "
292 "failed to look up \"%s\" in cache (%s).\n",
293 rsrc
, strerror(errno
));
294 (void) mutex_unlock(&cache_lock
);
295 retval
= RCM_NO_CONSTRAINT
;
299 if (strcmp(entry
->fstype
, "zfs") == 0) {
300 retval
= RCM_NO_CONSTRAINT
;
301 rcm_log_message(RCM_TRACE1
,
302 "FILESYS: zfs: NO_CONSTRAINT: %s\n", rsrc
);
304 retval
= RCM_SUCCESS
;
305 for (i
= 0; dependents
[i
] != NULL
; i
++) {
306 if (is_critical(dependents
[i
])) {
307 retval
= RCM_FAILURE
;
308 rcm_log_message(RCM_TRACE1
, "FILESYS: "
309 "CRITICAL %s\n", rsrc
);
314 (void) mutex_unlock(&cache_lock
);
318 retval
= RCM_FAILURE
;
320 /* Convert the gathered dependents into an error message */
321 *errorp
= create_message(MSG_HDR_STD
, MSG_HDR_STD_MULTI
, dependents
);
322 if (*errorp
== NULL
) {
323 rcm_log_message(RCM_ERROR
,
324 "FILESYS: failed to construct offline message (%s).\n",
329 free_list(dependents
);
336 * Filesystem resources aren't offlined, so there's really nothing to do
340 mnt_online(rcm_handle_t
*hd
, char *rsrc
, id_t id
, uint_t flag
, char **errorp
,
341 rcm_info_t
**dependent_reason
)
344 assert(rsrc
!= NULL
);
345 assert(id
== (id_t
)0);
346 assert(errorp
!= NULL
);
348 rcm_log_message(RCM_TRACE1
, "FILESYS: online(%s)\n", rsrc
);
350 return (RCM_SUCCESS
);
356 * Report how a given resource is in use by this module. And also
357 * possibly include dependent consumers of the mounted filesystems.
360 mnt_getinfo(rcm_handle_t
*hd
, char *rsrc
, id_t id
, uint_t flag
, char **usagep
,
361 char **errorp
, nvlist_t
*props
, rcm_info_t
**depend_info
)
363 int rv
= RCM_SUCCESS
;
367 assert(rsrc
!= NULL
);
368 assert(id
== (id_t
)0);
369 assert(usagep
!= NULL
);
370 assert(errorp
!= NULL
);
371 assert(props
!= NULL
);
373 rcm_log_message(RCM_TRACE1
, "FILESYS: getinfo(%s)\n", rsrc
);
375 /* Retrieve necessary info from the cache */
376 if (use_cache(rsrc
, errorp
, &dependents
) < 0)
377 return (RCM_FAILURE
);
379 /* Convert the gathered dependents into a usage message */
380 *usagep
= create_message(MSG_HDR_STD
, MSG_HDR_STD_MULTI
, dependents
);
381 if (*usagep
== NULL
) {
382 rcm_log_message(RCM_ERROR
,
383 "FILESYS: failed to construct usage message (%s).\n",
385 *errorp
= strdup(MSG_FAIL_USAGE
);
386 free_list(dependents
);
387 return (RCM_FAILURE
);
390 /* Recurse on dependents if necessary */
391 if ((flag
& RCM_INCLUDE_DEPENDENT
) && (dependents
!= NULL
)) {
392 prune_dependents(dependents
, rsrc
);
393 if (dependents
[0] != NULL
) {
394 if ((rv
= rcm_get_info_list(hd
, dependents
, flag
,
395 depend_info
)) != RCM_SUCCESS
) {
396 *errorp
= strdup(MSG_FAIL_DEPENDENTS
);
401 /* Free up info retrieved from the cache */
402 free_list(dependents
);
410 * Notify all dependents that the resource is being suspended.
411 * Since no real action is taken, QUERY or not doesn't matter.
414 mnt_suspend(rcm_handle_t
*hd
, char *rsrc
, id_t id
, timespec_t
*interval
,
415 uint_t flag
, char **errorp
, rcm_info_t
**depend_info
)
417 int rv
= RCM_SUCCESS
;
421 assert(rsrc
!= NULL
);
422 assert(id
== (id_t
)0);
423 assert(interval
!= NULL
);
424 assert(errorp
!= NULL
);
426 rcm_log_message(RCM_TRACE1
, "FILESYS: suspend(%s)\n", rsrc
);
428 /* Retrieve necessary info from the cache */
429 if (use_cache(rsrc
, errorp
, &dependents
) < 0)
430 return (RCM_FAILURE
);
432 /* Unforced suspensions fail if any of the dependents are critical */
433 if (detect_critical_failure(errorp
, flag
, dependents
)) {
434 free_list(dependents
);
435 return (RCM_FAILURE
);
438 /* Recurse on dependents if necessary */
439 if ((flag
& RCM_INCLUDE_DEPENDENT
) && (dependents
!= NULL
)) {
440 prune_dependents(dependents
, rsrc
);
441 if (dependents
[0] != NULL
)
442 if ((rv
= rcm_request_suspend_list(hd
, dependents
, flag
,
443 interval
, depend_info
)) != RCM_SUCCESS
) {
444 *errorp
= strdup(MSG_FAIL_DEPENDENTS
);
447 free_list(dependents
);
455 * Resume all the dependents of a suspended filesystem.
458 mnt_resume(rcm_handle_t
*hd
, char *rsrc
, id_t id
, uint_t flag
, char **errorp
,
459 rcm_info_t
**depend_info
)
461 int rv
= RCM_SUCCESS
;
465 assert(rsrc
!= NULL
);
466 assert(id
== (id_t
)0);
467 assert(errorp
!= NULL
);
469 rcm_log_message(RCM_TRACE1
, "FILESYS: resume(%s)\n", rsrc
);
471 /* Retrieve necessary info from the cache */
472 if (use_cache(rsrc
, errorp
, &dependents
) < 0)
473 return (RCM_FAILURE
);
475 /* Recurse on dependents if necessary */
476 if ((flag
& RCM_INCLUDE_DEPENDENT
) && (dependents
!= NULL
)) {
477 prune_dependents(dependents
, rsrc
);
478 if (dependents
[0] != NULL
) {
479 if ((rv
= rcm_notify_resume_list(hd
, dependents
, flag
,
480 depend_info
)) != RCM_SUCCESS
) {
481 *errorp
= strdup(MSG_FAIL_DEPENDENTS
);
485 free_list(dependents
);
491 get_spec(char *line
, char *spec
, size_t ssz
)
496 if (strlcpy(spec
, line
, ssz
) >= ssz
) {
497 rcm_log_message(RCM_ERROR
, "FILESYS: get_spec() failed: "
503 while (*cp
== ' ' || *cp
== '\t')
511 while (*cp
!= ' ' && *cp
!= '\t' && *cp
!= '\0')
515 (void) memmove(spec
, start
, strlen(start
) + 1);
521 path_match(char *rsrc
, char *spec
)
527 if (realpath(rsrc
, r
) == NULL
)
530 if (realpath(spec
, s
) == NULL
)
533 len
= strlen("/devices/");
535 if (strncmp(r
, "/devices/", len
) != 0) {
540 if (strncmp(s
, "/devices/", len
) != 0) {
546 if (strncmp(r
, s
, len
) == 0 && (s
[len
] == '\0' || s
[len
] == ':'))
552 rcm_log_message(RCM_DEBUG
, "FILESYS: path_match() failed "
553 "rsrc=%s spec=%s: %s\n", rsrc
, spec
, strerror(errno
));
557 #define VFSTAB "/etc/vfstab"
558 #define RETIRED_PREFIX "## RETIRED ##"
561 disable_vfstab_entry(char *rsrc
)
568 char line
[MNT_LINE_MAX
+ 1];
570 vfp
= fopen(VFSTAB
, "r");
572 rcm_log_message(RCM_ERROR
, "FILESYS: failed to open /etc/vfstab"
573 " for reading: %s\n", strerror(errno
));
574 return (RCM_FAILURE
);
577 (void) snprintf(tmp
, sizeof (tmp
), "/etc/vfstab.retire.%lu", getpid());
579 tfp
= fopen(tmp
, "w");
581 rcm_log_message(RCM_ERROR
, "FILESYS: failed to open "
582 "/etc/vfstab.retire for writing: %s\n", strerror(errno
));
584 return (RCM_FAILURE
);
587 retval
= RCM_SUCCESS
;
589 while (fgets(line
, sizeof (line
), vfp
)) {
591 char spec
[MNT_LINE_MAX
+ 1];
592 char newline
[MNT_LINE_MAX
+ 1];
595 if (get_spec(line
, spec
, sizeof (spec
)) == -1) {
600 if (path_match(rsrc
, spec
) != 0) {
607 /* Paths match. Disable this entry */
608 (void) snprintf(newline
, sizeof (newline
), "%s %s",
609 RETIRED_PREFIX
, line
);
611 rcm_log_message(RCM_TRACE1
, "FILESYS: disabling line\n\t%s\n",
616 if (fputs(l
, tfp
) == EOF
) {
617 rcm_log_message(RCM_ERROR
, "FILESYS: failed to write "
618 "new vfstab: %s\n", strerror(errno
));
620 retval
= RCM_FAILURE
;
631 if (rename(tmp
, VFSTAB
) != 0) {
632 rcm_log_message(RCM_ERROR
, "FILESYS: vfstab rename "
633 "failed: %s\n", strerror(errno
));
634 retval
= RCM_FAILURE
;
646 * Remove will only be called in the retire case i.e. if RCM_RETIRE_NOTIFY
649 * If the flag is not set, then return failure and log the mistake if a
650 * remove is ever received for a mounted filesystem resource.
653 mnt_remove(rcm_handle_t
*hd
, char *rsrc
, id_t id
, uint_t flag
, char **errorp
,
654 rcm_info_t
**depend_info
)
657 assert(rsrc
!= NULL
);
658 assert(id
== (id_t
)0);
659 assert(errorp
!= NULL
);
661 rcm_log_message(RCM_TRACE1
, "FILESYS: remove(%s)\n", rsrc
);
663 if (!(flag
& RCM_RETIRE_NOTIFY
)) {
664 /* Log the mistake */
665 rcm_log_message(RCM_ERROR
, "FILESYS: invalid remove of "
667 *errorp
= strdup(MSG_FAIL_REMOVE
);
668 return (RCM_FAILURE
);
671 return (disable_vfstab_entry(rsrc
));
675 * Cache management routines
681 * This routine constructs a new cache of the current mnttab file.
683 * Locking: the cache must be locked prior to calling this function.
685 * Return Values: NULL with errno set on failure, new cache point on
699 * To keep the hash table relatively sparse, default values are
700 * used for smaller mnttab files and these values are scaled up
701 * as a fraction of the total mnttab file size for larger ones.
703 if (stat(MNTTAB
, &st
) < 0) {
704 rcm_log_message(RCM_ERROR
,
705 "FILESYS: failed to stat \"%s\" (%s).\n", MNTTAB
,
710 if (st
.st_size
> HASH_THRESHOLD
) {
711 size
= st
.st_size
/ HASH_THRESHOLD
;
712 for (i
= 0; size
> 1; i
++, size
>>= 1);
713 for (; i
> -1; i
--, size
<<= 1);
718 /* Allocate a new empty cache */
719 if ((cache
= (cache_t
*)calloc(1, sizeof (cache_t
))) == NULL
) {
720 rcm_log_message(RCM_ERROR
,
721 "FILESYS: failed to allocate cache (%s).\n",
726 cache
->hash_size
= size
;
727 cache
->timestamp
= st
.st_mtime
;
729 /* Allocate an empty hash table for the registered special devices */
730 cache
->mounts
= (hashentry_t
**)calloc(size
, sizeof (hashentry_t
*));
731 if (cache
->mounts
== NULL
) {
732 rcm_log_message(RCM_ERROR
,
733 "FILESYS: failed to allocate mount table (%s).\n",
740 /* Open the mnttab file */
741 if ((fp
= fopen(MNTTAB
, "r")) == NULL
) {
742 rcm_log_message(RCM_ERROR
,
743 "FILESYS: failed to open \"%s\" (%s).\n", MNTTAB
,
750 /* Insert each mnttab entry into the cache */
751 while (getmntent(fp
, &mt
) == 0) {
753 /* Well, not each entry... some are meant to be ignored */
754 if ((mt
.mnt_mntopts
!= NULL
) &&
755 (hasmntopt(&mt
, OPT_IGNORE
) != NULL
))
758 if (cache_insert(cache
, &mt
) < 0) {
759 rcm_log_message(RCM_ERROR
,
760 "FILESYS: cache insertion failure (%s).\n",
769 /* Close the mnttab file */
778 * Free up all the memory associated with a cache.
780 * Locking: the cache must be locked before calling this function.
783 free_cache(cache_t
**cachep
)
787 hashentry_t
*entry_tmp
;
789 /* Do nothing with empty caches */
790 if ((cachep
== NULL
) || (*cachep
== NULL
))
793 if ((*cachep
)->mounts
) {
794 /* Walk through the hashtable, emptying it */
795 for (index
= 0; index
< (*cachep
)->hash_size
; index
++) {
796 entry
= (*cachep
)->mounts
[index
];
798 entry_tmp
= entry
->next
;
803 free((*cachep
)->mounts
);
813 * Free up memory associated with a hashtable entry.
815 * Locking: the cache must be locked before calling this function.
818 free_entry(hashentry_t
**entryp
)
822 free((*entryp
)->special
);
823 free((*entryp
)->fstype
);
824 free_list((*entryp
)->mountps
);
834 * Free up memory associated with a null terminated list of names.
837 free_list(char **list
)
842 for (i
= 0; list
[i
] != NULL
; i
++)
851 * Resynchronize the mnttab cache with the mnttab file.
853 * Locking: the cache must be locked before calling this function.
855 * Return Values: -1 with errno set on failure, 0 on success.
858 cache_sync(rcm_handle_t
*hd
, cache_t
**cachep
)
866 /* Only accept valid arguments */
867 if ((hd
== NULL
) || (cachep
== NULL
)) {
868 rcm_log_message(RCM_ERROR
,
869 "FILESYS: invalid arguments to cache_sync().\n");
874 /* Do nothing if there's already an up-to-date cache */
877 if (stat(MNTTAB
, &st
) == 0) {
878 if (old_cache
->timestamp
>= st
.st_mtime
) {
882 rcm_log_message(RCM_WARNING
,
883 "FILESYS: failed to stat \"%s\", cache is stale "
884 "(%s).\n", MNTTAB
, strerror(errno
));
890 /* Create a new cache based on the new mnttab file. */
891 if ((new_cache
= cache_create()) == NULL
) {
892 rcm_log_message(RCM_WARNING
,
893 "FILESYS: failed creating cache, cache is stale (%s).\n",
899 /* Register any specials found in the new cache but not the old one */
900 for (index
= 0; index
< new_cache
->hash_size
; index
++) {
901 for (entry
= new_cache
->mounts
[index
]; entry
!= NULL
;
902 entry
= entry
->next
) {
903 if (cache_lookup(old_cache
, entry
->special
) == NULL
) {
904 register_rsrc(hd
, entry
->special
);
909 /* Pass the new cache pointer to the calling function */
912 /* If there wasn't an old cache, return successfully now */
913 if (old_cache
== NULL
)
917 * If there was an old cache, then unregister whatever specials it
918 * contains that aren't in the new cache. And then destroy the old
921 for (index
= 0; index
< old_cache
->hash_size
; index
++) {
922 for (entry
= old_cache
->mounts
[index
]; entry
!= NULL
;
923 entry
= entry
->next
) {
924 if (cache_lookup(new_cache
, entry
->special
) == NULL
) {
925 unregister_rsrc(hd
, entry
->special
);
929 free_cache(&old_cache
);
937 * Given a cache and a mnttab entry, this routine inserts that entry in
938 * the cache. The mnttab entry's special device and filesystem type
939 * is added to the 'mounts' hashtable of the cache, and the entry's
940 * mountp value is added to the list of associated mountpoints for the
941 * corresponding hashtable entry.
943 * Locking: the cache must be locked before calling this function.
945 * Return Values: -1 with errno set on failure, 0 on success.
948 cache_insert(cache_t
*cache
, struct mnttab
*mt
)
954 /* Only accept valid arguments */
955 if ((cache
== NULL
) ||
956 (cache
->mounts
== NULL
) ||
958 (mt
->mnt_special
== NULL
) ||
959 (mt
->mnt_mountp
== NULL
) ||
960 (mt
->mnt_fstype
== NULL
)) {
966 * Disregard any non-loopback mounts whose special device names
967 * don't begin with "/dev".
969 if ((strncmp(mt
->mnt_special
, "/dev", strlen("/dev")) != 0) &&
970 (strcmp(mt
->mnt_fstype
, "lofs") != 0))
974 * Find the special device's entry in the mounts hashtable, allocating
975 * a new entry if necessary.
977 index
= hash(cache
->hash_size
, mt
->mnt_special
);
978 for (entry
= cache
->mounts
[index
]; entry
!= NULL
; entry
= entry
->next
) {
979 if (strcmp(entry
->special
, mt
->mnt_special
) == 0)
983 entry
= (hashentry_t
*)calloc(1, sizeof (hashentry_t
));
984 if ((entry
== NULL
) ||
985 ((entry
->special
= strdup(mt
->mnt_special
)) == NULL
) ||
986 ((entry
->fstype
= strdup(mt
->mnt_fstype
)) == NULL
)) {
987 rcm_log_message(RCM_ERROR
,
988 "FILESYS: failed to allocate special device name "
989 "or filesystem type: (%s).\n", strerror(errno
));
994 entry
->next
= cache
->mounts
[index
];
995 cache
->mounts
[index
] = entry
;
999 * Keep entries in the list of mounts unique, so exit early if the
1000 * mount is already in the list.
1002 for (index
= 0; index
< entry
->n_mounts
; index
++) {
1003 if (strcmp(entry
->mountps
[index
], mt
->mnt_mountp
) == 0)
1008 * Add this mountpoint to the list of mounts associated with the
1011 mountps
= (char **)reallocarray(entry
->mountps
,
1012 entry
->n_mounts
+ 2, sizeof (char *));
1013 if ((mountps
== NULL
) ||
1014 ((mountps
[entry
->n_mounts
] = strdup(mt
->mnt_mountp
)) == NULL
)) {
1015 rcm_log_message(RCM_ERROR
,
1016 "FILESYS: failed to allocate mountpoint name (%s).\n",
1018 if (entry
->n_mounts
== 0) {
1019 cache
->mounts
[index
] = entry
->next
;
1025 mountps
[entry
->n_mounts
+ 1] = NULL
;
1027 entry
->mountps
= mountps
;
1035 * Searches the cached table of mounts for a special device entry.
1037 * Locking: the cache must be locked before calling this function.
1039 * Return Value: NULL with errno set if failure, pointer to existing
1040 * cache entry when successful.
1042 static hashentry_t
*
1043 cache_lookup(cache_t
*cache
, char *rsrc
)
1048 /* Only accept valid arguments */
1049 if ((cache
== NULL
) || (cache
->mounts
== NULL
) || (rsrc
== NULL
)) {
1054 /* Search the cached mounts table for the resource's entry */
1055 index
= hash(cache
->hash_size
, rsrc
);
1056 if (cache
->mounts
[index
]) {
1057 for (entry
= cache
->mounts
[index
]; entry
!= NULL
;
1058 entry
= entry
->next
) {
1059 if (strcmp(entry
->special
, rsrc
) == 0)
1069 * Miscellaneous Functions
1075 * A naive hashing function that converts a string 's' to an index in a
1076 * hash table of size 'h'. It seems to spread entries around well enough.
1079 hash(uint32_t h
, char *s
)
1082 unsigned char *byte
;
1084 if ((byte
= (unsigned char *)s
) != NULL
) {
1086 sum
+= 0x3F & (uint32_t)*byte
;
1097 * Registers for any given resource, unless it's "/".
1100 register_rsrc(rcm_handle_t
*hd
, char *rsrc
)
1102 /* Only accept valid arguments */
1103 if ((hd
== NULL
) || (rsrc
== NULL
))
1107 * Register any resource other than "/" or "/devices"
1109 if ((strcmp(rsrc
, "/") != 0) && (strcmp(rsrc
, "/devices") != 0)) {
1110 rcm_log_message(RCM_DEBUG
, "FILESYS: registering %s\n", rsrc
);
1111 if (rcm_register_interest(hd
, rsrc
, 0, NULL
) != RCM_SUCCESS
) {
1112 rcm_log_message(RCM_WARNING
,
1113 "FILESYS: failed to register %s\n", rsrc
);
1122 * Unregister a resource. This does a little filtering since we know
1123 * "/" can't be registered, so we never bother unregistering for it.
1126 unregister_rsrc(rcm_handle_t
*hd
, char *rsrc
)
1129 assert(rsrc
!= NULL
);
1131 /* Unregister any resource other than "/" */
1132 if (strcmp(rsrc
, "/") != 0) {
1133 rcm_log_message(RCM_DEBUG
, "FILESYS: unregistering %s\n", rsrc
);
1134 (void) rcm_unregister_interest(hd
, rsrc
, 0);
1141 * Given some header strings and a list of dependent names, this
1142 * constructs a single string. If there's only one dependent, the
1143 * string consists of the first header and the only dependent appended
1144 * to the end of the string enclosed in quotemarks. If there are
1145 * multiple dependents, then the string uses the second header and the
1146 * full list of dependents is appended at the end as a comma separated
1147 * list of names enclosed in quotemarks.
1150 create_message(char *header
, char *header_multi
, char **dependents
)
1157 char *separator
= MSG_SEPARATOR
;
1159 assert(header
!= NULL
);
1160 assert(header_multi
!= NULL
);
1161 assert(dependents
!= NULL
);
1163 /* Count the number of dependents */
1164 for (ndependents
= 0; dependents
[ndependents
] != NULL
; ndependents
++);
1166 /* If there are no dependents, fail */
1167 if (ndependents
== 0) {
1172 /* Pick the appropriate header to use based on amount of dependents */
1173 if (ndependents
== 1) {
1174 msg_header
= header
;
1176 msg_header
= header_multi
;
1179 /* Compute the size required for the message buffer */
1180 len
= strlen(msg_header
) + 2; /* +2 for the space and a NULL */
1181 for (i
= 0; dependents
[i
] != NULL
; i
++)
1182 len
+= strlen(dependents
[i
]) + 2; /* +2 for quotemarks */
1183 len
+= strlen(separator
) * (ndependents
- 1);
1185 /* Allocate the message buffer */
1186 if ((msg_buf
= (char *)calloc(len
, sizeof (char))) == NULL
) {
1187 rcm_log_message(RCM_ERROR
,
1188 "FILESYS: failed to allocate message buffer (%s).\n",
1194 /* Fill in the message buffer */
1195 (void) snprintf(msg_buf
, len
, "%s ", msg_header
);
1196 for (i
= 0; dependents
[i
] != NULL
; i
++) {
1197 (void) strlcat(msg_buf
, "\"", len
);
1198 (void) strlcat(msg_buf
, dependents
[i
], len
);
1199 (void) strlcat(msg_buf
, "\"", len
);
1200 if ((i
+ 1) < ndependents
)
1201 (void) strlcat(msg_buf
, separator
, len
);
1208 * create_dependents()
1210 * Creates a copy of the list of dependent mounts associated with a
1211 * given hashtable entry from the cache.
1213 * Return Values: NULL with errno set on failure, the resulting list of
1214 * dependent resources when successful.
1217 create_dependents(hashentry_t
*entry
)
1222 if (entry
== NULL
) {
1227 if (entry
->n_mounts
== 0) {
1232 /* Allocate space for the full dependency list */
1233 dependents
= (char **)calloc(entry
->n_mounts
+ 1, sizeof (char *));
1234 if (dependents
== NULL
) {
1235 rcm_log_message(RCM_ERROR
,
1236 "FILESYS: failed to allocate dependents (%s).\n",
1242 /* Copy all the dependent names into the new list of dependents */
1243 for (i
= 0; i
< entry
->n_mounts
; i
++) {
1244 if ((dependents
[i
] = strdup(entry
->mountps
[i
])) == NULL
) {
1245 rcm_log_message(RCM_ERROR
,
1246 "FILESYS: failed to allocate dependent \"%s\" "
1247 "(%s).\n", entry
->mountps
[i
], strerror(errno
));
1248 free_list(dependents
);
1254 return (dependents
);
1258 * detect_critical_failure()
1260 * Given a list of dependents, a place to store an error message, and
1261 * the flags associated with an operation, this function detects whether
1262 * or not the operation should fail due to the presence of any critical
1263 * filesystem resources. When a failure is detected, an appropriate
1264 * error message is constructed and passed back to the caller. This is
1265 * called during a suspend request operation.
1267 * Return Values: 0 when a critical resource failure shouldn't prevent
1268 * the operation, and 1 when such a failure condition does exist.
1271 detect_critical_failure(char **errorp
, uint_t flags
, char **dependents
)
1277 /* Do nothing if the operation is forced or there are no dependents */
1278 if ((errorp
== NULL
) || (flags
& RCM_FORCE
) || (dependents
== NULL
))
1282 * Count how many of the dependents are critical, and shift the
1283 * critical resources to the head of the list.
1286 for (i
= 0, n_critical
= 0; dependents
[i
] != NULL
; i
++) {
1287 if (is_critical(dependents
[i
])) {
1288 if (n_critical
!= i
) {
1289 tmp
= dependents
[n_critical
];
1290 dependents
[n_critical
] = dependents
[i
];
1291 dependents
[i
] = tmp
;
1298 /* If no criticals were found, do nothing and return */
1299 if (n_critical
== 0)
1303 * Criticals were found. Prune the list appropriately and construct
1307 /* Prune non-criticals out of the list */
1308 for (i
= n_critical
; dependents
[i
] != NULL
; i
++) {
1309 free(dependents
[i
]);
1310 dependents
[i
] = NULL
;
1313 /* Construct the critical resource error message */
1314 *errorp
= create_message(MSG_HDR_CRIT
, MSG_HDR_CRIT_MULTI
, dependents
);
1322 * Test a resource to determine if it's critical to the system and thus
1323 * cannot be suspended.
1325 * Return Values: 1 if the named resource is critical, 0 if not.
1328 is_critical(char *rsrc
)
1330 assert(rsrc
!= NULL
);
1332 if ((strcmp(rsrc
, "/") == 0) ||
1333 (strcmp(rsrc
, "/usr") == 0) ||
1334 (strcmp(rsrc
, "/lib") == 0) ||
1335 (strcmp(rsrc
, "/usr/lib") == 0) ||
1336 (strcmp(rsrc
, "/bin") == 0) ||
1337 (strcmp(rsrc
, "/usr/bin") == 0) ||
1338 (strcmp(rsrc
, "/tmp") == 0) ||
1339 (strcmp(rsrc
, "/var") == 0) ||
1340 (strcmp(rsrc
, "/var/run") == 0) ||
1341 (strcmp(rsrc
, "/etc") == 0) ||
1342 (strcmp(rsrc
, "/etc/mnttab") == 0) ||
1343 (strcmp(rsrc
, "/platform") == 0) ||
1344 (strcmp(rsrc
, "/usr/platform") == 0) ||
1345 (strcmp(rsrc
, "/sbin") == 0) ||
1346 (strcmp(rsrc
, "/usr/sbin") == 0))
1356 * This routine handles all the tasks necessary to lookup a resource
1357 * in the cache and extract a separate list of dependents for that
1358 * entry. If an error occurs while doing this, an appropriate error
1359 * message is passed back to the caller.
1361 * Locking: the cache is locked for the whole duration of this function.
1364 use_cache(char *rsrc
, char **errorp
, char ***dependentsp
)
1368 (void) mutex_lock(&cache_lock
);
1369 if ((entry
= cache_lookup(mnt_cache
, rsrc
)) == NULL
) {
1370 rcm_log_message(RCM_ERROR
,
1371 "FILESYS: failed looking up \"%s\" in cache (%s).\n",
1372 rsrc
, strerror(errno
));
1373 *errorp
= strdup(MSG_FAIL_INTERNAL
);
1374 (void) mutex_unlock(&cache_lock
);
1377 *dependentsp
= create_dependents(entry
);
1378 (void) mutex_unlock(&cache_lock
);
1384 * prune_dependents()
1386 * Before calling back into RCM with a list of dependents, the list
1387 * must be cleaned up a little. To avoid infinite recursion, "/" and
1388 * the named resource must be pruned out of the list.
1391 prune_dependents(char **dependents
, char *rsrc
)
1398 /* Set 'n' to the total length of the list */
1399 for (n
= 0; dependents
[n
] != NULL
; n
++);
1402 * Move offending dependents to the tail of the list and
1403 * then truncate the list.
1405 for (i
= 0; dependents
[i
] != NULL
; i
++) {
1406 if ((strcmp(dependents
[i
], rsrc
) == 0) ||
1407 (strcmp(dependents
[i
], "/") == 0)) {
1408 free(dependents
[i
]);
1409 dependents
[i
] = dependents
[n
- 1];
1410 dependents
[n
] = NULL
;