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
com_err_initialize(void)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
com_err_terminate(void)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 *
get_thread_buffer()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
error_message(long code)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
add_error_table(const struct error_table * et)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
remove_error_table(const struct error_table * et)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
com_err_finish_init()306 int com_err_finish_init()
307 {
308 return CALL_INIT_FUNCTION(com_err_initialize);
309 }
310