1 #pragma ident "%Z%%M% %I% %E% SMI" 2 3 /* Can't include krb5.h here, or k5-int.h which includes it, because 4 krb5.h needs to be generated with error tables, after util/et, 5 which builds after this directory. */ 6 #include <stdarg.h> 7 #include <string.h> 8 #include <stdlib.h> 9 #include <stdio.h> 10 #include <k5-err.h> 11 12 #include "k5-thread.h" 13 #include <k5-platform.h> 14 #include "supp-int.h" 15 16 #ifdef _WIN32 17 #ifndef vsnprintf 18 #define vsnprintf _vsnprintf 19 #endif 20 #endif 21 22 /* It would be nice to just use error_message() always. Pity that 23 it's defined in a library that depends on this one, and we're not 24 allowed to make circular dependencies. */ 25 /* We really want a rwlock here, since we should hold it while calling 26 the function and copying out its results. But I haven't 27 implemented shims for rwlock yet. */ 28 static k5_mutex_t krb5int_error_info_support_mutex = 29 K5_MUTEX_PARTIAL_INITIALIZER; 30 static const char *(KRB5_CALLCONV *fptr)(long); /* = &error_message */ 31 32 int 33 krb5int_err_init (void) 34 { 35 return k5_mutex_finish_init (&krb5int_error_info_support_mutex); 36 } 37 #define initialize() krb5int_call_thread_support_init() 38 #define lock() k5_mutex_lock(&krb5int_error_info_support_mutex) 39 #define unlock() k5_mutex_unlock(&krb5int_error_info_support_mutex) 40 41 void 42 krb5int_set_error (struct errinfo *ep, long code, const char *fmt, ...) 43 { 44 va_list args; 45 va_start (args, fmt); 46 krb5int_vset_error (ep, code, fmt, args); 47 va_end (args); 48 } 49 50 void 51 krb5int_vset_error (struct errinfo *ep, long code, 52 const char *fmt, va_list args) 53 { 54 char *p; 55 56 if (ep->msg && ep->msg != ep->scratch_buf) { 57 free ((void *)ep->msg); 58 ep->msg = NULL; 59 } 60 ep->code = code; 61 #ifdef HAVE_VASPRINTF 62 { 63 char *str = NULL; 64 if (vasprintf(&str, fmt, args) >= 0 && str != NULL) { 65 ep->msg = str; 66 return; 67 } 68 } 69 #endif 70 vsnprintf(ep->scratch_buf, sizeof(ep->scratch_buf), fmt, args); 71 p = strdup(ep->scratch_buf); 72 ep->msg = p ? p : ep->scratch_buf; 73 } 74 75 const char * 76 krb5int_get_error (struct errinfo *ep, long code) 77 { 78 char *r, *r2; 79 if (code == ep->code && ep->msg) { 80 r = strdup(ep->msg); 81 if (r == NULL) { 82 strcpy(ep->scratch_buf, _("Out of memory")); 83 r = ep->scratch_buf; 84 } 85 return r; 86 } 87 if (initialize() != 0) { 88 strncpy(ep->scratch_buf, _("Kerberos library initialization failure"), 89 sizeof(ep->scratch_buf)); 90 ep->scratch_buf[sizeof(ep->scratch_buf)-1] = 0; 91 ep->msg = NULL; 92 return ep->scratch_buf; 93 } 94 lock(); 95 if (fptr == NULL) { 96 unlock(); 97 #ifdef HAVE_STRERROR_R 98 if (strerror_r (code, ep->scratch_buf, sizeof(ep->scratch_buf)) == 0) { 99 char *p = strdup(ep->scratch_buf); 100 if (p) 101 return p; 102 return ep->scratch_buf; 103 } 104 /* If strerror_r didn't work with the 1K buffer, we can try a 105 really big one. This seems kind of gratuitous though. */ 106 #define BIG_ERR_BUFSIZ 8192 107 r = malloc(BIG_ERR_BUFSIZ); 108 if (r) { 109 if (strerror_r (code, r, BIG_ERR_BUFSIZ) == 0) { 110 r2 = realloc (r, 1 + strlen(r)); 111 if (r2) 112 return r2; 113 return r; 114 } 115 free (r); 116 } 117 #endif 118 r = strerror (code); 119 if (r) { 120 if (strlen (r) < sizeof (ep->scratch_buf) 121 || (r2 = strdup (r)) == NULL) { 122 strncpy (ep->scratch_buf, r, sizeof(ep->scratch_buf)); 123 return ep->scratch_buf; 124 } else 125 return r2; 126 } 127 format_number: 128 sprintf (ep->scratch_buf, _("error %ld"), code); 129 return ep->scratch_buf; 130 } 131 r = (char *) fptr(code); 132 if (r == NULL) { 133 unlock(); 134 goto format_number; 135 } 136 r2 = strdup (r); 137 if (r2 == NULL) { 138 strncpy(ep->scratch_buf, r, sizeof(ep->scratch_buf)); 139 unlock(); 140 return ep->scratch_buf; 141 } else { 142 unlock(); 143 return r2; 144 } 145 } 146 147 void 148 krb5int_free_error (struct errinfo *ep, const char *msg) 149 { 150 if (msg != ep->scratch_buf) 151 free ((char *) msg); 152 } 153 154 void 155 krb5int_clear_error (struct errinfo *ep) 156 { 157 krb5int_free_error (ep, ep->msg); 158 ep->msg = NULL; 159 } 160 161 void 162 krb5int_set_error_info_callout_fn (const char *(KRB5_CALLCONV *f)(long)) 163 { 164 initialize(); 165 lock(); 166 fptr = f; 167 unlock(); 168 } 169