PageRenderTime 54ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/collectd-4.10.7/src/memcached.c

#
C | 533 lines | 372 code | 79 blank | 82 comment | 120 complexity | 144accb1379d06d197b5f744cff696e8 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1
  1. /**
  2. * collectd - src/memcached.c, based on src/hddtemp.c
  3. * Copyright (C) 2007 Antony Dovgal
  4. * Copyright (C) 2007-2009 Florian Forster
  5. * Copyright (C) 2009 Doug MacEachern
  6. * Copyright (C) 2009 Franck Lombardi
  7. *
  8. * This program is free software; you can redistribute it and/or modify it
  9. * under the terms of the GNU General Public License as published by the
  10. * Free Software Foundation; either version 2 of the License, or (at your
  11. * option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful, but
  14. * WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. * General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License along
  19. * with this program; if not, write to the Free Software Foundation, Inc.,
  20. * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  21. *
  22. * Authors:
  23. * Antony Dovgal <tony at daylessday dot org>
  24. * Florian octo Forster <octo at verplant.org>
  25. * Doug MacEachern <dougm at hyperic.com>
  26. * Franck Lombardi
  27. **/
  28. #include "collectd.h"
  29. #include "common.h"
  30. #include "plugin.h"
  31. #include "configfile.h"
  32. # include <poll.h>
  33. # include <netdb.h>
  34. # include <sys/socket.h>
  35. # include <sys/un.h>
  36. # include <netinet/in.h>
  37. # include <netinet/tcp.h>
  38. /* Hack to work around the missing define in AIX */
  39. #ifndef MSG_DONTWAIT
  40. # define MSG_DONTWAIT MSG_NONBLOCK
  41. #endif
  42. #define MEMCACHED_DEF_HOST "127.0.0.1"
  43. #define MEMCACHED_DEF_PORT "11211"
  44. #define MEMCACHED_RETRY_COUNT 100
  45. static const char *config_keys[] =
  46. {
  47. "Socket",
  48. "Host",
  49. "Port"
  50. };
  51. static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
  52. static char *memcached_socket = NULL;
  53. static char *memcached_host = NULL;
  54. static char memcached_port[16];
  55. static int memcached_query_daemon (char *buffer, int buffer_size) /* {{{ */
  56. {
  57. int fd;
  58. ssize_t status;
  59. int buffer_fill;
  60. int i = 0;
  61. if (memcached_socket != NULL) {
  62. struct sockaddr_un serv_addr;
  63. memset (&serv_addr, 0, sizeof (serv_addr));
  64. serv_addr.sun_family = AF_UNIX;
  65. sstrncpy (serv_addr.sun_path, memcached_socket,
  66. sizeof (serv_addr.sun_path));
  67. /* create our socket descriptor */
  68. fd = socket (AF_UNIX, SOCK_STREAM, 0);
  69. if (fd < 0) {
  70. char errbuf[1024];
  71. ERROR ("memcached: unix socket: %s", sstrerror (errno, errbuf,
  72. sizeof (errbuf)));
  73. return -1;
  74. }
  75. /* connect to the memcached daemon */
  76. status = (ssize_t) connect (fd, (struct sockaddr *) &serv_addr,
  77. sizeof (serv_addr));
  78. if (status != 0) {
  79. shutdown (fd, SHUT_RDWR);
  80. close (fd);
  81. fd = -1;
  82. }
  83. }
  84. else { /* if (memcached_socket == NULL) */
  85. const char *host;
  86. const char *port;
  87. struct addrinfo ai_hints;
  88. struct addrinfo *ai_list, *ai_ptr;
  89. int ai_return = 0;
  90. memset (&ai_hints, '\0', sizeof (ai_hints));
  91. ai_hints.ai_flags = 0;
  92. #ifdef AI_ADDRCONFIG
  93. /* ai_hints.ai_flags |= AI_ADDRCONFIG; */
  94. #endif
  95. ai_hints.ai_family = AF_INET;
  96. ai_hints.ai_socktype = SOCK_STREAM;
  97. ai_hints.ai_protocol = 0;
  98. host = memcached_host;
  99. if (host == NULL) {
  100. host = MEMCACHED_DEF_HOST;
  101. }
  102. port = memcached_port;
  103. if (strlen (port) == 0) {
  104. port = MEMCACHED_DEF_PORT;
  105. }
  106. if ((ai_return = getaddrinfo (host, port, &ai_hints, &ai_list)) != 0) {
  107. char errbuf[1024];
  108. ERROR ("memcached: getaddrinfo (%s, %s): %s",
  109. host, port,
  110. (ai_return == EAI_SYSTEM)
  111. ? sstrerror (errno, errbuf, sizeof (errbuf))
  112. : gai_strerror (ai_return));
  113. return -1;
  114. }
  115. fd = -1;
  116. for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next) {
  117. /* create our socket descriptor */
  118. fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
  119. if (fd < 0) {
  120. char errbuf[1024];
  121. ERROR ("memcached: socket: %s", sstrerror (errno, errbuf, sizeof (errbuf)));
  122. continue;
  123. }
  124. /* connect to the memcached daemon */
  125. status = (ssize_t) connect (fd, (struct sockaddr *) ai_ptr->ai_addr, ai_ptr->ai_addrlen);
  126. if (status != 0) {
  127. shutdown (fd, SHUT_RDWR);
  128. close (fd);
  129. fd = -1;
  130. continue;
  131. }
  132. /* A socket could be opened and connecting succeeded. We're
  133. * done. */
  134. break;
  135. }
  136. freeaddrinfo (ai_list);
  137. }
  138. if (fd < 0) {
  139. ERROR ("memcached: Could not connect to daemon.");
  140. return -1;
  141. }
  142. if (send(fd, "stats\r\n", sizeof("stats\r\n") - 1, MSG_DONTWAIT) != (sizeof("stats\r\n") - 1)) {
  143. ERROR ("memcached: Could not send command to the memcached daemon.");
  144. return -1;
  145. }
  146. {
  147. struct pollfd p;
  148. int status;
  149. memset (&p, 0, sizeof (p));
  150. p.fd = fd;
  151. p.events = POLLIN | POLLERR | POLLHUP;
  152. p.revents = 0;
  153. status = poll (&p, /* nfds = */ 1, /* timeout = */ 1000 * interval_g);
  154. if (status <= 0)
  155. {
  156. if (status == 0)
  157. {
  158. ERROR ("memcached: poll(2) timed out after %i seconds.", interval_g);
  159. }
  160. else
  161. {
  162. char errbuf[1024];
  163. ERROR ("memcached: poll(2) failed: %s",
  164. sstrerror (errno, errbuf, sizeof (errbuf)));
  165. }
  166. shutdown (fd, SHUT_RDWR);
  167. close (fd);
  168. return (-1);
  169. }
  170. }
  171. /* receive data from the memcached daemon */
  172. memset (buffer, '\0', buffer_size);
  173. buffer_fill = 0;
  174. while ((status = recv (fd, buffer + buffer_fill, buffer_size - buffer_fill, MSG_DONTWAIT)) != 0) {
  175. if (i > MEMCACHED_RETRY_COUNT) {
  176. ERROR("recv() timed out");
  177. break;
  178. }
  179. i++;
  180. if (status == -1) {
  181. char errbuf[1024];
  182. if (errno == EAGAIN) {
  183. continue;
  184. }
  185. ERROR ("memcached: Error reading from socket: %s",
  186. sstrerror (errno, errbuf, sizeof (errbuf)));
  187. shutdown(fd, SHUT_RDWR);
  188. close (fd);
  189. return -1;
  190. }
  191. buffer_fill += status;
  192. if (buffer_fill > 3 && buffer[buffer_fill-5] == 'E' && buffer[buffer_fill-4] == 'N' && buffer[buffer_fill-3] == 'D') {
  193. /* we got all the data */
  194. break;
  195. }
  196. }
  197. if (buffer_fill >= buffer_size) {
  198. buffer[buffer_size - 1] = '\0';
  199. WARNING ("memcached: Message from memcached has been truncated.");
  200. } else if (buffer_fill == 0) {
  201. WARNING ("memcached: Peer has unexpectedly shut down the socket. "
  202. "Buffer: `%s'", buffer);
  203. shutdown(fd, SHUT_RDWR);
  204. close(fd);
  205. return -1;
  206. }
  207. shutdown(fd, SHUT_RDWR);
  208. close(fd);
  209. return 0;
  210. }
  211. /* }}} */
  212. static int memcached_config (const char *key, const char *value) /* {{{ */
  213. {
  214. if (strcasecmp (key, "Socket") == 0) {
  215. if (memcached_socket != NULL) {
  216. free (memcached_socket);
  217. }
  218. memcached_socket = strdup (value);
  219. } else if (strcasecmp (key, "Host") == 0) {
  220. if (memcached_host != NULL) {
  221. free (memcached_host);
  222. }
  223. memcached_host = strdup (value);
  224. } else if (strcasecmp (key, "Port") == 0) {
  225. int port = (int) (atof (value));
  226. if ((port > 0) && (port <= 65535)) {
  227. ssnprintf (memcached_port, sizeof (memcached_port), "%i", port);
  228. } else {
  229. sstrncpy (memcached_port, value, sizeof (memcached_port));
  230. }
  231. } else {
  232. return -1;
  233. }
  234. return 0;
  235. }
  236. /* }}} */
  237. static void submit_counter (const char *type, const char *type_inst,
  238. counter_t value) /* {{{ */
  239. {
  240. value_t values[1];
  241. value_list_t vl = VALUE_LIST_INIT;
  242. values[0].counter = value;
  243. vl.values = values;
  244. vl.values_len = 1;
  245. sstrncpy (vl.host, hostname_g, sizeof (vl.host));
  246. sstrncpy (vl.plugin, "memcached", sizeof (vl.plugin));
  247. sstrncpy (vl.type, type, sizeof (vl.type));
  248. if (type_inst != NULL)
  249. sstrncpy (vl.type_instance, type_inst, sizeof (vl.type_instance));
  250. plugin_dispatch_values (&vl);
  251. } /* void memcached_submit_cmd */
  252. /* }}} */
  253. static void submit_counter2 (const char *type, const char *type_inst,
  254. counter_t value0, counter_t value1) /* {{{ */
  255. {
  256. value_t values[2];
  257. value_list_t vl = VALUE_LIST_INIT;
  258. values[0].counter = value0;
  259. values[1].counter = value1;
  260. vl.values = values;
  261. vl.values_len = 2;
  262. vl.time = time (NULL);
  263. sstrncpy (vl.host, hostname_g, sizeof (vl.host));
  264. sstrncpy (vl.plugin, "memcached", sizeof (vl.plugin));
  265. sstrncpy (vl.type, type, sizeof (vl.type));
  266. if (type_inst != NULL)
  267. sstrncpy (vl.type_instance, type_inst, sizeof (vl.type_instance));
  268. plugin_dispatch_values (&vl);
  269. } /* void memcached_submit_cmd */
  270. /* }}} */
  271. static void submit_gauge (const char *type, const char *type_inst,
  272. gauge_t value) /* {{{ */
  273. {
  274. value_t values[1];
  275. value_list_t vl = VALUE_LIST_INIT;
  276. values[0].gauge = value;
  277. vl.values = values;
  278. vl.values_len = 1;
  279. vl.time = time (NULL);
  280. sstrncpy (vl.host, hostname_g, sizeof (vl.host));
  281. sstrncpy (vl.plugin, "memcached", sizeof (vl.plugin));
  282. sstrncpy (vl.type, type, sizeof (vl.type));
  283. if (type_inst != NULL)
  284. sstrncpy (vl.type_instance, type_inst, sizeof (vl.type_instance));
  285. plugin_dispatch_values (&vl);
  286. }
  287. /* }}} */
  288. static void submit_gauge2 (const char *type, const char *type_inst,
  289. gauge_t value0, gauge_t value1) /* {{{ */
  290. {
  291. value_t values[2];
  292. value_list_t vl = VALUE_LIST_INIT;
  293. values[0].gauge = value0;
  294. values[1].gauge = value1;
  295. vl.values = values;
  296. vl.values_len = 2;
  297. vl.time = time (NULL);
  298. sstrncpy (vl.host, hostname_g, sizeof (vl.host));
  299. sstrncpy (vl.plugin, "memcached", sizeof (vl.plugin));
  300. sstrncpy (vl.type, type, sizeof (vl.type));
  301. if (type_inst != NULL)
  302. sstrncpy (vl.type_instance, type_inst, sizeof (vl.type_instance));
  303. plugin_dispatch_values (&vl);
  304. }
  305. /* }}} */
  306. static int memcached_read (void) /* {{{ */
  307. {
  308. char buf[4096];
  309. char *fields[3];
  310. char *ptr;
  311. char *line;
  312. char *saveptr;
  313. int fields_num;
  314. gauge_t bytes_used = NAN;
  315. gauge_t bytes_total = NAN;
  316. gauge_t hits = NAN;
  317. gauge_t gets = NAN;
  318. counter_t rusage_user = 0;
  319. counter_t rusage_syst = 0;
  320. counter_t octets_rx = 0;
  321. counter_t octets_tx = 0;
  322. /* get data from daemon */
  323. if (memcached_query_daemon (buf, sizeof (buf)) < 0) {
  324. return -1;
  325. }
  326. #define FIELD_IS(cnst) \
  327. (((sizeof(cnst) - 1) == name_len) && (strcmp (cnst, fields[1]) == 0))
  328. ptr = buf;
  329. saveptr = NULL;
  330. while ((line = strtok_r (ptr, "\n\r", &saveptr)) != NULL)
  331. {
  332. int name_len;
  333. ptr = NULL;
  334. fields_num = strsplit(line, fields, 3);
  335. if (fields_num != 3)
  336. continue;
  337. name_len = strlen(fields[1]);
  338. if (name_len == 0)
  339. continue;
  340. /*
  341. * For an explanation on these fields please refer to
  342. * <http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt>
  343. */
  344. /*
  345. * CPU time consumed by the memcached process
  346. */
  347. if (FIELD_IS ("rusage_user"))
  348. {
  349. rusage_user = atoll (fields[2]);
  350. }
  351. else if (FIELD_IS ("rusage_system"))
  352. {
  353. rusage_syst = atoll(fields[2]);
  354. }
  355. /*
  356. * Number of threads of this instance
  357. */
  358. else if (FIELD_IS ("threads"))
  359. {
  360. submit_gauge2 ("ps_count", NULL, NAN, atof (fields[2]));
  361. }
  362. /*
  363. * Number of items stored
  364. */
  365. else if (FIELD_IS ("curr_items"))
  366. {
  367. submit_gauge ("memcached_items", "current", atof (fields[2]));
  368. }
  369. /*
  370. * Number of bytes used and available (total - used)
  371. */
  372. else if (FIELD_IS ("bytes"))
  373. {
  374. bytes_used = atof (fields[2]);
  375. }
  376. else if (FIELD_IS ("limit_maxbytes"))
  377. {
  378. bytes_total = atof(fields[2]);
  379. }
  380. /*
  381. * Connections
  382. */
  383. else if (FIELD_IS ("curr_connections"))
  384. {
  385. submit_gauge ("memcached_connections", "current", atof (fields[2]));
  386. }
  387. /*
  388. * Commands
  389. */
  390. else if ((name_len > 4) && (strncmp (fields[1], "cmd_", 4) == 0))
  391. {
  392. const char *name = fields[1] + 4;
  393. submit_counter ("memcached_command", name, atoll (fields[2]));
  394. if (strcmp (name, "get") == 0)
  395. gets = atof (fields[2]);
  396. }
  397. /*
  398. * Operations on the cache, i. e. cache hits, cache misses and evictions of items
  399. */
  400. else if (FIELD_IS ("get_hits"))
  401. {
  402. submit_counter ("memcached_ops", "hits", atoll (fields[2]));
  403. hits = atof (fields[2]);
  404. }
  405. else if (FIELD_IS ("get_misses"))
  406. {
  407. submit_counter ("memcached_ops", "misses", atoll (fields[2]));
  408. }
  409. else if (FIELD_IS ("evictions"))
  410. {
  411. submit_counter ("memcached_ops", "evictions", atoll (fields[2]));
  412. }
  413. /*
  414. * Network traffic
  415. */
  416. else if (FIELD_IS ("bytes_read"))
  417. {
  418. octets_rx = atoll (fields[2]);
  419. }
  420. else if (FIELD_IS ("bytes_written"))
  421. {
  422. octets_tx = atoll (fields[2]);
  423. }
  424. } /* while ((line = strtok_r (ptr, "\n\r", &saveptr)) != NULL) */
  425. if (!isnan (bytes_used) && !isnan (bytes_total) && (bytes_used <= bytes_total))
  426. submit_gauge2 ("df", "cache", bytes_used, bytes_total - bytes_used);
  427. if ((rusage_user != 0) || (rusage_syst != 0))
  428. submit_counter2 ("ps_cputime", NULL, rusage_user, rusage_syst);
  429. if ((octets_rx != 0) || (octets_tx != 0))
  430. submit_counter2 ("memcached_octets", NULL, octets_rx, octets_tx);
  431. if (!isnan (gets) && !isnan (hits))
  432. {
  433. gauge_t rate = NAN;
  434. if (gets != 0.0)
  435. rate = 100.0 * hits / gets;
  436. submit_gauge ("percent", "hitratio", rate);
  437. }
  438. return 0;
  439. }
  440. /* }}} */
  441. void module_register (void) /* {{{ */
  442. {
  443. plugin_register_config ("memcached", memcached_config, config_keys, config_keys_num);
  444. plugin_register_read ("memcached", memcached_read);
  445. }
  446. /* }}} */
  447. /*
  448. * Local variables:
  449. * tab-width: 4
  450. * c-basic-offset: 4
  451. * End:
  452. * vim600: sw=4 ts=4 fdm=marker noexpandtab
  453. * vim<600: sw=4 ts=4 noexpandtab
  454. */