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.
26 #pragma ident "%Z%%M% %I% %E% SMI"
29 * This module adds support to the RCM framework for mounted filesystems.
31 * The module provides this functionality:
32 * 1) reports device usage for mounted filesystems
33 * 2) prevents offline operations for mounted resources
34 * 3) prevents suspend operations (unless forced) of those filesystems
35 * deemed critical for the continued operation of the OS
36 * 4) propagates RCM operations from mounted resources to the consumers
37 * of files within the mounted filesystems
46 #include <sys/mnttab.h>
47 #include <sys/param.h>
49 #include <sys/utssys.h>
53 #include "rcm_module.h"
57 #define HASH_DEFAULT 4
58 #define HASH_THRESHOLD 256
60 #define OPT_IGNORE "ignore"
62 #define MSG_HDR_STD gettext("mounted filesystem")
63 #define MSG_HDR_STD_MULTI gettext("mounted filesystems")
64 #define MSG_HDR_CRIT gettext("cannot suspend filesystem")
65 #define MSG_HDR_CRIT_MULTI gettext("cannot suspend filesystems")
66 #define MSG_SEPARATOR gettext(", ")
67 #define MSG_FAIL_USAGE gettext("failed to construct usage string.")
68 #define MSG_FAIL_DEPENDENTS gettext("failed while calling dependents.")
69 #define MSG_FAIL_REMOVE gettext("filesystems cannot be removed.")
70 #define MSG_FAIL_INTERNAL gettext("internal processing failure.")
72 typedef struct hashentry
{
77 struct hashentry
*next
;
86 /* Forward Declarations */
88 /* module interface routines */
89 static int mnt_register(rcm_handle_t
*);
90 static int mnt_unregister(rcm_handle_t
*);
91 static int mnt_getinfo(rcm_handle_t
*, char *, id_t
, uint_t
, char **, char **,
92 nvlist_t
*, rcm_info_t
**);
93 static int mnt_suspend(rcm_handle_t
*, char *, id_t
, timespec_t
*,
94 uint_t
, char **, rcm_info_t
**);
95 static int mnt_resume(rcm_handle_t
*, char *, id_t
, uint_t
, char **,
97 static int mnt_offline(rcm_handle_t
*, char *, id_t
, uint_t
, char **,
99 static int mnt_online(rcm_handle_t
*, char *, id_t
, uint_t
, char **,
101 static int mnt_remove(rcm_handle_t
*, char *, id_t
, uint_t
, char **,
104 /* cache functions */
105 static cache_t
*cache_create();
106 static int cache_insert(cache_t
*, struct mnttab
*);
107 static int cache_sync(rcm_handle_t
*, cache_t
**);
108 static hashentry_t
*cache_lookup(cache_t
*, char *);
109 static void free_cache(cache_t
**);
110 static void free_entry(hashentry_t
**);
111 static void free_list(char **);
113 /* miscellaneous functions */
114 static uint32_t hash(uint32_t, char *);
115 static void register_rsrc(rcm_handle_t
*, char *);
116 static void unregister_rsrc(rcm_handle_t
*, char *);
117 static char *create_message(char *, char *, char **);
118 static int detect_critical_failure(char **, uint_t
, char **);
119 static int is_critical(char *);
120 static int use_cache(char *, char **, char ***);
121 static void prune_dependents(char **, char *);
122 static char **create_dependents(hashentry_t
*);
124 /* Module-Private data */
126 static struct rcm_mod_ops mnt_ops
=
139 static cache_t
*mnt_cache
;
140 static mutex_t cache_lock
;
142 /* Module Interface Routines */
147 * Called when module is loaded. Returns the ops vector.
158 * Returns a string identifying this module.
163 return ("File system module 1.9");
169 * Called when module is unloaded. Frees up all used memory.
171 * Locking: the cache is locked for the duration of this function.
176 (void) mutex_lock(&cache_lock
);
177 free_cache(&mnt_cache
);
178 (void) mutex_unlock(&cache_lock
);
180 return (RCM_SUCCESS
);
186 * Called to synchronize the module's registrations. Results in the
187 * construction of a new cache, destruction of any old cache data,
188 * and a full synchronization of the module's registrations.
190 * Locking: the cache is locked for the duration of this function.
193 mnt_register(rcm_handle_t
*hd
)
197 rcm_log_message(RCM_TRACE1
, "FILESYS: register()\n");
199 (void) mutex_lock(&cache_lock
);
201 /* cache_sync() does all of the necessary work */
202 if (cache_sync(hd
, &mnt_cache
) < 0) {
203 rcm_log_message(RCM_ERROR
,
204 "FILESYS: failed to synchronize cache (%s).\n",
206 (void) mutex_unlock(&cache_lock
);
207 return (RCM_FAILURE
);
210 (void) mutex_unlock(&cache_lock
);
212 return (RCM_SUCCESS
);
218 * Manually walk through the cache, unregistering all the special
219 * files and mount points.
221 * Locking: the cache is locked throughout the execution of this
222 * routine because it reads and modifies cache links continuously.
225 mnt_unregister(rcm_handle_t
*hd
)
232 rcm_log_message(RCM_TRACE1
, "FILESYS: unregister()\n");
234 (void) mutex_lock(&cache_lock
);
236 /* Unregister everything in the cache */
238 for (index
= 0; index
< mnt_cache
->hash_size
; index
++) {
239 for (entry
= mnt_cache
->mounts
[index
]; entry
!= NULL
;
240 entry
= entry
->next
) {
241 unregister_rsrc(hd
, entry
->special
);
246 /* Destroy the cache */
247 free_cache(&mnt_cache
);
249 (void) mutex_unlock(&cache_lock
);
251 return (RCM_SUCCESS
);
257 * Filesystem resources cannot be offlined. They can however be retired
258 * if they don't provide a critical service. The offline entry point
259 * checks if this is a retire operation and if it is and the filesystem
260 * doesn't provide a critical service, the entry point returns success
261 * For all other cases, failure is returned.
262 * Since no real action is taken, QUERY or not doesn't matter.
265 mnt_offline(rcm_handle_t
*hd
, char *rsrc
, id_t id
, uint_t flags
,
266 char **errorp
, rcm_info_t
**dependent_info
)
274 assert(rsrc
!= NULL
);
275 assert(id
== (id_t
)0);
276 assert(errorp
!= NULL
);
280 rcm_log_message(RCM_TRACE1
, "FILESYS: offline(%s)\n", rsrc
);
282 /* Retrieve necessary info from the cache */
283 if (use_cache(rsrc
, errorp
, &dependents
) < 0) {
284 if (flags
& RCM_RETIRE_REQUEST
)
285 return (RCM_NO_CONSTRAINT
);
287 return (RCM_FAILURE
);
290 if (flags
& RCM_RETIRE_REQUEST
) {
291 (void) mutex_lock(&cache_lock
);
292 if ((entry
= cache_lookup(mnt_cache
, rsrc
)) == NULL
) {
293 rcm_log_message(RCM_ERROR
, "FILESYS: "
294 "failed to look up \"%s\" in cache (%s).\n",
295 rsrc
, strerror(errno
));
296 (void) mutex_unlock(&cache_lock
);
297 retval
= RCM_NO_CONSTRAINT
;
301 if (strcmp(entry
->fstype
, "zfs") == 0) {
302 retval
= RCM_NO_CONSTRAINT
;
303 rcm_log_message(RCM_TRACE1
,
304 "FILESYS: zfs: NO_CONSTRAINT: %s\n", rsrc
);
306 retval
= RCM_SUCCESS
;
307 for (i
= 0; dependents
[i
] != NULL
; i
++) {
308 if (is_critical(dependents
[i
])) {
309 retval
= RCM_FAILURE
;
310 rcm_log_message(RCM_TRACE1
, "FILESYS: "
311 "CRITICAL %s\n", rsrc
);
316 (void) mutex_unlock(&cache_lock
);
320 retval
= RCM_FAILURE
;
322 /* Convert the gathered dependents into an error message */
323 *errorp
= create_message(MSG_HDR_STD
, MSG_HDR_STD_MULTI
, dependents
);
324 if (*errorp
== NULL
) {
325 rcm_log_message(RCM_ERROR
,
326 "FILESYS: failed to construct offline message (%s).\n",
331 free_list(dependents
);
338 * Filesystem resources aren't offlined, so there's really nothing to do
342 mnt_online(rcm_handle_t
*hd
, char *rsrc
, id_t id
, uint_t flag
, char **errorp
,
343 rcm_info_t
**dependent_reason
)
346 assert(rsrc
!= NULL
);
347 assert(id
== (id_t
)0);
348 assert(errorp
!= NULL
);
350 rcm_log_message(RCM_TRACE1
, "FILESYS: online(%s)\n", rsrc
);
352 return (RCM_SUCCESS
);
358 * Report how a given resource is in use by this module. And also
359 * possibly include dependent consumers of the mounted filesystems.
362 mnt_getinfo(rcm_handle_t
*hd
, char *rsrc
, id_t id
, uint_t flag
, char **usagep
,
363 char **errorp
, nvlist_t
*props
, rcm_info_t
**depend_info
)
365 int rv
= RCM_SUCCESS
;
369 assert(rsrc
!= NULL
);
370 assert(id
== (id_t
)0);
371 assert(usagep
!= NULL
);
372 assert(errorp
!= NULL
);
373 assert(props
!= NULL
);
375 rcm_log_message(RCM_TRACE1
, "FILESYS: getinfo(%s)\n", rsrc
);
377 /* Retrieve necessary info from the cache */
378 if (use_cache(rsrc
, errorp
, &dependents
) < 0)
379 return (RCM_FAILURE
);
381 /* Convert the gathered dependents into a usage message */
382 *usagep
= create_message(MSG_HDR_STD
, MSG_HDR_STD_MULTI
, dependents
);
383 if (*usagep
== NULL
) {
384 rcm_log_message(RCM_ERROR
,
385 "FILESYS: failed to construct usage message (%s).\n",
387 *errorp
= strdup(MSG_FAIL_USAGE
);
388 free_list(dependents
);
389 return (RCM_FAILURE
);
392 /* Recurse on dependents if necessary */
393 if ((flag
& RCM_INCLUDE_DEPENDENT
) && (dependents
!= NULL
)) {
394 prune_dependents(dependents
, rsrc
);
395 if (dependents
[0] != NULL
) {
396 if ((rv
= rcm_get_info_list(hd
, dependents
, flag
,
397 depend_info
)) != RCM_SUCCESS
) {
398 *errorp
= strdup(MSG_FAIL_DEPENDENTS
);
403 /* Free up info retrieved from the cache */
404 free_list(dependents
);
412 * Notify all dependents that the resource is being suspended.
413 * Since no real action is taken, QUERY or not doesn't matter.
416 mnt_suspend(rcm_handle_t
*hd
, char *rsrc
, id_t id
, timespec_t
*interval
,
417 uint_t flag
, char **errorp
, rcm_info_t
**depend_info
)
419 int rv
= RCM_SUCCESS
;
423 assert(rsrc
!= NULL
);
424 assert(id
== (id_t
)0);
425 assert(interval
!= NULL
);
426 assert(errorp
!= NULL
);
428 rcm_log_message(RCM_TRACE1
, "FILESYS: suspend(%s)\n", rsrc
);
430 /* Retrieve necessary info from the cache */
431 if (use_cache(rsrc
, errorp
, &dependents
) < 0)
432 return (RCM_FAILURE
);
434 /* Unforced suspensions fail if any of the dependents are critical */
435 if (detect_critical_failure(errorp
, flag
, dependents
)) {
436 free_list(dependents
);
437 return (RCM_FAILURE
);
440 /* Recurse on dependents if necessary */
441 if ((flag
& RCM_INCLUDE_DEPENDENT
) && (dependents
!= NULL
)) {
442 prune_dependents(dependents
, rsrc
);
443 if (dependents
[0] != NULL
)
444 if ((rv
= rcm_request_suspend_list(hd
, dependents
, flag
,
445 interval
, depend_info
)) != RCM_SUCCESS
) {
446 *errorp
= strdup(MSG_FAIL_DEPENDENTS
);
449 free_list(dependents
);
457 * Resume all the dependents of a suspended filesystem.
460 mnt_resume(rcm_handle_t
*hd
, char *rsrc
, id_t id
, uint_t flag
, char **errorp
,
461 rcm_info_t
**depend_info
)
463 int rv
= RCM_SUCCESS
;
467 assert(rsrc
!= NULL
);
468 assert(id
== (id_t
)0);
469 assert(errorp
!= NULL
);
471 rcm_log_message(RCM_TRACE1
, "FILESYS: resume(%s)\n", rsrc
);
473 /* Retrieve necessary info from the cache */
474 if (use_cache(rsrc
, errorp
, &dependents
) < 0)
475 return (RCM_FAILURE
);
477 /* Recurse on dependents if necessary */
478 if ((flag
& RCM_INCLUDE_DEPENDENT
) && (dependents
!= NULL
)) {
479 prune_dependents(dependents
, rsrc
);
480 if (dependents
[0] != NULL
) {
481 if ((rv
= rcm_notify_resume_list(hd
, dependents
, flag
,
482 depend_info
)) != RCM_SUCCESS
) {
483 *errorp
= strdup(MSG_FAIL_DEPENDENTS
);
487 free_list(dependents
);
493 get_spec(char *line
, char *spec
, size_t ssz
)
498 if (strlcpy(spec
, line
, ssz
) >= ssz
) {
499 rcm_log_message(RCM_ERROR
, "FILESYS: get_spec() failed: "
505 while (*cp
== ' ' || *cp
== '\t')
513 while (*cp
!= ' ' && *cp
!= '\t' && *cp
!= '\0')
517 (void) memmove(spec
, start
, strlen(start
) + 1);
523 path_match(char *rsrc
, char *spec
)
529 if (realpath(rsrc
, r
) == NULL
)
532 if (realpath(spec
, s
) == NULL
)
535 len
= strlen("/devices/");
537 if (strncmp(r
, "/devices/", len
) != 0) {
542 if (strncmp(s
, "/devices/", len
) != 0) {
548 if (strncmp(r
, s
, len
) == 0 && (s
[len
] == '\0' || s
[len
] == ':'))
554 rcm_log_message(RCM_DEBUG
, "FILESYS: path_match() failed "
555 "rsrc=%s spec=%s: %s\n", rsrc
, spec
, strerror(errno
));
559 #define VFSTAB "/etc/vfstab"
560 #define RETIRED_PREFIX "## RETIRED ##"
563 disable_vfstab_entry(char *rsrc
)
570 char line
[MNT_LINE_MAX
+ 1];
572 vfp
= fopen(VFSTAB
, "r");
574 rcm_log_message(RCM_ERROR
, "FILESYS: failed to open /etc/vfstab"
575 " for reading: %s\n", strerror(errno
));
576 return (RCM_FAILURE
);
579 (void) snprintf(tmp
, sizeof (tmp
), "/etc/vfstab.retire.%lu", getpid());
581 tfp
= fopen(tmp
, "w");
583 rcm_log_message(RCM_ERROR
, "FILESYS: failed to open "
584 "/etc/vfstab.retire for writing: %s\n", strerror(errno
));
586 return (RCM_FAILURE
);
589 retval
= RCM_SUCCESS
;
591 while (fgets(line
, sizeof (line
), vfp
)) {
593 char spec
[MNT_LINE_MAX
+ 1];
594 char newline
[MNT_LINE_MAX
+ 1];
597 if (get_spec(line
, spec
, sizeof (spec
)) == -1) {
602 if (path_match(rsrc
, spec
) != 0) {
609 /* Paths match. Disable this entry */
610 (void) snprintf(newline
, sizeof (newline
), "%s %s",
611 RETIRED_PREFIX
, line
);
613 rcm_log_message(RCM_TRACE1
, "FILESYS: disabling line\n\t%s\n",
618 if (fputs(l
, tfp
) == EOF
) {
619 rcm_log_message(RCM_ERROR
, "FILESYS: failed to write "
620 "new vfstab: %s\n", strerror(errno
));
622 retval
= RCM_FAILURE
;
633 if (rename(tmp
, VFSTAB
) != 0) {
634 rcm_log_message(RCM_ERROR
, "FILESYS: vfstab rename "
635 "failed: %s\n", strerror(errno
));
636 retval
= RCM_FAILURE
;
648 * Remove will only be called in the retire case i.e. if RCM_RETIRE_NOTIFY
651 * If the flag is not set, then return failure and log the mistake if a
652 * remove is ever received for a mounted filesystem resource.
655 mnt_remove(rcm_handle_t
*hd
, char *rsrc
, id_t id
, uint_t flag
, char **errorp
,
656 rcm_info_t
**depend_info
)
659 assert(rsrc
!= NULL
);
660 assert(id
== (id_t
)0);
661 assert(errorp
!= NULL
);
663 rcm_log_message(RCM_TRACE1
, "FILESYS: remove(%s)\n", rsrc
);
665 if (!(flag
& RCM_RETIRE_NOTIFY
)) {
666 /* Log the mistake */
667 rcm_log_message(RCM_ERROR
, "FILESYS: invalid remove of "
669 *errorp
= strdup(MSG_FAIL_REMOVE
);
670 return (RCM_FAILURE
);
673 return (disable_vfstab_entry(rsrc
));
677 * Cache management routines
683 * This routine constructs a new cache of the current mnttab file.
685 * Locking: the cache must be locked prior to calling this function.
687 * Return Values: NULL with errno set on failure, new cache point on
701 * To keep the hash table relatively sparse, default values are
702 * used for smaller mnttab files and these values are scaled up
703 * as a fraction of the total mnttab file size for larger ones.
705 if (stat(MNTTAB
, &st
) < 0) {
706 rcm_log_message(RCM_ERROR
,
707 "FILESYS: failed to stat \"%s\" (%s).\n", MNTTAB
,
712 if (st
.st_size
> HASH_THRESHOLD
) {
713 size
= st
.st_size
/ HASH_THRESHOLD
;
714 for (i
= 0; size
> 1; i
++, size
>>= 1);
715 for (; i
> -1; i
--, size
<<= 1);
720 /* Allocate a new empty cache */
721 if ((cache
= (cache_t
*)calloc(1, sizeof (cache_t
))) == NULL
) {
722 rcm_log_message(RCM_ERROR
,
723 "FILESYS: failed to allocate cache (%s).\n",
728 cache
->hash_size
= size
;
729 cache
->timestamp
= st
.st_mtime
;
731 /* Allocate an empty hash table for the registered special devices */
732 cache
->mounts
= (hashentry_t
**)calloc(size
, sizeof (hashentry_t
*));
733 if (cache
->mounts
== NULL
) {
734 rcm_log_message(RCM_ERROR
,
735 "FILESYS: failed to allocate mount table (%s).\n",
742 /* Open the mnttab file */
743 if ((fp
= fopen(MNTTAB
, "r")) == NULL
) {
744 rcm_log_message(RCM_ERROR
,
745 "FILESYS: failed to open \"%s\" (%s).\n", MNTTAB
,
752 /* Insert each mnttab entry into the cache */
753 while (getmntent(fp
, &mt
) == 0) {
755 /* Well, not each entry... some are meant to be ignored */
756 if ((mt
.mnt_mntopts
!= NULL
) &&
757 (hasmntopt(&mt
, OPT_IGNORE
) != NULL
))
760 if (cache_insert(cache
, &mt
) < 0) {
761 rcm_log_message(RCM_ERROR
,
762 "FILESYS: cache insertion failure (%s).\n",
771 /* Close the mnttab file */
780 * Free up all the memory associated with a cache.
782 * Locking: the cache must be locked before calling this function.
785 free_cache(cache_t
**cachep
)
789 hashentry_t
*entry_tmp
;
791 /* Do nothing with empty caches */
792 if ((cachep
== NULL
) || (*cachep
== NULL
))
795 if ((*cachep
)->mounts
) {
796 /* Walk through the hashtable, emptying it */
797 for (index
= 0; index
< (*cachep
)->hash_size
; index
++) {
798 entry
= (*cachep
)->mounts
[index
];
800 entry_tmp
= entry
->next
;
805 free((*cachep
)->mounts
);
815 * Free up memory associated with a hashtable entry.
817 * Locking: the cache must be locked before calling this function.
820 free_entry(hashentry_t
**entryp
)
824 if ((*entryp
)->special
)
825 free((*entryp
)->special
);
826 if ((*entryp
)->fstype
)
827 free((*entryp
)->fstype
);
828 free_list((*entryp
)->mountps
);
838 * Free up memory associated with a null terminated list of names.
841 free_list(char **list
)
846 for (i
= 0; list
[i
] != NULL
; i
++)
855 * Resynchronize the mnttab cache with the mnttab file.
857 * Locking: the cache must be locked before calling this function.
859 * Return Values: -1 with errno set on failure, 0 on success.
862 cache_sync(rcm_handle_t
*hd
, cache_t
**cachep
)
870 /* Only accept valid arguments */
871 if ((hd
== NULL
) || (cachep
== NULL
)) {
872 rcm_log_message(RCM_ERROR
,
873 "FILESYS: invalid arguments to cache_sync().\n");
878 /* Do nothing if there's already an up-to-date cache */
881 if (stat(MNTTAB
, &st
) == 0) {
882 if (old_cache
->timestamp
>= st
.st_mtime
) {
886 rcm_log_message(RCM_WARNING
,
887 "FILESYS: failed to stat \"%s\", cache is stale "
888 "(%s).\n", MNTTAB
, strerror(errno
));
894 /* Create a new cache based on the new mnttab file. */
895 if ((new_cache
= cache_create()) == NULL
) {
896 rcm_log_message(RCM_WARNING
,
897 "FILESYS: failed creating cache, cache is stale (%s).\n",
903 /* Register any specials found in the new cache but not the old one */
904 for (index
= 0; index
< new_cache
->hash_size
; index
++) {
905 for (entry
= new_cache
->mounts
[index
]; entry
!= NULL
;
906 entry
= entry
->next
) {
907 if (cache_lookup(old_cache
, entry
->special
) == NULL
) {
908 register_rsrc(hd
, entry
->special
);
913 /* Pass the new cache pointer to the calling function */
916 /* If there wasn't an old cache, return successfully now */
917 if (old_cache
== NULL
)
921 * If there was an old cache, then unregister whatever specials it
922 * contains that aren't in the new cache. And then destroy the old
925 for (index
= 0; index
< old_cache
->hash_size
; index
++) {
926 for (entry
= old_cache
->mounts
[index
]; entry
!= NULL
;
927 entry
= entry
->next
) {
928 if (cache_lookup(new_cache
, entry
->special
) == NULL
) {
929 unregister_rsrc(hd
, entry
->special
);
933 free_cache(&old_cache
);
941 * Given a cache and a mnttab entry, this routine inserts that entry in
942 * the cache. The mnttab entry's special device and filesystem type
943 * is added to the 'mounts' hashtable of the cache, and the entry's
944 * mountp value is added to the list of associated mountpoints for the
945 * corresponding hashtable entry.
947 * Locking: the cache must be locked before calling this function.
949 * Return Values: -1 with errno set on failure, 0 on success.
952 cache_insert(cache_t
*cache
, struct mnttab
*mt
)
958 /* Only accept valid arguments */
959 if ((cache
== NULL
) ||
960 (cache
->mounts
== NULL
) ||
962 (mt
->mnt_special
== NULL
) ||
963 (mt
->mnt_mountp
== NULL
) ||
964 (mt
->mnt_fstype
== NULL
)) {
970 * Disregard any non-loopback mounts whose special device names
971 * don't begin with "/dev".
973 if ((strncmp(mt
->mnt_special
, "/dev", strlen("/dev")) != 0) &&
974 (strcmp(mt
->mnt_fstype
, "lofs") != 0))
978 * Find the special device's entry in the mounts hashtable, allocating
979 * a new entry if necessary.
981 index
= hash(cache
->hash_size
, mt
->mnt_special
);
982 for (entry
= cache
->mounts
[index
]; entry
!= NULL
; entry
= entry
->next
) {
983 if (strcmp(entry
->special
, mt
->mnt_special
) == 0)
987 entry
= (hashentry_t
*)calloc(1, sizeof (hashentry_t
));
988 if ((entry
== NULL
) ||
989 ((entry
->special
= strdup(mt
->mnt_special
)) == NULL
) ||
990 ((entry
->fstype
= strdup(mt
->mnt_fstype
)) == NULL
)) {
991 rcm_log_message(RCM_ERROR
,
992 "FILESYS: failed to allocate special device name "
993 "or filesystem type: (%s).\n", strerror(errno
));
998 entry
->next
= cache
->mounts
[index
];
999 cache
->mounts
[index
] = entry
;
1003 * Keep entries in the list of mounts unique, so exit early if the
1004 * mount is already in the list.
1006 for (index
= 0; index
< entry
->n_mounts
; index
++) {
1007 if (strcmp(entry
->mountps
[index
], mt
->mnt_mountp
) == 0)
1012 * Add this mountpoint to the list of mounts associated with the
1015 mountps
= (char **)realloc(entry
->mountps
,
1016 (entry
->n_mounts
+ 2) * sizeof (char *));
1017 if ((mountps
== NULL
) ||
1018 ((mountps
[entry
->n_mounts
] = strdup(mt
->mnt_mountp
)) == NULL
)) {
1019 rcm_log_message(RCM_ERROR
,
1020 "FILESYS: failed to allocate mountpoint name (%s).\n",
1022 if (entry
->n_mounts
== 0) {
1023 cache
->mounts
[index
] = entry
->next
;
1029 mountps
[entry
->n_mounts
+ 1] = NULL
;
1031 entry
->mountps
= mountps
;
1039 * Searches the cached table of mounts for a special device entry.
1041 * Locking: the cache must be locked before calling this function.
1043 * Return Value: NULL with errno set if failure, pointer to existing
1044 * cache entry when successful.
1046 static hashentry_t
*
1047 cache_lookup(cache_t
*cache
, char *rsrc
)
1052 /* Only accept valid arguments */
1053 if ((cache
== NULL
) || (cache
->mounts
== NULL
) || (rsrc
== NULL
)) {
1058 /* Search the cached mounts table for the resource's entry */
1059 index
= hash(cache
->hash_size
, rsrc
);
1060 if (cache
->mounts
[index
]) {
1061 for (entry
= cache
->mounts
[index
]; entry
!= NULL
;
1062 entry
= entry
->next
) {
1063 if (strcmp(entry
->special
, rsrc
) == 0)
1073 * Miscellaneous Functions
1079 * A naive hashing function that converts a string 's' to an index in a
1080 * hash table of size 'h'. It seems to spread entries around well enough.
1083 hash(uint32_t h
, char *s
)
1086 unsigned char *byte
;
1088 if ((byte
= (unsigned char *)s
) != NULL
) {
1090 sum
+= 0x3F & (uint32_t)*byte
;
1101 * Registers for any given resource, unless it's "/".
1104 register_rsrc(rcm_handle_t
*hd
, char *rsrc
)
1106 /* Only accept valid arguments */
1107 if ((hd
== NULL
) || (rsrc
== NULL
))
1111 * Register any resource other than "/" or "/devices"
1113 if ((strcmp(rsrc
, "/") != 0) && (strcmp(rsrc
, "/devices") != 0)) {
1114 rcm_log_message(RCM_DEBUG
, "FILESYS: registering %s\n", rsrc
);
1115 if (rcm_register_interest(hd
, rsrc
, 0, NULL
) != RCM_SUCCESS
) {
1116 rcm_log_message(RCM_WARNING
,
1117 "FILESYS: failed to register %s\n", rsrc
);
1126 * Unregister a resource. This does a little filtering since we know
1127 * "/" can't be registered, so we never bother unregistering for it.
1130 unregister_rsrc(rcm_handle_t
*hd
, char *rsrc
)
1133 assert(rsrc
!= NULL
);
1135 /* Unregister any resource other than "/" */
1136 if (strcmp(rsrc
, "/") != 0) {
1137 rcm_log_message(RCM_DEBUG
, "FILESYS: unregistering %s\n", rsrc
);
1138 (void) rcm_unregister_interest(hd
, rsrc
, 0);
1145 * Given some header strings and a list of dependent names, this
1146 * constructs a single string. If there's only one dependent, the
1147 * string consists of the first header and the only dependent appended
1148 * to the end of the string enclosed in quotemarks. If there are
1149 * multiple dependents, then the string uses the second header and the
1150 * full list of dependents is appended at the end as a comma separated
1151 * list of names enclosed in quotemarks.
1154 create_message(char *header
, char *header_multi
, char **dependents
)
1161 char *separator
= MSG_SEPARATOR
;
1163 assert(header
!= NULL
);
1164 assert(header_multi
!= NULL
);
1165 assert(dependents
!= NULL
);
1167 /* Count the number of dependents */
1168 for (ndependents
= 0; dependents
[ndependents
] != NULL
; ndependents
++);
1170 /* If there are no dependents, fail */
1171 if (ndependents
== 0) {
1176 /* Pick the appropriate header to use based on amount of dependents */
1177 if (ndependents
== 1) {
1178 msg_header
= header
;
1180 msg_header
= header_multi
;
1183 /* Compute the size required for the message buffer */
1184 len
= strlen(msg_header
) + 2; /* +2 for the space and a NULL */
1185 for (i
= 0; dependents
[i
] != NULL
; i
++)
1186 len
+= strlen(dependents
[i
]) + 2; /* +2 for quotemarks */
1187 len
+= strlen(separator
) * (ndependents
- 1);
1189 /* Allocate the message buffer */
1190 if ((msg_buf
= (char *)calloc(len
, sizeof (char))) == NULL
) {
1191 rcm_log_message(RCM_ERROR
,
1192 "FILESYS: failed to allocate message buffer (%s).\n",
1198 /* Fill in the message buffer */
1199 (void) snprintf(msg_buf
, len
, "%s ", msg_header
);
1200 for (i
= 0; dependents
[i
] != NULL
; i
++) {
1201 (void) strlcat(msg_buf
, "\"", len
);
1202 (void) strlcat(msg_buf
, dependents
[i
], len
);
1203 (void) strlcat(msg_buf
, "\"", len
);
1204 if ((i
+ 1) < ndependents
)
1205 (void) strlcat(msg_buf
, separator
, len
);
1212 * create_dependents()
1214 * Creates a copy of the list of dependent mounts associated with a
1215 * given hashtable entry from the cache.
1217 * Return Values: NULL with errno set on failure, the resulting list of
1218 * dependent resources when successful.
1221 create_dependents(hashentry_t
*entry
)
1226 if (entry
== NULL
) {
1231 if (entry
->n_mounts
== 0) {
1236 /* Allocate space for the full dependency list */
1237 dependents
= (char **)calloc(entry
->n_mounts
+ 1, sizeof (char *));
1238 if (dependents
== NULL
) {
1239 rcm_log_message(RCM_ERROR
,
1240 "FILESYS: failed to allocate dependents (%s).\n",
1246 /* Copy all the dependent names into the new list of dependents */
1247 for (i
= 0; i
< entry
->n_mounts
; i
++) {
1248 if ((dependents
[i
] = strdup(entry
->mountps
[i
])) == NULL
) {
1249 rcm_log_message(RCM_ERROR
,
1250 "FILESYS: failed to allocate dependent \"%s\" "
1251 "(%s).\n", entry
->mountps
[i
], strerror(errno
));
1252 free_list(dependents
);
1258 return (dependents
);
1262 * detect_critical_failure()
1264 * Given a list of dependents, a place to store an error message, and
1265 * the flags associated with an operation, this function detects whether
1266 * or not the operation should fail due to the presence of any critical
1267 * filesystem resources. When a failure is detected, an appropriate
1268 * error message is constructed and passed back to the caller. This is
1269 * called during a suspend request operation.
1271 * Return Values: 0 when a critical resource failure shouldn't prevent
1272 * the operation, and 1 when such a failure condition does exist.
1275 detect_critical_failure(char **errorp
, uint_t flags
, char **dependents
)
1281 /* Do nothing if the operation is forced or there are no dependents */
1282 if ((errorp
== NULL
) || (flags
& RCM_FORCE
) || (dependents
== NULL
))
1286 * Count how many of the dependents are critical, and shift the
1287 * critical resources to the head of the list.
1290 for (i
= 0, n_critical
= 0; dependents
[i
] != NULL
; i
++) {
1291 if (is_critical(dependents
[i
])) {
1292 if (n_critical
!= i
) {
1293 tmp
= dependents
[n_critical
];
1294 dependents
[n_critical
] = dependents
[i
];
1295 dependents
[i
] = tmp
;
1302 /* If no criticals were found, do nothing and return */
1303 if (n_critical
== 0)
1307 * Criticals were found. Prune the list appropriately and construct
1311 /* Prune non-criticals out of the list */
1312 for (i
= n_critical
; dependents
[i
] != NULL
; i
++) {
1313 free(dependents
[i
]);
1314 dependents
[i
] = NULL
;
1317 /* Construct the critical resource error message */
1318 *errorp
= create_message(MSG_HDR_CRIT
, MSG_HDR_CRIT_MULTI
, dependents
);
1326 * Test a resource to determine if it's critical to the system and thus
1327 * cannot be suspended.
1329 * Return Values: 1 if the named resource is critical, 0 if not.
1332 is_critical(char *rsrc
)
1334 assert(rsrc
!= NULL
);
1336 if ((strcmp(rsrc
, "/") == 0) ||
1337 (strcmp(rsrc
, "/usr") == 0) ||
1338 (strcmp(rsrc
, "/lib") == 0) ||
1339 (strcmp(rsrc
, "/usr/lib") == 0) ||
1340 (strcmp(rsrc
, "/bin") == 0) ||
1341 (strcmp(rsrc
, "/usr/bin") == 0) ||
1342 (strcmp(rsrc
, "/tmp") == 0) ||
1343 (strcmp(rsrc
, "/var") == 0) ||
1344 (strcmp(rsrc
, "/var/run") == 0) ||
1345 (strcmp(rsrc
, "/etc") == 0) ||
1346 (strcmp(rsrc
, "/etc/mnttab") == 0) ||
1347 (strcmp(rsrc
, "/platform") == 0) ||
1348 (strcmp(rsrc
, "/usr/platform") == 0) ||
1349 (strcmp(rsrc
, "/sbin") == 0) ||
1350 (strcmp(rsrc
, "/usr/sbin") == 0))
1360 * This routine handles all the tasks necessary to lookup a resource
1361 * in the cache and extract a separate list of dependents for that
1362 * entry. If an error occurs while doing this, an appropriate error
1363 * message is passed back to the caller.
1365 * Locking: the cache is locked for the whole duration of this function.
1368 use_cache(char *rsrc
, char **errorp
, char ***dependentsp
)
1372 (void) mutex_lock(&cache_lock
);
1373 if ((entry
= cache_lookup(mnt_cache
, rsrc
)) == NULL
) {
1374 rcm_log_message(RCM_ERROR
,
1375 "FILESYS: failed looking up \"%s\" in cache (%s).\n",
1376 rsrc
, strerror(errno
));
1377 *errorp
= strdup(MSG_FAIL_INTERNAL
);
1378 (void) mutex_unlock(&cache_lock
);
1381 *dependentsp
= create_dependents(entry
);
1382 (void) mutex_unlock(&cache_lock
);
1388 * prune_dependents()
1390 * Before calling back into RCM with a list of dependents, the list
1391 * must be cleaned up a little. To avoid infinite recursion, "/" and
1392 * the named resource must be pruned out of the list.
1395 prune_dependents(char **dependents
, char *rsrc
)
1402 /* Set 'n' to the total length of the list */
1403 for (n
= 0; dependents
[n
] != NULL
; n
++);
1406 * Move offending dependents to the tail of the list and
1407 * then truncate the list.
1409 for (i
= 0; dependents
[i
] != NULL
; i
++) {
1410 if ((strcmp(dependents
[i
], rsrc
) == 0) ||
1411 (strcmp(dependents
[i
], "/") == 0)) {
1412 free(dependents
[i
]);
1413 dependents
[i
] = dependents
[n
- 1];
1414 dependents
[n
] = NULL
;