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