/*
* 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
* or http://www.opensolaris.org/os/licensing.
* 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
*/
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
*/
#include <sys/stat.h>
#include <sys/paths.h>
#include <string.h>
#include <strings.h>
#include <stdio.h>
#include <libintl.h>
#include <errno.h>
#include <stdarg.h>
#include <assert.h>
#include <libuutil.h>
#include "suri_impl.h"
#include "suri_strings.h"
/*
* Error model in libsuri(3LIB)
* ============================
* See suri.h for a generic description of the error model.
*
* How to work with error setting functions in libsuri(3LIB) code
* ==============================================================
* If a public API function fails it MUST return a specific libsuri(3LIB) error
* value since libsuri does not explicitly set errno(3C) unless as specifically
* documented in internal man page suri_alloc(3SURI). Libsuri error values are
* defined in <suri.h>. The returned error code is also cached in the handle and
* cat be accessed via suri_err_code().
*
* If a public AP function succeeds, if MUST return ESURI_OK unless it is
* designed to always succeed, and it MUST reset the action and the description
* strings via suri_err_clear(). We assert if error messages are being retrieved
* from the handle upon success.
*
* We always expect the action string to carry an object name that is being
* worked on, be it a URI or a device path (being mapped back to a URI).
*
* Any non-public API function that accepts a handle and fails MUST set the
* description string and MUST return a libsuri(3LIB) errno unless it is
* designed to always succeed. Use only suri_err_set_desc() or
* suri_err_set_static_desc() to set the description. So, if a function
* accepting a handle fails, the caller knows that the description has been
* already set and will not try to re-set it. Usually, the libsuri(3LIB) errno
* set in the lowest level function that failed SHOULD be propagated up to the
* highest level caller without a change. When the highest level libsuri(3LIB)
* function finally gets an error, it knows that a description error string has
* been already set and MUST set an action string via suri_err_set_action().
* Internally, we assert that the description string is already set in
* suri_err_set_action(). After that, the highest level function returns the
* libsuri(3LIB) errno to the consumer of the library.
*
* How libsuri(3LIB) consumers are supposed to use error functions
* ===============================================================
*
* if ((ret = suri_map(h)) != ESURI_OK) {
* if (ret == ESURI_NOENT) {
* ret = suri_create(...)
* ...
* } else {
* (void) fprintf(stderr, "%s: %s\n",
* suri_err_action(h), suri_err_description(h));
* }
* }
* ...
*
* Example error messages (format is "action: description")
* ========================================================
*
* Failed to parse URI "lu:xuname.naa.2000001d38089fb": Expecting "luname", got
* "xuname"
* Failed to parse URI "file://authorityX": Authority not empty: "authorityX"
* Failed to map URI "file:///xljsfdlfjd": File does not exist: "/xljsfdlfjd"
* Failed to map "/x" to a URI: Not block or character device: "/x"
*/
/*
* Check whether there are warnings set in the handle.
*/
boolean_t
suri_err_check_null_warnings(struct suri_handle *sh)
{
int i;
for (i = 0; i < UU_NELEM(sh->sh_warnings); ++i) {
if (sh->sh_warn[i] != NULL || sh->sh_warnings[i][0] != '\0')
return (B_FALSE);
}
return (B_TRUE);
}
/*
* Clear all warnings.
*/
static void
suri_err_clear_warnings(struct suri_handle *sh)
{
(void) memset(sh->sh_warn, '\0', sizeof (sh->sh_warn));
(void) memset(sh->sh_warnings, '\0', sizeof (sh->sh_warnings));
}
/*
* Check whether there are errors set in the handle.
*/
boolean_t
suri_err_check_null_errors(struct suri_handle *sh)
{
if (sh->sh_err_desc[0] != '\0' ||
sh->sh_err_static_desc != NULL ||
sh->sh_err_action[0] != '\0' ||
sh->sh_err_code != ESURI_OK) {
return (B_FALSE);
}
return (B_TRUE);
}
/*
* Get cached return code for the last operation. Useful with functions that may
* return multiple handles.
*/
suri_err_t
suri_err_code(suri_handle_t h)
{
struct suri_handle *sh = (struct suri_handle *)h;
return (sh->sh_err_code);
}
/*
* Get an action string.
*/
const char *
suri_err_action(suri_handle_t h)
{
struct suri_handle *sh = (struct suri_handle *)h;
/*
* Calling this function when a previous operation succeeded is
* considered an unrecoverable error.
*/
assert(sh->sh_err_action[0] != '\0');
assert(suri_err_code((suri_handle_t)sh) != ESURI_OK);
/* We have cleared any possible warnings in suri_err_description() */
assert(suri_err_check_null_warnings(sh));
return (sh->sh_err_action);
}
/*
* Get a description string.
*/
const char *
suri_err_description(suri_handle_t h)
{
struct suri_handle *sh = (struct suri_handle *)h;
/*
* Calling this function when a previous operation succeeded is
* considered an unrecoverable error.
*/
assert(sh->sh_err_desc[0] != '\0' || sh->sh_err_static_desc != NULL);
assert(suri_err_code((suri_handle_t)sh) != ESURI_OK);
if (sh->sh_err_static_desc != NULL)
return (sh->sh_err_static_desc);
return (sh->sh_err_desc);
}
/*
* Get warning strings.
*/
char **
suri_warning(suri_handle_t h)
{
struct suri_handle *sh = (struct suri_handle *)h;
if (sh->sh_warn[0] == NULL)
return (NULL);
return (sh->sh_warn);
}
/*
* This internal function is used to set the action string.
*/
/* PRINTFLIKE2 */
void
suri_err_set_action(struct suri_handle *sh, const char *fmt, ...)
{
int n;
va_list ap;
/* Make sure we set the action string just once per error. */
assert(sh->sh_err_action[0] == '\0');
/*
* The description is expected to have been always set before this
* function is called.
*/
assert(sh->sh_err_desc[0] != '\0' || sh->sh_err_static_desc != NULL);
/*
* If we had any warnings they have been already cleared in our
* suri_err_set(_static|)_desc() functions.
*/
assert(suri_err_check_null_warnings(sh));
va_start(ap, fmt);
n = vsnprintf(sh->sh_err_action, sizeof (sh->sh_err_action), fmt, ap);
assert(n < sizeof (sh->sh_err_action));
va_end(ap);
}
/*
* Internal function to set a dynamic error description string.
*/
/* PRINTFLIKE2 */
void
suri_err_set_desc(struct suri_handle *sh, const char *fmt, ...)
{
int n;
va_list ap;
/*
* Make sure we set the description string just once per error and
* before setting the action error string.
*/
assert(suri_err_check_null_errors(sh));
va_start(ap, fmt);
n = vsnprintf(sh->sh_err_desc, sizeof (sh->sh_err_desc), fmt, ap);
assert(n < sizeof (sh->sh_err_desc));
va_end(ap);
/*
* While we could have some warnings in the handle, they are no longer
* relevant if the operation fails.
*/
suri_err_clear_warnings(sh);
}
/*
* If a dynamic description is not needed, use a static description derived from
* the libsuri(3LIB) list of errno values and return 'err'.
*/
suri_err_t
suri_err_set_static_desc(struct suri_handle *sh, suri_err_t err)
{
/*
* Make sure we set the description string just once per error and
* before setting the action error string.
*/
assert(suri_err_check_null_errors(sh));
switch (err) {
case ESURI_NOMEM:
sh->sh_err_static_desc = ERR_SURI_NOMEM;
break;
case ESURI_NAMETOOLONG:
sh->sh_err_static_desc = ERR_SURI_NAMETOOLONG;
break;
default:
assert(0);
}
/*
* While we could have some warnings in the handle, they are no longer
* relevant if the operation fails.
*/
suri_err_clear_warnings(sh);
return (err);
}
/*
* Internal function to set a warning. Warnings can be used only if the function
* returns ESURI_OK. Note that for now we support only two warnings per a
* handle.
*/
/* PRINTFLIKE2 */
void
suri_set_warn(struct suri_handle *sh, const char *fmt, ...)
{
int i, n;
va_list ap;
assert(suri_err_check_null_errors(sh));
/* Find the first free warning string */
for (i = 0; i < UU_NELEM(sh->sh_warnings); ++i) {
if (sh->sh_warn[i] == NULL)
break;
}
/* If the warning array if already full it it an internal error. */
assert(i < UU_NELEM(sh->sh_warnings));
/* "Publicize" the warning string */
sh->sh_warn[i] = sh->sh_warnings[i];
/* The warning we are gonna use must be empty; no leftovers allowed. */
assert(sh->sh_warn[i][0] == '\0');
va_start(ap, fmt);
n = vsnprintf(sh->sh_warn[i], SURI_WARN_STR_LEN, fmt, ap);
va_end(ap);
/* We expect the terminating NULL to be already there. */
assert(sh->sh_warn[i + 1] == NULL);
assert(n < SURI_WARN_STR_LEN);
}
/*
* Clear any previous handle errors. This function is usually to be used only
* upon entry to public API functions. However, we also use it for URI specific
* mapping operations involving devinfo tree where we must clear errors before
* trying again with a full device discovery.
*/
void
suri_err_clear(struct suri_handle *sh)
{
int i;
sh->sh_err_code = ESURI_OK;
sh->sh_err_action[0] = '\0';
sh->sh_err_desc[0] = '\0';
sh->sh_err_static_desc = NULL;
(void) memset(sh->sh_warn, '\0',
sizeof (sh->sh_warn) * sizeof (char *));
for (i = 0; i < 2; ++i)
sh->sh_warnings[i][0] = '\0';
}