WIP
[m6w6/libmemcached] / src / libmemcached / error.cc
1 /*
2 +--------------------------------------------------------------------+
3 | libmemcached - C/C++ Client Library for memcached |
4 +--------------------------------------------------------------------+
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted under the terms of the BSD license. |
7 | You should have received a copy of the license in a bundled file |
8 | named LICENSE; in case you did not receive a copy you can review |
9 | the terms online at: https://opensource.org/licenses/BSD-3-Clause |
10 +--------------------------------------------------------------------+
11 | Copyright (c) 2006-2014 Brian Aker https://datadifferential.com/ |
12 | Copyright (c) 2020 Michael Wallner <mike@php.net> |
13 +--------------------------------------------------------------------+
14 */
15
16 #include "libmemcached/common.h"
17
18 #include "libmemcached/assert.hpp"
19
20 #include <cerrno>
21 #include <cstdarg>
22 #include <cstdio>
23
24 #define MAX_ERROR_LENGTH 2048
25 struct memcached_error_t {
26 Memcached *root;
27 uint64_t query_id;
28 struct memcached_error_t *next;
29 memcached_return_t rc;
30 int local_errno;
31 size_t size;
32 char message[MAX_ERROR_LENGTH];
33 };
34
35 static void _set(memcached_instance_st &server, Memcached &memc) {
36 if (server.error_messages and server.error_messages->query_id != server.root->query_id) {
37 memcached_error_free(server);
38 }
39
40 if (memc.error_messages) {
41 if (memc.error_messages->rc == MEMCACHED_TIMEOUT) {
42 server.io_wait_count.timeouts++;
43 }
44
45 memcached_error_t *error = libmemcached_xmalloc(&memc, memcached_error_t);
46 if (error) {
47 memcpy(error, memc.error_messages, sizeof(memcached_error_t));
48 error->next = server.error_messages;
49 server.error_messages = error;
50 }
51 }
52 }
53
54 #if 0
55 static int error_log_fd= -1;
56 #endif
57
58 static void _set(Memcached &memc, memcached_string_t *str, memcached_return_t &rc, const char *at,
59 int local_errno = 0) {
60 if (memc.error_messages && memc.error_messages->query_id != memc.query_id) {
61 memcached_error_free(memc);
62 }
63
64 if (memcached_fatal(rc) or rc == MEMCACHED_CLIENT_ERROR) {
65 // For memory allocation we use our error since it is a bit more specific
66 if (local_errno == ENOMEM and rc == MEMCACHED_ERRNO) {
67 rc = MEMCACHED_MEMORY_ALLOCATION_FAILURE;
68 }
69
70 if (rc == MEMCACHED_MEMORY_ALLOCATION_FAILURE) {
71 local_errno = ENOMEM;
72 }
73
74 if (rc == MEMCACHED_ERRNO and not local_errno) {
75 local_errno = errno;
76 rc = MEMCACHED_ERRNO;
77 }
78
79 if (rc == MEMCACHED_ERRNO and local_errno == ENOTCONN) {
80 rc = MEMCACHED_CONNECTION_FAILURE;
81 }
82
83 if (rc == MEMCACHED_ERRNO and local_errno == ECONNRESET) {
84 rc = MEMCACHED_CONNECTION_FAILURE;
85 }
86
87 if (local_errno == EINVAL) {
88 rc = MEMCACHED_INVALID_ARGUMENTS;
89 }
90
91 if (local_errno == ECONNREFUSED) {
92 rc = MEMCACHED_CONNECTION_FAILURE;
93 }
94
95 if (rc == MEMCACHED_TIMEOUT) {
96 }
97
98 memcached_error_t *error = libmemcached_xmalloc(&memc, memcached_error_t);
99 if (error == NULL) // Bad business if this happens
100 {
101 assert_msg(error, "libmemcached_xmalloc() failed to allocate a memcached_error_t");
102 return;
103 }
104
105 error->root = &memc;
106 error->query_id = memc.query_id;
107 error->rc = rc;
108 error->local_errno = local_errno;
109
110 // MEMCACHED_CLIENT_ERROR is a special case because it is an error coming from the server
111 if (rc == MEMCACHED_CLIENT_ERROR) {
112 assert(str);
113 assert(str->size);
114 if (str and str->size) {
115 assert(error->local_errno == 0);
116 error->local_errno = 0;
117
118 error->size = (int) snprintf(error->message, MAX_ERROR_LENGTH, "(%p) %.*s", error->root,
119 int(str->size), str->c_str);
120 }
121 } else if (local_errno) {
122 const char *errmsg_ptr;
123 char errmsg[MAX_ERROR_LENGTH];
124 errmsg[0] = 0;
125 errmsg_ptr = errmsg;
126
127 #if defined(HAVE_STRERROR_R_CHAR_P) && HAVE_STRERROR_R_CHAR_P
128 errmsg_ptr = strerror_r(local_errno, errmsg, sizeof(errmsg));
129 #elif defined(HAVE_STRERROR_R) && HAVE_STRERROR_R
130 strerror_r(local_errno, errmsg, sizeof(errmsg));
131 errmsg_ptr = errmsg;
132 #else
133 snprintf(errmsg, sizeof(errmsg), "%s", strerror(local_errno));
134 errmsg_ptr = errmsg;
135 #endif
136
137 if (str and str->size and local_errno) {
138 error->size = (int) snprintf(error->message, MAX_ERROR_LENGTH, "(%p) %s(%s), %.*s -> %s",
139 error->root, memcached_strerror(&memc, rc), errmsg_ptr,
140 memcached_string_printf(*str), at);
141 } else {
142 error->size = (int) snprintf(error->message, MAX_ERROR_LENGTH, "(%p) %s(%s) -> %s",
143 error->root, memcached_strerror(&memc, rc), errmsg_ptr, at);
144 }
145 } else if (rc == MEMCACHED_PARSE_ERROR and str and str->size) {
146 error->size = (int) snprintf(error->message, MAX_ERROR_LENGTH, "(%p) %.*s -> %s", error->root,
147 int(str->size), str->c_str, at);
148 } else if (str and str->size) {
149 error->size =
150 (int) snprintf(error->message, MAX_ERROR_LENGTH, "(%p) %s, %.*s -> %s", error->root,
151 memcached_strerror(&memc, rc), int(str->size), str->c_str, at);
152 } else {
153 error->size = (int) snprintf(error->message, MAX_ERROR_LENGTH, "(%p) %s -> %s", error->root,
154 memcached_strerror(&memc, rc), at);
155 }
156
157 error->next = memc.error_messages;
158 memc.error_messages = error;
159 #if 0
160 if (error_log_fd == -1)
161 {
162 // unlink("/tmp/libmemcachd.log");
163 if ((error_log_fd= open("/tmp/libmemcachd.log", O_CREAT | O_WRONLY | O_APPEND, 0644)) < 0)
164 {
165 perror("open");
166 error_log_fd= -1;
167 }
168 }
169 ::write(error_log_fd, error->message, error->size);
170 ::write(error_log_fd, "\n", 1);
171 #endif
172 }
173 }
174
175 memcached_return_t memcached_set_error(Memcached &memc, memcached_return_t rc, const char *at,
176 const char *str, size_t length) {
177 assert_msg(rc != MEMCACHED_ERRNO,
178 "Programmer error, MEMCACHED_ERRNO was set to be returned to client");
179 memcached_string_t tmp = {str, length};
180 return memcached_set_error(memc, rc, at, tmp);
181 }
182
183 memcached_return_t memcached_set_error(memcached_instance_st &self, memcached_return_t rc,
184 const char *at, const char *str, size_t length) {
185 assert_msg(rc != MEMCACHED_ERRNO,
186 "Programmer error, MEMCACHED_ERRNO was set to be returned to client");
187 assert_msg(rc != MEMCACHED_SOME_ERRORS,
188 "Programmer error, MEMCACHED_SOME_ERRORS was about to be set on a Instance");
189
190 memcached_string_t tmp = {str, length};
191 return memcached_set_error(self, rc, at, tmp);
192 }
193
194 #ifndef __INTEL_COMPILER
195 # pragma GCC diagnostic ignored "-Wformat-nonliteral"
196 #endif
197
198 memcached_return_t memcached_set_error(Memcached &memc, memcached_return_t rc, const char *at,
199 memcached_string_t &str) {
200 assert_msg(rc != MEMCACHED_ERRNO,
201 "Programmer error, MEMCACHED_ERRNO was set to be returned to client");
202 if (memcached_fatal(rc)) {
203 _set(memc, &str, rc, at);
204 }
205
206 return rc;
207 }
208
209 memcached_return_t memcached_set_parser_error(Memcached &memc, const char *at, const char *format,
210 ...) {
211 va_list args;
212
213 char buffer[BUFSIZ];
214 va_start(args, format);
215 int length = vsnprintf(buffer, sizeof(buffer), format, args);
216 va_end(args);
217
218 return memcached_set_error(memc, MEMCACHED_PARSE_ERROR, at, buffer, length);
219 }
220
221 static inline size_t append_host_to_string(memcached_instance_st &self, char *buffer,
222 const size_t buffer_length) {
223 size_t size = 0;
224 switch (self.type) {
225 case MEMCACHED_CONNECTION_TCP:
226 case MEMCACHED_CONNECTION_UDP:
227 size += snprintf(buffer, buffer_length, " host: %s:%d", self.hostname(), int(self.port()));
228 break;
229
230 case MEMCACHED_CONNECTION_UNIX_SOCKET:
231 size += snprintf(buffer, buffer_length, " socket: %s", self.hostname());
232 break;
233 }
234
235 return size;
236 }
237
238 memcached_return_t memcached_set_error(memcached_instance_st &self, memcached_return_t rc,
239 const char *at, memcached_string_t &str) {
240 assert_msg(rc != MEMCACHED_ERRNO,
241 "Programmer error, MEMCACHED_ERRNO was set to be returned to client");
242 assert_msg(
243 rc != MEMCACHED_SOME_ERRORS,
244 "Programmer error, MEMCACHED_SOME_ERRORS was about to be set on a memcached_instance_st");
245 if (memcached_fatal(rc) == false and rc != MEMCACHED_CLIENT_ERROR) {
246 return rc;
247 }
248
249 char hostname_port_message[MAX_ERROR_LENGTH];
250 char *hostname_port_message_ptr = hostname_port_message;
251 int size = 0;
252 if (str.size) {
253 size = snprintf(hostname_port_message_ptr, sizeof(hostname_port_message), "%.*s, ",
254 memcached_string_printf(str));
255 hostname_port_message_ptr += size;
256 }
257
258 size +=
259 append_host_to_string(self, hostname_port_message_ptr, sizeof(hostname_port_message) - size);
260
261 memcached_string_t error_host = {hostname_port_message, size_t(size)};
262
263 assert_msg(self.root, "Programmer error, root was not set on instance");
264 if (self.root) {
265 _set(*self.root, &error_host, rc, at);
266 _set(self, (*self.root));
267 assert(self.error_messages);
268 assert(self.root->error_messages);
269 assert(self.error_messages->rc == self.root->error_messages->rc);
270 }
271
272 return rc;
273 }
274
275 memcached_return_t memcached_set_error(memcached_instance_st &self, memcached_return_t rc,
276 const char *at) {
277 assert_msg(
278 rc != MEMCACHED_SOME_ERRORS,
279 "Programmer error, MEMCACHED_SOME_ERRORS was about to be set on a memcached_instance_st");
280 if (memcached_fatal(rc) == false) {
281 return rc;
282 }
283
284 char hostname_port[MEMCACHED_NI_MAXHOST + MEMCACHED_NI_MAXSERV + sizeof("host : ")];
285 size_t size = append_host_to_string(self, hostname_port, sizeof(hostname_port));
286
287 memcached_string_t error_host = {hostname_port, size};
288
289 if (self.root) {
290 _set(*self.root, &error_host, rc, at);
291 _set(self, *self.root);
292 }
293
294 return rc;
295 }
296
297 memcached_return_t memcached_set_error(Memcached &self, memcached_return_t rc, const char *at) {
298 assert_msg(rc != MEMCACHED_ERRNO,
299 "Programmer error, MEMCACHED_ERRNO was set to be returned to client");
300 if (memcached_fatal(rc) == false) {
301 return rc;
302 }
303
304 _set(self, NULL, rc, at);
305
306 return rc;
307 }
308
309 memcached_return_t memcached_set_errno(Memcached &self, int local_errno, const char *at,
310 const char *str, size_t length) {
311 memcached_string_t tmp = {str, length};
312 return memcached_set_errno(self, local_errno, at, tmp);
313 }
314
315 memcached_return_t memcached_set_errno(memcached_instance_st &self, int local_errno, const char *at,
316 const char *str, size_t length) {
317 memcached_string_t tmp = {str, length};
318 return memcached_set_errno(self, local_errno, at, tmp);
319 }
320
321 memcached_return_t memcached_set_errno(Memcached &self, int local_errno, const char *at) {
322 if (local_errno == 0) {
323 return MEMCACHED_SUCCESS;
324 }
325
326 memcached_return_t rc = MEMCACHED_ERRNO;
327 _set(self, NULL, rc, at, local_errno);
328
329 return rc;
330 }
331
332 memcached_return_t memcached_set_errno(Memcached &memc, int local_errno, const char *at,
333 memcached_string_t &str) {
334 if (local_errno == 0) {
335 return MEMCACHED_SUCCESS;
336 }
337
338 memcached_return_t rc = MEMCACHED_ERRNO;
339 _set(memc, &str, rc, at, local_errno);
340
341 return rc;
342 }
343
344 memcached_return_t memcached_set_errno(memcached_instance_st &self, int local_errno, const char *at,
345 memcached_string_t &str) {
346 if (local_errno == 0) {
347 return MEMCACHED_SUCCESS;
348 }
349
350 char hostname_port_message[MAX_ERROR_LENGTH];
351 char *hostname_port_message_ptr = hostname_port_message;
352 size_t size = 0;
353 if (str.size) {
354 size = snprintf(hostname_port_message_ptr, sizeof(hostname_port_message), "%.*s, ",
355 memcached_string_printf(str));
356 }
357 size +=
358 append_host_to_string(self, hostname_port_message_ptr, sizeof(hostname_port_message) - size);
359
360 memcached_string_t error_host = {hostname_port_message, size};
361
362 memcached_return_t rc = MEMCACHED_ERRNO;
363 if (self.root == NULL) {
364 return rc;
365 }
366
367 _set(*self.root, &error_host, rc, at, local_errno);
368 _set(self, (*self.root));
369
370 #if 0
371 if (self.root->error_messages->rc != self.error_messages->rc)
372 {
373 fprintf(stderr, "%s:%d %s != %s\n", __FILE__, __LINE__,
374 memcached_strerror(NULL, self.root->error_messages->rc),
375 memcached_strerror(NULL, self.error_messages->rc));
376 }
377 #endif
378
379 return rc;
380 }
381
382 memcached_return_t memcached_set_errno(memcached_instance_st &self, int local_errno,
383 const char *at) {
384 if (local_errno == 0) {
385 return MEMCACHED_SUCCESS;
386 }
387
388 char hostname_port_message[MAX_ERROR_LENGTH];
389 size_t size = append_host_to_string(self, hostname_port_message, sizeof(hostname_port_message));
390
391 memcached_string_t error_host = {hostname_port_message, size};
392
393 memcached_return_t rc = MEMCACHED_ERRNO;
394 if (self.root == NULL) {
395 return rc;
396 }
397
398 _set(*self.root, &error_host, rc, at, local_errno);
399 _set(self, (*self.root));
400
401 return rc;
402 }
403
404 static void _error_print(const memcached_error_t *error) {
405 if (error == NULL) {
406 return;
407 }
408
409 if (error->size == 0) {
410 fprintf(stderr, "\t%s\n", memcached_strerror(NULL, error->rc));
411 } else {
412 fprintf(stderr, "\t%s %s\n", memcached_strerror(NULL, error->rc), error->message);
413 }
414
415 _error_print(error->next);
416 }
417
418 void memcached_error_print(const Memcached *shell) {
419 const Memcached *self = memcached2Memcached(shell);
420 if (self == NULL) {
421 return;
422 }
423
424 _error_print(self->error_messages);
425
426 for (uint32_t x = 0; x < memcached_server_count(self); x++) {
427 memcached_instance_st *instance = memcached_instance_by_position(self, x);
428
429 _error_print(instance->error_messages);
430 }
431 }
432
433 static void _error_free(memcached_error_t *error) {
434 if (error) {
435 _error_free(error->next);
436
437 libmemcached_free(error->root, error);
438 }
439 }
440
441 void memcached_error_free(Memcached &self) {
442 _error_free(self.error_messages);
443 self.error_messages = NULL;
444 }
445
446 void memcached_error_free(memcached_instance_st &self) {
447 _error_free(self.error_messages);
448 self.error_messages = NULL;
449 }
450
451 void memcached_error_free(memcached_server_st &self) {
452 _error_free(self.error_messages);
453 self.error_messages = NULL;
454 }
455
456 const char *memcached_error(const memcached_st *memc) {
457 return memcached_last_error_message(memc);
458 }
459
460 const char *memcached_last_error_message(const memcached_st *shell) {
461 const Memcached *memc = memcached2Memcached(shell);
462 if (memc) {
463 if (memc->error_messages) {
464 if (memc->error_messages->size and memc->error_messages->message[0]) {
465 return memc->error_messages->message;
466 }
467
468 return memcached_strerror(memc, memc->error_messages->rc);
469 }
470
471 return memcached_strerror(memc, MEMCACHED_SUCCESS);
472 }
473
474 return memcached_strerror(memc, MEMCACHED_INVALID_ARGUMENTS);
475 }
476
477 bool memcached_has_current_error(Memcached &memc) {
478 if (memc.error_messages and memc.error_messages->query_id == memc.query_id
479 and memcached_failed(memc.error_messages->rc))
480 {
481 return true;
482 }
483
484 return false;
485 }
486
487 bool memcached_has_current_error(memcached_instance_st &server) {
488 return memcached_has_current_error(*(server.root));
489 }
490
491 memcached_return_t memcached_last_error(const memcached_st *shell) {
492 const Memcached *memc = memcached2Memcached(shell);
493 if (memc) {
494 if (memc->error_messages) {
495 return memc->error_messages->rc;
496 }
497
498 return MEMCACHED_SUCCESS;
499 }
500
501 return MEMCACHED_INVALID_ARGUMENTS;
502 }
503
504 int memcached_last_error_errno(const memcached_st *shell) {
505 const Memcached *memc = memcached2Memcached(shell);
506 if (memc == NULL) {
507 return 0;
508 }
509
510 if (memc->error_messages == NULL) {
511 return 0;
512 }
513
514 return memc->error_messages->local_errno;
515 }
516
517 const char *memcached_server_error(const memcached_instance_st *server) {
518 if (server == NULL) {
519 return NULL;
520 }
521
522 if (server->error_messages == NULL) {
523 return memcached_strerror(server->root, MEMCACHED_SUCCESS);
524 }
525
526 if (server->error_messages->size == 0) {
527 return memcached_strerror(server->root, server->error_messages->rc);
528 }
529
530 return server->error_messages->message;
531 }
532
533 memcached_error_t *memcached_error_copy(const memcached_instance_st &server) {
534 if (server.error_messages == NULL) {
535 return NULL;
536 }
537
538 memcached_error_t *error = libmemcached_xmalloc(server.root, memcached_error_t);
539 memcpy(error, server.error_messages, sizeof(memcached_error_t));
540 error->next = NULL;
541
542 return error;
543 }
544
545 memcached_return_t memcached_server_error_return(const memcached_instance_st *ptr) {
546 if (ptr == NULL) {
547 return MEMCACHED_INVALID_ARGUMENTS;
548 }
549
550 if (ptr->error_messages) {
551 return ptr->error_messages->rc;
552 }
553
554 return MEMCACHED_SUCCESS;
555 }
556
557 memcached_return_t memcached_instance_error_return(memcached_instance_st *instance) {
558 if (instance == NULL) {
559 return MEMCACHED_INVALID_ARGUMENTS;
560 }
561
562 if (instance->error_messages) {
563 return instance->error_messages->rc;
564 }
565
566 return MEMCACHED_SUCCESS;
567 }