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