/src/server/mysys/my_error.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (c) 2000, 2013, 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 as published by |
5 | | the Free Software Foundation; version 2 of the License. |
6 | | |
7 | | This program is distributed in the hope that it will be useful, |
8 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
9 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
10 | | GNU General Public License for more details. |
11 | | |
12 | | You should have received a copy of the GNU General Public License |
13 | | along with this program; if not, write to the Free Software |
14 | | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ |
15 | | |
16 | | #include "mysys_priv.h" |
17 | | #include "mysys_err.h" |
18 | | #include <m_string.h> |
19 | | #include <stdarg.h> |
20 | | #include <m_ctype.h> |
21 | | |
22 | | /* Max length of a error message. Should be kept in sync with MYSQL_ERRMSG_SIZE. */ |
23 | | #define ERRMSGSIZE (512) |
24 | | |
25 | | /* Define some external variables for error handling */ |
26 | | |
27 | | /* |
28 | | WARNING! |
29 | | my_error family functions have to be used according following rules: |
30 | | - if message have not parameters use my_message(ER_CODE, ER(ER_CODE), MYF(N)) |
31 | | - if message registered use my_error(ER_CODE, MYF(N), ...). |
32 | | - With some special text of errror message use: |
33 | | my_printf_error(ER_CODE, format, MYF(N), ...) |
34 | | */ |
35 | | |
36 | | /* |
37 | | Message texts are registered into a linked list of 'my_err_head' structs. |
38 | | Each struct contains (1.) an array of pointers to C character strings with |
39 | | '\0' termination, (2.) the error number for the first message in the array |
40 | | (array index 0) and (3.) the error number for the last message in the array |
41 | | (array index (last - first)). |
42 | | The array may contain gaps with NULL pointers and pointers to empty strings. |
43 | | Both kinds of gaps will be translated to "Unknown error %d.", if my_error() |
44 | | is called with a respective error number. |
45 | | The list of header structs is sorted in increasing order of error numbers. |
46 | | Negative error numbers are allowed. Overlap of error numbers is not allowed. |
47 | | Not registered error numbers will be translated to "Unknown error %d.". |
48 | | */ |
49 | | static struct my_err_head |
50 | | { |
51 | | struct my_err_head *meh_next; /* chain link */ |
52 | | const char** (*get_errmsgs)(int nr); /* returns error message format */ |
53 | | uint meh_first; /* error number matching array slot 0 */ |
54 | | uint meh_last; /* error number matching last slot */ |
55 | | } my_errmsgs_globerrs= |
56 | | {NULL, get_global_errmsgs, EE_ERROR_FIRST, EE_ERROR_LAST}; |
57 | | |
58 | | static struct my_err_head *my_errmsgs_list= &my_errmsgs_globerrs; |
59 | | |
60 | | |
61 | | /** |
62 | | @brief Get an error format string from one of the my_error_register()ed sets |
63 | | |
64 | | @note |
65 | | NULL values are possible even within a registered range. |
66 | | |
67 | | @param nr Errno |
68 | | |
69 | | @retval NULL if no message is registered for this error number |
70 | | @retval str C-string |
71 | | */ |
72 | | |
73 | | const char *my_get_err_msg(uint nr) |
74 | 0 | { |
75 | 0 | const char *format; |
76 | 0 | struct my_err_head *meh_p; |
77 | | |
78 | | /* Search for the range this error is in. */ |
79 | 0 | for (meh_p= my_errmsgs_list; meh_p; meh_p= meh_p->meh_next) |
80 | 0 | if (nr <= meh_p->meh_last) |
81 | 0 | break; |
82 | | |
83 | | /* |
84 | | If we found the range this error number is in, get the format string. |
85 | | If the string is empty, or a NULL pointer, or if we're out of return, |
86 | | we return NULL. |
87 | | */ |
88 | 0 | if (!(format= (meh_p && (nr >= meh_p->meh_first)) ? |
89 | 0 | meh_p->get_errmsgs(nr)[nr - meh_p->meh_first] : NULL) || |
90 | 0 | !*format) |
91 | 0 | return NULL; |
92 | | |
93 | 0 | return format; |
94 | 0 | } |
95 | | |
96 | | |
97 | | /** |
98 | | Fill in and print a previously registered error message. |
99 | | |
100 | | @note |
101 | | Goes through the (sole) function registered in error_handler_hook |
102 | | |
103 | | @param nr error number |
104 | | @param MyFlags Flags |
105 | | @param ... variable list matching that error format string |
106 | | */ |
107 | | |
108 | | void my_error(uint nr, myf MyFlags, ...) |
109 | 0 | { |
110 | 0 | const char *format; |
111 | 0 | va_list args; |
112 | 0 | char ebuff[ERRMSGSIZE]; |
113 | 0 | DBUG_ENTER("my_error"); |
114 | 0 | DBUG_PRINT("my", ("nr: %d MyFlags: %lu errno: %d", nr, MyFlags, errno)); |
115 | 0 | if (!(format = my_get_err_msg(nr))) |
116 | 0 | (void) my_snprintf(ebuff, sizeof(ebuff), "Unknown error %d", nr); |
117 | 0 | else |
118 | 0 | { |
119 | 0 | va_start(args,MyFlags); |
120 | 0 | (void) my_vsnprintf_ex(&my_charset_utf8mb3_general_ci, ebuff, |
121 | 0 | sizeof(ebuff), format, args); |
122 | 0 | va_end(args); |
123 | 0 | } |
124 | 0 | (*error_handler_hook)(nr, ebuff, MyFlags); |
125 | 0 | DBUG_VOID_RETURN; |
126 | 0 | } |
127 | | |
128 | | |
129 | | /** |
130 | | Print an error message. |
131 | | |
132 | | @note |
133 | | Just like my_error, but for cases when the error message is not ER(error) |
134 | | |
135 | | @param error error number |
136 | | @param format format string |
137 | | @param MyFlags Flags |
138 | | @param ... variable list matching that error format string |
139 | | */ |
140 | | |
141 | | void my_printf_error(uint error, const char *format, myf MyFlags, ...) |
142 | 0 | { |
143 | 0 | va_list args; |
144 | 0 | char ebuff[ERRMSGSIZE]; |
145 | 0 | DBUG_ENTER("my_printf_error"); |
146 | 0 | DBUG_PRINT("my", ("nr: %d MyFlags: %lu errno: %d format: %s", |
147 | 0 | error, MyFlags, errno, format)); |
148 | |
|
149 | 0 | va_start(args,MyFlags); |
150 | 0 | (void) my_vsnprintf_ex(&my_charset_utf8mb3_general_ci, ebuff, |
151 | 0 | sizeof(ebuff), format, args); |
152 | 0 | va_end(args); |
153 | 0 | (*error_handler_hook)(error, ebuff, MyFlags); |
154 | 0 | DBUG_VOID_RETURN; |
155 | 0 | } |
156 | | |
157 | | /** |
158 | | Print an error message. |
159 | | |
160 | | @note |
161 | | Goes through the (sole) function registered in error_handler_hook |
162 | | |
163 | | @param error error number |
164 | | @param format format string |
165 | | @param MyFlags Flags |
166 | | @param ap variable list matching that error format string |
167 | | */ |
168 | | |
169 | | void my_printv_error(uint error, const char *format, myf MyFlags, va_list ap) |
170 | 0 | { |
171 | 0 | char ebuff[ERRMSGSIZE]; |
172 | 0 | DBUG_ENTER("my_printv_error"); |
173 | 0 | DBUG_PRINT("my", ("nr: %d MyFlags: %lu errno: %d format: %s", |
174 | 0 | error, MyFlags, errno, format)); |
175 | |
|
176 | 0 | (void) my_vsnprintf(ebuff, sizeof(ebuff), format, ap); |
177 | 0 | (*error_handler_hook)(error, ebuff, MyFlags); |
178 | 0 | DBUG_VOID_RETURN; |
179 | 0 | } |
180 | | |
181 | | |
182 | | /** |
183 | | Print an error message. |
184 | | |
185 | | @note |
186 | | Goes through the (sole) function registered in error_handler_hook |
187 | | |
188 | | @param error error number |
189 | | @param str error message |
190 | | @param MyFlags Flags |
191 | | */ |
192 | | |
193 | | void my_message(uint error, const char *str, register myf MyFlags) |
194 | 0 | { |
195 | 0 | (*error_handler_hook)(error, str, MyFlags); |
196 | 0 | } |
197 | | |
198 | | |
199 | | /** |
200 | | Register error messages for use with my_error(). |
201 | | |
202 | | @description |
203 | | |
204 | | The pointer array is expected to contain addresses to NUL-terminated |
205 | | C character strings. The array contains (last - first + 1) pointers. |
206 | | NULL pointers and empty strings ("") are allowed. These will be mapped to |
207 | | "Unknown error" when my_error() is called with a matching error number. |
208 | | This function registers the error numbers 'first' to 'last'. |
209 | | No overlapping with previously registered error numbers is allowed. |
210 | | |
211 | | @param errmsgs array of pointers to error messages |
212 | | @param first error number of first message in the array |
213 | | @param last error number of last message in the array |
214 | | |
215 | | @retval 0 OK |
216 | | @retval != 0 Error |
217 | | */ |
218 | | |
219 | | int my_error_register(const char** (*get_errmsgs)(int error), uint first, |
220 | | uint last) |
221 | 0 | { |
222 | 0 | struct my_err_head *meh_p; |
223 | 0 | struct my_err_head **search_meh_pp; |
224 | | |
225 | | /* Allocate a new header structure. */ |
226 | 0 | if (! (meh_p= (struct my_err_head*) my_malloc(key_memory_my_err_head, |
227 | 0 | sizeof(struct my_err_head), |
228 | 0 | MYF(MY_WME)))) |
229 | 0 | return 1; |
230 | 0 | meh_p->get_errmsgs= get_errmsgs; |
231 | 0 | meh_p->meh_first= first; |
232 | 0 | meh_p->meh_last= last; |
233 | | |
234 | | /* Search for the right position in the list. */ |
235 | 0 | for (search_meh_pp= &my_errmsgs_list; |
236 | 0 | *search_meh_pp; |
237 | 0 | search_meh_pp= &(*search_meh_pp)->meh_next) |
238 | 0 | { |
239 | 0 | if ((*search_meh_pp)->meh_last > first) |
240 | 0 | break; |
241 | 0 | } |
242 | | |
243 | | /* Error numbers must be unique. No overlapping is allowed. */ |
244 | 0 | if (*search_meh_pp && ((*search_meh_pp)->meh_first <= last)) |
245 | 0 | { |
246 | 0 | my_free(meh_p); |
247 | 0 | return 1; |
248 | 0 | } |
249 | | |
250 | | /* Insert header into the chain. */ |
251 | 0 | meh_p->meh_next= *search_meh_pp; |
252 | 0 | *search_meh_pp= meh_p; |
253 | 0 | return 0; |
254 | 0 | } |
255 | | |
256 | | |
257 | | /** |
258 | | Unregister formerly registered error messages. |
259 | | |
260 | | @description |
261 | | |
262 | | This function unregisters the error numbers 'first' to 'last'. |
263 | | These must have been previously registered by my_error_register(). |
264 | | 'first' and 'last' must exactly match the registration. |
265 | | If a matching registration is present, the header is removed from the |
266 | | list and the pointer to the error messages pointers array is returned. |
267 | | (The messages themselves are not released here as they may be static.) |
268 | | Otherwise, NULL is returned. |
269 | | |
270 | | @param first error number of first message |
271 | | @param last error number of last message |
272 | | |
273 | | @retval NULL Error, no such number range registered. |
274 | | @retval non-NULL OK, returns address of error messages pointers array. |
275 | | */ |
276 | | |
277 | | my_bool my_error_unregister(uint first, uint last) |
278 | 0 | { |
279 | 0 | struct my_err_head *meh_p; |
280 | 0 | struct my_err_head **search_meh_pp; |
281 | | |
282 | | /* Search for the registration in the list. */ |
283 | 0 | for (search_meh_pp= &my_errmsgs_list; |
284 | 0 | *search_meh_pp; |
285 | 0 | search_meh_pp= &(*search_meh_pp)->meh_next) |
286 | 0 | { |
287 | 0 | if (((*search_meh_pp)->meh_first == first) && |
288 | 0 | ((*search_meh_pp)->meh_last == last)) |
289 | 0 | break; |
290 | 0 | } |
291 | 0 | if (! *search_meh_pp) |
292 | 0 | return TRUE; |
293 | | |
294 | | /* Remove header from the chain. */ |
295 | 0 | meh_p= *search_meh_pp; |
296 | 0 | *search_meh_pp= meh_p->meh_next; |
297 | |
|
298 | 0 | my_free(meh_p); |
299 | | |
300 | 0 | return FALSE; |
301 | 0 | } |
302 | | |
303 | | |
304 | | /** |
305 | | Unregister all formerly registered error messages. |
306 | | |
307 | | @description |
308 | | |
309 | | This function unregisters all error numbers that previously have |
310 | | been previously registered by my_error_register(). |
311 | | All headers are removed from the list; the messages themselves are |
312 | | not released here as they may be static. |
313 | | */ |
314 | | |
315 | | void my_error_unregister_all(void) |
316 | 0 | { |
317 | 0 | struct my_err_head *cursor, *saved_next; |
318 | |
|
319 | 0 | for (cursor= my_errmsgs_globerrs.meh_next; cursor != NULL; cursor= saved_next) |
320 | 0 | { |
321 | | /* We need this ptr, but we're about to free its container, so save it. */ |
322 | 0 | saved_next= cursor->meh_next; |
323 | |
|
324 | 0 | my_free(cursor); |
325 | 0 | } |
326 | 0 | my_errmsgs_globerrs.meh_next= NULL; /* Freed in first iteration above. */ |
327 | |
|
328 | 0 | my_errmsgs_list= &my_errmsgs_globerrs; |
329 | 0 | } |