8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / rcm_daemon / common / filesys_rcm.c
blob44c31ef6c6cdf890a46891bdf7f92be199d46ba8
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
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
40 #include <stdio.h>
41 #include <assert.h>
42 #include <string.h>
43 #include <synch.h>
44 #include <libintl.h>
45 #include <errno.h>
46 #include <sys/mnttab.h>
47 #include <sys/param.h>
48 #include <sys/stat.h>
49 #include <sys/utssys.h>
50 #include <unistd.h>
51 #include <limits.h>
53 #include "rcm_module.h"
55 /* Definitions */
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 {
73 int n_mounts;
74 char *special;
75 char *fstype;
76 char **mountps;
77 struct hashentry *next;
78 } hashentry_t;
80 typedef struct {
81 time_t timestamp;
82 uint32_t hash_size;
83 hashentry_t **mounts;
84 } cache_t;
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 **,
96 rcm_info_t **);
97 static int mnt_offline(rcm_handle_t *, char *, id_t, uint_t, char **,
98 rcm_info_t **);
99 static int mnt_online(rcm_handle_t *, char *, id_t, uint_t, char **,
100 rcm_info_t **);
101 static int mnt_remove(rcm_handle_t *, char *, id_t, uint_t, char **,
102 rcm_info_t **);
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 =
128 RCM_MOD_OPS_VERSION,
129 mnt_register,
130 mnt_unregister,
131 mnt_getinfo,
132 mnt_suspend,
133 mnt_resume,
134 mnt_offline,
135 mnt_online,
136 mnt_remove
139 static cache_t *mnt_cache;
140 static mutex_t cache_lock;
142 /* Module Interface Routines */
145 * rcm_mod_init()
147 * Called when module is loaded. Returns the ops vector.
149 struct rcm_mod_ops *
150 rcm_mod_init()
152 return (&mnt_ops);
156 * rcm_mod_info()
158 * Returns a string identifying this module.
160 const char *
161 rcm_mod_info()
163 return ("File system module 1.9");
167 * rcm_mod_fini()
169 * Called when module is unloaded. Frees up all used memory.
171 * Locking: the cache is locked for the duration of this function.
174 rcm_mod_fini()
176 (void) mutex_lock(&cache_lock);
177 free_cache(&mnt_cache);
178 (void) mutex_unlock(&cache_lock);
180 return (RCM_SUCCESS);
184 * mnt_register()
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)
195 assert(hd != NULL);
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",
205 strerror(errno));
206 (void) mutex_unlock(&cache_lock);
207 return (RCM_FAILURE);
210 (void) mutex_unlock(&cache_lock);
212 return (RCM_SUCCESS);
216 * mnt_unregister()
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)
227 uint32_t index;
228 hashentry_t *entry;
230 assert(hd != NULL);
232 rcm_log_message(RCM_TRACE1, "FILESYS: unregister()\n");
234 (void) mutex_lock(&cache_lock);
236 /* Unregister everything in the cache */
237 if (mnt_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);
255 * mnt_offline()
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)
268 char **dependents;
269 hashentry_t *entry;
270 int retval;
271 int i;
273 assert(hd != NULL);
274 assert(rsrc != NULL);
275 assert(id == (id_t)0);
276 assert(errorp != NULL);
278 *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);
286 else
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;
298 goto out;
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);
305 } else {
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);
312 break;
316 (void) mutex_unlock(&cache_lock);
317 goto out;
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",
327 strerror(errno));
330 out:
331 free_list(dependents);
332 return (retval);
336 * mnt_online()
338 * Filesystem resources aren't offlined, so there's really nothing to do
339 * here.
342 mnt_online(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flag, char **errorp,
343 rcm_info_t **dependent_reason)
345 assert(hd != NULL);
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);
356 * mnt_getinfo()
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;
366 char **dependents;
368 assert(hd != NULL);
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",
386 strerror(errno));
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);
406 return (rv);
410 * mnt_suspend()
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;
420 char **dependents;
422 assert(hd != NULL);
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);
451 return (rv);
455 * mnt_resume()
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;
464 char **dependents;
466 assert(hd != NULL);
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);
489 return (rv);
492 static int
493 get_spec(char *line, char *spec, size_t ssz)
495 char *cp;
496 char *start;
498 if (strlcpy(spec, line, ssz) >= ssz) {
499 rcm_log_message(RCM_ERROR, "FILESYS: get_spec() failed: "
500 "line: %s\n", line);
501 return (-1);
504 cp = spec;
505 while (*cp == ' ' || *cp == '\t')
506 cp++;
508 if (*cp == '#')
509 return (-1);
511 start = cp;
513 while (*cp != ' ' && *cp != '\t' && *cp != '\0')
514 cp++;
515 *cp = '\0';
517 (void) memmove(spec, start, strlen(start) + 1);
519 return (0);
522 static int
523 path_match(char *rsrc, char *spec)
525 char r[PATH_MAX];
526 char s[PATH_MAX];
527 size_t len;
529 if (realpath(rsrc, r) == NULL)
530 goto error;
532 if (realpath(spec, s) == NULL)
533 goto error;
535 len = strlen("/devices/");
537 if (strncmp(r, "/devices/", len) != 0) {
538 errno = ENXIO;
539 goto error;
542 if (strncmp(s, "/devices/", len) != 0) {
543 errno = ENXIO;
544 goto error;
547 len = strlen(r);
548 if (strncmp(r, s, len) == 0 && (s[len] == '\0' || s[len] == ':'))
549 return (0);
550 else
551 return (1);
553 error:
554 rcm_log_message(RCM_DEBUG, "FILESYS: path_match() failed "
555 "rsrc=%s spec=%s: %s\n", rsrc, spec, strerror(errno));
556 return (-1);
559 #define VFSTAB "/etc/vfstab"
560 #define RETIRED_PREFIX "## RETIRED ##"
562 static int
563 disable_vfstab_entry(char *rsrc)
565 FILE *vfp;
566 FILE *tfp;
567 int retval;
568 int update;
569 char tmp[PATH_MAX];
570 char line[MNT_LINE_MAX + 1];
572 vfp = fopen(VFSTAB, "r");
573 if (vfp == NULL) {
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");
582 if (tfp == NULL) {
583 rcm_log_message(RCM_ERROR, "FILESYS: failed to open "
584 "/etc/vfstab.retire for writing: %s\n", strerror(errno));
585 (void) fclose(vfp);
586 return (RCM_FAILURE);
589 retval = RCM_SUCCESS;
590 update = 0;
591 while (fgets(line, sizeof (line), vfp)) {
593 char spec[MNT_LINE_MAX + 1];
594 char newline[MNT_LINE_MAX + 1];
595 char *l;
597 if (get_spec(line, spec, sizeof (spec)) == -1) {
598 l = line;
599 goto foot;
602 if (path_match(rsrc, spec) != 0) {
603 l = line;
604 goto foot;
607 update = 1;
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",
614 line);
616 l = newline;
617 foot:
618 if (fputs(l, tfp) == EOF) {
619 rcm_log_message(RCM_ERROR, "FILESYS: failed to write "
620 "new vfstab: %s\n", strerror(errno));
621 update = 0;
622 retval = RCM_FAILURE;
623 break;
627 if (vfp)
628 (void) fclose(vfp);
629 if (tfp)
630 (void) fclose(tfp);
632 if (update) {
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;
640 (void) unlink(tmp);
642 return (retval);
646 * mnt_remove()
648 * Remove will only be called in the retire case i.e. if RCM_RETIRE_NOTIFY
649 * flag is set.
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)
658 assert(hd != NULL);
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 "
668 "\"%s\"\n", rsrc);
669 *errorp = strdup(MSG_FAIL_REMOVE);
670 return (RCM_FAILURE);
673 return (disable_vfstab_entry(rsrc));
677 * Cache management routines
681 * cache_create()
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
688 * success.
690 static cache_t *
691 cache_create()
693 FILE *fp;
694 cache_t *cache;
695 int i;
696 uint32_t size;
697 struct stat st;
698 struct mnttab mt;
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,
708 strerror(errno));
709 errno = EBADF;
710 return (NULL);
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);
716 } else {
717 size = HASH_DEFAULT;
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",
724 strerror(errno));
725 errno = ENOMEM;
726 return (NULL);
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",
736 strerror(errno));
737 free_cache(&cache);
738 errno = ENOMEM;
739 return (NULL);
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,
746 strerror(errno));
747 free_cache(&cache);
748 errno = EIO;
749 return (NULL);
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))
758 continue;
760 if (cache_insert(cache, &mt) < 0) {
761 rcm_log_message(RCM_ERROR,
762 "FILESYS: cache insertion failure (%s).\n",
763 strerror(errno));
764 free_cache(&cache);
765 (void) fclose(fp);
766 errno = EFAULT;
767 return (NULL);
771 /* Close the mnttab file */
772 (void) fclose(fp);
774 return (cache);
778 * free_cache()
780 * Free up all the memory associated with a cache.
782 * Locking: the cache must be locked before calling this function.
784 static void
785 free_cache(cache_t **cachep)
787 uint32_t index;
788 hashentry_t *entry;
789 hashentry_t *entry_tmp;
791 /* Do nothing with empty caches */
792 if ((cachep == NULL) || (*cachep == NULL))
793 return;
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];
799 while (entry) {
800 entry_tmp = entry->next;
801 free_entry(&entry);
802 entry = entry_tmp;
805 free((*cachep)->mounts);
808 free(*cachep);
809 *cachep = NULL;
813 * free_entry()
815 * Free up memory associated with a hashtable entry.
817 * Locking: the cache must be locked before calling this function.
819 static void
820 free_entry(hashentry_t **entryp)
822 if (entryp) {
823 if (*entryp) {
824 if ((*entryp)->special)
825 free((*entryp)->special);
826 if ((*entryp)->fstype)
827 free((*entryp)->fstype);
828 free_list((*entryp)->mountps);
829 free(*entryp);
831 *entryp = NULL;
836 * free_list()
838 * Free up memory associated with a null terminated list of names.
840 static void
841 free_list(char **list)
843 int i;
845 if (list) {
846 for (i = 0; list[i] != NULL; i++)
847 free(list[i]);
848 free(list);
853 * cache_sync()
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.
861 static int
862 cache_sync(rcm_handle_t *hd, cache_t **cachep)
864 uint32_t index;
865 cache_t *new_cache;
866 cache_t *old_cache;
867 hashentry_t *entry;
868 struct stat st;
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");
874 errno = EINVAL;
875 return (-1);
878 /* Do nothing if there's already an up-to-date cache */
879 old_cache = *cachep;
880 if (old_cache) {
881 if (stat(MNTTAB, &st) == 0) {
882 if (old_cache->timestamp >= st.st_mtime) {
883 return (0);
885 } else {
886 rcm_log_message(RCM_WARNING,
887 "FILESYS: failed to stat \"%s\", cache is stale "
888 "(%s).\n", MNTTAB, strerror(errno));
889 errno = EIO;
890 return (-1);
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",
898 strerror(errno));
899 errno = EIO;
900 return (-1);
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 */
914 *cachep = new_cache;
916 /* If there wasn't an old cache, return successfully now */
917 if (old_cache == NULL)
918 return (0);
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
923 * cache.
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);
935 return (0);
939 * cache_insert()
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.
951 static int
952 cache_insert(cache_t *cache, struct mnttab *mt)
954 uint32_t index;
955 hashentry_t *entry;
956 char **mountps;
958 /* Only accept valid arguments */
959 if ((cache == NULL) ||
960 (cache->mounts == NULL) ||
961 (mt == NULL) ||
962 (mt->mnt_special == NULL) ||
963 (mt->mnt_mountp == NULL) ||
964 (mt->mnt_fstype == NULL)) {
965 errno = EINVAL;
966 return (-1);
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))
975 return (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)
984 break;
986 if (entry == NULL) {
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));
994 free_entry(&entry);
995 errno = ENOMEM;
996 return (-1);
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)
1008 return (0);
1012 * Add this mountpoint to the list of mounts associated with the
1013 * special device.
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",
1021 strerror(errno));
1022 if (entry->n_mounts == 0) {
1023 cache->mounts[index] = entry->next;
1024 free_entry(&entry);
1026 errno = ENOMEM;
1027 return (-1);
1029 mountps[entry->n_mounts + 1] = NULL;
1030 entry->n_mounts++;
1031 entry->mountps = mountps;
1033 return (0);
1037 * cache_lookup()
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)
1049 uint32_t index;
1050 hashentry_t *entry;
1052 /* Only accept valid arguments */
1053 if ((cache == NULL) || (cache->mounts == NULL) || (rsrc == NULL)) {
1054 errno = EINVAL;
1055 return (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)
1064 return (entry);
1068 errno = ENOENT;
1069 return (NULL);
1073 * Miscellaneous Functions
1077 * hash()
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.
1082 static uint32_t
1083 hash(uint32_t h, char *s)
1085 uint32_t sum = 0;
1086 unsigned char *byte;
1088 if ((byte = (unsigned char *)s) != NULL) {
1089 while (*byte) {
1090 sum += 0x3F & (uint32_t)*byte;
1091 byte++;
1095 return (sum % h);
1099 * register_rsrc()
1101 * Registers for any given resource, unless it's "/".
1103 static void
1104 register_rsrc(rcm_handle_t *hd, char *rsrc)
1106 /* Only accept valid arguments */
1107 if ((hd == NULL) || (rsrc == NULL))
1108 return;
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);
1124 * unregister_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.
1129 static void
1130 unregister_rsrc(rcm_handle_t *hd, char *rsrc)
1132 assert(hd != NULL);
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);
1143 * create_message()
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.
1153 static char *
1154 create_message(char *header, char *header_multi, char **dependents)
1156 int i;
1157 size_t len;
1158 int ndependents;
1159 char *msg_buf;
1160 char *msg_header;
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) {
1172 errno = ENOENT;
1173 return (NULL);
1176 /* Pick the appropriate header to use based on amount of dependents */
1177 if (ndependents == 1) {
1178 msg_header = header;
1179 } else {
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",
1193 strerror(errno));
1194 errno = ENOMEM;
1195 return (NULL);
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);
1208 return (msg_buf);
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.
1220 static char **
1221 create_dependents(hashentry_t *entry)
1223 int i;
1224 char **dependents;
1226 if (entry == NULL) {
1227 errno = EINVAL;
1228 return (NULL);
1231 if (entry->n_mounts == 0) {
1232 errno = ENOENT;
1233 return (NULL);
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",
1241 strerror(errno));
1242 errno = ENOMEM;
1243 return (NULL);
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);
1253 errno = ENOMEM;
1254 return (NULL);
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.
1274 static int
1275 detect_critical_failure(char **errorp, uint_t flags, char **dependents)
1277 int i;
1278 int n_critical;
1279 char *tmp;
1281 /* Do nothing if the operation is forced or there are no dependents */
1282 if ((errorp == NULL) || (flags & RCM_FORCE) || (dependents == NULL))
1283 return (0);
1286 * Count how many of the dependents are critical, and shift the
1287 * critical resources to the head of the list.
1289 if (dependents) {
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;
1297 n_critical++;
1302 /* If no criticals were found, do nothing and return */
1303 if (n_critical == 0)
1304 return (0);
1307 * Criticals were found. Prune the list appropriately and construct
1308 * an error message.
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);
1320 return (1);
1324 * is_critical()
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.
1331 static int
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))
1351 return (1);
1353 return (0);
1358 * use_cache()
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.
1367 static int
1368 use_cache(char *rsrc, char **errorp, char ***dependentsp)
1370 hashentry_t *entry;
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);
1379 return (-1);
1381 *dependentsp = create_dependents(entry);
1382 (void) mutex_unlock(&cache_lock);
1384 return (0);
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.
1394 static void
1395 prune_dependents(char **dependents, char *rsrc)
1397 int i;
1398 int n;
1400 if (dependents) {
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;
1415 i--;
1416 n--;