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