1 /* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License, version 2.0,
5 as published by the Free Software Foundation.
6
7 This program is also distributed with certain software (including
8 but not limited to OpenSSL) that is licensed under separate terms,
9 as designated in a particular file or component or in included license
10 documentation. The authors of MySQL hereby grant you an additional
11 permission to link the program and your derivative works with the
12 separately licensed software that they have included with MySQL.
13
14 Without limiting anything contained in the foregoing, this file,
15 which is part of C Driver for MySQL (Connector/C), is also subject to the
16 Universal FOSS Exception, version 1.0, a copy of which can be found at
17 http://oss.oracle.com/licenses/universal-foss-exception.
18
19 This program is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 GNU General Public License, version 2.0, for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with this program; if not, write to the Free Software
26 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
27
28 #include "mysys_priv.h"
29 #include "mysys_err.h"
30 #include <m_string.h>
31 #include <stdarg.h>
32 #include <m_ctype.h>
33 #include "my_base.h"
34 #include "my_handler_errors.h"
35
36 /* Max length of a error message. Should be kept in sync with MYSQL_ERRMSG_SIZE. */
37 #define ERRMSGSIZE (512)
38
39 /* Define some external variables for error handling */
40
41 /*
42 WARNING!
43 my_error family functions have to be used according following rules:
44 - if message have not parameters use my_message(ER_CODE, ER(ER_CODE), MYF(N))
45 - if message registered use my_error(ER_CODE, MYF(N), ...).
46 - With some special text of errror message use:
47 my_printf_error(ER_CODE, format, MYF(N), ...)
48 */
49
50 /*
51 Message texts are registered into a linked list of 'my_err_head' structs.
52 Each struct contains (1.) an array of pointers to C character strings with
53 '\0' termination, (2.) the error number for the first message in the array
54 (array index 0) and (3.) the error number for the last message in the array
55 (array index (last - first)).
56 The array may contain gaps with NULL pointers and pointers to empty strings.
57 Both kinds of gaps will be translated to "Unknown error %d.", if my_error()
58 is called with a respective error number.
59 The list of header structs is sorted in increasing order of error numbers.
60 Negative error numbers are allowed. Overlap of error numbers is not allowed.
61 Not registered error numbers will be translated to "Unknown error %d.".
62 */
63 static struct my_err_head
64 {
65 struct my_err_head *meh_next; /* chain link */
66 const char** (*get_errmsgs) (); /* returns error message format */
67 int meh_first; /* error number matching array slot 0 */
68 int meh_last; /* error number matching last slot */
69 } my_errmsgs_globerrs = {NULL, get_global_errmsgs, EE_ERROR_FIRST, EE_ERROR_LAST};
70
71 static struct my_err_head *my_errmsgs_list= &my_errmsgs_globerrs;
72
73
74 /**
75 Get a string describing a system or handler error. thread-safe.
76
77 @param buf a buffer in which to return the error message
78 @param len the size of the aforementioned buffer
79 @param nr the error number
80
81 @retval buf always buf. for signature compatibility with strerror(3).
82 */
83
my_strerror(char * buf,size_t len,int nr)84 char *my_strerror(char *buf, size_t len, int nr)
85 {
86 char *msg= NULL;
87
88 buf[0]= '\0'; /* failsafe */
89
90 /*
91 These (handler-) error messages are shared by perror, as required
92 by the principle of least surprise.
93 */
94 if ((nr >= HA_ERR_FIRST) && (nr <= HA_ERR_LAST))
95 msg= (char *) handler_error_messages[nr - HA_ERR_FIRST];
96
97 if (msg != NULL)
98 strmake(buf, msg, len - 1);
99 else
100 {
101 /*
102 On Windows, do things the Windows way. On a system that supports both
103 the GNU and the XSI variant, use whichever was configured (GNU); if
104 this choice is not advertised, use the default (POSIX/XSI). Testing
105 for __GNUC__ is not sufficient to determine whether this choice exists.
106 */
107 #if defined(__WIN__)
108 strerror_s(buf, len, nr);
109 #elif ((defined _POSIX_C_SOURCE && (_POSIX_C_SOURCE >= 200112L)) || \
110 (defined _XOPEN_SOURCE && (_XOPEN_SOURCE >= 600))) && \
111 ! defined _GNU_SOURCE
112 strerror_r(nr, buf, len); /* I can build with or without GNU */
113 #elif defined _GNU_SOURCE
114 char *r= strerror_r(nr, buf, len);
115 if (r != buf) /* Want to help, GNU? */
116 strmake(buf, r, len - 1); /* Then don't. */
117 #else
118 strerror_r(nr, buf, len);
119 #endif
120 }
121
122 /*
123 strerror() return values are implementation-dependent, so let's
124 be pragmatic.
125 */
126 if (!buf[0])
127 strmake(buf, "unknown error", len - 1);
128
129 return buf;
130 }
131
132
133 /**
134 @brief Get an error format string from one of the my_error_register()ed sets
135
136 @note
137 NULL values are possible even within a registered range.
138
139 @param nr Errno
140
141 @retval NULL if no message is registered for this error number
142 @retval str C-string
143 */
144
my_get_err_msg(int nr)145 const char *my_get_err_msg(int nr)
146 {
147 const char *format;
148 struct my_err_head *meh_p;
149
150 /* Search for the range this error is in. */
151 for (meh_p= my_errmsgs_list; meh_p; meh_p= meh_p->meh_next)
152 if (nr <= meh_p->meh_last)
153 break;
154
155 /*
156 If we found the range this error number is in, get the format string.
157 If the string is empty, or a NULL pointer, or if we're out of return,
158 we return NULL.
159 */
160 if (!(format= (meh_p && (nr >= meh_p->meh_first)) ?
161 meh_p->get_errmsgs()[nr - meh_p->meh_first] : NULL) ||
162 !*format)
163 return NULL;
164
165 return format;
166 }
167
168
169 /**
170 Fill in and print a previously registered error message.
171
172 @note
173 Goes through the (sole) function registered in error_handler_hook
174
175 @param nr error number
176 @param MyFlags Flags
177 @param ... variable list matching that error format string
178 */
179
my_error(int nr,myf MyFlags,...)180 void my_error(int nr, myf MyFlags, ...)
181 {
182 const char *format;
183 va_list args;
184 char ebuff[ERRMSGSIZE];
185 DBUG_ENTER("my_error");
186 DBUG_PRINT("my", ("nr: %d MyFlags: %d errno: %d", nr, MyFlags, errno));
187
188 if (!(format = my_get_err_msg(nr)))
189 (void) my_snprintf(ebuff, sizeof(ebuff), "Unknown error %d", nr);
190 else
191 {
192 va_start(args,MyFlags);
193 (void) my_vsnprintf_ex(&my_charset_utf8_general_ci, ebuff,
194 sizeof(ebuff), format, args);
195 va_end(args);
196 }
197 (*error_handler_hook)(nr, ebuff, MyFlags);
198 DBUG_VOID_RETURN;
199 }
200
201
202 /**
203 Print an error message.
204
205 @note
206 Goes through the (sole) function registered in error_handler_hook
207
208 @param error error number
209 @param format format string
210 @param MyFlags Flags
211 @param ... variable list matching that error format string
212 */
213
my_printf_error(uint error,const char * format,myf MyFlags,...)214 void my_printf_error(uint error, const char *format, myf MyFlags, ...)
215 {
216 va_list args;
217 char ebuff[ERRMSGSIZE];
218 DBUG_ENTER("my_printf_error");
219 DBUG_PRINT("my", ("nr: %d MyFlags: %d errno: %d Format: %s",
220 error, MyFlags, errno, format));
221
222 va_start(args,MyFlags);
223 (void) my_vsnprintf_ex(&my_charset_utf8_general_ci, ebuff,
224 sizeof(ebuff), format, args);
225 va_end(args);
226 (*error_handler_hook)(error, ebuff, MyFlags);
227 DBUG_VOID_RETURN;
228 }
229
230 /**
231 Print an error message.
232
233 @note
234 Goes through the (sole) function registered in error_handler_hook
235
236 @param error error number
237 @param format format string
238 @param MyFlags Flags
239 @param ap variable list matching that error format string
240 */
241
my_printv_error(uint error,const char * format,myf MyFlags,va_list ap)242 void my_printv_error(uint error, const char *format, myf MyFlags, va_list ap)
243 {
244 char ebuff[ERRMSGSIZE];
245 DBUG_ENTER("my_printv_error");
246 DBUG_PRINT("my", ("nr: %d MyFlags: %d errno: %d format: %s",
247 error, MyFlags, errno, format));
248
249 (void) my_vsnprintf(ebuff, sizeof(ebuff), format, ap);
250 (*error_handler_hook)(error, ebuff, MyFlags);
251 DBUG_VOID_RETURN;
252 }
253
254 /*
255 Warning as printf
256
257 SYNOPSIS
258 my_printf_warning()
259 format> Format string
260 ...> variable list
261 */
262 void(*sql_print_warning_hook)(const char *format,...);
my_printf_warning(const char * format,...)263 void my_printf_warning(const char *format, ...)
264 {
265 va_list args;
266 char wbuff[ERRMSGSIZE];
267 DBUG_ENTER("my_printf_warning");
268 DBUG_PRINT("my", ("Format: %s", format));
269 va_start(args,format);
270 (void) my_vsnprintf (wbuff, sizeof(wbuff), format, args);
271 va_end(args);
272 (*sql_print_warning_hook)(wbuff);
273 DBUG_VOID_RETURN;
274 }
275
276 /**
277 Print an error message.
278
279 @note
280 Goes through the (sole) function registered in error_handler_hook
281
282 @param error error number
283 @param str error message
284 @param MyFlags Flags
285 */
286
my_message(uint error,const char * str,myf MyFlags)287 void my_message(uint error, const char *str, myf MyFlags)
288 {
289 (*error_handler_hook)(error, str, MyFlags);
290 }
291
292
293 /**
294 Register error messages for use with my_error().
295
296 @description
297
298 The pointer array is expected to contain addresses to NUL-terminated
299 C character strings. The array contains (last - first + 1) pointers.
300 NULL pointers and empty strings ("") are allowed. These will be mapped to
301 "Unknown error" when my_error() is called with a matching error number.
302 This function registers the error numbers 'first' to 'last'.
303 No overlapping with previously registered error numbers is allowed.
304
305 @param errmsgs array of pointers to error messages
306 @param first error number of first message in the array
307 @param last error number of last message in the array
308
309 @retval 0 OK
310 @retval != 0 Error
311 */
312
my_error_register(const char ** (* get_errmsgs)(),int first,int last)313 int my_error_register(const char** (*get_errmsgs) (), int first, int last)
314 {
315 struct my_err_head *meh_p;
316 struct my_err_head **search_meh_pp;
317
318 /* Allocate a new header structure. */
319 if (! (meh_p= (struct my_err_head*) my_malloc(sizeof(struct my_err_head),
320 MYF(MY_WME))))
321 return 1;
322 meh_p->get_errmsgs= get_errmsgs;
323 meh_p->meh_first= first;
324 meh_p->meh_last= last;
325
326 /* Search for the right position in the list. */
327 for (search_meh_pp= &my_errmsgs_list;
328 *search_meh_pp;
329 search_meh_pp= &(*search_meh_pp)->meh_next)
330 {
331 if ((*search_meh_pp)->meh_last > first)
332 break;
333 }
334
335 /* Error numbers must be unique. No overlapping is allowed. */
336 if (*search_meh_pp && ((*search_meh_pp)->meh_first <= last))
337 {
338 my_free(meh_p);
339 return 1;
340 }
341
342 /* Insert header into the chain. */
343 meh_p->meh_next= *search_meh_pp;
344 *search_meh_pp= meh_p;
345 return 0;
346 }
347
348
349 /**
350 Unregister formerly registered error messages.
351
352 @description
353
354 This function unregisters the error numbers 'first' to 'last'.
355 These must have been previously registered by my_error_register().
356 'first' and 'last' must exactly match the registration.
357 If a matching registration is present, the header is removed from the
358 list and the pointer to the error messages pointers array is returned.
359 (The messages themselves are not released here as they may be static.)
360 Otherwise, NULL is returned.
361
362 @param first error number of first message
363 @param last error number of last message
364
365 @retval NULL Error, no such number range registered.
366 @retval non-NULL OK, returns address of error messages pointers array.
367 */
368
my_error_unregister(int first,int last)369 const char **my_error_unregister(int first, int last)
370 {
371 struct my_err_head *meh_p;
372 struct my_err_head **search_meh_pp;
373 const char **errmsgs;
374
375 /* Search for the registration in the list. */
376 for (search_meh_pp= &my_errmsgs_list;
377 *search_meh_pp;
378 search_meh_pp= &(*search_meh_pp)->meh_next)
379 {
380 if (((*search_meh_pp)->meh_first == first) &&
381 ((*search_meh_pp)->meh_last == last))
382 break;
383 }
384 if (! *search_meh_pp)
385 return NULL;
386
387 /* Remove header from the chain. */
388 meh_p= *search_meh_pp;
389 *search_meh_pp= meh_p->meh_next;
390
391 /* Save the return value and free the header. */
392 errmsgs= meh_p->get_errmsgs();
393 my_free(meh_p);
394
395 return errmsgs;
396 }
397
398
399 /**
400 Unregister all formerly registered error messages.
401
402 @description
403
404 This function unregisters all error numbers that previously have
405 been previously registered by my_error_register().
406 All headers are removed from the list; the messages themselves are
407 not released here as they may be static.
408 */
409
my_error_unregister_all(void)410 void my_error_unregister_all(void)
411 {
412 struct my_err_head *cursor, *saved_next;
413
414 for (cursor= my_errmsgs_globerrs.meh_next; cursor != NULL; cursor= saved_next)
415 {
416 /* We need this ptr, but we're about to free its container, so save it. */
417 saved_next= cursor->meh_next;
418
419 my_free(cursor);
420 }
421 my_errmsgs_globerrs.meh_next= NULL; /* Freed in first iteration above. */
422
423 my_errmsgs_list= &my_errmsgs_globerrs;
424 }
425