3260d16f34
Change-Id: Ib64cef83a646bce2b0afa72b607fb9e5c306e859
578 lines
17 KiB
C
578 lines
17 KiB
C
// Copyright 2012 Google Inc.
|
|
// All rights reserved.
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without
|
|
// modification, are permitted provided that the following conditions are
|
|
// met:
|
|
//
|
|
// * Redistributions of source code must retain the above copyright
|
|
// notice, this list of conditions and the following disclaimer.
|
|
// * Redistributions in binary form must reproduce the above copyright
|
|
// notice, this list of conditions and the following disclaimer in the
|
|
// documentation and/or other materials provided with the distribution.
|
|
// * Neither the name of Google Inc. nor the names of its contributors
|
|
// may be used to endorse or promote products derived from this software
|
|
// without specific prior written permission.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
#include "error.h"
|
|
|
|
#include <assert.h>
|
|
#include <err.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
|
|
/// Generic hook to format an error that does not have a format callback.
|
|
///
|
|
/// \param error Error for which to generate a message.
|
|
/// \param output_buffer Buffer to hold the generated message.
|
|
/// \param output_size Length of output_buffer.
|
|
static int
|
|
generic_format_callback(const kyua_error_t error, char* const output_buffer,
|
|
size_t output_size)
|
|
{
|
|
assert(error != NULL);
|
|
return snprintf(output_buffer, output_size, "Error '%s'", error->type_name);
|
|
}
|
|
|
|
|
|
/// Initializes an error object.
|
|
///
|
|
/// \param error Error for which to generate a message.
|
|
/// \param type_name Name of the error type.
|
|
/// \param data Opaque data that belongs to the error, for usage by
|
|
/// error-specific methods like format_callback.
|
|
/// \param data_size Size of the opaque data object.
|
|
/// \param format_callback Type-specific method to generate a user
|
|
/// representation of the error.
|
|
///
|
|
/// \return True if the initialization succeeds; false otherwise. If
|
|
/// false, the error object passed in has not been modified.
|
|
static bool
|
|
error_init(kyua_error_t const error, const char* const type_name,
|
|
void* const data, const size_t data_size,
|
|
const kyua_error_format_callback format_callback)
|
|
{
|
|
assert(data != NULL || data_size == 0);
|
|
assert(data_size != 0 || data == NULL);
|
|
|
|
bool ok;
|
|
|
|
if (data == NULL) {
|
|
error->data = NULL;
|
|
error->needs_free = false;
|
|
ok = true;
|
|
} else {
|
|
void* new_data = malloc(data_size);
|
|
if (new_data == NULL) {
|
|
ok = false;
|
|
} else {
|
|
memcpy(new_data, data, data_size);
|
|
error->data = new_data;
|
|
ok = true;
|
|
}
|
|
}
|
|
|
|
if (ok) {
|
|
error->type_name = type_name;
|
|
error->format_callback = (format_callback == NULL) ?
|
|
generic_format_callback : format_callback;
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
|
|
|
|
/// Allocates and initializes a new error.
|
|
///
|
|
/// \param type_name Name of the error type.
|
|
/// \param data Opaque data that belongs to the error, for usage by
|
|
/// error-specific methods like format_callback.
|
|
/// \param data_size Size of the opaque data object.
|
|
/// \param format_callback Type-specific method to generate a user
|
|
/// representation of the error.
|
|
///
|
|
/// \return The newly initialized error, or an out of memory error.
|
|
kyua_error_t
|
|
kyua_error_new(const char* const type_name, void* const data,
|
|
const size_t data_size,
|
|
const kyua_error_format_callback format_callback)
|
|
{
|
|
assert(data != NULL || data_size == 0);
|
|
assert(data_size != 0 || data == NULL);
|
|
|
|
kyua_error_t error = malloc(sizeof(struct kyua_error));
|
|
if (error == NULL)
|
|
error = kyua_oom_error_new();
|
|
else {
|
|
if (!error_init(error, type_name, data, data_size, format_callback)) {
|
|
free(error);
|
|
error = kyua_oom_error_new();
|
|
} else {
|
|
error->needs_free = true;
|
|
}
|
|
}
|
|
|
|
assert(error != NULL);
|
|
return error;
|
|
}
|
|
|
|
|
|
/// Releases an error.
|
|
///
|
|
/// \param error The error object to release.
|
|
void
|
|
kyua_error_free(kyua_error_t error)
|
|
{
|
|
assert(error != NULL);
|
|
|
|
const bool needs_free = error->needs_free;
|
|
|
|
if (error->data != NULL)
|
|
free(error->data);
|
|
if (needs_free)
|
|
free(error);
|
|
}
|
|
|
|
|
|
/// Returns the "most important" of two errors.
|
|
///
|
|
/// "Most important" is defined as: the primary error is returned if set,
|
|
/// otherwise the secondary error is returned.
|
|
///
|
|
/// It is the responsibility of the caller to free the *resulting* error of this
|
|
/// call. The original errors passed in should not be consulted any longer,
|
|
/// because it is impossible to know which one was chosen.
|
|
///
|
|
/// \param primary The primary error to compare.
|
|
/// \param [in,out] secondary The secondary error to compare. This is freed if
|
|
/// the primary error is set.
|
|
///
|
|
/// \return Either primary or secondary.
|
|
kyua_error_t
|
|
kyua_error_subsume(kyua_error_t primary, kyua_error_t secondary)
|
|
{
|
|
if (kyua_error_is_set(primary)) {
|
|
if (kyua_error_is_set(secondary))
|
|
kyua_error_free(secondary);
|
|
return primary;
|
|
} else {
|
|
return secondary;
|
|
}
|
|
}
|
|
|
|
|
|
/// Constructor for a no-error condition.
|
|
///
|
|
/// \return Opaque representation of a no-error condition.
|
|
kyua_error_t
|
|
kyua_error_ok(void)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/// Checks if the given error object represents an error or not.
|
|
///
|
|
/// \param error The error to check.
|
|
///
|
|
/// \return True if the error is set.
|
|
bool
|
|
kyua_error_is_set(const kyua_error_t error)
|
|
{
|
|
return error != NULL;
|
|
}
|
|
|
|
|
|
/// Checks if the given error object is of a specific type.
|
|
///
|
|
/// \pre The error must be set.
|
|
///
|
|
/// \param error The error to check.
|
|
/// \param type_name The type of the expected error.
|
|
///
|
|
/// \return True if the error is of type type_name.
|
|
bool
|
|
kyua_error_is_type(const kyua_error_t error, const char* type_name)
|
|
{
|
|
assert(error != NULL);
|
|
|
|
return strcmp(error->type_name, type_name) == 0;
|
|
}
|
|
|
|
|
|
/// Returns a pointer to the error-specific data.
|
|
///
|
|
/// \pre The error must be set.
|
|
///
|
|
/// \param error The error to query.
|
|
///
|
|
/// \return An opaque pointer to the error data. This should only be
|
|
/// dereferenced by the methods of the error class that created it.
|
|
const void*
|
|
kyua_error_data(const kyua_error_t error)
|
|
{
|
|
assert(error != NULL);
|
|
|
|
return error->data;
|
|
}
|
|
|
|
|
|
/// Generates a user-friendly representation of the error.
|
|
///
|
|
/// This cannot fail, but it is possible that the generated error does not
|
|
/// fit in the provided buffer.
|
|
///
|
|
/// \pre The error must be set.
|
|
///
|
|
/// \param error Error for which to generate a message.
|
|
/// \param output_buffer Buffer to hold the generated message.
|
|
/// \param output_size Length of output_buffer.
|
|
///
|
|
/// \return The number of bytes written to output_buffer, or a negative value if
|
|
/// there was an error.
|
|
int
|
|
kyua_error_format(const kyua_error_t error, char* const output_buffer,
|
|
const size_t output_size)
|
|
{
|
|
assert(kyua_error_is_set(error));
|
|
return error->format_callback(error, output_buffer, output_size);
|
|
}
|
|
|
|
|
|
/// Formats a string and appends an error code to it.
|
|
///
|
|
/// \param error Error to append to the formatted message.
|
|
/// \param format User-specified message, as a formatting string.
|
|
/// \param ap List of arguments to the format string.
|
|
/// \param [out] output_buffer Buffer into which to write the message.
|
|
/// \param output_size Length of the output_buffer.
|
|
///
|
|
/// \return The number of bytes written to output_buffer, or a negative value if
|
|
/// there was an error.
|
|
static int
|
|
format_user_message(const kyua_error_t error, const char* format, va_list ap,
|
|
char* const output_buffer, const size_t output_size)
|
|
{
|
|
assert(kyua_error_is_set(error));
|
|
|
|
va_list ap2;
|
|
va_copy(ap2, ap);
|
|
size_t written = vsnprintf(output_buffer, output_size, format, ap2);
|
|
va_end(ap2);
|
|
if (written >= output_size)
|
|
return -1;
|
|
|
|
written += snprintf(output_buffer + written, output_size - written, ": ");
|
|
if (written >= output_size)
|
|
return -1;
|
|
|
|
return kyua_error_format(error, output_buffer + written,
|
|
output_size - written);
|
|
}
|
|
|
|
|
|
/// Version of err(3) that works with kyua_error_t objects.
|
|
///
|
|
/// \param exit_code Error code with which to terminate the execution.
|
|
/// \param error Error to append to the output.
|
|
/// \param format User-specified message, as a formatting string.
|
|
/// \param ... Positional arguments to the format string.
|
|
///
|
|
/// \post Execution terminates with exit_code.
|
|
void
|
|
kyua_error_err(const int exit_code, const kyua_error_t error,
|
|
const char* format, ...)
|
|
{
|
|
char buffer[2048];
|
|
|
|
va_list ap;
|
|
va_start(ap, format);
|
|
(void)format_user_message(error, format, ap, buffer, sizeof(buffer));
|
|
va_end(ap);
|
|
kyua_error_free(error);
|
|
|
|
errx(exit_code, "%s", buffer);
|
|
}
|
|
|
|
|
|
/// Writes an error to a file stream.
|
|
///
|
|
/// \param stream Stream to which to write the message.
|
|
/// \param error Error to append to the output. This is not released.
|
|
/// \param format User-specified message, as a formatting string.
|
|
/// \param ... Positional arguments to the format string.
|
|
void
|
|
kyua_error_fprintf(FILE* stream, const kyua_error_t error,
|
|
const char* format, ...)
|
|
{
|
|
char buffer[2048];
|
|
|
|
va_list ap;
|
|
va_start(ap, format);
|
|
(void)format_user_message(error, format, ap, buffer, sizeof(buffer));
|
|
va_end(ap);
|
|
|
|
fprintf(stream, "%s", buffer);
|
|
}
|
|
|
|
|
|
/// Version of warn(3) that works with kyua_error_t objects.
|
|
///
|
|
/// \param error Error to append to the output. This is not released.
|
|
/// \param format User-specified message, as a formatting string.
|
|
/// \param ... Positional arguments to the format string.
|
|
void
|
|
kyua_error_warn(const kyua_error_t error, const char* format, ...)
|
|
{
|
|
char buffer[2048];
|
|
|
|
va_list ap;
|
|
va_start(ap, format);
|
|
(void)format_user_message(error, format, ap, buffer, sizeof(buffer));
|
|
va_end(ap);
|
|
|
|
warnx("%s", buffer);
|
|
}
|
|
|
|
|
|
/// Name of an generic error type.
|
|
const char* const kyua_generic_error_type = "generic";
|
|
|
|
|
|
/// Generates a user-friendly representation of the error.
|
|
///
|
|
/// \pre The error must be set.
|
|
///
|
|
/// \param error Error for which to generate a message.
|
|
/// \param output_buffer Buffer to hold the generated message.
|
|
/// \param output_size Length of output_buffer.
|
|
///
|
|
/// \return The number of bytes written to output_buffer, or a negative value if
|
|
/// there was an error.
|
|
static int
|
|
generic_format(const kyua_error_t error, char* const output_buffer,
|
|
const size_t output_size)
|
|
{
|
|
assert(kyua_error_is_type(error, kyua_generic_error_type));
|
|
|
|
const char* message = kyua_error_data(error);
|
|
return snprintf(output_buffer, output_size, "%s", message);
|
|
}
|
|
|
|
|
|
/// Constructs a new generic error.
|
|
///
|
|
/// \param message Textual description of the problem.
|
|
/// \param ... Positional arguments for the description.
|
|
///
|
|
/// \return The generated error.
|
|
kyua_error_t
|
|
kyua_generic_error_new(const char* message, ...)
|
|
{
|
|
char formatted[1024];
|
|
va_list ap;
|
|
|
|
va_start(ap, message);
|
|
(void)vsnprintf(formatted, sizeof(formatted), message, ap);
|
|
va_end(ap);
|
|
|
|
return kyua_error_new(kyua_generic_error_type, formatted, sizeof(formatted),
|
|
generic_format);
|
|
}
|
|
|
|
|
|
/// Name of a libc type.
|
|
const char* const kyua_libc_error_type = "libc";
|
|
|
|
|
|
/// Representation of a libc error.
|
|
struct libc_error_data {
|
|
/// Value of the errno captured during the error creation.
|
|
int original_errno;
|
|
|
|
/// Explanation of the problem that lead to the error.
|
|
char description[4096];
|
|
};
|
|
/// Shorthand for a libc_error_data structure.
|
|
typedef struct libc_error_data libc_error_data_t;
|
|
|
|
|
|
/// Generates a user-friendly representation of the error.
|
|
///
|
|
/// \pre The error must be set.
|
|
///
|
|
/// \param error Error for which to generate a message.
|
|
/// \param output_buffer Buffer to hold the generated message.
|
|
/// \param output_size Length of output_buffer.
|
|
///
|
|
/// \return The number of bytes written to output_buffer, or a negative value if
|
|
/// there was an error.
|
|
static int
|
|
libc_format(const kyua_error_t error, char* const output_buffer,
|
|
const size_t output_size)
|
|
{
|
|
assert(kyua_error_is_type(error, kyua_libc_error_type));
|
|
|
|
const libc_error_data_t* data = kyua_error_data(error);
|
|
return snprintf(output_buffer, output_size, "%s: %s", data->description,
|
|
strerror(data->original_errno));
|
|
}
|
|
|
|
|
|
/// Constructs a new libc error.
|
|
///
|
|
/// \param original_errno libc error code for this error.
|
|
/// \param description Textual description of the problem.
|
|
/// \param ... Positional arguments for the description.
|
|
///
|
|
/// \return The generated error.
|
|
kyua_error_t
|
|
kyua_libc_error_new(const int original_errno, const char* description, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
const size_t data_size = sizeof(libc_error_data_t);
|
|
libc_error_data_t* data = (libc_error_data_t*)malloc(data_size);
|
|
if (data == NULL)
|
|
return kyua_oom_error_new();
|
|
|
|
data->original_errno = original_errno;
|
|
va_start(ap, description);
|
|
(void)vsnprintf(data->description, sizeof(data->description),
|
|
description, ap);
|
|
va_end(ap);
|
|
|
|
return kyua_error_new(kyua_libc_error_type, data, data_size, libc_format);
|
|
}
|
|
|
|
|
|
/// Extracts the original errno of a libc error.
|
|
///
|
|
/// \pre error must have been constructed by kyua_libc_error_new.
|
|
///
|
|
/// \param error The error object to access.
|
|
///
|
|
/// \return The libc error code.
|
|
int
|
|
kyua_libc_error_errno(const kyua_error_t error)
|
|
{
|
|
assert(kyua_error_is_type(error, kyua_libc_error_type));
|
|
|
|
const struct libc_error_data* data = kyua_error_data(error);
|
|
return data->original_errno;
|
|
}
|
|
|
|
|
|
/// Name of an OOM type.
|
|
const char* const kyua_oom_error_type = "oom";
|
|
|
|
|
|
/// Data of an out of memory error.
|
|
///
|
|
/// All error types are allocated in dynamic memory. However, doing so for
|
|
/// an out of memory error is not possible because, when we are out of
|
|
/// memory, we probably cannot allocate more memory to generate an error.
|
|
/// Therefore, we just keep a single static instance of the out of memory
|
|
/// error around all the time.
|
|
static struct kyua_error oom_error;
|
|
|
|
|
|
/// Generates a user-friendly representation of the error.
|
|
///
|
|
/// \pre The error must be set.
|
|
///
|
|
/// \param error Error for which to generate a message.
|
|
/// \param output_buffer Buffer to hold the generated message.
|
|
/// \param output_size Length of output_buffer.
|
|
///
|
|
/// \return The number of bytes written to output_buffer, or a negative value if
|
|
/// there was an error.
|
|
static int
|
|
oom_format(const kyua_error_t error, char* const output_buffer,
|
|
const size_t output_size)
|
|
{
|
|
assert(kyua_error_is_type(error, kyua_oom_error_type));
|
|
|
|
return snprintf(output_buffer, output_size, "Not enough memory");
|
|
}
|
|
|
|
|
|
/// Constructs a new out-of-memory error.
|
|
///
|
|
/// This will always succeed because we just return a reference to the
|
|
/// statically-allocated oom_error.
|
|
///
|
|
/// \return An error representing an out of memory condition.
|
|
kyua_error_t
|
|
kyua_oom_error_new(void)
|
|
{
|
|
// This is idempotent; no need to ensure that we call it only once.
|
|
#if defined(__minix) && !defined(NDEBUG)
|
|
const bool ok =
|
|
#endif /* defined(__minix) && !defined(NDEBUG) */
|
|
error_init(&oom_error, kyua_oom_error_type, NULL, 0, oom_format);
|
|
assert(ok);
|
|
|
|
return &oom_error;
|
|
}
|
|
|
|
|
|
/// Name of an usage error type.
|
|
const char* const kyua_usage_error_type = "usage";
|
|
|
|
|
|
/// Generates a user-friendly representation of the error.
|
|
///
|
|
/// \pre The error must be set.
|
|
///
|
|
/// \param error Error for which to generate a message.
|
|
/// \param output_buffer Buffer to hold the generated message.
|
|
/// \param output_size Length of output_buffer.
|
|
///
|
|
/// \return The number of bytes written to output_buffer, or a negative value if
|
|
/// there was an error.
|
|
static int
|
|
usage_format(const kyua_error_t error, char* const output_buffer,
|
|
const size_t output_size)
|
|
{
|
|
assert(kyua_error_is_type(error, kyua_usage_error_type));
|
|
|
|
const char* message = kyua_error_data(error);
|
|
return snprintf(output_buffer, output_size, "%s", message);
|
|
}
|
|
|
|
|
|
/// Constructs a new usage error.
|
|
///
|
|
/// \param message Textual description of the problem.
|
|
/// \param ... Positional arguments for the description.
|
|
///
|
|
/// \return The generated error.
|
|
kyua_error_t
|
|
kyua_usage_error_new(const char* message, ...)
|
|
{
|
|
char formatted[1024];
|
|
va_list ap;
|
|
|
|
va_start(ap, message);
|
|
(void)vsnprintf(formatted, sizeof(formatted), message, ap);
|
|
va_end(ap);
|
|
|
|
return kyua_error_new(kyua_usage_error_type, formatted, sizeof(formatted),
|
|
usage_format);
|
|
}
|