281 lines
6.2 KiB
C
281 lines
6.2 KiB
C
/*
|
|
* 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) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
|
|
*/
|
|
|
|
#include "libuutil_common.h"
|
|
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <libintl.h>
|
|
#include <pthread.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/debug.h>
|
|
#include <unistd.h>
|
|
#include <ctype.h>
|
|
|
|
#if !defined(TEXT_DOMAIN)
|
|
#define TEXT_DOMAIN "SYS_TEST"
|
|
#endif
|
|
|
|
/*
|
|
* All of the old code under !defined(PTHREAD_ONCE_KEY_NP)
|
|
* is here to enable the building of a native version of
|
|
* libuutil.so when the build machine has not yet been upgraded
|
|
* to a version of libc that provides pthread_key_create_once_np().
|
|
* It should all be deleted when solaris_nevada ships.
|
|
* The code is not MT-safe in a relaxed memory model.
|
|
*/
|
|
|
|
#if defined(PTHREAD_ONCE_KEY_NP)
|
|
static pthread_key_t uu_error_key = PTHREAD_ONCE_KEY_NP;
|
|
#else /* PTHREAD_ONCE_KEY_NP */
|
|
static pthread_key_t uu_error_key = 0;
|
|
static pthread_mutex_t uu_key_lock = PTHREAD_MUTEX_INITIALIZER;
|
|
#endif /* PTHREAD_ONCE_KEY_NP */
|
|
|
|
static int uu_error_key_setup = 0;
|
|
|
|
static pthread_mutex_t uu_panic_lock = PTHREAD_MUTEX_INITIALIZER;
|
|
/* LINTED static unused */
|
|
static const char *uu_panic_format;
|
|
/* LINTED static unused */
|
|
static va_list uu_panic_args;
|
|
static pthread_t uu_panic_thread;
|
|
|
|
static uint32_t _uu_main_error;
|
|
static __thread int _uu_main_thread = 0;
|
|
|
|
void
|
|
uu_set_error(uint_t code)
|
|
{
|
|
if (_uu_main_thread) {
|
|
_uu_main_error = code;
|
|
return;
|
|
}
|
|
#if defined(PTHREAD_ONCE_KEY_NP)
|
|
if (pthread_key_create_once_np(&uu_error_key, NULL) != 0)
|
|
uu_error_key_setup = -1;
|
|
else
|
|
uu_error_key_setup = 1;
|
|
#else /* PTHREAD_ONCE_KEY_NP */
|
|
if (uu_error_key_setup == 0) {
|
|
(void) pthread_mutex_lock(&uu_key_lock);
|
|
if (uu_error_key_setup == 0) {
|
|
if (pthread_key_create(&uu_error_key, NULL) != 0)
|
|
uu_error_key_setup = -1;
|
|
else
|
|
uu_error_key_setup = 1;
|
|
}
|
|
(void) pthread_mutex_unlock(&uu_key_lock);
|
|
}
|
|
#endif /* PTHREAD_ONCE_KEY_NP */
|
|
if (uu_error_key_setup > 0)
|
|
(void) pthread_setspecific(uu_error_key,
|
|
(void *)(uintptr_t)code);
|
|
}
|
|
|
|
uint32_t
|
|
uu_error(void)
|
|
{
|
|
if (_uu_main_thread)
|
|
return (_uu_main_error);
|
|
|
|
if (uu_error_key_setup < 0) /* can't happen? */
|
|
return (UU_ERROR_UNKNOWN);
|
|
|
|
/*
|
|
* Because UU_ERROR_NONE == 0, if uu_set_error() was
|
|
* never called, then this will return UU_ERROR_NONE:
|
|
*/
|
|
return ((uint32_t)(uintptr_t)pthread_getspecific(uu_error_key));
|
|
}
|
|
|
|
const char *
|
|
uu_strerror(uint32_t code)
|
|
{
|
|
const char *str;
|
|
|
|
switch (code) {
|
|
case UU_ERROR_NONE:
|
|
str = dgettext(TEXT_DOMAIN, "No error");
|
|
break;
|
|
|
|
case UU_ERROR_INVALID_ARGUMENT:
|
|
str = dgettext(TEXT_DOMAIN, "Invalid argument");
|
|
break;
|
|
|
|
case UU_ERROR_UNKNOWN_FLAG:
|
|
str = dgettext(TEXT_DOMAIN, "Unknown flag passed");
|
|
break;
|
|
|
|
case UU_ERROR_NO_MEMORY:
|
|
str = dgettext(TEXT_DOMAIN, "Out of memory");
|
|
break;
|
|
|
|
case UU_ERROR_CALLBACK_FAILED:
|
|
str = dgettext(TEXT_DOMAIN, "Callback-initiated failure");
|
|
break;
|
|
|
|
case UU_ERROR_NOT_SUPPORTED:
|
|
str = dgettext(TEXT_DOMAIN, "Operation not supported");
|
|
break;
|
|
|
|
case UU_ERROR_EMPTY:
|
|
str = dgettext(TEXT_DOMAIN, "No value provided");
|
|
break;
|
|
|
|
case UU_ERROR_UNDERFLOW:
|
|
str = dgettext(TEXT_DOMAIN, "Value too small");
|
|
break;
|
|
|
|
case UU_ERROR_OVERFLOW:
|
|
str = dgettext(TEXT_DOMAIN, "Value too large");
|
|
break;
|
|
|
|
case UU_ERROR_INVALID_CHAR:
|
|
str = dgettext(TEXT_DOMAIN,
|
|
"Value contains unexpected character");
|
|
break;
|
|
|
|
case UU_ERROR_INVALID_DIGIT:
|
|
str = dgettext(TEXT_DOMAIN,
|
|
"Value contains digit not in base");
|
|
break;
|
|
|
|
case UU_ERROR_SYSTEM:
|
|
str = dgettext(TEXT_DOMAIN, "Underlying system error");
|
|
break;
|
|
|
|
case UU_ERROR_UNKNOWN:
|
|
str = dgettext(TEXT_DOMAIN, "Error status not known");
|
|
break;
|
|
|
|
default:
|
|
errno = ESRCH;
|
|
str = NULL;
|
|
break;
|
|
}
|
|
return (str);
|
|
}
|
|
|
|
void
|
|
uu_panic(const char *format, ...)
|
|
{
|
|
va_list args;
|
|
|
|
va_start(args, format);
|
|
|
|
(void) pthread_mutex_lock(&uu_panic_lock);
|
|
if (uu_panic_thread == 0) {
|
|
uu_panic_thread = pthread_self();
|
|
uu_panic_format = format;
|
|
va_copy(uu_panic_args, args);
|
|
}
|
|
(void) pthread_mutex_unlock(&uu_panic_lock);
|
|
|
|
(void) vfprintf(stderr, format, args);
|
|
|
|
va_end(args);
|
|
|
|
if (uu_panic_thread == pthread_self())
|
|
abort();
|
|
else
|
|
for (;;)
|
|
(void) pause();
|
|
}
|
|
|
|
static void
|
|
uu_lockup(void)
|
|
{
|
|
(void) pthread_mutex_lock(&uu_panic_lock);
|
|
#if !defined(PTHREAD_ONCE_KEY_NP)
|
|
(void) pthread_mutex_lock(&uu_key_lock);
|
|
#endif
|
|
uu_avl_lockup();
|
|
uu_list_lockup();
|
|
}
|
|
|
|
static void
|
|
uu_release(void)
|
|
{
|
|
(void) pthread_mutex_unlock(&uu_panic_lock);
|
|
#if !defined(PTHREAD_ONCE_KEY_NP)
|
|
(void) pthread_mutex_unlock(&uu_key_lock);
|
|
#endif
|
|
uu_avl_release();
|
|
uu_list_release();
|
|
}
|
|
|
|
static void
|
|
uu_release_child(void)
|
|
{
|
|
uu_panic_format = NULL;
|
|
uu_panic_thread = 0;
|
|
|
|
uu_release();
|
|
}
|
|
|
|
#ifdef __GNUC__
|
|
static void
|
|
uu_init(void) __attribute__((constructor));
|
|
#else
|
|
#pragma init(uu_init)
|
|
#endif
|
|
|
|
static void
|
|
uu_init(void)
|
|
{
|
|
_uu_main_thread = 1;
|
|
(void) pthread_atfork(uu_lockup, uu_release, uu_release_child);
|
|
}
|
|
|
|
/*
|
|
* Dump a block of memory in hex+ascii, for debugging
|
|
*/
|
|
void
|
|
uu_dump(FILE *out, const char *prefix, const void *buf, size_t len)
|
|
{
|
|
const unsigned char *p = buf;
|
|
int i;
|
|
|
|
for (i = 0; i < len; i += 16) {
|
|
int j;
|
|
|
|
(void) fprintf(out, "%s", prefix);
|
|
for (j = 0; j < 16 && i + j < len; j++) {
|
|
(void) fprintf(out, "%2.2x ", p[i + j]);
|
|
}
|
|
for (; j < 16; j++) {
|
|
(void) fprintf(out, " ");
|
|
}
|
|
for (j = 0; j < 16 && i + j < len; j++) {
|
|
(void) fprintf(out, "%c",
|
|
isprint(p[i + j]) ? p[i + j] : '.');
|
|
}
|
|
(void) fprintf(out, "\n");
|
|
}
|
|
}
|