1 /*
2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
5
6
7 /*
8 * Copyright 1997 by Massachusetts Institute of Technology
9 *
10 * Copyright 1987, 1988 by MIT Student Information Processing Board
11 *
12 * Permission to use, copy, modify, and distribute this software
13 * and its documentation for any purpose and without fee is
14 * hereby granted, provided that the above copyright notice
15 * appear in all copies and that both that copyright notice and
16 * this permission notice appear in supporting documentation,
17 * and that the names of M.I.T. and the M.I.T. S.I.P.B. not be
18 * used in advertising or publicity pertaining to distribution
19 * of the software without specific, written prior permission.
20 * Furthermore if you modify this software you must label
21 * your software as modified software and not distribute it in such a
22 * fashion that it might be confused with the original M.I.T. software.
23 * M.I.T. and the M.I.T. S.I.P.B. make no representations about
24 * the suitability of this software for any purpose. It is
25 * provided "as is" without express or implied warranty.
26 */
27
28 #include <stdio.h>
29 #include <string.h>
30 #include <stdlib.h>
31 #include <locale.h>
32
33 #include "com_err.h"
34 #include "error_table.h"
35
36 #if defined(_WIN32)
37 #include <io.h>
38 #endif
39
40 k5_mutex_t com_err_hook_lock = K5_MUTEX_PARTIAL_INITIALIZER;
41
42 static void default_com_err_proc
43 (const char *whoami, errcode_t code,
44 const char *fmt, va_list ap);
45
46 #if defined(_WIN32)
isGuiApp()47 BOOL isGuiApp() {
48 DWORD mypid;
49 HANDLE myprocess;
50 mypid = GetCurrentProcessId();
51 myprocess = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, mypid);
52 return GetGuiResources(myprocess, 1) > 0;
53 }
54 #endif
55
56 /*
57 * Solaris Kerberos:
58 * It is sometimes desirable to have more than a single hook called
59 * when com_err() is invoked. A number of new functions have been
60 * added which allow hooks to be added and removed:
61 * add_com_err_hook()
62 * add_default_com_err_hook()
63 * remove_com_err_hook()
64 * remove_default_com_err_hook()
65 * The existing functions:
66 * set_com_err_hook()
67 * reset_com_err_hook()
68 * com_err()
69 * have been modified to work with the new scheme. Applications using
70 * the original function calls are not affected.
71 */
72 #define MAX_HOOKS 3
73 static et_old_error_hook_func com_err_hook[MAX_HOOKS] = { default_com_err_proc,
74 NULL, NULL };
75 static int hook_count = 1;
76
77 /* Solaris Kerberos specific fix start --------------------------- */
78
79 #define gettext(X) X
80
81 struct msg_map {
82 char *msgid;
83 char *c_msgstr;
84 };
85
86 struct msg_map msgmap[] = {
87
88 #define MSG_WHILE 0
89 { gettext("%s\n## com_err msg of format: 'while ...'"),
90 "%s\n" },
91
92 #define MSG_ERROR_MSG 1
93 { gettext("%s\n## com_err message of format: 'error msg ...'"),
94 "%s\n" },
95
96 #define MSG_ERROR_MSG_WHILE 2
97 { gettext("%1$s %2$s\n## com_err message of format: "
98 "'error msg ... while ...'"),
99 "%1$s %2$s\n" },
100
101 #define MSG_WHOAMI_WHILE 3
102 { gettext("%1$s: %2$s\n## com_err msg of format: 'whoami: while ...'"),
103 "%1$s: %2$s\n" },
104
105 #define MSG_WHOAMI_ERROR_MSG 4
106 { gettext("%1$s: %2$s\n## com_err message of format: "
107 "'whoami: error msg ...'"),
108 "%1$s: %2$s\n" },
109
110 #define MSG_WHOAMI_ERROR_MSG_WHILE 5
111 { gettext("%1$s: %2$s %3$s\n## com_err message of format: "
112 "'whoami: error msg ... while ...'"),
113 "%1$s: %2$s %3$s\n" },
114
115 #define MSG_WHOAMI 6
116 { gettext("%s:\n ## com_err message of format: "
117 "'whoami: with no error msg or while ...'"),
118 "%s:\n " }
119 };
120
121 #undef gettext
122
123 /*
124 * The idea is that we provide a unique message id that contains extra junk
125 * that we never want to display in the C locale. If dgettext() returns
126 * a string that is equal to the message id, then we return the c_msgstr,
127 * for display in the locale.
128 */
129 static char *
my_gettext(int msg_idx)130 my_gettext(int msg_idx)
131 {
132 char *msgid = msgmap[msg_idx].msgid;
133 char *c_msgstr = msgmap[msg_idx].c_msgstr;
134 char *msgstr = dgettext(TEXT_DOMAIN, msgid);
135
136 if (strcmp(msgstr, msgid) == 0)
137 return (c_msgstr);
138 else
139 return (msgstr);
140 }
141
142 /* Solaris Kerberos specific fix end --------------------------- */
143
144 /* Solaris Kerberos: this code is significantly altered from
145 * the MIT 1.2.1 version to work with internationalization */
146
default_com_err_proc(const char * whoami,errcode_t code,const char * fmt,va_list ap)147 static void default_com_err_proc (const char *whoami, errcode_t code,
148 const char *fmt, va_list ap)
149 {
150 char whilebuf[1024] = "";
151
152 *whilebuf = '\0';
153
154 /*
155 * Because 'while ...' message could contain a format string
156 * we have to intepret it now, in a buffer. We need to put it
157 * into a buffer so that the message can be juxtaposed in a locale
158 * meaningful manner. In some natural languages, the 'while ...' phrase
159 * must be first.
160 */
161 if (fmt) {
162 vsprintf(whilebuf, fmt, ap);
163 }
164
165 /*
166 * There are 8 possible combinations here depending on whether
167 * a whoami string was provided, error code is non-zero, and if a
168 * a 'while ...' messge was provided.
169 */
170 if (!whoami) {
171
172 if ((!code) && fmt) {
173
174 fprintf(stderr, my_gettext(MSG_WHILE),
175 whilebuf);
176
177 } else if (code && !fmt) {
178
179 fprintf(stderr, my_gettext(MSG_ERROR_MSG),
180 error_message(code));
181
182 } else if (code && fmt) {
183
184 fprintf(stderr, my_gettext(MSG_ERROR_MSG_WHILE),
185 error_message(code), whilebuf);
186 } else
187 return;
188
189 } else {
190
191 if ((!code) && fmt) {
192
193 fprintf(stderr, my_gettext(MSG_WHOAMI_WHILE),
194 whoami, whilebuf);
195
196 } else if (code && !fmt) {
197
198 fprintf(stderr, my_gettext(MSG_WHOAMI_ERROR_MSG),
199 whoami, error_message(code));
200
201 } else if (code && fmt) {
202
203 fprintf(stderr,
204 my_gettext(MSG_WHOAMI_ERROR_MSG_WHILE),
205 whoami, error_message(code), whilebuf);
206 } else {
207
208 fprintf(stderr,
209 my_gettext(MSG_WHOAMI),
210 whoami);
211 }
212 }
213
214 fflush(stderr);
215 }
216
com_err_va(const char * whoami,errcode_t code,const char * fmt,va_list ap)217 void KRB5_CALLCONV com_err_va(const char *whoami,
218 errcode_t code,
219 const char *fmt,
220 va_list ap)
221 {
222 int err;
223 int i;
224 err = com_err_finish_init();
225 if (err)
226 goto best_try;
227 err = k5_mutex_lock(&com_err_hook_lock);
228 if (err)
229 goto best_try;
230 for (i = 0; i < hook_count; i++) {
231 (com_err_hook[i])(whoami, code, fmt, ap);
232 }
233 k5_mutex_unlock(&com_err_hook_lock);
234 return;
235
236 best_try:
237 /* Yikes. Our library initialization failed or we couldn't lock
238 the lock we want. We could be in trouble. Gosh, we should
239 probably print an error message. Oh, wait. That's what we're
240 trying to do. In fact, if we're losing on initialization here,
241 there's a good chance it has to do with failed initialization
242 of the caller. */
243
244 for (i = 0; i < hook_count; i++) {
245 (com_err_hook[i])(whoami, code, fmt, ap);
246 }
247 assert(err == 0);
248 abort();
249 }
250
251
com_err(const char * whoami,errcode_t code,const char * fmt,...)252 void KRB5_CALLCONV_C com_err(const char *whoami,
253 errcode_t code,
254 const char *fmt, ...)
255 {
256 va_list ap;
257
258 va_start(ap, fmt);
259 com_err_va(whoami, code, fmt, ap);
260 va_end(ap);
261 }
262
263 /* Make a separate function because the assert invocations below
264 use the macro expansion on some platforms, which may be insanely
265 long and incomprehensible. */
com_err_lock_hook_handle(void)266 static int com_err_lock_hook_handle(void)
267 {
268 return k5_mutex_lock(&com_err_hook_lock);
269 }
270
set_com_err_hook(et_old_error_hook_func new_proc)271 et_old_error_hook_func set_com_err_hook (et_old_error_hook_func new_proc)
272 {
273 int i;
274 et_old_error_hook_func x;
275
276 /* Broken initialization? What can we do? */
277 assert(com_err_finish_init() == 0);
278 assert(com_err_lock_hook_handle() == 0);
279
280 x = com_err_hook[0];
281
282 for (i = 0; i < hook_count; i++)
283 com_err_hook[i] = NULL;
284
285 com_err_hook[0] = new_proc;
286 hook_count = 1;
287
288 k5_mutex_unlock(&com_err_hook_lock);
289 return x;
290 }
291
reset_com_err_hook()292 et_old_error_hook_func reset_com_err_hook ()
293 {
294 int i;
295 et_old_error_hook_func x;
296
297 /* Broken initialization? What can we do? */
298 assert(com_err_finish_init() == 0);
299 assert(com_err_lock_hook_handle() == 0);
300 x = com_err_hook[0];
301 for (i = 0; i < hook_count; i++)
302 com_err_hook[i] = NULL;
303
304 com_err_hook[0] = default_com_err_proc;
305 hook_count = 1;
306 k5_mutex_unlock(&com_err_hook_lock);
307 return x;
308 }
309
310 /*
311 * Solaris Kerberos:
312 * Register a hook which will be called every time
313 * com_err() is called.
314 */
add_com_err_hook(et_old_error_hook_func f)315 void add_com_err_hook(et_old_error_hook_func f) {
316 int i;
317 if (hook_count < MAX_HOOKS) {
318 for (i = 0; i < hook_count; i++) {
319 if (com_err_hook[i] == NULL)
320 break;
321 }
322 com_err_hook[i] = f;
323 hook_count++;
324 }
325 }
326
327 /*
328 * Solaris Kerberos:
329 * Remove a logging hook. The first hook matching 'f' will
330 * be removed.
331 */
rem_com_err_hook(et_old_error_hook_func f)332 void rem_com_err_hook(et_old_error_hook_func f) {
333 int i, j;
334
335 for (i = 0; i < hook_count; i++) {
336 if (com_err_hook[i] == f) {
337 for (j = i; j < hook_count - 1; j++) {
338 com_err_hook[j] = com_err_hook[j+1];
339 }
340 com_err_hook[j] = NULL;
341 hook_count--;
342 }
343 }
344 }
345
346 /*
347 * Solaris Kerberos:
348 * Remove the default hook.
349 */
rem_default_com_err_hook()350 void rem_default_com_err_hook() {
351 rem_com_err_hook(default_com_err_proc);
352 }
353
354 /*
355 * Solaris Kerberos:
356 * Add back the default hook
357 */
add_default_com_err_hook()358 void add_default_com_err_hook() {
359 add_com_err_hook(default_com_err_proc);
360 }
361