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-2021 Michael Wallner https://awesome.co/ |
13 +--------------------------------------------------------------------+
16 #include "libmemcached/common.h"
18 static const char *memcached_stat_keys
[] = {"pid",
30 "connection_structures",
42 struct local_context
{
43 memcached_stat_fn func
;
46 const size_t args_length
;
48 local_context(memcached_stat_fn func_arg
, void *context_arg
, const char *args_arg
,
49 const size_t args_length_arg
)
51 , context(context_arg
)
53 , args_length(args_length_arg
) {}
56 static memcached_return_t
set_data(memcached_stat_st
*memc_stat
, const char *key
,
58 if (strlen(key
) < 1) {
59 WATCHPOINT_STRING(key
);
60 return MEMCACHED_UNKNOWN_STAT_KEY
;
61 } else if (strcmp("pid", key
) == 0) {
63 int64_t temp
= strtoll(value
, (char **) NULL
, 10);
65 return MEMCACHED_FAILURE
;
68 if (temp
<= INT32_MAX
and (sizeof(pid_t
) == sizeof(int32_t))) {
69 memc_stat
->pid
= pid_t(temp
);
70 } else if (temp
> -1) {
71 memc_stat
->pid
= pid_t(temp
);
73 // If we got a value less then -1 then something went wrong in the
76 } else if (not strcmp("uptime", key
)) {
78 memc_stat
->uptime
= strtoul(value
, (char **) NULL
, 10);
80 return MEMCACHED_FAILURE
;
82 } else if (not strcmp("time", key
)) {
84 memc_stat
->time
= strtoul(value
, (char **) NULL
, 10);
86 return MEMCACHED_FAILURE
;
88 } else if (not strcmp("version", key
)) {
89 memcpy(memc_stat
->version
, value
, strlen(value
));
90 memc_stat
->version
[strlen(value
)] = 0;
91 } else if (not strcmp("pointer_size", key
)) {
93 memc_stat
->pointer_size
= strtoul(value
, (char **) NULL
, 10);
95 return MEMCACHED_FAILURE
;
97 } else if (not strcmp("rusage_user", key
)) {
99 for (walk_ptr
= (char *) value
; (!ispunct(*walk_ptr
)); walk_ptr
++) {
105 memc_stat
->rusage_user_seconds
= strtoul(value
, (char **) NULL
, 10);
107 return MEMCACHED_FAILURE
;
111 memc_stat
->rusage_user_microseconds
= strtoul(walk_ptr
, (char **) NULL
, 10);
113 return MEMCACHED_FAILURE
;
115 } else if (not strcmp("rusage_system", key
)) {
117 for (walk_ptr
= (char *) value
; (!ispunct(*walk_ptr
)); walk_ptr
++) {
123 memc_stat
->rusage_system_seconds
= strtoul(value
, (char **) NULL
, 10);
125 return MEMCACHED_FAILURE
;
129 memc_stat
->rusage_system_microseconds
= strtoul(walk_ptr
, (char **) NULL
, 10);
131 return MEMCACHED_FAILURE
;
133 } else if (not strcmp("curr_items", key
)) {
135 memc_stat
->curr_items
= strtoul(value
, (char **) NULL
, 10);
137 return MEMCACHED_FAILURE
;
139 } else if (not strcmp("total_items", key
)) {
141 memc_stat
->total_items
= strtoul(value
, (char **) NULL
, 10);
143 return MEMCACHED_FAILURE
;
145 } else if (not strcmp("bytes_read", key
)) {
147 memc_stat
->bytes_read
= strtoull(value
, (char **) NULL
, 10);
149 return MEMCACHED_FAILURE
;
151 } else if (not strcmp("bytes_written", key
)) {
153 memc_stat
->bytes_written
= strtoull(value
, (char **) NULL
, 10);
155 return MEMCACHED_FAILURE
;
157 } else if (not strcmp("bytes", key
)) {
159 memc_stat
->bytes
= strtoull(value
, (char **) NULL
, 10);
161 return MEMCACHED_FAILURE
;
163 } else if (not strcmp("curr_connections", key
)) {
165 memc_stat
->curr_connections
= strtoull(value
, (char **) NULL
, 10);
167 return MEMCACHED_FAILURE
;
169 } else if (not strcmp("total_connections", key
)) {
171 memc_stat
->total_connections
= strtoull(value
, (char **) NULL
, 10);
173 return MEMCACHED_FAILURE
;
175 } else if (not strcmp("connection_structures", key
)) {
177 memc_stat
->connection_structures
= strtoul(value
, (char **) NULL
, 10);
179 return MEMCACHED_FAILURE
;
181 } else if (not strcmp("cmd_get", key
)) {
183 memc_stat
->cmd_get
= strtoull(value
, (char **) NULL
, 10);
185 return MEMCACHED_FAILURE
;
187 } else if (not strcmp("cmd_set", key
)) {
189 memc_stat
->cmd_set
= strtoull(value
, (char **) NULL
, 10);
191 return MEMCACHED_FAILURE
;
193 } else if (not strcmp("get_hits", key
)) {
195 memc_stat
->get_hits
= strtoull(value
, (char **) NULL
, 10);
197 return MEMCACHED_FAILURE
;
199 } else if (not strcmp("get_misses", key
)) {
201 memc_stat
->get_misses
= strtoull(value
, (char **) NULL
, 10);
203 return MEMCACHED_FAILURE
;
205 } else if (not strcmp("evictions", key
)) {
207 memc_stat
->evictions
= strtoull(value
, (char **) NULL
, 10);
209 return MEMCACHED_FAILURE
;
211 } else if (not strcmp("limit_maxbytes", key
)) {
213 memc_stat
->limit_maxbytes
= strtoull(value
, (char **) NULL
, 10);
215 return MEMCACHED_FAILURE
;
217 } else if (not strcmp("threads", key
)) {
219 memc_stat
->threads
= strtoul(value
, (char **) NULL
, 10);
221 return MEMCACHED_FAILURE
;
223 } else if ((strcmp("delete_misses", key
) == 0 or /* New stats in the 1.3 beta */
224 strcmp("delete_hits", key
) == 0 or /* Just swallow them for now.. */
225 strcmp("incr_misses", key
) == 0 or strcmp("incr_hits", key
) == 0
226 or strcmp("decr_misses", key
) == 0 or strcmp("decr_hits", key
) == 0
227 or strcmp("cas_misses", key
) == 0 or strcmp("cas_hits", key
) == 0
228 or strcmp("cas_badval", key
) == 0 or strcmp("cmd_flush", key
) == 0
229 or strcmp("accepting_conns", key
) == 0 or strcmp("listen_disabled_num", key
) == 0
230 or strcmp("conn_yields", key
) == 0 or strcmp("auth_cmds", key
) == 0
231 or strcmp("auth_errors", key
) == 0 or strcmp("reclaimed", key
) == 0)
234 WATCHPOINT_STRING(key
);
235 /* return MEMCACHED_UNKNOWN_STAT_KEY; */
236 return MEMCACHED_SUCCESS
;
239 return MEMCACHED_SUCCESS
;
242 char *memcached_stat_get_value(const memcached_st
*shell
, memcached_stat_st
*memc_stat
,
243 const char *key
, memcached_return_t
*error
) {
244 memcached_return_t not_used
;
249 if (memc_stat
== NULL
) {
250 *error
= MEMCACHED_INVALID_ARGUMENTS
;
254 char buffer
[SMALL_STRING_LEN
];
257 *error
= MEMCACHED_SUCCESS
;
259 if (!strcmp(key
, "pid")) {
260 length
= snprintf(buffer
, SMALL_STRING_LEN
, "%lld", (signed long long) memc_stat
->pid
);
261 } else if (!strcmp(key
, "uptime")) {
262 length
= snprintf(buffer
, SMALL_STRING_LEN
, "%lu", memc_stat
->uptime
);
263 } else if (!strcmp(key
, "time")) {
264 length
= snprintf(buffer
, SMALL_STRING_LEN
, "%llu", (unsigned long long) memc_stat
->time
);
265 } else if (!strcmp(key
, "version")) {
266 length
= snprintf(buffer
, SMALL_STRING_LEN
, "%s", memc_stat
->version
);
267 } else if (!strcmp(key
, "pointer_size")) {
268 length
= snprintf(buffer
, SMALL_STRING_LEN
, "%lu", memc_stat
->pointer_size
);
269 } else if (!strcmp(key
, "rusage_user")) {
270 length
= snprintf(buffer
, SMALL_STRING_LEN
, "%lu.%lu", memc_stat
->rusage_user_seconds
,
271 memc_stat
->rusage_user_microseconds
);
272 } else if (!strcmp(key
, "rusage_system")) {
273 length
= snprintf(buffer
, SMALL_STRING_LEN
, "%lu.%lu", memc_stat
->rusage_system_seconds
,
274 memc_stat
->rusage_system_microseconds
);
275 } else if (!strcmp(key
, "curr_items")) {
276 length
= snprintf(buffer
, SMALL_STRING_LEN
, "%lu", memc_stat
->curr_items
);
277 } else if (!strcmp(key
, "total_items")) {
278 length
= snprintf(buffer
, SMALL_STRING_LEN
, "%lu", memc_stat
->total_items
);
279 } else if (!strcmp(key
, "curr_connections")) {
280 length
= snprintf(buffer
, SMALL_STRING_LEN
, "%lu", memc_stat
->curr_connections
);
281 } else if (!strcmp(key
, "total_connections")) {
282 length
= snprintf(buffer
, SMALL_STRING_LEN
, "%lu", memc_stat
->total_connections
);
283 } else if (!strcmp(key
, "connection_structures")) {
284 length
= snprintf(buffer
, SMALL_STRING_LEN
, "%lu", memc_stat
->connection_structures
);
285 } else if (!strcmp(key
, "cmd_get")) {
286 length
= snprintf(buffer
, SMALL_STRING_LEN
, "%llu", (unsigned long long) memc_stat
->cmd_get
);
287 } else if (!strcmp(key
, "cmd_set")) {
288 length
= snprintf(buffer
, SMALL_STRING_LEN
, "%llu", (unsigned long long) memc_stat
->cmd_set
);
289 } else if (!strcmp(key
, "get_hits")) {
290 length
= snprintf(buffer
, SMALL_STRING_LEN
, "%llu", (unsigned long long) memc_stat
->get_hits
);
291 } else if (!strcmp(key
, "get_misses")) {
292 length
= snprintf(buffer
, SMALL_STRING_LEN
, "%llu", (unsigned long long) memc_stat
->get_misses
);
293 } else if (!strcmp(key
, "evictions")) {
294 length
= snprintf(buffer
, SMALL_STRING_LEN
, "%llu", (unsigned long long) memc_stat
->evictions
);
295 } else if (!strcmp(key
, "bytes_read")) {
296 length
= snprintf(buffer
, SMALL_STRING_LEN
, "%llu", (unsigned long long) memc_stat
->bytes_read
);
297 } else if (!strcmp(key
, "bytes_written")) {
299 snprintf(buffer
, SMALL_STRING_LEN
, "%llu", (unsigned long long) memc_stat
->bytes_written
);
300 } else if (!strcmp(key
, "bytes")) {
301 length
= snprintf(buffer
, SMALL_STRING_LEN
, "%llu", (unsigned long long) memc_stat
->bytes
);
302 } else if (!strcmp(key
, "limit_maxbytes")) {
304 snprintf(buffer
, SMALL_STRING_LEN
, "%llu", (unsigned long long) memc_stat
->limit_maxbytes
);
305 } else if (!strcmp(key
, "threads")) {
306 length
= snprintf(buffer
, SMALL_STRING_LEN
, "%lu", memc_stat
->threads
);
308 Memcached
*memc
= (Memcached
*) memcached2Memcached(shell
);
309 *error
= memcached_set_error(*memc
, MEMCACHED_INVALID_ARGUMENTS
, MEMCACHED_AT
,
310 memcached_literal_param("Invalid key provided"));
314 if (length
>= SMALL_STRING_LEN
|| length
< 0) {
315 Memcached
*memc
= (Memcached
*) memcached2Memcached(shell
);
316 *error
= memcached_set_error(
317 *memc
, MEMCACHED_FAILURE
, MEMCACHED_AT
,
318 memcached_literal_param("Internal failure occured with buffer, please report this bug."));
322 // User is responsible for free() memory, so use malloc()
323 char *ret
= static_cast<char *>(malloc(size_t(length
+ 1)));
324 memcpy(ret
, buffer
, (size_t) length
);
330 static memcached_return_t
binary_stats_fetch(memcached_stat_st
*memc_stat
, const char *args
,
331 const size_t args_length
,
332 memcached_instance_st
*instance
,
333 struct local_context
*check
) {
334 char buffer
[MEMCACHED_DEFAULT_COMMAND_SIZE
];
335 protocol_binary_request_stats request
= {}; // = {.bytes= {0}};
336 memcached_return_t rc
;
338 initialize_binary_request(instance
, request
.message
.header
);
340 request
.message
.header
.request
.opcode
= PROTOCOL_BINARY_CMD_STAT
;
341 request
.message
.header
.request
.datatype
= PROTOCOL_BINARY_RAW_BYTES
;
344 request
.message
.header
.request
.keylen
= htons(uint16_t(args_length
));
345 request
.message
.header
.request
.bodylen
= htonl(uint32_t(args_length
));
347 libmemcached_io_vector_st vector
[] = {{request
.bytes
, sizeof(request
.bytes
)},
348 {args
, args_length
}};
350 if (memcached_failed(rc
= memcached_vdo(instance
, vector
, 2, true))) {
354 libmemcached_io_vector_st vector
[] = {{request
.bytes
, sizeof(request
.bytes
)}};
356 if (memcached_failed(rc
= memcached_vdo(instance
, vector
, 1, true))) {
361 memcached_server_response_decrement(instance
);
363 rc
= memcached_response(instance
, buffer
, sizeof(buffer
), NULL
);
365 if (rc
== MEMCACHED_END
) {
369 if (rc
!= MEMCACHED_SUCCESS
) {
373 if (check
&& check
->func
) {
374 size_t key_length
= strlen(buffer
);
376 check
->func(instance
, buffer
, key_length
, buffer
+ key_length
+ 1,
377 strlen(buffer
+ key_length
+ 1), check
->context
);
381 if ((set_data(memc_stat
, buffer
, buffer
+ strlen(buffer
) + 1)) == MEMCACHED_UNKNOWN_STAT_KEY
)
383 WATCHPOINT_ERROR(MEMCACHED_UNKNOWN_STAT_KEY
);
384 WATCHPOINT_ASSERT(0);
390 * memcached_response will decrement the counter, so I need to reset it..
391 * todo: look at this and try to find a better solution.
393 instance
->cursor_active_
= 0;
395 return MEMCACHED_SUCCESS
;
398 static memcached_return_t
ascii_stats_fetch(memcached_stat_st
*memc_stat
, const char *args
,
399 const size_t args_length
,
400 memcached_instance_st
*instance
,
401 struct local_context
*check
) {
402 libmemcached_io_vector_st vector
[] = {
403 {memcached_literal_param("stats ")}, {args
, args_length
}, {memcached_literal_param("\r\n")}};
405 memcached_return_t rc
= memcached_vdo(instance
, vector
, 3, true);
406 if (memcached_success(rc
)) {
407 char buffer
[MEMCACHED_DEFAULT_COMMAND_SIZE
];
408 while ((rc
= memcached_response(instance
, buffer
, sizeof(buffer
), NULL
)) == MEMCACHED_STAT
) {
409 char *string_ptr
= buffer
;
410 string_ptr
+= 5; /* Move past STAT */
413 for (end_ptr
= string_ptr
; isgraph(*end_ptr
); end_ptr
++) {
415 char *key
= string_ptr
;
416 key
[size_t(end_ptr
- string_ptr
)] = 0;
418 string_ptr
= end_ptr
+ 1;
419 for (end_ptr
= string_ptr
; !(isspace(*end_ptr
)); end_ptr
++) {
421 char *value
= string_ptr
;
422 value
[(size_t)(end_ptr
- string_ptr
)] = 0;
424 bool check_bool
= bool(check
);
425 bool check_func_bool
= bool(check
) ? bool(check
->func
) : false;
426 fprintf(stderr
, "%s:%d %s %s %d:%d\n", __FILE__
, __LINE__
, key
, value
, check_bool
, check_func_bool
);
429 if (check
and check
->func
) {
430 check
->func(instance
, key
, strlen(key
), value
, strlen(value
), check
->context
);
434 if ((set_data(memc_stat
, key
, value
)) == MEMCACHED_UNKNOWN_STAT_KEY
) {
435 WATCHPOINT_ERROR(MEMCACHED_UNKNOWN_STAT_KEY
);
436 WATCHPOINT_ASSERT(0);
442 if (rc
== MEMCACHED_ERROR
) {
443 return MEMCACHED_INVALID_ARGUMENTS
;
446 if (rc
== MEMCACHED_END
) {
447 return MEMCACHED_SUCCESS
;
453 memcached_stat_st
*memcached_stat(memcached_st
*shell
, char *args
, memcached_return_t
*error
) {
454 Memcached
*self
= memcached2Memcached(shell
);
455 memcached_return_t unused
;
460 if (memcached_failed(*error
= initialize_query(self
, true))) {
464 if (memcached_is_udp(self
)) {
465 *error
= memcached_set_error(*self
, MEMCACHED_NOT_SUPPORTED
, MEMCACHED_AT
);
469 memcached_return_t rc
= MEMCACHED_SUCCESS
;
470 size_t args_length
= 0;
472 args_length
= strlen(args
);
473 if (memcached_failed(rc
= memcached_key_test(*self
, (const char **) &args
, &args_length
, 1))) {
474 *error
= memcached_set_error(*self
, rc
, MEMCACHED_AT
);
479 WATCHPOINT_ASSERT(error
);
481 memcached_stat_st
*stats
=
482 libmemcached_xcalloc(self
, memcached_server_count(self
), memcached_stat_st
);
484 *error
= memcached_set_error(*self
, MEMCACHED_MEMORY_ALLOCATION_FAILURE
, MEMCACHED_AT
);
488 WATCHPOINT_ASSERT(rc
== MEMCACHED_SUCCESS
);
489 rc
= MEMCACHED_SUCCESS
;
490 for (uint32_t x
= 0; x
< memcached_server_count(self
); x
++) {
491 memcached_stat_st
*stat_instance
= stats
+ x
;
493 stat_instance
->pid
= -1;
494 stat_instance
->root
= self
;
496 memcached_instance_st
*instance
= memcached_instance_fetch(self
, x
);
498 memcached_return_t temp_return
;
499 if (memcached_is_binary(self
)) {
500 temp_return
= binary_stats_fetch(stat_instance
, args
, args_length
, instance
, NULL
);
502 temp_return
= ascii_stats_fetch(stat_instance
, args
, args_length
, instance
, NULL
);
505 // Special case where "args" is invalid
506 if (temp_return
== MEMCACHED_INVALID_ARGUMENTS
) {
507 rc
= MEMCACHED_INVALID_ARGUMENTS
;
511 if (memcached_failed(temp_return
)) {
512 rc
= MEMCACHED_SOME_ERRORS
;
521 memcached_return_t
memcached_stat_servername(memcached_stat_st
*memc_stat
, char *args
,
522 const char *hostname
, in_port_t port
) {
525 memcached_stat_st unused_memc_stat
;
526 if (memc_stat
== NULL
) {
527 memc_stat
= &unused_memc_stat
;
530 memset(memc_stat
, 0, sizeof(memcached_stat_st
));
532 memcached_st
*memc_ptr
= memcached_create(&memc
);
533 if (memc_ptr
== NULL
) {
534 return MEMCACHED_MEMORY_ALLOCATION_FAILURE
;
537 memcached_return_t rc
;
538 if (memcached_failed(rc
= memcached_server_add(&memc
, hostname
, port
))) {
539 memcached_free(&memc
);
543 if (memcached_success(rc
= initialize_query(memc_ptr
, true))) {
544 size_t args_length
= 0;
546 args_length
= strlen(args
);
547 rc
= memcached_key_test(*memc_ptr
, (const char **) &args
, &args_length
, 1);
550 if (memcached_success(rc
)) {
551 memcached_instance_st
*instance
= memcached_instance_fetch(memc_ptr
, 0);
552 if (memc
.flags
.binary_protocol
) {
553 rc
= binary_stats_fetch(memc_stat
, args
, args_length
, instance
, NULL
);
555 rc
= ascii_stats_fetch(memc_stat
, args
, args_length
, instance
, NULL
);
560 memcached_free(&memc
);
566 We make a copy of the keys since at some point in the not so distant future
567 we will add support for "found" keys.
569 char **memcached_stat_get_keys(memcached_st
*shell
, memcached_stat_st
*,
570 memcached_return_t
*error
) {
571 Memcached
*memc
= memcached2Memcached(shell
);
573 char **list
= static_cast<char **>(libmemcached_malloc(memc
, sizeof(memcached_stat_keys
)));
576 *error
= memcached_set_error(*memc
, MEMCACHED_MEMORY_ALLOCATION_FAILURE
, MEMCACHED_AT
);
582 memcpy(list
, memcached_stat_keys
, sizeof(memcached_stat_keys
));
585 *error
= MEMCACHED_SUCCESS
;
594 void memcached_stat_free(const memcached_st
*, memcached_stat_st
*memc_stat
) {
595 WATCHPOINT_ASSERT(memc_stat
); // Be polite, but when debugging catch this as an error
597 libmemcached_free(memc_stat
->root
, memc_stat
);
601 static memcached_return_t
call_stat_fn(memcached_st
*memc
, memcached_instance_st
*instance
,
604 local_context
*check
= (struct local_context
*) context
;
606 if (memcached_is_binary(memc
)) {
607 return binary_stats_fetch(NULL
, check
->args
, check
->args_length
, instance
, check
);
609 return ascii_stats_fetch(NULL
, check
->args
, check
->args_length
, instance
, check
);
613 return MEMCACHED_INVALID_ARGUMENTS
;
616 memcached_return_t
memcached_stat_execute(memcached_st
*shell
, const char *args
,
617 memcached_stat_fn func
, void *context
) {
618 Memcached
*memc
= memcached2Memcached(shell
);
619 if (memcached_fatal(memcached_version(memc
))) {
620 return memcached_last_error(memc
);
623 local_context
check(func
, context
, args
, args
? strlen(args
) : 0);
625 return memcached_server_execute(memc
, call_stat_fn
, (void *) &check
);