Added --args to memstat
[awesomized/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 ret= libmemcached_malloc(ptr, (size_t) (length + 1));
231 memcpy(ret, buffer, (size_t) length);
232 ret[length]= '\0';
233
234 return ret;
235 }
236
237 static memcached_return_t binary_stats_fetch(memcached_stat_st *memc_stat,
238 const char *args,
239 memcached_server_write_instance_st instance,
240 struct local_context *check)
241 {
242 memcached_return_t rc;
243
244 char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE];
245 protocol_binary_request_stats request= {.bytes= {0}};
246 request.message.header.request.magic= PROTOCOL_BINARY_REQ;
247 request.message.header.request.opcode= PROTOCOL_BINARY_CMD_STAT;
248 request.message.header.request.datatype= PROTOCOL_BINARY_RAW_BYTES;
249
250 if (args != NULL)
251 {
252 size_t len= strlen(args);
253
254 rc= memcached_validate_key_length(len, true);
255 unlikely (rc != MEMCACHED_SUCCESS)
256 return rc;
257
258 request.message.header.request.keylen= htons((uint16_t)len);
259 request.message.header.request.bodylen= htonl((uint32_t) len);
260
261 struct __write_vector_st vector[]=
262 {
263 { .length= sizeof(request.bytes), .buffer= request.bytes },
264 { .length= len, .buffer= args }
265 };
266
267 if (memcached_vdo(instance, vector, 2, true) != MEMCACHED_SUCCESS)
268 {
269 memcached_io_reset(instance);
270 return MEMCACHED_WRITE_FAILURE;
271 }
272 }
273 else
274 {
275 if (memcached_do(instance, request.bytes,
276 sizeof(request.bytes), true) != MEMCACHED_SUCCESS)
277 {
278 memcached_io_reset(instance);
279 return MEMCACHED_WRITE_FAILURE;
280 }
281 }
282
283 memcached_server_response_decrement(instance);
284 do
285 {
286 rc= memcached_response(instance, buffer, sizeof(buffer), NULL);
287
288 if (rc == MEMCACHED_END)
289 break;
290
291 unlikely (rc != MEMCACHED_SUCCESS)
292 {
293 memcached_io_reset(instance);
294 return rc;
295 }
296
297 if (memc_stat)
298 {
299 unlikely((set_data(memc_stat, buffer, buffer + strlen(buffer) + 1)) == MEMCACHED_UNKNOWN_STAT_KEY)
300 {
301 WATCHPOINT_ERROR(MEMCACHED_UNKNOWN_STAT_KEY);
302 WATCHPOINT_ASSERT(0);
303 }
304 }
305
306 if (check && check->func)
307 {
308 size_t key_length= strlen(buffer);
309
310 check->func(instance,
311 buffer, key_length,
312 buffer+key_length+1, strlen(buffer+key_length+1),
313 check->context);
314 }
315 } while (1);
316
317 /* shit... memcached_response will decrement the counter, so I need to
318 ** reset it.. todo: look at this and try to find a better solution.
319 */
320 instance->cursor_active= 0;
321
322 return MEMCACHED_SUCCESS;
323 }
324
325 static memcached_return_t ascii_stats_fetch(memcached_stat_st *memc_stat,
326 const char *args,
327 memcached_server_write_instance_st instance,
328 struct local_context *check)
329 {
330 memcached_return_t rc;
331 char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE];
332 size_t send_length;
333
334 if (args)
335 send_length= (size_t) snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE,
336 "stats %s\r\n", args);
337 else
338 send_length= (size_t) snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE,
339 "stats\r\n");
340
341 if (send_length >= MEMCACHED_DEFAULT_COMMAND_SIZE)
342 return MEMCACHED_WRITE_FAILURE;
343
344 rc= memcached_do(instance, buffer, send_length, true);
345 if (rc != MEMCACHED_SUCCESS)
346 goto error;
347
348 while (1)
349 {
350 rc= memcached_response(instance, buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, NULL);
351
352 if (rc == MEMCACHED_STAT)
353 {
354 char *string_ptr, *end_ptr;
355 char *key, *value;
356
357 string_ptr= buffer;
358 string_ptr+= 5; /* Move past STAT */
359 for (end_ptr= string_ptr; isgraph(*end_ptr); end_ptr++);
360 key= string_ptr;
361 key[(size_t)(end_ptr-string_ptr)]= 0;
362
363 string_ptr= end_ptr + 1;
364 for (end_ptr= string_ptr; !(isspace(*end_ptr)); end_ptr++);
365 value= string_ptr;
366 value[(size_t)(end_ptr-string_ptr)]= 0;
367 string_ptr= end_ptr + 2;
368 if (memc_stat)
369 {
370 unlikely((set_data(memc_stat, key, value)) == 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 check->func(instance,
380 key, strlen(key),
381 value, strlen(value),
382 check->context);
383 }
384 }
385 else
386 break;
387 }
388
389 error:
390 if (rc == MEMCACHED_END)
391 return MEMCACHED_SUCCESS;
392 else
393 return rc;
394 }
395
396 memcached_stat_st *memcached_stat(memcached_st *ptr, char *args, memcached_return_t *error)
397 {
398 memcached_return_t rc;
399 memcached_stat_st *stats;
400
401 unlikely (ptr->flags.use_udp)
402 {
403 *error= MEMCACHED_NOT_SUPPORTED;
404 return NULL;
405 }
406
407 stats= libmemcached_calloc(ptr, memcached_server_count(ptr), sizeof(memcached_stat_st));
408
409 if (! stats)
410 {
411 *error= MEMCACHED_MEMORY_ALLOCATION_FAILURE;
412 return NULL;
413 }
414
415 rc= MEMCACHED_SUCCESS;
416 for (uint32_t x= 0; x < memcached_server_count(ptr); x++)
417 {
418 memcached_return_t temp_return;
419 memcached_server_write_instance_st instance;
420 memcached_stat_st *stat_instance;
421
422 stat_instance= stats + x;
423
424 stat_instance->root= ptr;
425
426 instance= memcached_server_instance_fetch(ptr, x);
427
428 if (ptr->flags.binary_protocol)
429 {
430 temp_return= binary_stats_fetch(stat_instance, args, instance, NULL);
431 }
432 else
433 {
434 temp_return= ascii_stats_fetch(stat_instance, args, instance, NULL);
435 }
436
437 if (temp_return != MEMCACHED_SUCCESS)
438 rc= MEMCACHED_SOME_ERRORS;
439 }
440
441 *error= rc;
442 return stats;
443 }
444
445 memcached_return_t memcached_stat_servername(memcached_stat_st *memc_stat, char *args,
446 const char *hostname, in_port_t port)
447 {
448 memcached_return_t rc;
449 memcached_st memc;
450 memcached_st *memc_ptr;
451 memcached_server_write_instance_st instance;
452
453 memset(memc_stat, 0, sizeof(memcached_stat_st));
454
455 memc_ptr= memcached_create(&memc);
456 WATCHPOINT_ASSERT(memc_ptr);
457
458 memcached_server_add(&memc, hostname, port);
459
460 instance= memcached_server_instance_fetch(memc_ptr, 0);
461
462 if (memc.flags.binary_protocol)
463 {
464 rc= binary_stats_fetch(memc_stat, args, instance, NULL);
465 }
466 else
467 {
468 rc= ascii_stats_fetch(memc_stat, args, instance, NULL);
469 }
470
471 memcached_free(&memc);
472
473 return rc;
474 }
475
476 /*
477 We make a copy of the keys since at some point in the not so distant future
478 we will add support for "found" keys.
479 */
480 char ** memcached_stat_get_keys(const memcached_st *ptr,
481 memcached_stat_st *memc_stat,
482 memcached_return_t *error)
483 {
484 char **list;
485 size_t length= sizeof(memcached_stat_keys);
486
487 (void)memc_stat;
488
489 list= libmemcached_malloc(ptr, length);
490
491 if (! list)
492 {
493 *error= MEMCACHED_MEMORY_ALLOCATION_FAILURE;
494 return NULL;
495 }
496
497 memcpy(list, memcached_stat_keys, sizeof(memcached_stat_keys));
498
499 *error= MEMCACHED_SUCCESS;
500
501 return list;
502 }
503
504 void memcached_stat_free(const memcached_st *ptr, memcached_stat_st *memc_stat)
505 {
506 if (memc_stat == NULL)
507 {
508 WATCHPOINT_ASSERT(0); /* Be polite, but when debugging catch this as an error */
509 return;
510 }
511
512 if (memc_stat->root)
513 {
514 libmemcached_free(memc_stat->root, memc_stat);
515 }
516 else if (ptr)
517 {
518 libmemcached_free(ptr, memc_stat);
519 }
520 else
521 {
522 free(memc_stat);
523 }
524 }
525
526 static memcached_return_t call_stat_fn(memcached_st *ptr,
527 memcached_server_write_instance_st instance,
528 void *context)
529 {
530 memcached_return_t rc;
531 struct local_context *check= (struct local_context *)context;
532
533 if (ptr->flags.binary_protocol)
534 {
535 rc= binary_stats_fetch(NULL, check->args, instance, check);
536 }
537 else
538 {
539 rc= ascii_stats_fetch(NULL, check->args, instance, check);
540 }
541
542 return rc;
543 }
544
545 memcached_return_t memcached_stat_execute(memcached_st *memc, const char *args, memcached_stat_fn func, void *context)
546 {
547 memcached_version(memc);
548
549 struct local_context check= { .func= func, .context= context, .args= args };
550
551 return memcached_server_execute(memc, call_stat_fn, (void *)&check);
552 }