1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 /* 3 * Copyright 1997,2000,2001,2004,2008 by Massachusetts Institute of Technology 4 * 5 * Copyright 1987, 1988 by MIT Student Information Processing Board 6 * 7 * Permission to use, copy, modify, and distribute this software 8 * and its documentation for any purpose and without fee is 9 * hereby granted, provided that the above copyright notice 10 * appear in all copies and that both that copyright notice and 11 * this permission notice appear in supporting documentation, 12 * and that the names of M.I.T. and the M.I.T. S.I.P.B. not be 13 * used in advertising or publicity pertaining to distribution 14 * of the software without specific, written prior permission. 15 * Furthermore if you modify this software you must label 16 * your software as modified software and not distribute it in such a 17 * fashion that it might be confused with the original M.I.T. software. 18 * M.I.T. and the M.I.T. S.I.P.B. make no representations about 19 * the suitability of this software for any purpose. It is 20 * provided "as is" without express or implied warranty. 21 */ 22 23 #include "k5-platform.h" 24 #include "com_err.h" 25 #include "error_table.h" 26 27 static struct et_list *et_list; 28 static k5_mutex_t et_list_lock = K5_MUTEX_PARTIAL_INITIALIZER; 29 static int terminated = 0; /* for safety and finalization debugging */ 30 31 MAKE_INIT_FUNCTION(com_err_initialize); 32 MAKE_FINI_FUNCTION(com_err_terminate); 33 34 int com_err_initialize(void) 35 { 36 int err; 37 #ifdef SHOW_INITFINI_FUNCS 38 printf("com_err_initialize\n"); 39 #endif 40 terminated = 0; 41 err = k5_mutex_finish_init(&et_list_lock); 42 if (err) 43 return err; 44 err = k5_mutex_finish_init(&com_err_hook_lock); 45 if (err) 46 return err; 47 err = k5_key_register(K5_KEY_COM_ERR, free); 48 if (err) 49 return err; 50 return 0; 51 } 52 53 void com_err_terminate(void) 54 { 55 struct et_list *e, *enext; 56 if (! INITIALIZER_RAN(com_err_initialize) || PROGRAM_EXITING()) { 57 #ifdef SHOW_INITFINI_FUNCS 58 printf("com_err_terminate: skipping\n"); 59 #endif 60 return; 61 } 62 #ifdef SHOW_INITFINI_FUNCS 63 printf("com_err_terminate\n"); 64 #endif 65 k5_key_delete(K5_KEY_COM_ERR); 66 k5_mutex_destroy(&com_err_hook_lock); 67 k5_mutex_lock(&et_list_lock); 68 for (e = et_list; e; e = enext) { 69 enext = e->next; 70 free(e); 71 } 72 et_list = NULL; 73 k5_mutex_unlock(&et_list_lock); 74 k5_mutex_destroy(&et_list_lock); 75 terminated = 1; 76 } 77 78 #ifndef DEBUG_TABLE_LIST 79 #define dprintf(X) 80 #else 81 #define dprintf(X) printf X 82 #endif 83 84 static char * 85 get_thread_buffer () 86 { 87 char *cp; 88 cp = k5_getspecific(K5_KEY_COM_ERR); 89 if (cp == NULL) { 90 cp = malloc(ET_EBUFSIZ); 91 if (cp == NULL) { 92 return NULL; 93 } 94 if (k5_setspecific(K5_KEY_COM_ERR, cp) != 0) { 95 free(cp); 96 return NULL; 97 } 98 } 99 return cp; 100 } 101 102 const char * KRB5_CALLCONV 103 error_message(long code) 104 { 105 unsigned long offset; 106 unsigned long l_offset; 107 struct et_list *e; 108 unsigned long table_num; 109 int started = 0; 110 unsigned int divisor = 100; 111 char *cp, *cp1; 112 const struct error_table *table; 113 114 if (CALL_INIT_FUNCTION(com_err_initialize)) 115 return 0; 116 117 l_offset = (unsigned long)code & ((1<<ERRCODE_RANGE)-1); 118 offset = l_offset; 119 table_num = ((unsigned long)code - l_offset) & ERRCODE_MAX; 120 if (table_num == 0 121 #ifdef __sgi 122 /* Irix 6.5 uses a much bigger table than other UNIX 123 systems I've looked at, but the table is sparse. The 124 sparse entries start around 500, but sys_nerr is only 125 152. */ 126 || (code > 0 && code <= 1600) 127 #endif 128 ) { 129 if (code == 0) 130 goto oops; 131 132 /* This could trip if int is 16 bits. */ 133 if ((unsigned long)(int)code != (unsigned long)code) 134 abort (); 135 cp = get_thread_buffer(); 136 if (cp && strerror_r(code, cp, ET_EBUFSIZ) == 0) 137 return cp; 138 return strerror(code); 139 } 140 141 k5_mutex_lock(&et_list_lock); 142 dprintf(("scanning list for %x\n", table_num)); 143 for (e = et_list; e != NULL; e = e->next) { 144 dprintf(("\t%x = %s\n", e->table->base & ERRCODE_MAX, 145 e->table->msgs[0])); 146 if ((e->table->base & ERRCODE_MAX) == table_num) { 147 table = e->table; 148 goto found; 149 } 150 } 151 goto no_table_found; 152 153 found: 154 k5_mutex_unlock(&et_list_lock); 155 dprintf (("found it!\n")); 156 /* This is the right table */ 157 158 /* This could trip if int is 16 bits. */ 159 if ((unsigned long)(unsigned int)offset != offset) 160 goto no_table_found; 161 162 if (table->n_msgs <= (unsigned int) offset) 163 goto no_table_found; 164 165 /* If there's a string at the end of the table, it's a text domain. */ 166 if (table->msgs[table->n_msgs] != NULL) 167 return dgettext(table->msgs[table->n_msgs], table->msgs[offset]); 168 else 169 return table->msgs[offset]; 170 171 no_table_found: 172 k5_mutex_unlock(&et_list_lock); 173 #if defined(_WIN32) 174 /* 175 * WinSock errors exist in the 10000 and 11000 ranges 176 * but might not appear if WinSock is not initialized 177 */ 178 if (code >= WSABASEERR && code < WSABASEERR + 1100) { 179 table_num = 0; 180 offset = code; 181 divisor = WSABASEERR; 182 } 183 #endif 184 #ifdef _WIN32 185 { 186 LPVOID msgbuf; 187 188 if (! FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, 189 NULL /* lpSource */, 190 (DWORD) code, 191 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 192 (LPTSTR) &msgbuf, 193 (DWORD) 0 /*sizeof(buffer)*/, 194 NULL /* va_list */ )) { 195 /* 196 * WinSock errors exist in the 10000 and 11000 ranges 197 * but might not appear if WinSock is not initialized 198 */ 199 if (code >= WSABASEERR && code < WSABASEERR + 1100) { 200 table_num = 0; 201 offset = code; 202 divisor = 10000; 203 } 204 205 goto oops; 206 } else { 207 char *buffer; 208 cp = get_thread_buffer(); 209 if (cp == NULL) 210 return "Unknown error code"; 211 buffer = cp; 212 strncpy(buffer, msgbuf, ET_EBUFSIZ); 213 buffer[ET_EBUFSIZ-1] = '\0'; 214 cp = buffer + strlen(buffer) - 1; 215 if (*cp == '\n') *cp-- = '\0'; 216 if (*cp == '\r') *cp-- = '\0'; 217 if (*cp == '.') *cp-- = '\0'; 218 219 LocalFree(msgbuf); 220 return buffer; 221 } 222 } 223 #endif 224 225 oops: 226 227 cp = get_thread_buffer(); 228 if (cp == NULL) 229 return "Unknown error code"; 230 cp1 = cp; 231 strlcpy(cp, "Unknown code ", ET_EBUFSIZ); 232 cp += sizeof("Unknown code ") - 1; 233 if (table_num != 0L) { 234 (void) error_table_name_r(table_num, cp); 235 while (*cp != '\0') 236 cp++; 237 *cp++ = ' '; 238 } 239 while (divisor > 1) { 240 if (started != 0 || offset >= divisor) { 241 *cp++ = '0' + offset / divisor; 242 offset %= divisor; 243 started++; 244 } 245 divisor /= 10; 246 } 247 *cp++ = '0' + offset; 248 *cp = '\0'; 249 return(cp1); 250 } 251 252 errcode_t KRB5_CALLCONV 253 add_error_table(const struct error_table *et) 254 { 255 struct et_list *e; 256 257 if (CALL_INIT_FUNCTION(com_err_initialize)) 258 return 0; 259 260 e = malloc(sizeof(struct et_list)); 261 if (e == NULL) 262 return ENOMEM; 263 264 e->table = et; 265 266 k5_mutex_lock(&et_list_lock); 267 e->next = et_list; 268 et_list = e; 269 270 /* If there are two strings at the end of the table, they are a text domain 271 * and locale dir, and we are supposed to call bindtextdomain. */ 272 if (et->msgs[et->n_msgs] != NULL && et->msgs[et->n_msgs + 1] != NULL) 273 bindtextdomain(et->msgs[et->n_msgs], et->msgs[et->n_msgs + 1]); 274 275 k5_mutex_unlock(&et_list_lock); 276 return 0; 277 } 278 279 errcode_t KRB5_CALLCONV 280 remove_error_table(const struct error_table *et) 281 { 282 struct et_list **ep, *e; 283 284 /* Safety check in case libraries are finalized in the wrong order. */ 285 if (terminated) 286 return ENOENT; 287 288 if (CALL_INIT_FUNCTION(com_err_initialize)) 289 return 0; 290 k5_mutex_lock(&et_list_lock); 291 292 /* Remove the entry that matches the error table instance. */ 293 for (ep = &et_list; *ep; ep = &(*ep)->next) { 294 if ((*ep)->table == et) { 295 e = *ep; 296 *ep = e->next; 297 free(e); 298 k5_mutex_unlock(&et_list_lock); 299 return 0; 300 } 301 } 302 k5_mutex_unlock(&et_list_lock); 303 return ENOENT; 304 } 305 306 int com_err_finish_init() 307 { 308 return CALL_INIT_FUNCTION(com_err_initialize); 309 } 310