1 /*  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
2  *
3  *  Libmemcached library
4  *
5  *  Copyright (C) 2011-2013 Data Differential, http://datadifferential.com/
6  *
7  *  Redistribution and use in source and binary forms, with or without
8  *  modification, are permitted provided that the following conditions are
9  *  met:
10  *
11  *      * Redistributions of source code must retain the above copyright
12  *  notice, this list of conditions and the following disclaimer.
13  *
14  *      * Redistributions in binary form must reproduce the above
15  *  copyright notice, this list of conditions and the following disclaimer
16  *  in the documentation and/or other materials provided with the
17  *  distribution.
18  *
19  *      * The names of its contributors may not be used to endorse or
20  *  promote products derived from this software without specific prior
21  *  written permission.
22  *
23  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26  *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27  *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28  *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29  *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30  *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31  *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32  *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33  *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34  *
35  */
36 
37 #include <libmemcached/common.h>
38 
39 static const char *memcached_stat_keys[] = {
40   "pid",
41   "uptime",
42   "time",
43   "version",
44   "pointer_size",
45   "rusage_user",
46   "rusage_system",
47   "curr_items",
48   "total_items",
49   "bytes",
50   "curr_connections",
51   "total_connections",
52   "connection_structures",
53   "cmd_get",
54   "cmd_set",
55   "get_hits",
56   "get_misses",
57   "evictions",
58   "bytes_read",
59   "bytes_written",
60   "limit_maxbytes",
61   "threads",
62   NULL
63 };
64 
65 struct local_context
66 {
67   memcached_stat_fn func;
68   void *context;
69   const char *args;
70   const size_t args_length;
71 
72   local_context(memcached_stat_fn func_arg,
73                 void *context_arg,
74                 const char *args_arg,
75                 const size_t args_length_arg) :
76     func(func_arg),
77     context(context_arg),
78     args(args_arg),
79     args_length(args_length_arg)
80   { }
81 };
82 
83 
84 static memcached_return_t set_data(memcached_stat_st *memc_stat, const char *key, const char *value)
85 {
86 
87   if (strlen(key) < 1)
88   {
89     WATCHPOINT_STRING(key);
90     return MEMCACHED_UNKNOWN_STAT_KEY;
91   }
92   else if (strcmp("pid", key) == 0)
93   {
94     errno= 0;
95     int64_t temp= strtoll(value, (char **)NULL, 10);
96     if (errno != 0)
97     {
98       return MEMCACHED_FAILURE;
99     }
100 
101     if (temp <= INT32_MAX and ( sizeof(pid_t) == sizeof(int32_t) ))
102     {
103       memc_stat->pid= pid_t(temp);
104     }
105     else if (temp > -1)
106     {
107       memc_stat->pid= pid_t(temp);
108     }
109     else
110     {
111       // If we got a value less then -1 then something went wrong in the
112       // protocol
113     }
114   }
115   else if (not strcmp("uptime", key))
116   {
117     errno= 0;
118     memc_stat->uptime= strtoul(value, (char **)NULL, 10);
119     if (errno != 0)
120     {
121       return MEMCACHED_FAILURE;
122     }
123   }
124   else if (not strcmp("time", key))
125   {
126     errno= 0;
127     memc_stat->time= strtoul(value, (char **)NULL, 10);
128     if (errno != 0)
129     {
130       return MEMCACHED_FAILURE;
131     }
132   }
133   else if (not strcmp("version", key))
134   {
135     memcpy(memc_stat->version, value, strlen(value));
136     memc_stat->version[strlen(value)]= 0;
137   }
138   else if (not strcmp("pointer_size", key))
139   {
140     errno= 0;
141     memc_stat->pointer_size= strtoul(value, (char **)NULL, 10);
142     if (errno != 0)
143     {
144       return MEMCACHED_FAILURE;
145     }
146   }
147   else if (not strcmp("rusage_user", key))
148   {
149     char *walk_ptr;
150     for (walk_ptr= (char*)value; (!ispunct(*walk_ptr)); walk_ptr++) {};
151     *walk_ptr= 0;
152     walk_ptr++;
153 
154     errno= 0;
155     memc_stat->rusage_user_seconds= strtoul(value, (char **)NULL, 10);
156     if (errno != 0)
157     {
158       return MEMCACHED_FAILURE;
159     }
160 
161     errno= 0;
162     memc_stat->rusage_user_microseconds= strtoul(walk_ptr, (char **)NULL, 10);
163     if (errno != 0)
164     {
165       return MEMCACHED_FAILURE;
166     }
167   }
168   else if (not strcmp("rusage_system", key))
169   {
170     char *walk_ptr;
171     for (walk_ptr= (char*)value; (!ispunct(*walk_ptr)); walk_ptr++) {};
172     *walk_ptr= 0;
173     walk_ptr++;
174 
175     errno= 0;
176     memc_stat->rusage_system_seconds= strtoul(value, (char **)NULL, 10);
177     if (errno != 0)
178     {
179       return MEMCACHED_FAILURE;
180     }
181 
182     errno= 0;
183     memc_stat->rusage_system_microseconds= strtoul(walk_ptr, (char **)NULL, 10);
184     if (errno != 0)
185     {
186       return MEMCACHED_FAILURE;
187     }
188   }
189   else if (not strcmp("curr_items", key))
190   {
191     errno= 0;
192     memc_stat->curr_items= strtoul(value, (char **)NULL, 10);
193     if (errno != 0)
194     {
195       return MEMCACHED_FAILURE;
196     }
197   }
198   else if (not strcmp("total_items", key))
199   {
200     errno= 0;
201     memc_stat->total_items= strtoul(value, (char **)NULL, 10);
202     if (errno != 0)
203     {
204       return MEMCACHED_FAILURE;
205     }
206   }
207   else if (not strcmp("bytes_read", key))
208   {
209     errno= 0;
210     memc_stat->bytes_read= strtoull(value, (char **)NULL, 10);
211     if (errno != 0)
212     {
213       return MEMCACHED_FAILURE;
214     }
215   }
216   else if (not strcmp("bytes_written", key))
217   {
218     errno= 0;
219     memc_stat->bytes_written= strtoull(value, (char **)NULL, 10);
220     if (errno != 0)
221     {
222       return MEMCACHED_FAILURE;
223     }
224   }
225   else if (not strcmp("bytes", key))
226   {
227     errno= 0;
228     memc_stat->bytes= strtoull(value, (char **)NULL, 10);
229     if (errno != 0)
230     {
231       return MEMCACHED_FAILURE;
232     }
233   }
234   else if (not strcmp("curr_connections", key))
235   {
236     errno= 0;
237     memc_stat->curr_connections= strtoull(value, (char **)NULL, 10);
238     if (errno != 0)
239     {
240       return MEMCACHED_FAILURE;
241     }
242   }
243   else if (not strcmp("total_connections", key))
244   {
245     errno= 0;
246     memc_stat->total_connections= strtoull(value, (char **)NULL, 10);
247     if (errno != 0)
248     {
249       return MEMCACHED_FAILURE;
250     }
251   }
252   else if (not strcmp("connection_structures", key))
253   {
254     errno= 0;
255     memc_stat->connection_structures= strtoul(value, (char **)NULL, 10);
256     if (errno != 0)
257     {
258       return MEMCACHED_FAILURE;
259     }
260   }
261   else if (not strcmp("cmd_get", key))
262   {
263     errno= 0;
264     memc_stat->cmd_get= strtoull(value, (char **)NULL, 10);
265     if (errno != 0)
266     {
267       return MEMCACHED_FAILURE;
268     }
269   }
270   else if (not strcmp("cmd_set", key))
271   {
272     errno= 0;
273     memc_stat->cmd_set= strtoull(value, (char **)NULL, 10);
274     if (errno != 0)
275     {
276       return MEMCACHED_FAILURE;
277     }
278   }
279   else if (not strcmp("get_hits", key))
280   {
281     errno= 0;
282     memc_stat->get_hits= strtoull(value, (char **)NULL, 10);
283     if (errno != 0)
284     {
285       return MEMCACHED_FAILURE;
286     }
287   }
288   else if (not strcmp("get_misses", key))
289   {
290     errno= 0;
291     memc_stat->get_misses= strtoull(value, (char **)NULL, 10);
292     if (errno != 0)
293     {
294       return MEMCACHED_FAILURE;
295     }
296   }
297   else if (not strcmp("evictions", key))
298   {
299     errno= 0;
300     memc_stat->evictions= strtoull(value, (char **)NULL, 10);
301     if (errno != 0)
302     {
303       return MEMCACHED_FAILURE;
304     }
305   }
306   else if (not strcmp("limit_maxbytes", key))
307   {
308     errno= 0;
309     memc_stat->limit_maxbytes= strtoull(value, (char **)NULL, 10);
310     if (errno != 0)
311     {
312       return MEMCACHED_FAILURE;
313     }
314   }
315   else if (not strcmp("threads", key))
316   {
317     errno= 0;
318     memc_stat->threads= strtoul(value, (char **)NULL, 10);
319     if (errno != 0)
320     {
321       return MEMCACHED_FAILURE;
322     }
323   }
324   else if ((strcmp("delete_misses", key) == 0 or /* New stats in the 1.3 beta */
325             strcmp("delete_hits", key) == 0 or /* Just swallow them for now.. */
326             strcmp("incr_misses", key) == 0 or
327             strcmp("incr_hits", key) == 0 or
328             strcmp("decr_misses", key) == 0 or
329             strcmp("decr_hits", key) == 0 or
330             strcmp("cas_misses", key) == 0 or
331             strcmp("cas_hits", key) == 0 or
332             strcmp("cas_badval", key) == 0 or
333             strcmp("cmd_flush", key) == 0 or
334             strcmp("accepting_conns", key) == 0 or
335             strcmp("listen_disabled_num", key) == 0 or
336             strcmp("conn_yields", key) == 0 or
337             strcmp("auth_cmds", key) == 0 or
338             strcmp("auth_errors", key) == 0 or
339             strcmp("reclaimed", key) == 0) == 0)
340   {
341     WATCHPOINT_STRING(key);
342     /* return MEMCACHED_UNKNOWN_STAT_KEY; */
343     return MEMCACHED_SUCCESS;
344   }
345 
346   return MEMCACHED_SUCCESS;
347 }
348 
349 char *memcached_stat_get_value(const memcached_st* shell, memcached_stat_st *memc_stat,
350                                const char *key, memcached_return_t *error)
351 {
352   memcached_return_t not_used;
353   if (error == NULL)
354   {
355     error= &not_used;
356   }
357 
358   if (memc_stat == NULL)
359   {
360     *error= MEMCACHED_INVALID_ARGUMENTS;
361     return NULL;
362   }
363 
364   char buffer[SMALL_STRING_LEN];
365   int length;
366 
367   *error= MEMCACHED_SUCCESS;
368 
369   if (memcmp("pid", key, sizeof("pid") -1) == 0)
370   {
371     length= snprintf(buffer, SMALL_STRING_LEN,"%lld", (signed long long)memc_stat->pid);
372   }
373   else if (not memcmp("uptime", key, sizeof("uptime") -1))
374   {
375     length= snprintf(buffer, SMALL_STRING_LEN,"%lu", memc_stat->uptime);
376   }
377   else if (not memcmp("time", key, sizeof("time") -1))
378   {
379     length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->time);
380   }
381   else if (not memcmp("version", key, sizeof("version") -1))
382   {
383     length= snprintf(buffer, SMALL_STRING_LEN,"%s", memc_stat->version);
384   }
385   else if (not memcmp("pointer_size", key, sizeof("pointer_size") -1))
386   {
387     length= snprintf(buffer, SMALL_STRING_LEN,"%lu", memc_stat->pointer_size);
388   }
389   else if (not memcmp("rusage_user", key, sizeof("rusage_user") -1))
390   {
391     length= snprintf(buffer, SMALL_STRING_LEN,"%lu.%lu", memc_stat->rusage_user_seconds, memc_stat->rusage_user_microseconds);
392   }
393   else if (not memcmp("rusage_system", key, sizeof("rusage_system") -1))
394   {
395     length= snprintf(buffer, SMALL_STRING_LEN,"%lu.%lu", memc_stat->rusage_system_seconds, memc_stat->rusage_system_microseconds);
396   }
397   else if (not memcmp("curr_items", key, sizeof("curr_items") -1))
398   {
399     length= snprintf(buffer, SMALL_STRING_LEN,"%lu", memc_stat->curr_items);
400   }
401   else if (not memcmp("total_items", key, sizeof("total_items") -1))
402   {
403     length= snprintf(buffer, SMALL_STRING_LEN,"%lu", memc_stat->total_items);
404   }
405   else if (not memcmp("curr_connections", key, sizeof("curr_connections") -1))
406   {
407     length= snprintf(buffer, SMALL_STRING_LEN,"%lu", memc_stat->curr_connections);
408   }
409   else if (not memcmp("total_connections", key, sizeof("total_connections") -1))
410   {
411     length= snprintf(buffer, SMALL_STRING_LEN,"%lu", memc_stat->total_connections);
412   }
413   else if (not memcmp("connection_structures", key, sizeof("connection_structures") -1))
414   {
415     length= snprintf(buffer, SMALL_STRING_LEN,"%lu", memc_stat->connection_structures);
416   }
417   else if (not memcmp("cmd_get", key, sizeof("cmd_get") -1))
418   {
419     length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->cmd_get);
420   }
421   else if (not memcmp("cmd_set", key, sizeof("cmd_set") -1))
422   {
423     length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->cmd_set);
424   }
425   else if (not memcmp("get_hits", key, sizeof("get_hits") -1))
426   {
427     length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->get_hits);
428   }
429   else if (not memcmp("get_misses", key, sizeof("get_misses") -1))
430   {
431     length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->get_misses);
432   }
433   else if (not memcmp("evictions", key, sizeof("evictions") -1))
434   {
435     length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->evictions);
436   }
437   else if (not memcmp("bytes_read", key, sizeof("bytes_read") -1))
438   {
439     length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->bytes_read);
440   }
441   else if (not memcmp("bytes_written", key, sizeof("bytes_written") -1))
442   {
443     length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->bytes_written);
444   }
445   else if (not memcmp("bytes", key, sizeof("bytes") -1))
446   {
447     length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->bytes);
448   }
449   else if (not memcmp("limit_maxbytes", key, sizeof("limit_maxbytes") -1))
450   {
451     length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->limit_maxbytes);
452   }
453   else if (not memcmp("threads", key, sizeof("threads") -1))
454   {
455     length= snprintf(buffer, SMALL_STRING_LEN,"%lu", memc_stat->threads);
456   }
457   else
458   {
459     Memcached* memc= (Memcached*)memcached2Memcached(shell);
460     *error= memcached_set_error(*memc, MEMCACHED_INVALID_ARGUMENTS, MEMCACHED_AT, memcached_literal_param("Invalid key provided"));
461     return NULL;
462   }
463 
464   if (length >= SMALL_STRING_LEN || length < 0)
465   {
466     Memcached* memc= (Memcached*)memcached2Memcached(shell);
467     *error= memcached_set_error(*memc, MEMCACHED_FAILURE, MEMCACHED_AT, memcached_literal_param("Internal failure occured with buffer, please report this bug."));
468     return NULL;
469   }
470 
471   // User is responsible for free() memory, so use malloc()
472   char *ret= static_cast<char *>(malloc(size_t(length +1)));
473   memcpy(ret, buffer, (size_t) length);
474   ret[length]= '\0';
475 
476   return ret;
477 }
478 
479 static memcached_return_t binary_stats_fetch(memcached_stat_st *memc_stat,
480                                              const char *args,
481                                              const size_t args_length,
482                                              memcached_instance_st* instance,
483                                              struct local_context *check)
484 {
485   char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE];
486   protocol_binary_request_stats request= {}; // = {.bytes= {0}};
487 
488   initialize_binary_request(instance, request.message.header);
489 
490   request.message.header.request.opcode= PROTOCOL_BINARY_CMD_STAT;
491   request.message.header.request.datatype= PROTOCOL_BINARY_RAW_BYTES;
492 
493   if (args_length)
494   {
495     request.message.header.request.keylen= htons(uint16_t(args_length));
496     request.message.header.request.bodylen= htonl(uint32_t( args_length));
497 
498     libmemcached_io_vector_st vector[]=
499     {
500       { request.bytes, sizeof(request.bytes) },
501       { args, args_length }
502     };
503 
504     if (memcached_vdo(instance, vector, 2, true) != MEMCACHED_SUCCESS)
505     {
506       memcached_io_reset(instance);
507       return MEMCACHED_WRITE_FAILURE;
508     }
509   }
510   else
511   {
512     libmemcached_io_vector_st vector[]=
513     {
514       { request.bytes, sizeof(request.bytes) }
515     };
516 
517     if (memcached_vdo(instance, vector, 1, true) != MEMCACHED_SUCCESS)
518     {
519       memcached_io_reset(instance);
520       return MEMCACHED_WRITE_FAILURE;
521     }
522   }
523 
524   memcached_server_response_decrement(instance);
525   while (1)
526   {
527     memcached_return_t rc= memcached_response(instance, buffer, sizeof(buffer), NULL);
528 
529     if (rc == MEMCACHED_END)
530     {
531       break;
532     }
533 
534     if (rc != MEMCACHED_SUCCESS)
535     {
536       memcached_io_reset(instance);
537       return rc;
538     }
539 
540     if (check && check->func)
541     {
542       size_t key_length= strlen(buffer);
543 
544       check->func(instance,
545                   buffer, key_length,
546                   buffer+key_length+1, strlen(buffer+key_length+1),
547                   check->context);
548     }
549 
550     if (memc_stat)
551     {
552       if ((set_data(memc_stat, buffer, buffer + strlen(buffer) + 1)) == MEMCACHED_UNKNOWN_STAT_KEY)
553       {
554         WATCHPOINT_ERROR(MEMCACHED_UNKNOWN_STAT_KEY);
555         WATCHPOINT_ASSERT(0);
556       }
557     }
558   }
559 
560   /*
561    * memcached_response will decrement the counter, so I need to reset it..
562    * todo: look at this and try to find a better solution.
563    * */
564   instance->cursor_active_= 0;
565 
566   return MEMCACHED_SUCCESS;
567 }
568 
569 static memcached_return_t ascii_stats_fetch(memcached_stat_st *memc_stat,
570                                             const char *args,
571                                             const size_t args_length,
572                                             memcached_instance_st* instance,
573                                             struct local_context *check)
574 {
575   libmemcached_io_vector_st vector[]=
576   {
577     { memcached_literal_param("stats ") },
578     { args, args_length },
579     { memcached_literal_param("\r\n") }
580   };
581 
582   memcached_return_t rc= memcached_vdo(instance, vector, 3, true);
583   if (memcached_success(rc))
584   {
585     char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE];
586     while ((rc= memcached_response(instance, buffer, sizeof(buffer), NULL)) == MEMCACHED_STAT)
587     {
588       char *string_ptr= buffer;
589       string_ptr+= 5; /* Move past STAT */
590 
591       char *end_ptr;
592       for (end_ptr= string_ptr; isgraph(*end_ptr); end_ptr++) {};
593       char *key= string_ptr;
594       key[size_t(end_ptr-string_ptr)]= 0;
595 
596       string_ptr= end_ptr + 1;
597       for (end_ptr= string_ptr; !(isspace(*end_ptr)); end_ptr++) {};
598       char *value= string_ptr;
599       value[(size_t)(end_ptr -string_ptr)]= 0;
600 #if 0
601       bool check_bool= bool(check);
602       bool check_func_bool= bool(check) ? bool(check->func) : false;
603       fprintf(stderr, "%s:%d %s %s %d:%d\n", __FILE__, __LINE__, key, value, check_bool, check_func_bool);
604 #endif
605 
606       if (check and check->func)
607       {
608         check->func(instance,
609                     key, strlen(key),
610                     value, strlen(value),
611                     check->context);
612       }
613 
614       if (memc_stat)
615       {
616         if((set_data(memc_stat, key, value)) == MEMCACHED_UNKNOWN_STAT_KEY)
617         {
618           WATCHPOINT_ERROR(MEMCACHED_UNKNOWN_STAT_KEY);
619           WATCHPOINT_ASSERT(0);
620         }
621       }
622     }
623   }
624 
625   if (rc == MEMCACHED_ERROR)
626   {
627     return MEMCACHED_INVALID_ARGUMENTS;
628   }
629 
630   if (rc == MEMCACHED_END)
631   {
632     return MEMCACHED_SUCCESS;
633   }
634 
635   return rc;
636 }
637 
638 memcached_stat_st *memcached_stat(memcached_st *shell, char *args, memcached_return_t *error)
639 {
640   Memcached* self= memcached2Memcached(shell);
641   memcached_return_t unused;
642   if (error == NULL)
643   {
644     error= &unused;
645   }
646 
647   if (memcached_failed(*error= initialize_query(self, true)))
648   {
649     return NULL;
650   }
651 
652   if (memcached_is_udp(self))
653   {
654     *error= memcached_set_error(*self, MEMCACHED_NOT_SUPPORTED, MEMCACHED_AT);
655     return NULL;
656   }
657 
658   memcached_return_t rc;
659   size_t args_length= 0;
660   if (args)
661   {
662     args_length= strlen(args);
663     if (memcached_failed(rc= memcached_key_test(*self, (const char **)&args, &args_length, 1)))
664     {
665       *error= memcached_set_error(*self, rc, MEMCACHED_AT);
666       return NULL;
667     }
668   }
669 
670   WATCHPOINT_ASSERT(error);
671 
672   memcached_stat_st *stats= libmemcached_xcalloc(self, memcached_server_count(self), memcached_stat_st);
673   if (stats == NULL)
674   {
675     *error= memcached_set_error(*self, MEMCACHED_MEMORY_ALLOCATION_FAILURE, MEMCACHED_AT);
676     return NULL;
677   }
678 
679   WATCHPOINT_ASSERT(rc == MEMCACHED_SUCCESS);
680   rc= MEMCACHED_SUCCESS;
681   for (uint32_t x= 0; x < memcached_server_count(self); x++)
682   {
683     memcached_stat_st* stat_instance= stats +x;
684 
685     stat_instance->pid= -1;
686     stat_instance->root= self;
687 
688     memcached_instance_st* instance= memcached_instance_fetch(self, x);
689 
690     memcached_return_t temp_return;
691     if (memcached_is_binary(self))
692     {
693       temp_return= binary_stats_fetch(stat_instance, args, args_length, instance, NULL);
694     }
695     else
696     {
697       temp_return= ascii_stats_fetch(stat_instance, args, args_length, instance, NULL);
698     }
699 
700     // Special case where "args" is invalid
701     if (temp_return == MEMCACHED_INVALID_ARGUMENTS)
702     {
703       rc= MEMCACHED_INVALID_ARGUMENTS;
704       break;
705     }
706 
707     if (memcached_failed(temp_return))
708     {
709       rc= MEMCACHED_SOME_ERRORS;
710     }
711   }
712 
713   *error= rc;
714 
715   return stats;
716 }
717 
718 memcached_return_t memcached_stat_servername(memcached_stat_st *memc_stat, char *args,
719                                              const char *hostname, in_port_t port)
720 {
721   memcached_st memc;
722 
723   memcached_stat_st unused_memc_stat;
724   if (memc_stat == NULL)
725   {
726     memc_stat= &unused_memc_stat;
727   }
728 
729   memset(memc_stat, 0, sizeof(memcached_stat_st));
730 
731   memcached_st *memc_ptr= memcached_create(&memc);
732   if (memc_ptr == NULL)
733   {
734     return MEMCACHED_MEMORY_ALLOCATION_FAILURE;
735   }
736 
737   memcached_return_t rc;
738   if (memcached_failed(rc= memcached_server_add(&memc, hostname, port)))
739   {
740     memcached_free(&memc);
741     return rc;
742   }
743 
744   if (memcached_success(rc= initialize_query(memc_ptr, true)))
745   {
746     size_t args_length= 0;
747     if (args)
748     {
749       args_length= strlen(args);
750       rc= memcached_key_test(*memc_ptr, (const char **)&args, &args_length, 1);
751     }
752 
753     if (memcached_success(rc))
754     {
755       memcached_instance_st* instance= memcached_instance_fetch(memc_ptr, 0);
756       if (memc.flags.binary_protocol)
757       {
758         rc= binary_stats_fetch(memc_stat, args, args_length, instance, NULL);
759       }
760       else
761       {
762         rc= ascii_stats_fetch(memc_stat, args, args_length, instance, NULL);
763       }
764     }
765   }
766 
767   memcached_free(&memc);
768 
769   return rc;
770 }
771 
772 /*
773   We make a copy of the keys since at some point in the not so distant future
774   we will add support for "found" keys.
775 */
776 char ** memcached_stat_get_keys(memcached_st *shell,
777                                 memcached_stat_st *,
778                                 memcached_return_t *error)
779 {
780   Memcached* memc= memcached2Memcached(shell);
781   if (memc)
782   {
783     char **list= static_cast<char **>(libmemcached_malloc(memc, sizeof(memcached_stat_keys)));
784     if (list == NULL)
785     {
786       if (error)
787       {
788         *error= memcached_set_error(*memc, MEMCACHED_MEMORY_ALLOCATION_FAILURE, MEMCACHED_AT);
789       }
790 
791       return NULL;
792     }
793 
794     memcpy(list, memcached_stat_keys, sizeof(memcached_stat_keys));
795 
796     if (error)
797     {
798       *error= MEMCACHED_SUCCESS;
799     }
800 
801     return list;
802   }
803 
804   return NULL;
805 }
806 
807 void memcached_stat_free(const memcached_st *, memcached_stat_st *memc_stat)
808 {
809   WATCHPOINT_ASSERT(memc_stat); // Be polite, but when debugging catch this as an error
810   if (memc_stat)
811   {
812     libmemcached_free(memc_stat->root, memc_stat);
813   }
814 }
815 
816 static memcached_return_t call_stat_fn(memcached_st *memc,
817                                        memcached_instance_st* instance,
818                                        void *context)
819 {
820   if (memc)
821   {
822     local_context *check= (struct local_context *)context;
823 
824     if (memcached_is_binary(memc))
825     {
826       return binary_stats_fetch(NULL, check->args, check->args_length, instance, check);
827     }
828     else
829     {
830       return ascii_stats_fetch(NULL, check->args, check->args_length, instance, check);
831     }
832   }
833 
834   return MEMCACHED_INVALID_ARGUMENTS;
835 }
836 
837 memcached_return_t memcached_stat_execute(memcached_st *shell, const char *args,  memcached_stat_fn func, void *context)
838 {
839   Memcached* memc= memcached2Memcached(shell);
840   if (memcached_fatal(memcached_version(memc)))
841   {
842     return memcached_last_error(memc);
843   }
844 
845  local_context check(func, context, args, args ? strlen(args) : 0);
846 
847  return memcached_server_execute(memc, call_stat_fn, (void *)&check);
848 }
849