/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
*/
/*
* Handle initialization of the suspend subsystem. This includes setting
* platform default values for the suspend-enable property and creating
* the cpr file.
*/
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <libgen.h>
#include <errno.h>
#include <sys/efi_partition.h>
#include <libnvpair.h>
#include <libuutil.h>
#include <libzfs.h>
#include <instzones_api.h>
#include <libpower.h>
#include <libpower_impl.h>
#ifdef sparc
#include <sys/openpromio.h>
#endif
int get_cpr_config_path(char **cpr_conf);
#ifdef sparc
#endif
typedef struct dir_data {
char *dir;
char *ds;
} dir_data_t;
if (_zhp) { \
}
static char *be_get_ds_from_dir(char *dir);
/*
* Initialize any suspend facilities for this machine if needed or configured.
*/
{
/*
* Retrieve all of the SMF properties to see if suspend-enabled is
* already configured in SMF. If it is, then the service has been
* started before and no re-setting of the default is required.
*/
errno = 0;
if (err != PM_SUCCESS) {
/*
* An error occurred reading from SMF. Pass the error up
* to the caller to process.
*/
return (err);
}
/*
* The suspend-enable property is not yet initialized.
* Since this implies that suspend is not enabled,
* there is nothing left to do here, so return failure.
*/
return (PM_ERROR_PROPERTY_NOT_FOUND);
}
/* Recurse to find the value of the property */
&nvp)) == 0) {
}
/* Clean up */
/* Things to run if suspend is enabled */
/*
* Update the cprconfig file or create if it does not
* already exist.
*/
if (update_cprconfig() != PM_SUCCESS) {
return (PM_ERROR_NOT_SUPPORTED);
else
return (PM_ERROR_SYSTEM);
}
}
/*
* If we get here, always return success, as we have done
* what is needed to "initialize" suspend.
*/
return (PM_SUCCESS);
}
/*
* returns B_TRUE if the slice is good (e.g. does not start at block
* zero, or a string describing the error if it doesn't
*/
static boolean_t
{
char *x, *y;
/* convert from dsk to rdsk */
if (x != NULL) {
*x++ = 'r';
(void) strcpy(x, y);
}
*err = "could not open '%s'\n";
/*
* we got a slice number; now check the block
* number where the slice starts
*/
*err = "using '%s' would clobber the disk label\n";
} else if ((rc == VT_ENOTSUP) &&
/* EFI slices don't clobber the disk label */
return (B_TRUE);
} else
*err = "could not read partition table from '%s'\n";
return (B_FALSE);
}
/*
* This should go in a header file, but for now it is private, so leave
* it here.
*/
/*
* These describe the parent pathname to block disk devices used
* for the statefile. As there is no API for acquiring them, we
* define them here so that they are easily identified should they
* need to be updated.
*/
/*
* Get and verify the device for saving a statefile.
* If there is not a defined property, choose the ZFS dump device.
*/
static void
/*
* First see if we have a property that specifies the
* statefile path. If any attempt to find it via SMF
* fails, fallback to the "default" path of the ZFS dump
* device.
*/
"%s get statefile path\n", __FUNCTION__);
/* Get the property defining the statefile path */
/*
* We have a property, so now extract the value
* from the property
*/
}
}
}
/*
* Define the "default" statefile.
* Note that this suffers from the same problem as
* get_cpr_config_path(), in that it uses a copy of
* a private interface to get the pool name.
* Technically, we have free'd the stuff at fp, but that
* is OK, as we don't expect to use the data, but only know
* that we found data.
*/
} else {
/* Couldn't find pool, return. */
"%s statefile requires zfs\n", __FUNCTION__);
return;
}
}
/* If we still cannot find a path, return. */
"%s statefile not specified\n", __FUNCTION__);
return;
}
/*
* Statefile must exist before it can be used here.
*/
return;
}
/*
* If not a block device, return.
*/
"%s statefile not block device\n", __FUNCTION__);
return;
}
/*
* If not a good "slice", return.
*/
return;
}
/*
* OK, we have a block device, and we know it is valid,
* so identify the prom device
* If the path is *not* a zvol, it will be treated as a
* SPECFS device.
*/
#ifdef sparc
"%s could not convert to prom name: %s\n",
return;
}
#else
sizeof (cc->cf_dev_prom));
#endif
"%s statefile is specfs device %s\n", __FUNCTION__,
cc->cf_dev_prom);
} else {
/*
* Should be ZFS, get the underlying device.
* If anything fails, just return as we haven't yet
* filled in any config file entries.
* Note that we don't really care about the vdev on
* x86 platforms, but this code does give us the
* oppertunity to validate the zvol.
*/
*p = '\0';
"%s couldn't init zfs\n", __FUNCTION__);
return;
}
"%s couldn't open zfs\n", __FUNCTION__);
return;
}
&nvroot) != 0) {
"%s couldn't identify pool %s\n", __FUNCTION__,
return;
}
if (children != 1) {
"%s couldn't get pool children\n", __FUNCTION__);
return;
}
B_FALSE);
"%s couldn't identify vdev\n", __FUNCTION__);
return;
}
#ifdef sparc
"%s could not convert to prom name: %s\n",
return;
}
#else
sizeof (cc->cf_dev_prom));
#endif
"%s statefile is zfs device: %s\n", __FUNCTION__,
cc->cf_dev_prom);
}
/*
* By virtue of arriving here, we have identified and validated
* the statefile path. All that remains is to stuff it in the
* last two config file entries.
*/
}
/*
* Update or create cpr_config file.
*/
{
/*
* A note on the use of dirname and the strings we use to get it.
* dirname(3c) usually manipulates the string that is passed in,
* such that the original string is not usable unless it was
* dup'd or copied before. The exception, is if a NULL is passed,
* and then we get get a new string that is ".". So we must
* always check the return value, and if we care about the original
* string, we must dup it before calling dirname.
*/
"%s cannot identify cpr_file\n", __FUNCTION__);
return (PM_ERROR_SYSTEM);
}
"%s buffer overflow error\n", __FUNCTION__);
return (PM_ERROR_SYSTEM);
}
(cpr_conf_dir[0] != '/')) {
"%s cpr_config directory unknown or invalid\n",
return (PM_ERROR_SYSTEM);
}
/*
* If the confdir doesn't exist, it needs to be created,
* unless the parent doesn't exist, and then we bail.
*/
/*
* cpr_config directory doesn't exist. If the parent does,
* set it up so that cpr_conf_dir can be created a little
* further down. Otherwise, return an error.
*/
MAXPATHLEN) {
"%s buffer overflow error\n", __FUNCTION__);
return (PM_ERROR_SYSTEM);
}
/*
* We already know that cpr_conf_dir properly started
* with '/', so the copy should as well, and we don't
* need to test for it. However, if the parent is
* just '/', this is an error.
*/
"%s cpr_config parent directory missing\n",
return (PM_ERROR_SYSTEM);
} else {
createdir = 1;
}
}
/*
* Create the cpr_config directory if we have determined it is missing
*/
if (createdir) {
"%s conf directory %s missing\n",
return (PM_ERROR_SYSTEM);
}
}
/* Is there an existing cpr_config */
/* Use it to initially populate cprconfig struct. */
"%s: %s is corrupted, regenerating.\n",
}
}
} else {
}
/* Always make sure the magic number is correct. */
/*
* Fetch the statefile path for this platform, and populate
* the cpr config struct.
*/
/*
* Effectively, we always want a fresh file, so create it if
* it doesn't exist, and truncate it if it does.
*/
return (PM_ERROR_SYSTEM);
}
"%s error writing \"%s\", %s\\n",
return (PM_ERROR_SYSTEM);
}
return (PM_SUCCESS);
}
/*
* Find the path to the cpr_config file.
*/
int
{
int cpsize;
#ifdef sparc
/* On Sparc, this is easy, as it (currently) must be CPR_CONFIG */
} else {
}
#else
/* On others, we need to find the pool the root is running. */
/* Clear and add the leading '/' to the pool pathname. */
pool[0] = '/';
return (-1);
/*
* We have identified the mounted pool, extract everything
* up to "/ROOT" from it.
*/
}
#endif
return (-1);
else
return (0);
}
#ifdef sparc
/*
* Convert a Unix device to a prom device and save on success,
* log any ioctl/conversion error.
*/
static int
{
union obpbuf {
};
"%s cannot open \"%s\", %s.\n",
return (-1);
}
if (upval == 0) {
} else {
"%s cannot convert \"%s\" to prom device\n",
}
return (upval);
}
#endif
/*
* Function: be_get_ds_from_dir_callback
* Description: This is a callback function used to iterate all datasets
* to find the one that is currently mounted at the directory
* being searched for. If matched, the name of the dataset is
* returned in heap storage, so the caller is responsible for
* freeing it.
* Parameters:
* zhp - zfs_handle_t pointer to current dataset being processed.
* data - dir_data_t pointer providing name of directory being
* searched for.
* Returns:
* 1 - This dataset is mounted at directory being searched for.
* 0 - This dataset is not mounted at directory being searched for.
* Scope:
* Private
*/
static int
{
int zret = 0;
return (0);
}
"%s memory allocation failed\n", __FUNCTION__);
return (0);
}
return (1);
}
return (zret);
}
/*
* Function: be_get_ds_from_dir(char *dir)
* Description: Given a directory path, find the underlying dataset mounted
* at that directory path if there is one. The returned name
* is allocated in heap storage, so the caller is responsible
* for freeing it.
* Parameters:
* dir - char pointer of directory to find.
* Returns:
* NULL - if directory is not mounted from a dataset.
* name of dataset mounted at dir.
* Scope:
* Semi-private (library wide use only)
*
* This is what I need to find the root pool from a mounted root dir.
*/
static char *
{
return (NULL);
}
/* Make sure length of dir is within the max length */
return (NULL);
/* Resolve dir in case its lofs mounted */
}