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