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) 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 * 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 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 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 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. */ 266 static int com_err_lock_hook_handle(void) 267 { 268 return k5_mutex_lock(&com_err_hook_lock); 269 } 270 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 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 */ 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 */ 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 */ 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 */ 358 void add_default_com_err_hook() { 359 add_com_err_hook(default_com_err_proc); 360 } 361