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
40k5_mutex_t com_err_hook_lock = K5_MUTEX_PARTIAL_INITIALIZER;
41
42static void default_com_err_proc
43(const char  *whoami, errcode_t code,
44	const char  *fmt, va_list ap);
45
46#if defined(_WIN32)
47BOOL  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
73static et_old_error_hook_func com_err_hook[MAX_HOOKS] = { default_com_err_proc,
74    NULL, NULL };
75static int hook_count = 1;
76
77/* Solaris Kerberos specific fix start --------------------------- */
78
79#define gettext(X)	X
80
81struct msg_map {
82	char *msgid;
83	char *c_msgstr;
84};
85
86struct 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 */
129static char *
130my_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
147static 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
217void 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
236best_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
252void 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.  */
266static int com_err_lock_hook_handle(void)
267{
268    return k5_mutex_lock(&com_err_hook_lock);
269}
270
271et_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
292et_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 */
315void 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 */
332void 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 */
350void 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 */
358void add_default_com_err_hook() {
359	add_com_err_hook(default_com_err_proc);
360}
361