Imported Upstream version 1.10.2
[platform/upstream/krb5.git] / src / util / et / error_message.c
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 "autoconf.h"
24 #include <stdio.h>
25 #ifdef HAVE_STDLIB_H
26 #include <stdlib.h>
27 #endif
28 #include <string.h>
29 #include "com_err.h"
30 #include "error_table.h"
31 #include "k5-platform.h"
32
33 #if !defined(HAVE_STRERROR) && !defined(SYS_ERRLIST_DECLARED)
34 extern char const * const sys_errlist[];
35 extern const int sys_nerr;
36 #endif
37
38 static struct et_list *et_list;
39 static k5_mutex_t et_list_lock = K5_MUTEX_PARTIAL_INITIALIZER;
40 static int terminated = 0;      /* for debugging shlib fini sequence errors */
41
42 MAKE_INIT_FUNCTION(com_err_initialize);
43 MAKE_FINI_FUNCTION(com_err_terminate);
44
45 int com_err_initialize(void)
46 {
47     int err;
48 #ifdef SHOW_INITFINI_FUNCS
49     printf("com_err_initialize\n");
50 #endif
51     terminated = 0;
52     err = k5_mutex_finish_init(&et_list_lock);
53     if (err)
54         return err;
55     err = k5_mutex_finish_init(&com_err_hook_lock);
56     if (err)
57         return err;
58     err = k5_key_register(K5_KEY_COM_ERR, free);
59     if (err)
60         return err;
61     return 0;
62 }
63
64 void com_err_terminate(void)
65 {
66     struct et_list *e, *enext;
67     if (! INITIALIZER_RAN(com_err_initialize) || PROGRAM_EXITING()) {
68 #ifdef SHOW_INITFINI_FUNCS
69         printf("com_err_terminate: skipping\n");
70 #endif
71         return;
72     }
73 #ifdef SHOW_INITFINI_FUNCS
74     printf("com_err_terminate\n");
75 #endif
76     k5_key_delete(K5_KEY_COM_ERR);
77     k5_mutex_destroy(&com_err_hook_lock);
78     if (k5_mutex_lock(&et_list_lock) != 0)
79         return;
80     for (e = et_list; e; e = enext) {
81         enext = e->next;
82         free(e);
83     }
84     k5_mutex_unlock(&et_list_lock);
85     k5_mutex_destroy(&et_list_lock);
86     terminated = 1;
87 }
88
89 #ifndef DEBUG_TABLE_LIST
90 #define dprintf(X)
91 #else
92 #define dprintf(X) printf X
93 #endif
94
95 static char *
96 get_thread_buffer ()
97 {
98     char *cp;
99     cp = k5_getspecific(K5_KEY_COM_ERR);
100     if (cp == NULL) {
101         cp = malloc(ET_EBUFSIZ);
102         if (cp == NULL) {
103             return NULL;
104         }
105         if (k5_setspecific(K5_KEY_COM_ERR, cp) != 0) {
106             free(cp);
107             return NULL;
108         }
109     }
110     return cp;
111 }
112
113 const char * KRB5_CALLCONV
114 error_message(long code)
115 {
116     unsigned long offset;
117     unsigned long l_offset;
118     struct et_list *e;
119     unsigned long table_num;
120     int started = 0;
121     unsigned int divisor = 100;
122     char *cp, *cp1;
123     const struct error_table *table;
124     int merr;
125
126     l_offset = (unsigned long)code & ((1<<ERRCODE_RANGE)-1);
127     offset = l_offset;
128     table_num = ((unsigned long)code - l_offset) & ERRCODE_MAX;
129     if (table_num == 0
130 #ifdef __sgi
131         /* Irix 6.5 uses a much bigger table than other UNIX
132            systems I've looked at, but the table is sparse.  The
133            sparse entries start around 500, but sys_nerr is only
134            152.  */
135         || (code > 0 && code <= 1600)
136 #endif
137     ) {
138         if (code == 0)
139             goto oops;
140
141         /* This could trip if int is 16 bits.  */
142         if ((unsigned long)(int)code != (unsigned long)code)
143             abort ();
144 #ifdef HAVE_STRERROR_R
145         cp = get_thread_buffer();
146         if (cp && strerror_r((int) code, cp, ET_EBUFSIZ) == 0)
147             return cp;
148 #endif
149 #ifdef HAVE_STRERROR
150         cp = strerror((int) code);
151         if (cp)
152             return cp;
153 #elif defined HAVE_SYS_ERRLIST
154         if (offset < sys_nerr)
155             return(sys_errlist[offset]);
156 #endif
157         goto oops;
158     }
159
160     if (CALL_INIT_FUNCTION(com_err_initialize))
161         return 0;
162     merr = k5_mutex_lock(&et_list_lock);
163     if (merr)
164         goto oops;
165     dprintf(("scanning list for %x\n", table_num));
166     for (e = et_list; e != NULL; e = e->next) {
167         dprintf(("\t%x = %s\n", e->table->base & ERRCODE_MAX,
168                  e->table->msgs[0]));
169         if ((e->table->base & ERRCODE_MAX) == table_num) {
170             table = e->table;
171             goto found;
172         }
173     }
174     goto no_table_found;
175
176 found:
177     k5_mutex_unlock(&et_list_lock);
178     dprintf (("found it!\n"));
179     /* This is the right table */
180
181     /* This could trip if int is 16 bits.  */
182     if ((unsigned long)(unsigned int)offset != offset)
183         goto no_table_found;
184
185     if (table->n_msgs <= (unsigned int) offset)
186         goto no_table_found;
187
188     /* If there's a string at the end of the table, it's a text domain. */
189     if (table->msgs[table->n_msgs] != NULL)
190         return dgettext(table->msgs[table->n_msgs], table->msgs[offset]);
191     else
192         return table->msgs[offset];
193
194 no_table_found:
195     k5_mutex_unlock(&et_list_lock);
196 #if defined(_WIN32)
197     /*
198      * WinSock errors exist in the 10000 and 11000 ranges
199      * but might not appear if WinSock is not initialized
200      */
201     if (code >= WSABASEERR && code < WSABASEERR + 1100) {
202         table_num = 0;
203         offset = code;
204         divisor = WSABASEERR;
205     }
206 #endif
207 #ifdef _WIN32
208     {
209         LPVOID msgbuf;
210
211         if (! FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
212                              NULL /* lpSource */,
213                              (DWORD) code,
214                              MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
215                              (LPTSTR) &msgbuf,
216                              (DWORD) 0 /*sizeof(buffer)*/,
217                              NULL /* va_list */ )) {
218             /*
219              * WinSock errors exist in the 10000 and 11000 ranges
220              * but might not appear if WinSock is not initialized
221              */
222             if (code >= WSABASEERR && code < WSABASEERR + 1100) {
223                 table_num = 0;
224                 offset = code;
225                 divisor = 10000;
226             }
227
228             goto oops;
229         } else {
230             char *buffer;
231             cp = get_thread_buffer();
232             if (cp == NULL)
233                 return "Unknown error code";
234             buffer = cp;
235             strncpy(buffer, msgbuf, ET_EBUFSIZ);
236             buffer[ET_EBUFSIZ-1] = '\0';
237             cp = buffer + strlen(buffer) - 1;
238             if (*cp == '\n') *cp-- = '\0';
239             if (*cp == '\r') *cp-- = '\0';
240             if (*cp == '.') *cp-- = '\0';
241
242             LocalFree(msgbuf);
243             return buffer;
244         }
245     }
246 #endif
247
248 oops:
249
250     cp = get_thread_buffer();
251     if (cp == NULL)
252         return "Unknown error code";
253     cp1 = cp;
254     strlcpy(cp, "Unknown code ", ET_EBUFSIZ);
255     cp += sizeof("Unknown code ") - 1;
256     if (table_num != 0L) {
257         (void) error_table_name_r(table_num, cp);
258         while (*cp != '\0')
259             cp++;
260         *cp++ = ' ';
261     }
262     while (divisor > 1) {
263         if (started != 0 || offset >= divisor) {
264             *cp++ = '0' + offset / divisor;
265             offset %= divisor;
266             started++;
267         }
268         divisor /= 10;
269     }
270     *cp++ = '0' + offset;
271     *cp = '\0';
272     return(cp1);
273 }
274
275 errcode_t KRB5_CALLCONV
276 add_error_table(const struct error_table *et)
277 {
278     struct et_list *e;
279     int merr;
280
281     if (CALL_INIT_FUNCTION(com_err_initialize))
282         return 0;
283
284     e = malloc(sizeof(struct et_list));
285     if (e == NULL)
286         return ENOMEM;
287
288     e->table = et;
289
290     merr = k5_mutex_lock(&et_list_lock);
291     if (merr) {
292         free(e);
293         return merr;
294     }
295     e->next = et_list;
296     et_list = e;
297
298     /* If there are two strings at the end of the table, they are a text domain
299      * and locale dir, and we are supposed to call bindtextdomain. */
300     if (et->msgs[et->n_msgs] != NULL && et->msgs[et->n_msgs + 1] != NULL)
301         bindtextdomain(et->msgs[et->n_msgs], et->msgs[et->n_msgs + 1]);
302
303     return k5_mutex_unlock(&et_list_lock);
304 }
305
306 errcode_t KRB5_CALLCONV
307 remove_error_table(const struct error_table *et)
308 {
309     struct et_list **ep, *e;
310     int merr;
311
312     if (CALL_INIT_FUNCTION(com_err_initialize))
313         return 0;
314     merr = k5_mutex_lock(&et_list_lock);
315     if (merr)
316         return merr;
317
318     /* Remove the entry that matches the error table instance. */
319     for (ep = &et_list; *ep; ep = &(*ep)->next) {
320         if ((*ep)->table == et) {
321             e = *ep;
322             *ep = e->next;
323             free(e);
324             return k5_mutex_unlock(&et_list_lock);
325         }
326     }
327     k5_mutex_unlock(&et_list_lock);
328     return ENOENT;
329 }
330
331 int com_err_finish_init()
332 {
333     return CALL_INIT_FUNCTION(com_err_initialize);
334 }