1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26#include "libuutil_common.h"
27
28#define HAVE_ASSFAIL	1
29
30#include <assert.h>
31#include <errno.h>
32#include <libintl.h>
33#include <pthread.h>
34#include <stdarg.h>
35#include <stdio.h>
36#include <stdlib.h>
37#include <string.h>
38#include <sys/debug.h>
39#include <thread.h>
40#include <unistd.h>
41#include <ctype.h>
42
43#if !defined(TEXT_DOMAIN)
44#define	TEXT_DOMAIN "SYS_TEST"
45#endif
46
47/*
48 * All of the old code under !defined(PTHREAD_ONCE_KEY_NP)
49 * is here to enable the building of a native version of
50 * libuutil.so when the build machine has not yet been upgraded
51 * to a version of libc that provides pthread_key_create_once_np().
52 * It should all be deleted when solaris_nevada ships.
53 * The code is not MT-safe in a relaxed memory model.
54 */
55
56#if defined(PTHREAD_ONCE_KEY_NP)
57static pthread_key_t	uu_error_key = PTHREAD_ONCE_KEY_NP;
58#else	/* PTHREAD_ONCE_KEY_NP */
59static pthread_key_t	uu_error_key = 0;
60static pthread_mutex_t	uu_key_lock = PTHREAD_MUTEX_INITIALIZER;
61#endif	/* PTHREAD_ONCE_KEY_NP */
62
63static int		uu_error_key_setup = 0;
64
65static pthread_mutex_t	uu_panic_lock = PTHREAD_MUTEX_INITIALIZER;
66/* LINTED static unused */
67static const char	*uu_panic_format;
68/* LINTED static unused */
69static va_list		uu_panic_args;
70static pthread_t	uu_panic_thread;
71
72static uint32_t		_uu_main_error;
73
74void
75uu_set_error(uint_t code)
76{
77
78#if defined(PTHREAD_ONCE_KEY_NP)
79	if (pthread_key_create_once_np(&uu_error_key, NULL) != 0)
80		uu_error_key_setup = -1;
81	else
82		uu_error_key_setup = 1;
83#else	/* PTHREAD_ONCE_KEY_NP */
84	if (uu_error_key_setup == 0) {
85		(void) pthread_mutex_lock(&uu_key_lock);
86		if (uu_error_key_setup == 0) {
87			if (pthread_key_create(&uu_error_key, NULL) != 0)
88				uu_error_key_setup = -1;
89			else
90				uu_error_key_setup = 1;
91		}
92		(void) pthread_mutex_unlock(&uu_key_lock);
93	}
94#endif	/* PTHREAD_ONCE_KEY_NP */
95	if (uu_error_key_setup > 0)
96		(void) pthread_setspecific(uu_error_key,
97		    (void *)(uintptr_t)code);
98}
99
100uint32_t
101uu_error(void)
102{
103
104	if (uu_error_key_setup < 0)	/* can't happen? */
105		return (UU_ERROR_UNKNOWN);
106
107	/*
108	 * Because UU_ERROR_NONE == 0, if uu_set_error() was
109	 * never called, then this will return UU_ERROR_NONE:
110	 */
111	return ((uint32_t)(uintptr_t)pthread_getspecific(uu_error_key));
112}
113
114const char *
115uu_strerror(uint32_t code)
116{
117	const char *str;
118
119	switch (code) {
120	case UU_ERROR_NONE:
121		str = dgettext(TEXT_DOMAIN, "No error");
122		break;
123
124	case UU_ERROR_INVALID_ARGUMENT:
125		str = dgettext(TEXT_DOMAIN, "Invalid argument");
126		break;
127
128	case UU_ERROR_UNKNOWN_FLAG:
129		str = dgettext(TEXT_DOMAIN, "Unknown flag passed");
130		break;
131
132	case UU_ERROR_NO_MEMORY:
133		str = dgettext(TEXT_DOMAIN, "Out of memory");
134		break;
135
136	case UU_ERROR_CALLBACK_FAILED:
137		str = dgettext(TEXT_DOMAIN, "Callback-initiated failure");
138		break;
139
140	case UU_ERROR_NOT_SUPPORTED:
141		str = dgettext(TEXT_DOMAIN, "Operation not supported");
142		break;
143
144	case UU_ERROR_EMPTY:
145		str = dgettext(TEXT_DOMAIN, "No value provided");
146		break;
147
148	case UU_ERROR_UNDERFLOW:
149		str = dgettext(TEXT_DOMAIN, "Value too small");
150		break;
151
152	case UU_ERROR_OVERFLOW:
153		str = dgettext(TEXT_DOMAIN, "Value too large");
154		break;
155
156	case UU_ERROR_INVALID_CHAR:
157		str = dgettext(TEXT_DOMAIN,
158		    "Value contains unexpected character");
159		break;
160
161	case UU_ERROR_INVALID_DIGIT:
162		str = dgettext(TEXT_DOMAIN,
163		    "Value contains digit not in base");
164		break;
165
166	case UU_ERROR_SYSTEM:
167		str = dgettext(TEXT_DOMAIN, "Underlying system error");
168		break;
169
170	case UU_ERROR_UNKNOWN:
171		str = dgettext(TEXT_DOMAIN, "Error status not known");
172		break;
173
174	default:
175		errno = ESRCH;
176		str = NULL;
177		break;
178	}
179	return (str);
180}
181
182void
183uu_panic(const char *format, ...)
184{
185	va_list args;
186
187	va_start(args, format);
188
189	(void) pthread_mutex_lock(&uu_panic_lock);
190	if (uu_panic_thread == 0) {
191		uu_panic_thread = pthread_self();
192		uu_panic_format = format;
193		va_copy(uu_panic_args, args);
194	}
195	(void) pthread_mutex_unlock(&uu_panic_lock);
196
197	(void) vfprintf(stderr, format, args);
198
199	if (uu_panic_thread == pthread_self())
200		abort();
201	else
202		for (;;)
203			(void) pause();
204}
205
206int
207assfail(const char *astring, const char *file, int line)
208{
209	__assert(astring, file, line);
210	/*NOTREACHED*/
211	return (0);
212}
213
214static void
215uu_lockup(void)
216{
217	(void) pthread_mutex_lock(&uu_panic_lock);
218#if !defined(PTHREAD_ONCE_KEY_NP)
219	(void) pthread_mutex_lock(&uu_key_lock);
220#endif
221	uu_avl_lockup();
222	uu_list_lockup();
223}
224
225static void
226uu_release(void)
227{
228	(void) pthread_mutex_unlock(&uu_panic_lock);
229#if !defined(PTHREAD_ONCE_KEY_NP)
230	(void) pthread_mutex_unlock(&uu_key_lock);
231#endif
232	uu_avl_release();
233	uu_list_release();
234}
235
236static void
237uu_release_child(void)
238{
239	uu_panic_format = NULL;
240	uu_panic_thread = 0;
241
242	uu_release();
243}
244
245#pragma init(uu_init)
246static void
247uu_init(void)
248{
249	(void) pthread_atfork(uu_lockup, uu_release, uu_release_child);
250}
251
252/*
253 * Dump a block of memory in hex+ascii, for debugging
254 */
255void
256uu_dump(FILE *out, const char *prefix, const void *buf, size_t len)
257{
258	const unsigned char *p = buf;
259	int i;
260
261	for (i = 0; i < len; i += 16) {
262		int j;
263
264		(void) fprintf(out, "%s", prefix);
265		for (j = 0; j < 16 && i + j < len; j++) {
266			(void) fprintf(out, "%2.2x ", p[i + j]);
267		}
268		for (; j < 16; j++) {
269			(void) fprintf(out, "   ");
270		}
271		for (j = 0; j < 16 && i + j < len; j++) {
272			(void) fprintf(out, "%c",
273			    isprint(p[i + j]) ? p[i + j] : '.');
274		}
275		(void) fprintf(out, "\n");
276	}
277}
278