error_message.c revision d1154eb460efe588eaed3d439c1caaca149fa362
1/*
2 * $Header$
3 * $Source$
4 * $Locker$
5 *
6 * Copyright 1987 by the Student Information Processing Board
7 * of the Massachusetts Institute of Technology
8 *
9 * Permission to use, copy, modify, and distribute this software and
10 * its documentation for any purpose is hereby granted, provided that
11 * the names of M.I.T. and the M.I.T. S.I.P.B. not be used in
12 * advertising or publicity pertaining to distribution of the software
13 * without specific, written prior permission.  M.I.T. and the
14 * M.I.T. S.I.P.B. make no representations about the suitability of
15 * this software for any purpose.  It is provided "as is" without
16 * express or implied warranty.
17 */
18
19#include "config.h"
20#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
23#include <errno.h>
24#ifdef HAVE_SYS_PRCTL_H
25#include <sys/prctl.h>
26#else
27#define PR_GET_DUMPABLE 3
28#endif
29#if (!defined(HAVE_PRCTL) && defined(linux))
30#include <sys/syscall.h>
31#endif
32#ifdef HAVE_SEMAPHORE_H
33#include <semaphore.h>
34#endif
35#if HAVE_UNISTD_H
36#include <unistd.h>
37#endif
38#include <fcntl.h>
39#if HAVE_SYS_TYPES_H
40#include <sys/types.h>
41#endif
42#include "com_err.h"
43#include "error_table.h"
44#include "internal.h"
45
46#ifdef TLS
47#define THREAD_LOCAL static TLS
48#else
49#define THREAD_LOCAL static
50#endif
51
52THREAD_LOCAL char buffer[25];
53
54struct et_list * _et_list = (struct et_list *) NULL;
55struct et_list * _et_dynamic_list = (struct et_list *) NULL;
56
57#ifdef __GNUC__
58#define COMERR_ATTR(x) __attribute__(x)
59#else
60#define COMERR_ATTR(x)
61#endif
62
63#ifdef HAVE_SEM_INIT
64static sem_t _et_lock;
65static int _et_lock_initialized;
66
67static void COMERR_ATTR((constructor)) setup_et_lock(void)
68{
69	sem_init(&_et_lock, 0, 1);
70	_et_lock_initialized = 1;
71}
72
73static void COMERR_ATTR((destructor)) fini_et_lock(void)
74{
75	sem_destroy(&_et_lock);
76	_et_lock_initialized = 0;
77}
78#endif
79
80
81int et_list_lock(void)
82{
83#ifdef HAVE_SEM_INIT
84	if (!_et_lock_initialized)
85		setup_et_lock();
86	return sem_wait(&_et_lock);
87#else
88	return 0;
89#endif
90}
91
92int et_list_unlock(void)
93{
94#ifdef HAVE_SEM_INIT
95	if (_et_lock_initialized)
96		return sem_post(&_et_lock);
97#endif
98	return 0;
99}
100
101const char * error_message (errcode_t code)
102{
103    int offset;
104    struct et_list *et;
105    errcode_t table_num;
106    int started = 0;
107    char *cp;
108
109    offset = (int) (code & ((1<<ERRCODE_RANGE)-1));
110    table_num = code - offset;
111    if (!table_num) {
112#ifdef HAS_SYS_ERRLIST
113	if (offset < sys_nerr)
114	    return(sys_errlist[offset]);
115	else
116	    goto oops;
117#else
118	cp = strerror(offset);
119	if (cp)
120	    return(cp);
121	else
122	    goto oops;
123#endif
124    }
125    et_list_lock();
126    for (et = _et_list; et; et = et->next) {
127	if ((et->table->base & 0xffffffL) == (table_num & 0xffffffL)) {
128	    /* This is the right table */
129	    if (et->table->n_msgs <= offset) {
130		break;
131	    } else {
132		const char *msg = et->table->msgs[offset];
133		et_list_unlock();
134		return msg;
135	    }
136	}
137    }
138    for (et = _et_dynamic_list; et; et = et->next) {
139	if ((et->table->base & 0xffffffL) == (table_num & 0xffffffL)) {
140	    /* This is the right table */
141	    if (et->table->n_msgs <= offset) {
142		break;
143	    } else {
144		const char *msg = et->table->msgs[offset];
145		et_list_unlock();
146		return msg;
147	    }
148	}
149    }
150    et_list_unlock();
151oops:
152    strcpy (buffer, "Unknown code ");
153    if (table_num) {
154	strcat (buffer, error_table_name (table_num));
155	strcat (buffer, " ");
156    }
157    for (cp = buffer; *cp; cp++)
158	;
159    if (offset >= 100) {
160	*cp++ = '0' + offset / 100;
161	offset %= 100;
162	started++;
163    }
164    if (started || offset >= 10) {
165	*cp++ = '0' + offset / 10;
166	offset %= 10;
167    }
168    *cp++ = '0' + offset;
169    *cp = '\0';
170    return(buffer);
171}
172
173/*
174 * This routine will only return a value if the we are not running as
175 * a privileged process.
176 */
177static char *safe_getenv(const char *arg)
178{
179	if ((getuid() != geteuid()) || (getgid() != getegid()))
180		return NULL;
181#if HAVE_PRCTL
182	if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 0)
183		return NULL;
184#else
185#if (defined(linux) && defined(SYS_prctl))
186	if (syscall(SYS_prctl, PR_GET_DUMPABLE, 0, 0, 0, 0) == 0)
187		return NULL;
188#endif
189#endif
190
191#ifdef HAVE___SECURE_GETENV
192	return __secure_getenv(arg);
193#else
194	return getenv(arg);
195#endif
196}
197
198#define DEBUG_INIT	0x8000
199#define DEBUG_ADDREMOVE 0x0001
200
201static int debug_mask = 0;
202static FILE *debug_f = 0;
203
204static void init_debug(void)
205{
206	char	*dstr, *fn, *tmp;
207	int	fd, flags;
208
209	if (debug_mask & DEBUG_INIT)
210		return;
211
212	dstr = getenv("COMERR_DEBUG");
213	if (dstr) {
214		debug_mask = strtoul(dstr, &tmp, 0);
215		if (*tmp || errno)
216			debug_mask = 0;
217	}
218
219	debug_mask |= DEBUG_INIT;
220	if (debug_mask == DEBUG_INIT)
221		return;
222
223	fn = safe_getenv("COMERR_DEBUG_FILE");
224	if (fn)
225		debug_f = fopen(fn, "a");
226	if (!debug_f)
227		debug_f = fopen("/dev/tty", "a");
228	if (debug_f) {
229		fd = fileno(debug_f);
230		if (fd >= 0) {
231			flags = fcntl(fd, F_GETFD);
232			if (flags >= 0)
233				fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
234		}
235	} else
236		debug_mask = DEBUG_INIT;
237
238}
239
240/*
241 * New interface provided by krb5's com_err library
242 */
243errcode_t add_error_table(const struct error_table * et)
244{
245	struct et_list *el;
246
247	if (!(el = (struct et_list *) malloc(sizeof(struct et_list))))
248		return ENOMEM;
249
250	if (et_list_lock() != 0) {
251		free(el);
252		return errno;
253	}
254
255	el->table = et;
256	el->next = _et_dynamic_list;
257	_et_dynamic_list = el;
258
259	init_debug();
260	if (debug_mask & DEBUG_ADDREMOVE)
261		fprintf(debug_f, "add_error_table: %s (0x%p)\n",
262			error_table_name(et->base),
263			(const void *) et);
264
265	et_list_unlock();
266	return 0;
267}
268
269/*
270 * New interface provided by krb5's com_err library
271 */
272errcode_t remove_error_table(const struct error_table * et)
273{
274	struct et_list *el;
275	struct et_list *el2 = 0;
276
277	if (et_list_lock() != 0)
278		return ENOENT;
279
280	el = _et_dynamic_list;
281	init_debug();
282	while (el) {
283		if (el->table->base == et->base) {
284			if (el2)	/* Not the beginning of the list */
285				el2->next = el->next;
286			else
287				_et_dynamic_list = el->next;
288			(void) free(el);
289			if (debug_mask & DEBUG_ADDREMOVE)
290				fprintf(debug_f,
291					"remove_error_table: %s (0x%p)\n",
292					error_table_name(et->base),
293					(const void *) et);
294			et_list_unlock();
295			return 0;
296		}
297		el2 = el;
298		el = el->next;
299	}
300	if (debug_mask & DEBUG_ADDREMOVE)
301		fprintf(debug_f, "remove_error_table FAILED: %s (0x%p)\n",
302			error_table_name(et->base),
303			(const void *) et);
304	et_list_unlock();
305	return ENOENT;
306}
307
308/*
309 * Variant of the interface provided by Heimdal's com_err library
310 */
311void
312add_to_error_table(struct et_list *new_table)
313{
314	add_error_table(new_table->table);
315}
316