| /* |
| * 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"); |
| } |
| } |