Add a method for checking error/returning error.
[m6w6/libmemcached] / libmemcached / stats.cc
1 /*
2 */
3
4 #include "common.h"
5
6 static const char *memcached_stat_keys[] = {
7 "pid",
8 "uptime",
9 "time",
10 "version",
11 "pointer_size",
12 "rusage_user",
13 "rusage_system",
14 "curr_items",
15 "total_items",
16 "bytes",
17 "curr_connections",
18 "total_connections",
19 "connection_structures",
20 "cmd_get",
21 "cmd_set",
22 "get_hits",
23 "get_misses",
24 "evictions",
25 "bytes_read",
26 "bytes_written",
27 "limit_maxbytes",
28 "threads",
29 NULL
30 };
31
32 struct local_context
33 {
34 memcached_stat_fn func;
35 void *context;
36 const char *args;
37
38 local_context(memcached_stat_fn func_arg,
39 void *context_arg,
40 const char *args_arg) :
41 func(func_arg),
42 context(context_arg),
43 args(args_arg)
44 { }
45 };
46
47
48 static memcached_return_t set_data(memcached_stat_st *memc_stat, char *key, char *value)
49 {
50
51 if (strlen(key) < 1)
52 {
53 WATCHPOINT_STRING(key);
54 return MEMCACHED_UNKNOWN_STAT_KEY;
55 }
56 else if (not strcmp("pid", key))
57 {
58 int64_t temp= strtoll(value, (char **)NULL, 10);
59
60 if (temp <= INT32_MAX and ( sizeof(pid_t) == sizeof(int32_t) ))
61 {
62 memc_stat->pid= temp;
63 }
64 else if (temp > -1)
65 {
66 memc_stat->pid= temp;
67 }
68 else
69 {
70 // If we got a value less then -1 then something went wrong in the
71 // protocol
72 }
73 }
74 else if (not strcmp("uptime", key))
75 {
76 memc_stat->uptime= strtoul(value, (char **)NULL, 10);
77 }
78 else if (not strcmp("time", key))
79 {
80 memc_stat->time= strtoul(value, (char **)NULL, 10);
81 }
82 else if (not strcmp("version", key))
83 {
84 memcpy(memc_stat->version, value, strlen(value));
85 memc_stat->version[strlen(value)]= 0;
86 }
87 else if (not strcmp("pointer_size", key))
88 {
89 memc_stat->pointer_size= strtoul(value, (char **)NULL, 10);
90 }
91 else if (not strcmp("rusage_user", key))
92 {
93 char *walk_ptr;
94 for (walk_ptr= value; (!ispunct(*walk_ptr)); walk_ptr++) {};
95 *walk_ptr= 0;
96 walk_ptr++;
97 memc_stat->rusage_user_seconds= strtoul(value, (char **)NULL, 10);
98 memc_stat->rusage_user_microseconds= strtoul(walk_ptr, (char **)NULL, 10);
99 }
100 else if (not strcmp("rusage_system", key))
101 {
102 char *walk_ptr;
103 for (walk_ptr= value; (!ispunct(*walk_ptr)); walk_ptr++) {};
104 *walk_ptr= 0;
105 walk_ptr++;
106 memc_stat->rusage_system_seconds= strtoul(value, (char **)NULL, 10);
107 memc_stat->rusage_system_microseconds= strtoul(walk_ptr, (char **)NULL, 10);
108 }
109 else if (not strcmp("curr_items", key))
110 {
111 memc_stat->curr_items= strtoul(value, (char **)NULL, 10);
112 }
113 else if (not strcmp("total_items", key))
114 {
115 memc_stat->total_items= strtoul(value, (char **)NULL, 10);
116 }
117 else if (not strcmp("bytes_read", key))
118 {
119 memc_stat->bytes_read= strtoull(value, (char **)NULL, 10);
120 }
121 else if (not strcmp("bytes_written", key))
122 {
123 memc_stat->bytes_written= strtoull(value, (char **)NULL, 10);
124 }
125 else if (not strcmp("bytes", key))
126 {
127 memc_stat->bytes= strtoull(value, (char **)NULL, 10);
128 }
129 else if (not strcmp("curr_connections", key))
130 {
131 memc_stat->curr_connections= strtoull(value, (char **)NULL, 10);
132 }
133 else if (not strcmp("total_connections", key))
134 {
135 memc_stat->total_connections= strtoull(value, (char **)NULL, 10);
136 }
137 else if (not strcmp("connection_structures", key))
138 {
139 memc_stat->connection_structures= strtoul(value, (char **)NULL, 10);
140 }
141 else if (not strcmp("cmd_get", key))
142 {
143 memc_stat->cmd_get= strtoull(value, (char **)NULL, 10);
144 }
145 else if (not strcmp("cmd_set", key))
146 {
147 memc_stat->cmd_set= strtoull(value, (char **)NULL, 10);
148 }
149 else if (not strcmp("get_hits", key))
150 {
151 memc_stat->get_hits= strtoull(value, (char **)NULL, 10);
152 }
153 else if (not strcmp("get_misses", key))
154 {
155 memc_stat->get_misses= strtoull(value, (char **)NULL, 10);
156 }
157 else if (not strcmp("evictions", key))
158 {
159 memc_stat->evictions= strtoull(value, (char **)NULL, 10);
160 }
161 else if (not strcmp("limit_maxbytes", key))
162 {
163 memc_stat->limit_maxbytes= strtoull(value, (char **)NULL, 10);
164 }
165 else if (not strcmp("threads", key))
166 {
167 memc_stat->threads= strtoul(value, (char **)NULL, 10);
168 }
169 else if (not (strcmp("delete_misses", key) == 0 or /* New stats in the 1.3 beta */
170 strcmp("delete_hits", key) == 0 or /* Just swallow them for now.. */
171 strcmp("incr_misses", key) == 0 or
172 strcmp("incr_hits", key) == 0 or
173 strcmp("decr_misses", key) == 0 or
174 strcmp("decr_hits", key) == 0 or
175 strcmp("cas_misses", key) == 0 or
176 strcmp("cas_hits", key) == 0 or
177 strcmp("cas_badval", key) == 0 or
178 strcmp("cmd_flush", key) == 0 or
179 strcmp("accepting_conns", key) == 0 or
180 strcmp("listen_disabled_num", key) == 0 or
181 strcmp("conn_yields", key) == 0 or
182 strcmp("auth_cmds", key) == 0 or
183 strcmp("auth_errors", key) == 0 or
184 strcmp("reclaimed", key) == 0))
185 {
186 WATCHPOINT_STRING(key);
187 /* return MEMCACHED_UNKNOWN_STAT_KEY; */
188 return MEMCACHED_SUCCESS;
189 }
190
191 return MEMCACHED_SUCCESS;
192 }
193
194 char *memcached_stat_get_value(const memcached_st *ptr, memcached_stat_st *memc_stat,
195 const char *key, memcached_return_t *error)
196 {
197 char buffer[SMALL_STRING_LEN];
198 int length;
199 char *ret;
200
201 *error= MEMCACHED_SUCCESS;
202
203 if (not memcmp("pid", key, sizeof("pid") -1))
204 {
205 length= snprintf(buffer, SMALL_STRING_LEN,"%lld", (signed long long)memc_stat->pid);
206 }
207 else if (not memcmp("uptime", key, sizeof("uptime") -1))
208 {
209 length= snprintf(buffer, SMALL_STRING_LEN,"%lu", memc_stat->uptime);
210 }
211 else if (not memcmp("time", key, sizeof("time") -1))
212 {
213 length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->time);
214 }
215 else if (not memcmp("version", key, sizeof("version") -1))
216 {
217 length= snprintf(buffer, SMALL_STRING_LEN,"%s", memc_stat->version);
218 }
219 else if (not memcmp("pointer_size", key, sizeof("pointer_size") -1))
220 {
221 length= snprintf(buffer, SMALL_STRING_LEN,"%lu", memc_stat->pointer_size);
222 }
223 else if (not memcmp("rusage_user", key, sizeof("rusage_user") -1))
224 {
225 length= snprintf(buffer, SMALL_STRING_LEN,"%lu.%lu", memc_stat->rusage_user_seconds, memc_stat->rusage_user_microseconds);
226 }
227 else if (not memcmp("rusage_system", key, sizeof("rusage_system") -1))
228 {
229 length= snprintf(buffer, SMALL_STRING_LEN,"%lu.%lu", memc_stat->rusage_system_seconds, memc_stat->rusage_system_microseconds);
230 }
231 else if (not memcmp("curr_items", key, sizeof("curr_items") -1))
232 {
233 length= snprintf(buffer, SMALL_STRING_LEN,"%lu", memc_stat->curr_items);
234 }
235 else if (not memcmp("total_items", key, sizeof("total_items") -1))
236 {
237 length= snprintf(buffer, SMALL_STRING_LEN,"%lu", memc_stat->total_items);
238 }
239 else if (not memcmp("curr_connections", key, sizeof("curr_connections") -1))
240 {
241 length= snprintf(buffer, SMALL_STRING_LEN,"%lu", memc_stat->curr_connections);
242 }
243 else if (not memcmp("total_connections", key, sizeof("total_connections") -1))
244 {
245 length= snprintf(buffer, SMALL_STRING_LEN,"%lu", memc_stat->total_connections);
246 }
247 else if (not memcmp("connection_structures", key, sizeof("connection_structures") -1))
248 {
249 length= snprintf(buffer, SMALL_STRING_LEN,"%lu", memc_stat->connection_structures);
250 }
251 else if (not memcmp("cmd_get", key, sizeof("cmd_get") -1))
252 {
253 length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->cmd_get);
254 }
255 else if (not memcmp("cmd_set", key, sizeof("cmd_set") -1))
256 {
257 length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->cmd_set);
258 }
259 else if (not memcmp("get_hits", key, sizeof("get_hits") -1))
260 {
261 length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->get_hits);
262 }
263 else if (not memcmp("get_misses", key, sizeof("get_misses") -1))
264 {
265 length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->get_misses);
266 }
267 else if (not memcmp("evictions", key, sizeof("evictions") -1))
268 {
269 length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->evictions);
270 }
271 else if (not memcmp("bytes_read", key, sizeof("bytes_read") -1))
272 {
273 length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->bytes_read);
274 }
275 else if (not memcmp("bytes_written", key, sizeof("bytes_written") -1))
276 {
277 length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->bytes_written);
278 }
279 else if (not memcmp("bytes", key, sizeof("bytes") -1))
280 {
281 length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->bytes);
282 }
283 else if (not memcmp("limit_maxbytes", key, sizeof("limit_maxbytes") -1))
284 {
285 length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->limit_maxbytes);
286 }
287 else if (not memcmp("threads", key, sizeof("threads") -1))
288 {
289 length= snprintf(buffer, SMALL_STRING_LEN,"%lu", memc_stat->threads);
290 }
291 else
292 {
293 *error= MEMCACHED_NOTFOUND;
294 return NULL;
295 }
296
297 if (length >= SMALL_STRING_LEN || length < 0)
298 {
299 *error= MEMCACHED_FAILURE;
300 return NULL;
301 }
302
303 ret= static_cast<char *>(libmemcached_malloc(ptr, (size_t) (length + 1)));
304 memcpy(ret, buffer, (size_t) length);
305 ret[length]= '\0';
306
307 return ret;
308 }
309
310 static memcached_return_t binary_stats_fetch(memcached_stat_st *memc_stat,
311 const char *args,
312 memcached_server_write_instance_st instance,
313 struct local_context *check)
314 {
315 char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE];
316 protocol_binary_request_stats request= {}; // = {.bytes= {0}};
317 request.message.header.request.magic= PROTOCOL_BINARY_REQ;
318 request.message.header.request.opcode= PROTOCOL_BINARY_CMD_STAT;
319 request.message.header.request.datatype= PROTOCOL_BINARY_RAW_BYTES;
320
321 if (args)
322 {
323 size_t len= strlen(args);
324
325 memcached_return_t rc= memcached_validate_key_length(len, true);
326 unlikely (rc != MEMCACHED_SUCCESS)
327 return rc;
328
329 request.message.header.request.keylen= htons((uint16_t)len);
330 request.message.header.request.bodylen= htonl((uint32_t) len);
331
332 struct libmemcached_io_vector_st vector[]=
333 {
334 { sizeof(request.bytes), request.bytes },
335 { len, args }
336 };
337
338 if (memcached_vdo(instance, vector, 2, true) != MEMCACHED_SUCCESS)
339 {
340 memcached_io_reset(instance);
341 return MEMCACHED_WRITE_FAILURE;
342 }
343 }
344 else
345 {
346 if (memcached_do(instance, request.bytes,
347 sizeof(request.bytes), true) != MEMCACHED_SUCCESS)
348 {
349 memcached_io_reset(instance);
350 return MEMCACHED_WRITE_FAILURE;
351 }
352 }
353
354 memcached_server_response_decrement(instance);
355 do
356 {
357 memcached_return_t rc= memcached_response(instance, buffer, sizeof(buffer), NULL);
358
359 if (rc == MEMCACHED_END)
360 break;
361
362 unlikely (rc != MEMCACHED_SUCCESS)
363 {
364 memcached_io_reset(instance);
365 return rc;
366 }
367
368 if (memc_stat)
369 {
370 unlikely((set_data(memc_stat, buffer, buffer + strlen(buffer) + 1)) == MEMCACHED_UNKNOWN_STAT_KEY)
371 {
372 WATCHPOINT_ERROR(MEMCACHED_UNKNOWN_STAT_KEY);
373 WATCHPOINT_ASSERT(0);
374 }
375 }
376
377 if (check && check->func)
378 {
379 size_t key_length= strlen(buffer);
380
381 check->func(instance,
382 buffer, key_length,
383 buffer+key_length+1, strlen(buffer+key_length+1),
384 check->context);
385 }
386 } while (1);
387
388 /* shit... memcached_response will decrement the counter, so I need to
389 ** reset it.. todo: look at this and try to find a better solution.
390 */
391 instance->cursor_active= 0;
392
393 return MEMCACHED_SUCCESS;
394 }
395
396 static memcached_return_t ascii_stats_fetch(memcached_stat_st *memc_stat,
397 const char *args,
398 memcached_server_write_instance_st instance,
399 struct local_context *check)
400 {
401 char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE];
402 int send_length;
403
404 if (args)
405 {
406 send_length= (size_t) snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, "stats %s\r\n", args);
407 }
408 else
409 {
410 send_length= (size_t) snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, "stats\r\n");
411 }
412
413 if (send_length >= MEMCACHED_DEFAULT_COMMAND_SIZE || send_length < 0)
414 {
415 return memcached_set_error(*instance, MEMCACHED_MEMORY_ALLOCATION_FAILURE, MEMCACHED_AT,
416 memcached_literal_param("snprintf(MEMCACHED_DEFAULT_COMMAND_SIZE)"));
417 }
418
419 memcached_return_t rc= memcached_do(instance, buffer, (size_t)send_length, true);
420 if (memcached_success(rc))
421 {
422 while ((rc= memcached_response(instance, buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, NULL)) == MEMCACHED_STAT)
423 {
424 char *string_ptr, *end_ptr;
425 char *key, *value;
426
427 string_ptr= buffer;
428 string_ptr+= 5; /* Move past STAT */
429 for (end_ptr= string_ptr; isgraph(*end_ptr); end_ptr++) {};
430 key= string_ptr;
431 key[(size_t)(end_ptr-string_ptr)]= 0;
432
433 string_ptr= end_ptr + 1;
434 for (end_ptr= string_ptr; !(isspace(*end_ptr)); end_ptr++) {};
435 value= string_ptr;
436 value[(size_t)(end_ptr-string_ptr)]= 0;
437 string_ptr= end_ptr + 2;
438 if (memc_stat)
439 {
440 unlikely((set_data(memc_stat, key, value)) == MEMCACHED_UNKNOWN_STAT_KEY)
441 {
442 WATCHPOINT_ERROR(MEMCACHED_UNKNOWN_STAT_KEY);
443 WATCHPOINT_ASSERT(0);
444 }
445 }
446
447 if (check && check->func)
448 {
449 check->func(instance,
450 key, strlen(key),
451 value, strlen(value),
452 check->context);
453 }
454 }
455 }
456
457 if (rc == MEMCACHED_END)
458 return MEMCACHED_SUCCESS;
459 else
460 return rc;
461 }
462
463 memcached_stat_st *memcached_stat(memcached_st *self, char *args, memcached_return_t *error)
464 {
465 memcached_return_t unused;
466 if (error == NULL)
467 {
468 error= &unused;
469 }
470
471 memcached_return_t rc;
472 if (memcached_failed(rc= initialize_query(self)))
473 {
474 *error= rc;
475
476 return NULL;
477 }
478
479 WATCHPOINT_ASSERT(error);
480
481 if (self->flags.use_udp)
482 {
483 *error= memcached_set_error(*self, MEMCACHED_NOT_SUPPORTED, MEMCACHED_AT);
484
485 return NULL;
486 }
487
488 memcached_stat_st *stats= static_cast<memcached_stat_st *>(libmemcached_calloc(self, memcached_server_count(self), sizeof(memcached_stat_st)));
489
490 if (not stats)
491 {
492 *error= MEMCACHED_MEMORY_ALLOCATION_FAILURE;
493
494 return NULL;
495 }
496
497 WATCHPOINT_ASSERT(rc == MEMCACHED_SUCCESS);
498 rc= MEMCACHED_SUCCESS;
499 for (uint32_t x= 0; x < memcached_server_count(self); x++)
500 {
501 memcached_return_t temp_return;
502 memcached_server_write_instance_st instance;
503 memcached_stat_st *stat_instance;
504
505 stat_instance= stats +x;
506
507 stat_instance->pid= -1;
508 stat_instance->root= self;
509
510 instance= memcached_server_instance_fetch(self, x);
511
512 if (self->flags.binary_protocol)
513 {
514 temp_return= binary_stats_fetch(stat_instance, args, instance, NULL);
515 }
516 else
517 {
518 temp_return= ascii_stats_fetch(stat_instance, args, instance, NULL);
519 }
520
521 if (memcached_failed(temp_return))
522 {
523 rc= MEMCACHED_SOME_ERRORS;
524 }
525 }
526
527 *error= rc;
528
529 return stats;
530 }
531
532 memcached_return_t memcached_stat_servername(memcached_stat_st *memc_stat, char *args,
533 const char *hostname, in_port_t port)
534 {
535 memcached_st memc;
536 memcached_server_write_instance_st instance;
537
538 memset(memc_stat, 0, sizeof(memcached_stat_st));
539
540 memcached_st *memc_ptr= memcached_create(&memc);
541 if (not memc_ptr)
542 return MEMCACHED_MEMORY_ALLOCATION_FAILURE;
543
544 memcached_server_add(&memc, hostname, port);
545
546 memcached_return_t rc;
547 if ((rc= initialize_query(memc_ptr)) != MEMCACHED_SUCCESS)
548 {
549 return rc;
550 }
551
552 instance= memcached_server_instance_fetch(memc_ptr, 0);
553
554 if (memc.flags.binary_protocol)
555 {
556 rc= binary_stats_fetch(memc_stat, args, instance, NULL);
557 }
558 else
559 {
560 rc= ascii_stats_fetch(memc_stat, args, instance, NULL);
561 }
562
563 memcached_free(&memc);
564
565 return rc;
566 }
567
568 /*
569 We make a copy of the keys since at some point in the not so distant future
570 we will add support for "found" keys.
571 */
572 char ** memcached_stat_get_keys(memcached_st *ptr,
573 memcached_stat_st *,
574 memcached_return_t *error)
575 {
576 if (not ptr)
577 return NULL;
578
579 char **list= static_cast<char **>(libmemcached_malloc(ptr, sizeof(memcached_stat_keys)));
580 if (not list)
581 {
582 *error= memcached_set_error(*ptr, MEMCACHED_MEMORY_ALLOCATION_FAILURE, MEMCACHED_AT);
583 return NULL;
584 }
585
586 memcpy(list, memcached_stat_keys, sizeof(memcached_stat_keys));
587
588 *error= MEMCACHED_SUCCESS;
589
590 return list;
591 }
592
593 void memcached_stat_free(const memcached_st *, memcached_stat_st *memc_stat)
594 {
595 WATCHPOINT_ASSERT(memc_stat); // Be polite, but when debugging catch this as an error
596 if (not memc_stat)
597 {
598 return;
599 }
600
601 if (memc_stat->root)
602 {
603 libmemcached_free(memc_stat->root, memc_stat);
604 return;
605 }
606
607 libmemcached_free(NULL, memc_stat);
608 }
609
610 static memcached_return_t call_stat_fn(memcached_st *ptr,
611 memcached_server_write_instance_st instance,
612 void *context)
613 {
614 memcached_return_t rc;
615 struct local_context *check= (struct local_context *)context;
616
617 if (ptr->flags.binary_protocol)
618 {
619 rc= binary_stats_fetch(NULL, check->args, instance, check);
620 }
621 else
622 {
623 rc= ascii_stats_fetch(NULL, check->args, instance, check);
624 }
625
626 return rc;
627 }
628
629 memcached_return_t memcached_stat_execute(memcached_st *memc, const char *args, memcached_stat_fn func, void *context)
630 {
631 memcached_version(memc);
632
633 struct local_context check(func, context, args);
634
635 return memcached_server_execute(memc, call_stat_fn, (void *)&check);
636 }