xref: /freebsd/crypto/krb5/src/util/et/error_message.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
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