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