libcollectdclient: Include "config.h" unconditionally.
[collectd.git] / src / libcollectdclient / client.c
1 /**
2  * libcollectdclient - src/libcollectdclient/client.c
3  * Copyright (C) 2008-2012  Florian octo Forster
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  *
23  * Authors:
24  *   Florian octo Forster <octo at collectd.org>
25  **/
26
27 #include "config.h"
28
29 #if !defined(__GNUC__) || !__GNUC__
30 #define __attribute__(x) /**/
31 #endif
32
33 #include "collectd/lcc_features.h"
34
35 #include <assert.h>
36 #include <errno.h>
37 #include <math.h>
38 #include <netdb.h>
39 #include <stdarg.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <sys/socket.h>
44 #include <sys/types.h>
45 #include <sys/un.h>
46 #include <unistd.h>
47
48 #include "collectd/client.h"
49
50 /* NI_MAXHOST has been obsoleted by RFC 3493 which is a reason for SunOS 5.11
51  * to no longer define it. We'll use the old, RFC 2553 value here. */
52 #ifndef NI_MAXHOST
53 #define NI_MAXHOST 1025
54 #endif
55
56 /* OpenBSD doesn't have EPROTO, FreeBSD doesn't have EILSEQ. Oh what joy! */
57 #ifndef EILSEQ
58 #ifdef EPROTO
59 #define EILSEQ EPROTO
60 #else
61 #define EILSEQ EINVAL
62 #endif
63 #endif
64
65 /* Secure/static macros. They work like `strcpy' and `strcat', but assure null
66  * termination. They work for static buffers only, because they use `sizeof'.
67  * The `SSTRCATF' combines the functionality of `snprintf' and `strcat' which
68  * is very useful to add formatted stuff to the end of a buffer. */
69 #define SSTRCPY(d, s)                                                          \
70   do {                                                                         \
71     strncpy((d), (s), sizeof(d));                                              \
72     (d)[sizeof(d) - 1] = 0;                                                    \
73   } while (0)
74
75 #define SSTRCAT(d, s)                                                          \
76   do {                                                                         \
77     size_t _l = strlen(d);                                                     \
78     strncpy((d) + _l, (s), sizeof(d) - _l);                                    \
79     (d)[sizeof(d) - 1] = 0;                                                    \
80   } while (0)
81
82 #define SSTRCATF(d, ...)                                                       \
83   do {                                                                         \
84     char _b[sizeof(d)];                                                        \
85     snprintf(_b, sizeof(_b), __VA_ARGS__);                                     \
86     _b[sizeof(_b) - 1] = 0;                                                    \
87     SSTRCAT((d), _b);                                                          \
88   } while (0)
89
90 #define LCC_SET_ERRSTR(c, ...)                                                 \
91   do {                                                                         \
92     snprintf((c)->errbuf, sizeof((c)->errbuf), __VA_ARGS__);                   \
93     (c)->errbuf[sizeof((c)->errbuf) - 1] = 0;                                  \
94   } while (0)
95
96 /*
97  * Types
98  */
99 struct lcc_connection_s {
100   FILE *fh;
101   char errbuf[1024];
102 };
103
104 struct lcc_response_s {
105   int status;
106   char message[1024];
107   char **lines;
108   size_t lines_num;
109 };
110 typedef struct lcc_response_s lcc_response_t;
111
112 /*
113  * Private functions
114  */
115 __attribute__((format(printf, 1, 0))) static int lcc_tracef(char const *format,
116                                                             ...) {
117   va_list ap;
118   int status;
119
120   char const *trace = getenv(LCC_TRACE_ENV);
121   if (!trace || (strcmp("", trace) == 0) || (strcmp("0", trace) == 0))
122     return 0;
123
124   va_start(ap, format);
125   status = vprintf(format, ap);
126   va_end(ap);
127
128   return status;
129 }
130
131 /* Even though Posix requires "strerror_r" to return an "int",
132  * some systems (e.g. the GNU libc) return a "char *" _and_
133  * ignore the second argument ... -tokkee */
134 static char *sstrerror(int errnum, char *buf, size_t buflen) {
135   buf[0] = 0;
136
137 #if !HAVE_STRERROR_R
138   snprintf(buf, buflen, "Error #%i; strerror_r is not available.", errnum);
139 /* #endif !HAVE_STRERROR_R */
140
141 #elif STRERROR_R_CHAR_P
142   {
143     char *temp;
144     temp = strerror_r(errnum, buf, buflen);
145     if (buf[0] == 0) {
146       if ((temp != NULL) && (temp != buf) && (temp[0] != 0))
147         strncpy(buf, temp, buflen);
148       else
149         strncpy(buf, "strerror_r did not return "
150                      "an error message",
151                 buflen);
152     }
153   }
154 /* #endif STRERROR_R_CHAR_P */
155
156 #else
157   if (strerror_r(errnum, buf, buflen) != 0) {
158     snprintf(buf, buflen, "Error #%i; "
159                           "Additionally, strerror_r failed.",
160              errnum);
161   }
162 #endif /* STRERROR_R_CHAR_P */
163
164   buf[buflen - 1] = 0;
165
166   return (buf);
167 } /* char *sstrerror */
168
169 static int lcc_set_errno(lcc_connection_t *c, int err) /* {{{ */
170 {
171   if (c == NULL)
172     return (-1);
173
174   sstrerror(err, c->errbuf, sizeof(c->errbuf));
175   c->errbuf[sizeof(c->errbuf) - 1] = 0;
176
177   return (0);
178 } /* }}} int lcc_set_errno */
179
180 static char *lcc_strescape(char *dest, const char *src,
181                            size_t dest_size) /* {{{ */
182 {
183   size_t dest_pos;
184   size_t src_pos;
185
186   if ((dest == NULL) || (src == NULL))
187     return (NULL);
188
189   dest_pos = 0;
190   src_pos = 0;
191
192   assert(dest_size >= 3);
193
194   dest[dest_pos] = '"';
195   dest_pos++;
196
197   while (42) {
198     if ((dest_pos == (dest_size - 2)) || (src[src_pos] == 0))
199       break;
200
201     if ((src[src_pos] == '"') || (src[src_pos] == '\\')) {
202       /* Check if there is enough space for both characters.. */
203       if (dest_pos == (dest_size - 3))
204         break;
205
206       dest[dest_pos] = '\\';
207       dest_pos++;
208     }
209
210     dest[dest_pos] = src[src_pos];
211     dest_pos++;
212     src_pos++;
213   }
214
215   assert(dest_pos <= (dest_size - 2));
216
217   dest[dest_pos] = '"';
218   dest_pos++;
219
220   dest[dest_pos] = 0;
221   dest_pos++;
222   src_pos++;
223
224   return (dest);
225 } /* }}} char *lcc_strescape */
226
227 /* lcc_chomp: Removes all control-characters at the end of a string. */
228 static void lcc_chomp(char *str) /* {{{ */
229 {
230   size_t str_len;
231
232   str_len = strlen(str);
233   while (str_len > 0) {
234     if (str[str_len - 1] >= 32)
235       break;
236     str[str_len - 1] = 0;
237     str_len--;
238   }
239 } /* }}} void lcc_chomp */
240
241 static void lcc_response_free(lcc_response_t *res) /* {{{ */
242 {
243   if (res == NULL)
244     return;
245
246   for (size_t i = 0; i < res->lines_num; i++)
247     free(res->lines[i]);
248   free(res->lines);
249   res->lines = NULL;
250 } /* }}} void lcc_response_free */
251
252 static int lcc_send(lcc_connection_t *c, const char *command) /* {{{ */
253 {
254   int status;
255
256   lcc_tracef("send:    --> %s\n", command);
257
258   status = fprintf(c->fh, "%s\r\n", command);
259   if (status < 0) {
260     lcc_set_errno(c, errno);
261     return (-1);
262   }
263   fflush(c->fh);
264
265   return (0);
266 } /* }}} int lcc_send */
267
268 static int lcc_receive(lcc_connection_t *c, /* {{{ */
269                        lcc_response_t *ret_res) {
270   lcc_response_t res = {0};
271   char *ptr;
272   char buffer[4096];
273   size_t i;
274
275   /* Read the first line, containing the status and a message */
276   ptr = fgets(buffer, sizeof(buffer), c->fh);
277   if (ptr == NULL) {
278     lcc_set_errno(c, errno);
279     return (-1);
280   }
281   lcc_chomp(buffer);
282   lcc_tracef("receive: <-- %s\n", buffer);
283
284   /* Convert the leading status to an integer and make `ptr' to point to the
285    * beginning of the message. */
286   ptr = NULL;
287   errno = 0;
288   res.status = (int)strtol(buffer, &ptr, 0);
289   if ((errno != 0) || (ptr == &buffer[0])) {
290     lcc_set_errno(c, errno);
291     return (-1);
292   }
293
294   /* Skip white spaces after the status number */
295   while ((*ptr == ' ') || (*ptr == '\t'))
296     ptr++;
297
298   /* Now copy the message. */
299   strncpy(res.message, ptr, sizeof(res.message));
300   res.message[sizeof(res.message) - 1] = 0;
301
302   /* Error or no lines follow: We're done. */
303   if (res.status <= 0) {
304     memcpy(ret_res, &res, sizeof(res));
305     return (0);
306   }
307
308   /* Allocate space for the char-pointers */
309   res.lines_num = (size_t)res.status;
310   res.status = 0;
311   res.lines = malloc(res.lines_num * sizeof(*res.lines));
312   if (res.lines == NULL) {
313     lcc_set_errno(c, ENOMEM);
314     return (-1);
315   }
316
317   /* Now receive all the lines */
318   for (i = 0; i < res.lines_num; i++) {
319     ptr = fgets(buffer, sizeof(buffer), c->fh);
320     if (ptr == NULL) {
321       lcc_set_errno(c, errno);
322       break;
323     }
324     lcc_chomp(buffer);
325     lcc_tracef("receive: <-- %s\n", buffer);
326
327     res.lines[i] = strdup(buffer);
328     if (res.lines[i] == NULL) {
329       lcc_set_errno(c, ENOMEM);
330       break;
331     }
332   }
333
334   /* Check if the for-loop exited with an error. */
335   if (i < res.lines_num) {
336     while (i > 0) {
337       i--;
338       free(res.lines[i]);
339     }
340     free(res.lines);
341     return (-1);
342   }
343
344   memcpy(ret_res, &res, sizeof(res));
345   return (0);
346 } /* }}} int lcc_receive */
347
348 static int lcc_sendreceive(lcc_connection_t *c, /* {{{ */
349                            const char *command, lcc_response_t *ret_res) {
350   lcc_response_t res = {0};
351   int status;
352
353   if (c->fh == NULL) {
354     lcc_set_errno(c, EBADF);
355     return (-1);
356   }
357
358   status = lcc_send(c, command);
359   if (status != 0)
360     return (status);
361
362   status = lcc_receive(c, &res);
363   if (status == 0)
364     memcpy(ret_res, &res, sizeof(*ret_res));
365
366   return (status);
367 } /* }}} int lcc_sendreceive */
368
369 static int lcc_open_unixsocket(lcc_connection_t *c, const char *path) /* {{{ */
370 {
371   struct sockaddr_un sa = {0};
372   int fd;
373   int status;
374
375   assert(c != NULL);
376   assert(c->fh == NULL);
377   assert(path != NULL);
378
379   /* Don't use PF_UNIX here, because it's broken on Mac OS X (10.4, possibly
380    * others). */
381   fd = socket(AF_UNIX, SOCK_STREAM, /* protocol = */ 0);
382   if (fd < 0) {
383     lcc_set_errno(c, errno);
384     return (-1);
385   }
386
387   sa.sun_family = AF_UNIX;
388   strncpy(sa.sun_path, path, sizeof(sa.sun_path) - 1);
389
390   status = connect(fd, (struct sockaddr *)&sa, sizeof(sa));
391   if (status != 0) {
392     lcc_set_errno(c, errno);
393     close(fd);
394     return (-1);
395   }
396
397   c->fh = fdopen(fd, "r+");
398   if (c->fh == NULL) {
399     lcc_set_errno(c, errno);
400     close(fd);
401     return (-1);
402   }
403
404   return (0);
405 } /* }}} int lcc_open_unixsocket */
406
407 static int lcc_open_netsocket(lcc_connection_t *c, /* {{{ */
408                               const char *addr_orig) {
409   struct addrinfo *ai_res;
410   char addr_copy[NI_MAXHOST];
411   char *addr;
412   char *port;
413   int fd;
414   int status;
415
416   assert(c != NULL);
417   assert(c->fh == NULL);
418   assert(addr_orig != NULL);
419
420   strncpy(addr_copy, addr_orig, sizeof(addr_copy));
421   addr_copy[sizeof(addr_copy) - 1] = '\0';
422   addr = addr_copy;
423
424   port = NULL;
425   if (*addr == '[') /* IPv6+port format */
426   {
427     /* `addr' is something like "[2001:780:104:2:211:24ff:feab:26f8]:12345" */
428     addr++;
429
430     port = strchr(addr, ']');
431     if (port == NULL) {
432       LCC_SET_ERRSTR(c, "malformed address: %s", addr_orig);
433       return (-1);
434     }
435     *port = 0;
436     port++;
437
438     if (*port == ':')
439       port++;
440     else if (*port == 0)
441       port = NULL;
442     else {
443       LCC_SET_ERRSTR(c, "garbage after address: %s", port);
444       return (-1);
445     }
446   }                                   /* if (*addr = ']') */
447   else if (strchr(addr, '.') != NULL) /* Hostname or IPv4 */
448   {
449     port = strrchr(addr, ':');
450     if (port != NULL) {
451       *port = 0;
452       port++;
453     }
454   }
455
456   struct addrinfo ai_hints = {.ai_family = AF_UNSPEC,
457                               .ai_flags = AI_ADDRCONFIG,
458                               .ai_socktype = SOCK_STREAM};
459
460   status = getaddrinfo(addr, port == NULL ? LCC_DEFAULT_PORT : port, &ai_hints,
461                        &ai_res);
462   if (status != 0) {
463     LCC_SET_ERRSTR(c, "getaddrinfo: %s", gai_strerror(status));
464     return (-1);
465   }
466
467   for (struct addrinfo *ai_ptr = ai_res; ai_ptr != NULL;
468        ai_ptr = ai_ptr->ai_next) {
469     fd = socket(ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
470     if (fd < 0) {
471       status = errno;
472       continue;
473     }
474
475     status = connect(fd, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
476     if (status != 0) {
477       status = errno;
478       close(fd);
479       continue;
480     }
481
482     c->fh = fdopen(fd, "r+");
483     if (c->fh == NULL) {
484       status = errno;
485       close(fd);
486       continue;
487     }
488
489     assert(status == 0);
490     break;
491   } /* for (ai_ptr) */
492
493   if (status != 0) {
494     lcc_set_errno(c, status);
495     freeaddrinfo(ai_res);
496     return (-1);
497   }
498
499   freeaddrinfo(ai_res);
500   return (0);
501 } /* }}} int lcc_open_netsocket */
502
503 static int lcc_open_socket(lcc_connection_t *c, const char *addr) /* {{{ */
504 {
505   int status = 0;
506
507   if (addr == NULL)
508     return (-1);
509
510   assert(c != NULL);
511   assert(c->fh == NULL);
512   assert(addr != NULL);
513
514   if (strncmp("unix:", addr, strlen("unix:")) == 0)
515     status = lcc_open_unixsocket(c, addr + strlen("unix:"));
516   else if (addr[0] == '/')
517     status = lcc_open_unixsocket(c, addr);
518   else
519     status = lcc_open_netsocket(c, addr);
520
521   return (status);
522 } /* }}} int lcc_open_socket */
523
524 /*
525  * Public functions
526  */
527 unsigned int lcc_version(void) /* {{{ */
528 {
529   return (LCC_VERSION);
530 } /* }}} unsigned int lcc_version */
531
532 const char *lcc_version_string(void) /* {{{ */
533 {
534   return (LCC_VERSION_STRING);
535 } /* }}} const char *lcc_version_string */
536
537 const char *lcc_version_extra(void) /* {{{ */
538 {
539   return (LCC_VERSION_EXTRA);
540 } /* }}} const char *lcc_version_extra */
541
542 int lcc_connect(const char *address, lcc_connection_t **ret_con) /* {{{ */
543 {
544   lcc_connection_t *c;
545   int status;
546
547   if (address == NULL)
548     return (-1);
549
550   if (ret_con == NULL)
551     return (-1);
552
553   c = calloc(1, sizeof(*c));
554   if (c == NULL)
555     return (-1);
556
557   status = lcc_open_socket(c, address);
558   if (status != 0) {
559     lcc_disconnect(c);
560     return (status);
561   }
562
563   *ret_con = c;
564   return (0);
565 } /* }}} int lcc_connect */
566
567 int lcc_disconnect(lcc_connection_t *c) /* {{{ */
568 {
569   if (c == NULL)
570     return (-1);
571
572   if (c->fh != NULL) {
573     fclose(c->fh);
574     c->fh = NULL;
575   }
576
577   free(c);
578   return (0);
579 } /* }}} int lcc_disconnect */
580
581 int lcc_getval(lcc_connection_t *c, lcc_identifier_t *ident, /* {{{ */
582                size_t *ret_values_num, gauge_t **ret_values,
583                char ***ret_values_names) {
584   char ident_str[6 * LCC_NAME_LEN];
585   char ident_esc[12 * LCC_NAME_LEN];
586   char command[14 * LCC_NAME_LEN];
587
588   lcc_response_t res;
589   size_t values_num;
590   gauge_t *values = NULL;
591   char **values_names = NULL;
592
593   size_t i;
594   int status;
595
596   if (c == NULL)
597     return (-1);
598
599   if (ident == NULL) {
600     lcc_set_errno(c, EINVAL);
601     return (-1);
602   }
603
604   /* Build a commend with an escaped version of the identifier string. */
605   status = lcc_identifier_to_string(c, ident_str, sizeof(ident_str), ident);
606   if (status != 0)
607     return (status);
608
609   snprintf(command, sizeof(command), "GETVAL %s",
610            lcc_strescape(ident_esc, ident_str, sizeof(ident_esc)));
611   command[sizeof(command) - 1] = 0;
612
613   /* Send talk to the daemon.. */
614   status = lcc_sendreceive(c, command, &res);
615   if (status != 0)
616     return (status);
617
618   if (res.status != 0) {
619     LCC_SET_ERRSTR(c, "Server error: %s", res.message);
620     lcc_response_free(&res);
621     return (-1);
622   }
623
624   values_num = res.lines_num;
625
626 #define BAIL_OUT(e)                                                            \
627   do {                                                                         \
628     lcc_set_errno(c, (e));                                                     \
629     free(values);                                                              \
630     if (values_names != NULL) {                                                \
631       for (i = 0; i < values_num; i++) {                                       \
632         free(values_names[i]);                                                 \
633       }                                                                        \
634     }                                                                          \
635     free(values_names);                                                        \
636     lcc_response_free(&res);                                                   \
637     return (-1);                                                               \
638   } while (0)
639
640   /* If neither the values nor the names are requested, return here.. */
641   if ((ret_values == NULL) && (ret_values_names == NULL)) {
642     if (ret_values_num != NULL)
643       *ret_values_num = values_num;
644     lcc_response_free(&res);
645     return (0);
646   }
647
648   /* Allocate space for the values */
649   if (ret_values != NULL) {
650     values = malloc(values_num * sizeof(*values));
651     if (values == NULL)
652       BAIL_OUT(ENOMEM);
653   }
654
655   if (ret_values_names != NULL) {
656     values_names = calloc(values_num, sizeof(*values_names));
657     if (values_names == NULL)
658       BAIL_OUT(ENOMEM);
659   }
660
661   for (i = 0; i < res.lines_num; i++) {
662     char *key;
663     char *value;
664     char *endptr;
665
666     key = res.lines[i];
667     value = strchr(key, '=');
668     if (value == NULL)
669       BAIL_OUT(EILSEQ);
670
671     *value = 0;
672     value++;
673
674     if (values != NULL) {
675       endptr = NULL;
676       errno = 0;
677       values[i] = strtod(value, &endptr);
678
679       if ((endptr == value) || (errno != 0))
680         BAIL_OUT(errno);
681     }
682
683     if (values_names != NULL) {
684       values_names[i] = strdup(key);
685       if (values_names[i] == NULL)
686         BAIL_OUT(ENOMEM);
687     }
688   } /* for (i = 0; i < res.lines_num; i++) */
689
690   if (ret_values_num != NULL)
691     *ret_values_num = values_num;
692   if (ret_values != NULL)
693     *ret_values = values;
694   if (ret_values_names != NULL)
695     *ret_values_names = values_names;
696
697   lcc_response_free(&res);
698
699   return (0);
700 } /* }}} int lcc_getval */
701
702 int lcc_putval(lcc_connection_t *c, const lcc_value_list_t *vl) /* {{{ */
703 {
704   char ident_str[6 * LCC_NAME_LEN];
705   char ident_esc[12 * LCC_NAME_LEN];
706   char command[1024] = "";
707   lcc_response_t res;
708   int status;
709
710   if ((c == NULL) || (vl == NULL) || (vl->values_len < 1) ||
711       (vl->values == NULL) || (vl->values_types == NULL)) {
712     lcc_set_errno(c, EINVAL);
713     return (-1);
714   }
715
716   status = lcc_identifier_to_string(c, ident_str, sizeof(ident_str),
717                                     &vl->identifier);
718   if (status != 0)
719     return (status);
720
721   SSTRCATF(command, "PUTVAL %s",
722            lcc_strescape(ident_esc, ident_str, sizeof(ident_esc)));
723
724   if (vl->interval > 0.0)
725     SSTRCATF(command, " interval=%.3f", vl->interval);
726
727   if (vl->time > 0.0)
728     SSTRCATF(command, " %.3f", vl->time);
729   else
730     SSTRCAT(command, " N");
731
732   for (size_t i = 0; i < vl->values_len; i++) {
733     if (vl->values_types[i] == LCC_TYPE_COUNTER)
734       SSTRCATF(command, ":%" PRIu64, vl->values[i].counter);
735     else if (vl->values_types[i] == LCC_TYPE_GAUGE) {
736       if (isnan(vl->values[i].gauge))
737         SSTRCATF(command, ":U");
738       else
739         SSTRCATF(command, ":%g", vl->values[i].gauge);
740     } else if (vl->values_types[i] == LCC_TYPE_DERIVE)
741       SSTRCATF(command, ":%" PRIu64, vl->values[i].derive);
742     else if (vl->values_types[i] == LCC_TYPE_ABSOLUTE)
743       SSTRCATF(command, ":%" PRIu64, vl->values[i].absolute);
744
745   } /* for (i = 0; i < vl->values_len; i++) */
746
747   status = lcc_sendreceive(c, command, &res);
748   if (status != 0)
749     return (status);
750
751   if (res.status != 0) {
752     LCC_SET_ERRSTR(c, "Server error: %s", res.message);
753     lcc_response_free(&res);
754     return (-1);
755   }
756
757   lcc_response_free(&res);
758   return (0);
759 } /* }}} int lcc_putval */
760
761 int lcc_flush(lcc_connection_t *c, const char *plugin, /* {{{ */
762               lcc_identifier_t *ident, int timeout) {
763   char command[1024] = "";
764   lcc_response_t res;
765   int status;
766
767   if (c == NULL) {
768     lcc_set_errno(c, EINVAL);
769     return (-1);
770   }
771
772   SSTRCPY(command, "FLUSH");
773
774   if (timeout > 0)
775     SSTRCATF(command, " timeout=%i", timeout);
776
777   if (plugin != NULL) {
778     char buffer[2 * LCC_NAME_LEN];
779     SSTRCATF(command, " plugin=%s",
780              lcc_strescape(buffer, plugin, sizeof(buffer)));
781   }
782
783   if (ident != NULL) {
784     char ident_str[6 * LCC_NAME_LEN];
785     char ident_esc[12 * LCC_NAME_LEN];
786
787     status = lcc_identifier_to_string(c, ident_str, sizeof(ident_str), ident);
788     if (status != 0)
789       return (status);
790
791     SSTRCATF(command, " identifier=%s",
792              lcc_strescape(ident_esc, ident_str, sizeof(ident_esc)));
793   }
794
795   status = lcc_sendreceive(c, command, &res);
796   if (status != 0)
797     return (status);
798
799   if (res.status != 0) {
800     LCC_SET_ERRSTR(c, "Server error: %s", res.message);
801     lcc_response_free(&res);
802     return (-1);
803   }
804
805   lcc_response_free(&res);
806   return (0);
807 } /* }}} int lcc_flush */
808
809 /* TODO: Implement lcc_putnotif */
810
811 int lcc_listval(lcc_connection_t *c, /* {{{ */
812                 lcc_identifier_t **ret_ident, size_t *ret_ident_num) {
813   lcc_response_t res;
814   int status;
815
816   lcc_identifier_t *ident;
817   size_t ident_num;
818
819   if (c == NULL)
820     return (-1);
821
822   if ((ret_ident == NULL) || (ret_ident_num == NULL)) {
823     lcc_set_errno(c, EINVAL);
824     return (-1);
825   }
826
827   status = lcc_sendreceive(c, "LISTVAL", &res);
828   if (status != 0)
829     return (status);
830
831   if (res.status != 0) {
832     LCC_SET_ERRSTR(c, "Server error: %s", res.message);
833     lcc_response_free(&res);
834     return (-1);
835   }
836
837   ident_num = res.lines_num;
838   ident = malloc(ident_num * sizeof(*ident));
839   if (ident == NULL) {
840     lcc_response_free(&res);
841     lcc_set_errno(c, ENOMEM);
842     return (-1);
843   }
844
845   for (size_t i = 0; i < res.lines_num; i++) {
846     char *time_str;
847     char *ident_str;
848
849     /* First field is the time. */
850     time_str = res.lines[i];
851
852     /* Set `ident_str' to the beginning of the second field. */
853     ident_str = time_str;
854     while ((*ident_str != ' ') && (*ident_str != '\t') && (*ident_str != 0))
855       ident_str++;
856     while ((*ident_str == ' ') || (*ident_str == '\t')) {
857       *ident_str = 0;
858       ident_str++;
859     }
860
861     if (*ident_str == 0) {
862       lcc_set_errno(c, EILSEQ);
863       status = -1;
864       break;
865     }
866
867     status = lcc_string_to_identifier(c, ident + i, ident_str);
868     if (status != 0)
869       break;
870   }
871
872   lcc_response_free(&res);
873
874   if (status != 0) {
875     free(ident);
876     return (-1);
877   }
878
879   *ret_ident = ident;
880   *ret_ident_num = ident_num;
881
882   return (0);
883 } /* }}} int lcc_listval */
884
885 const char *lcc_strerror(lcc_connection_t *c) /* {{{ */
886 {
887   if (c == NULL)
888     return ("Invalid object");
889   return (c->errbuf);
890 } /* }}} const char *lcc_strerror */
891
892 int lcc_identifier_to_string(lcc_connection_t *c, /* {{{ */
893                              char *string, size_t string_size,
894                              const lcc_identifier_t *ident) {
895   if ((string == NULL) || (string_size < 6) || (ident == NULL)) {
896     lcc_set_errno(c, EINVAL);
897     return (-1);
898   }
899
900   if (ident->plugin_instance[0] == 0) {
901     if (ident->type_instance[0] == 0)
902       snprintf(string, string_size, "%s/%s/%s", ident->host, ident->plugin,
903                ident->type);
904     else
905       snprintf(string, string_size, "%s/%s/%s-%s", ident->host, ident->plugin,
906                ident->type, ident->type_instance);
907   } else {
908     if (ident->type_instance[0] == 0)
909       snprintf(string, string_size, "%s/%s-%s/%s", ident->host, ident->plugin,
910                ident->plugin_instance, ident->type);
911     else
912       snprintf(string, string_size, "%s/%s-%s/%s-%s", ident->host,
913                ident->plugin, ident->plugin_instance, ident->type,
914                ident->type_instance);
915   }
916
917   string[string_size - 1] = 0;
918   return (0);
919 } /* }}} int lcc_identifier_to_string */
920
921 int lcc_string_to_identifier(lcc_connection_t *c, /* {{{ */
922                              lcc_identifier_t *ident, const char *string) {
923   char *string_copy;
924   char *host;
925   char *plugin;
926   char *plugin_instance;
927   char *type;
928   char *type_instance;
929
930   string_copy = strdup(string);
931   if (string_copy == NULL) {
932     lcc_set_errno(c, ENOMEM);
933     return (-1);
934   }
935
936   host = string_copy;
937   plugin = strchr(host, '/');
938   if (plugin == NULL) {
939     LCC_SET_ERRSTR(c, "Malformed identifier string: %s", string);
940     free(string_copy);
941     return (-1);
942   }
943   *plugin = 0;
944   plugin++;
945
946   type = strchr(plugin, '/');
947   if (type == NULL) {
948     LCC_SET_ERRSTR(c, "Malformed identifier string: %s", string);
949     free(string_copy);
950     return (-1);
951   }
952   *type = 0;
953   type++;
954
955   plugin_instance = strchr(plugin, '-');
956   if (plugin_instance != NULL) {
957     *plugin_instance = 0;
958     plugin_instance++;
959   }
960
961   type_instance = strchr(type, '-');
962   if (type_instance != NULL) {
963     *type_instance = 0;
964     type_instance++;
965   }
966
967   memset(ident, 0, sizeof(*ident));
968
969   SSTRCPY(ident->host, host);
970   SSTRCPY(ident->plugin, plugin);
971   if (plugin_instance != NULL)
972     SSTRCPY(ident->plugin_instance, plugin_instance);
973   SSTRCPY(ident->type, type);
974   if (type_instance != NULL)
975     SSTRCPY(ident->type_instance, type_instance);
976
977   free(string_copy);
978   return (0);
979 } /* }}} int lcc_string_to_identifier */
980
981 int lcc_identifier_compare(const void *a, /* {{{ */
982                            const void *b) {
983   const lcc_identifier_t *i0 = a;
984   const lcc_identifier_t *i1 = b;
985   int status;
986
987   if ((i0 == NULL) && (i1 == NULL))
988     return (0);
989   else if (i0 == NULL)
990     return (-1);
991   else if (i1 == NULL)
992     return (1);
993
994 #define CMP_FIELD(f)                                                           \
995   do {                                                                         \
996     status = strcmp(i0->f, i1->f);                                             \
997     if (status != 0)                                                           \
998       return (status);                                                         \
999   } while (0);
1000
1001   CMP_FIELD(host);
1002   CMP_FIELD(plugin);
1003   CMP_FIELD(plugin_instance);
1004   CMP_FIELD(type);
1005   CMP_FIELD(type_instance);
1006
1007 #undef CMP_FIELD
1008
1009   return (0);
1010 } /* }}} int lcc_identifier_compare */
1011
1012 int lcc_sort_identifiers(lcc_connection_t *c, /* {{{ */
1013                          lcc_identifier_t *idents, size_t idents_num) {
1014   if (idents == NULL) {
1015     lcc_set_errno(c, EINVAL);
1016     return (-1);
1017   }
1018
1019   qsort(idents, idents_num, sizeof(*idents), lcc_identifier_compare);
1020   return (0);
1021 } /* }}} int lcc_sort_identifiers */