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