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